Add titles for token lending instructions (#15217)

* feat: add lending instruction names

* chore: capitalize words
This commit is contained in:
B 2021-02-09 19:50:34 -06:00 committed by GitHub
parent 948819dfa8
commit a0ba59a1ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 30 deletions

1
explorer/.gitignore vendored
View File

@ -10,6 +10,7 @@
# production # production
/build /build
/wasm/target
# misc # misc
.DS_Store .DS_Store

View File

@ -5,10 +5,10 @@ import Select, { InputActionMeta, ActionMeta, ValueType } from "react-select";
import StateManager from "react-select"; import StateManager from "react-select";
import { import {
LOADER_IDS, LOADER_IDS,
PROGRAM_IDS, PROGRAM_NAME_BY_ID,
SYSVAR_IDS, SYSVAR_IDS,
ProgramName,
LoaderName, LoaderName,
SEARCHABLE_PROGRAMS,
} from "utils/tx"; } from "utils/tx";
import { TokenRegistry } from "tokenRegistry"; import { TokenRegistry } from "tokenRegistry";
import { Cluster, useCluster } from "providers/cluster"; 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) { function buildProgramOptions(search: string) {
const matchedPrograms = Object.entries(PROGRAM_IDS).filter( const matchedPrograms = Object.entries(PROGRAM_NAME_BY_ID).filter(
([address, name]) => { ([address, name]) => {
return ( return (
SEARCHABLE_PROGRAMS.includes(name) && SEARCHABLE_PROGRAMS.includes(name) &&

View File

@ -37,6 +37,10 @@ import {
isTokenSwapInstruction, isTokenSwapInstruction,
parseTokenSwapInstructionTitle, parseTokenSwapInstructionTitle,
} from "components/instruction/token-swap/types"; } from "components/instruction/token-swap/types";
import {
isTokenLendingInstruction,
parseTokenLendingInstructionTitle,
} from "components/instruction/token-lending/types";
import { import {
isSerumInstruction, isSerumInstruction,
parseSerumInstructionTitle, parseSerumInstructionTitle,
@ -489,6 +493,16 @@ const TokenTransactionRow = React.memo(
reportError(error, { signature: tx.signature }); reportError(error, { signature: tx.signature });
return undefined; return undefined;
} }
} else if (
transactionInstruction &&
isTokenLendingInstruction(transactionInstruction)
) {
try {
name = parseTokenLendingInstructionTitle(transactionInstruction);
} catch (error) {
reportError(error, { signature: tx.signature });
return undefined;
}
} else { } else {
if ( if (
ix.accounts.findIndex((account) => ix.accounts.findIndex((account) =>

View File

@ -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
/>
);
}

View 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];
}

View File

@ -37,7 +37,9 @@ import { FetchStatus } from "providers/cache";
import { SerumDetailsCard } from "components/instruction/SerumDetailsCard"; import { SerumDetailsCard } from "components/instruction/SerumDetailsCard";
import { Slot } from "components/common/Slot"; import { Slot } from "components/common/Slot";
import { isTokenSwapInstruction } from "components/instruction/token-swap/types"; import { isTokenSwapInstruction } from "components/instruction/token-swap/types";
import { isTokenLendingInstruction } from "components/instruction/token-lending/types";
import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard"; import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard";
import { TokenLendingDetailsCard } from "components/instruction/TokenLendingDetailsCard";
import { isSerumInstruction } from "components/instruction/serum/types"; import { isSerumInstruction } from "components/instruction/serum/types";
import { MemoDetailsCard } from "components/instruction/MemoDetailsCard"; import { MemoDetailsCard } from "components/instruction/MemoDetailsCard";
import { BigNumber } from "bignumber.js"; import { BigNumber } from "bignumber.js";
@ -613,6 +615,8 @@ function renderInstructionCard({
return <SerumDetailsCard key={key} {...props} />; return <SerumDetailsCard key={key} {...props} />;
} else if (isTokenSwapInstruction(transactionIx)) { } else if (isTokenSwapInstruction(transactionIx)) {
return <TokenSwapDetailsCard key={key} {...props} />; return <TokenSwapDetailsCard key={key} {...props} />;
} else if (isTokenLendingInstruction(transactionIx)) {
return <TokenLendingDetailsCard key={key} {...props} />;
} else { } else {
return <UnknownDetailsCard key={key} {...props} />; return <UnknownDetailsCard key={key} {...props} />;
} }

View File

@ -19,23 +19,57 @@ import { TokenRegistry } from "tokenRegistry";
import { Cluster } from "providers/cluster"; import { Cluster } from "providers/cluster";
import { SerumMarketRegistry } from "serumMarketRegistry"; 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 = { export enum PROGRAM_NAMES {
BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: "Break Solana Program", BREAK_SOLANA = "Break Solana Program",
Budget1111111111111111111111111111111111111: "Budget Program", BUDGET = "Budget Program",
Config1111111111111111111111111111111111111: "Config Program", CONFIG = "Config Program",
Exchange11111111111111111111111111111111111: "Exchange Program", EXCHANGE = "Exchange Program",
[StakeProgram.programId.toBase58()]: "Stake Program", STAKE = "Stake Program",
Storage111111111111111111111111111111111111: "Storage Program", STORAGE = "Storage Program",
[SystemProgram.programId.toBase58()]: "System Program", SYSTEM = "System Program",
Vest111111111111111111111111111111111111111: "Vest Program", VEST = "Vest Program",
[VOTE_PROGRAM_ID.toBase58()]: "Vote Program", VOTE = "Vote Program",
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: "SPL Token Program", SPL_TOKEN = "SPL Token Program",
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: ASSOCIATED_TOKEN = "SPL Associated Token Program",
"SPL Associated Token Account Program", MEMO = "Memo Program",
Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo: "Memo Program", SWAP = "Swap Program",
SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8: "Token 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; } as const;
export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS]; export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS];
@ -67,7 +101,7 @@ export function addressLabel(
cluster: Cluster cluster: Cluster
): string | undefined { ): string | undefined {
return ( return (
PROGRAM_IDS[address] || PROGRAM_NAME_BY_ID[address] ||
LOADER_IDS[address] || LOADER_IDS[address] ||
SYSVAR_IDS[address] || SYSVAR_IDS[address] ||
SYSVAR_ID[address] || SYSVAR_ID[address] ||