Add titles for token lending instructions (#15217)
* feat: add lending instruction names * chore: capitalize words
This commit is contained in:
		
							
								
								
									
										1
									
								
								explorer/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								explorer/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,6 +10,7 @@
 | 
			
		||||
 | 
			
		||||
# production
 | 
			
		||||
/build
 | 
			
		||||
/wasm/target
 | 
			
		||||
 | 
			
		||||
# misc
 | 
			
		||||
.DS_Store
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,10 @@ import Select, { InputActionMeta, ActionMeta, ValueType } from "react-select";
 | 
			
		||||
import StateManager from "react-select";
 | 
			
		||||
import {
 | 
			
		||||
  LOADER_IDS,
 | 
			
		||||
  PROGRAM_IDS,
 | 
			
		||||
  PROGRAM_NAME_BY_ID,
 | 
			
		||||
  SYSVAR_IDS,
 | 
			
		||||
  ProgramName,
 | 
			
		||||
  LoaderName,
 | 
			
		||||
  SEARCHABLE_PROGRAMS,
 | 
			
		||||
} from "utils/tx";
 | 
			
		||||
import { TokenRegistry } from "tokenRegistry";
 | 
			
		||||
import { Cluster, useCluster } from "providers/cluster";
 | 
			
		||||
@@ -64,18 +64,8 @@ export function SearchBar() {
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SEARCHABLE_PROGRAMS: ProgramName[] = [
 | 
			
		||||
  "Break Solana Program",
 | 
			
		||||
  "Config Program",
 | 
			
		||||
  "Stake Program",
 | 
			
		||||
  "System Program",
 | 
			
		||||
  "Vote Program",
 | 
			
		||||
  "SPL Token Program",
 | 
			
		||||
  "Memo Program",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function buildProgramOptions(search: string) {
 | 
			
		||||
  const matchedPrograms = Object.entries(PROGRAM_IDS).filter(
 | 
			
		||||
  const matchedPrograms = Object.entries(PROGRAM_NAME_BY_ID).filter(
 | 
			
		||||
    ([address, name]) => {
 | 
			
		||||
      return (
 | 
			
		||||
        SEARCHABLE_PROGRAMS.includes(name) &&
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,10 @@ import {
 | 
			
		||||
  isTokenSwapInstruction,
 | 
			
		||||
  parseTokenSwapInstructionTitle,
 | 
			
		||||
} from "components/instruction/token-swap/types";
 | 
			
		||||
import {
 | 
			
		||||
  isTokenLendingInstruction,
 | 
			
		||||
  parseTokenLendingInstructionTitle,
 | 
			
		||||
} from "components/instruction/token-lending/types";
 | 
			
		||||
import {
 | 
			
		||||
  isSerumInstruction,
 | 
			
		||||
  parseSerumInstructionTitle,
 | 
			
		||||
@@ -489,6 +493,16 @@ const TokenTransactionRow = React.memo(
 | 
			
		||||
              reportError(error, { signature: tx.signature });
 | 
			
		||||
              return undefined;
 | 
			
		||||
            }
 | 
			
		||||
          } else if (
 | 
			
		||||
            transactionInstruction &&
 | 
			
		||||
            isTokenLendingInstruction(transactionInstruction)
 | 
			
		||||
          ) {
 | 
			
		||||
            try {
 | 
			
		||||
              name = parseTokenLendingInstructionTitle(transactionInstruction);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
              reportError(error, { signature: tx.signature });
 | 
			
		||||
              return undefined;
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            if (
 | 
			
		||||
              ix.accounts.findIndex((account) =>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { TransactionInstruction, SignatureResult } from "@solana/web3.js";
 | 
			
		||||
import { InstructionCard } from "./InstructionCard";
 | 
			
		||||
import { useCluster } from "providers/cluster";
 | 
			
		||||
import { reportError } from "utils/sentry";
 | 
			
		||||
import { parseTokenLendingInstructionTitle } from "./token-lending/types";
 | 
			
		||||
 | 
			
		||||
export function TokenLendingDetailsCard({
 | 
			
		||||
  ix,
 | 
			
		||||
  index,
 | 
			
		||||
  result,
 | 
			
		||||
  signature,
 | 
			
		||||
  innerCards,
 | 
			
		||||
  childIndex,
 | 
			
		||||
}: {
 | 
			
		||||
  ix: TransactionInstruction;
 | 
			
		||||
  index: number;
 | 
			
		||||
  result: SignatureResult;
 | 
			
		||||
  signature: string;
 | 
			
		||||
  innerCards?: JSX.Element[];
 | 
			
		||||
  childIndex?: number;
 | 
			
		||||
}) {
 | 
			
		||||
  const { url } = useCluster();
 | 
			
		||||
 | 
			
		||||
  let title;
 | 
			
		||||
  try {
 | 
			
		||||
    title = parseTokenLendingInstructionTitle(ix);
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    reportError(error, {
 | 
			
		||||
      url: url,
 | 
			
		||||
      signature: signature,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <InstructionCard
 | 
			
		||||
      ix={ix}
 | 
			
		||||
      index={index}
 | 
			
		||||
      result={result}
 | 
			
		||||
      title={`Token Lending: ${title || "Unknown"}`}
 | 
			
		||||
      innerCards={innerCards}
 | 
			
		||||
      childIndex={childIndex}
 | 
			
		||||
      defaultRaw
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								explorer/src/components/instruction/token-lending/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								explorer/src/components/instruction/token-lending/types.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import { TransactionInstruction } from "@solana/web3.js";
 | 
			
		||||
 | 
			
		||||
export const PROGRAM_IDS: string[] = [
 | 
			
		||||
  "LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi", // mainnet / testnet / devnet
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const INSTRUCTION_LOOKUP: { [key: number]: string } = {
 | 
			
		||||
  0: "Initialize Lending Market",
 | 
			
		||||
  1: "Initialize Reserve",
 | 
			
		||||
  2: "Initialize Obligation",
 | 
			
		||||
  3: "Reserve Deposit",
 | 
			
		||||
  4: "Reserve Withdraw",
 | 
			
		||||
  5: "Borrow",
 | 
			
		||||
  6: "Repay Loan",
 | 
			
		||||
  7: "Liquidate Loan",
 | 
			
		||||
  8: "Accrue Interest",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function isTokenLendingInstruction(
 | 
			
		||||
  instruction: TransactionInstruction
 | 
			
		||||
): boolean {
 | 
			
		||||
  return PROGRAM_IDS.includes(instruction.programId.toBase58());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function parseTokenLendingInstructionTitle(
 | 
			
		||||
  instruction: TransactionInstruction
 | 
			
		||||
): string {
 | 
			
		||||
  const code = instruction.data[0];
 | 
			
		||||
 | 
			
		||||
  if (!(code in INSTRUCTION_LOOKUP)) {
 | 
			
		||||
    throw new Error(`Unrecognized Token Swap instruction code: ${code}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return INSTRUCTION_LOOKUP[code];
 | 
			
		||||
}
 | 
			
		||||
@@ -37,7 +37,9 @@ import { FetchStatus } from "providers/cache";
 | 
			
		||||
import { SerumDetailsCard } from "components/instruction/SerumDetailsCard";
 | 
			
		||||
import { Slot } from "components/common/Slot";
 | 
			
		||||
import { isTokenSwapInstruction } from "components/instruction/token-swap/types";
 | 
			
		||||
import { isTokenLendingInstruction } from "components/instruction/token-lending/types";
 | 
			
		||||
import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard";
 | 
			
		||||
import { TokenLendingDetailsCard } from "components/instruction/TokenLendingDetailsCard";
 | 
			
		||||
import { isSerumInstruction } from "components/instruction/serum/types";
 | 
			
		||||
import { MemoDetailsCard } from "components/instruction/MemoDetailsCard";
 | 
			
		||||
import { BigNumber } from "bignumber.js";
 | 
			
		||||
@@ -613,6 +615,8 @@ function renderInstructionCard({
 | 
			
		||||
    return <SerumDetailsCard key={key} {...props} />;
 | 
			
		||||
  } else if (isTokenSwapInstruction(transactionIx)) {
 | 
			
		||||
    return <TokenSwapDetailsCard key={key} {...props} />;
 | 
			
		||||
  } else if (isTokenLendingInstruction(transactionIx)) {
 | 
			
		||||
    return <TokenLendingDetailsCard key={key} {...props} />;
 | 
			
		||||
  } else {
 | 
			
		||||
    return <UnknownDetailsCard key={key} {...props} />;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,23 +19,57 @@ import { TokenRegistry } from "tokenRegistry";
 | 
			
		||||
import { Cluster } from "providers/cluster";
 | 
			
		||||
import { SerumMarketRegistry } from "serumMarketRegistry";
 | 
			
		||||
 | 
			
		||||
export type ProgramName = typeof PROGRAM_IDS[keyof typeof PROGRAM_IDS];
 | 
			
		||||
export type ProgramName = typeof PROGRAM_NAME_BY_ID[keyof typeof PROGRAM_NAME_BY_ID];
 | 
			
		||||
 | 
			
		||||
export const PROGRAM_IDS = {
 | 
			
		||||
  BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: "Break Solana Program",
 | 
			
		||||
  Budget1111111111111111111111111111111111111: "Budget Program",
 | 
			
		||||
  Config1111111111111111111111111111111111111: "Config Program",
 | 
			
		||||
  Exchange11111111111111111111111111111111111: "Exchange Program",
 | 
			
		||||
  [StakeProgram.programId.toBase58()]: "Stake Program",
 | 
			
		||||
  Storage111111111111111111111111111111111111: "Storage Program",
 | 
			
		||||
  [SystemProgram.programId.toBase58()]: "System Program",
 | 
			
		||||
  Vest111111111111111111111111111111111111111: "Vest Program",
 | 
			
		||||
  [VOTE_PROGRAM_ID.toBase58()]: "Vote Program",
 | 
			
		||||
  TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: "SPL Token Program",
 | 
			
		||||
  ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL:
 | 
			
		||||
    "SPL Associated Token Account Program",
 | 
			
		||||
  Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo: "Memo Program",
 | 
			
		||||
  SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8: "Token Swap Program",
 | 
			
		||||
export enum PROGRAM_NAMES {
 | 
			
		||||
  BREAK_SOLANA = "Break Solana Program",
 | 
			
		||||
  BUDGET = "Budget Program",
 | 
			
		||||
  CONFIG = "Config Program",
 | 
			
		||||
  EXCHANGE = "Exchange Program",
 | 
			
		||||
  STAKE = "Stake Program",
 | 
			
		||||
  STORAGE = "Storage Program",
 | 
			
		||||
  SYSTEM = "System Program",
 | 
			
		||||
  VEST = "Vest Program",
 | 
			
		||||
  VOTE = "Vote Program",
 | 
			
		||||
  SPL_TOKEN = "SPL Token Program",
 | 
			
		||||
  ASSOCIATED_TOKEN = "SPL Associated Token Program",
 | 
			
		||||
  MEMO = "Memo Program",
 | 
			
		||||
  SWAP = "Swap Program",
 | 
			
		||||
  LENDING = "Lending Program",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SEARCHABLE_PROGRAMS: ProgramName[] = [
 | 
			
		||||
  PROGRAM_NAMES.BREAK_SOLANA,
 | 
			
		||||
  PROGRAM_NAMES.BUDGET,
 | 
			
		||||
  PROGRAM_NAMES.CONFIG,
 | 
			
		||||
  PROGRAM_NAMES.EXCHANGE,
 | 
			
		||||
  PROGRAM_NAMES.STAKE,
 | 
			
		||||
  PROGRAM_NAMES.STORAGE,
 | 
			
		||||
  PROGRAM_NAMES.SYSTEM,
 | 
			
		||||
  PROGRAM_NAMES.VEST,
 | 
			
		||||
  PROGRAM_NAMES.VOTE,
 | 
			
		||||
  PROGRAM_NAMES.SPL_TOKEN,
 | 
			
		||||
  PROGRAM_NAMES.ASSOCIATED_TOKEN,
 | 
			
		||||
  PROGRAM_NAMES.MEMO,
 | 
			
		||||
  PROGRAM_NAMES.SWAP,
 | 
			
		||||
  PROGRAM_NAMES.LENDING,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const PROGRAM_NAME_BY_ID = {
 | 
			
		||||
  BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: PROGRAM_NAMES.BREAK_SOLANA,
 | 
			
		||||
  Budget1111111111111111111111111111111111111: PROGRAM_NAMES.BUDGET,
 | 
			
		||||
  Config1111111111111111111111111111111111111: PROGRAM_NAMES.CONFIG,
 | 
			
		||||
  Exchange11111111111111111111111111111111111: PROGRAM_NAMES.EXCHANGE,
 | 
			
		||||
  [StakeProgram.programId.toBase58()]: PROGRAM_NAMES.STAKE,
 | 
			
		||||
  Storage111111111111111111111111111111111111: PROGRAM_NAMES.STORAGE,
 | 
			
		||||
  [SystemProgram.programId.toBase58()]: PROGRAM_NAMES.SYSTEM,
 | 
			
		||||
  Vest111111111111111111111111111111111111111: PROGRAM_NAMES.VEST,
 | 
			
		||||
  [VOTE_PROGRAM_ID.toBase58()]: PROGRAM_NAMES.VOTE,
 | 
			
		||||
  TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: PROGRAM_NAMES.SPL_TOKEN,
 | 
			
		||||
  ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: PROGRAM_NAMES.ASSOCIATED_TOKEN,
 | 
			
		||||
  Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo: PROGRAM_NAMES.MEMO,
 | 
			
		||||
  SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8: PROGRAM_NAMES.SWAP,
 | 
			
		||||
  LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi: PROGRAM_NAMES.LENDING,
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS];
 | 
			
		||||
@@ -67,7 +101,7 @@ export function addressLabel(
 | 
			
		||||
  cluster: Cluster
 | 
			
		||||
): string | undefined {
 | 
			
		||||
  return (
 | 
			
		||||
    PROGRAM_IDS[address] ||
 | 
			
		||||
    PROGRAM_NAME_BY_ID[address] ||
 | 
			
		||||
    LOADER_IDS[address] ||
 | 
			
		||||
    SYSVAR_IDS[address] ||
 | 
			
		||||
    SYSVAR_ID[address] ||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user