From a0ba59a1ea46dd5deefc7f2e38ffe4f7180b756e Mon Sep 17 00:00:00 2001 From: B <264380+bartosz-lipinski@users.noreply.github.com> Date: Tue, 9 Feb 2021 19:50:34 -0600 Subject: [PATCH] Add titles for token lending instructions (#15217) * feat: add lending instruction names * chore: capitalize words --- explorer/.gitignore | 1 + explorer/src/components/SearchBar.tsx | 16 +---- .../components/account/TokenHistoryCard.tsx | 14 ++++ .../instruction/TokenLendingDetailsCard.tsx | 46 +++++++++++++ .../instruction/token-lending/types.ts | 35 ++++++++++ explorer/src/pages/TransactionDetailsPage.tsx | 4 ++ explorer/src/utils/tx.ts | 68 ++++++++++++++----- 7 files changed, 154 insertions(+), 30 deletions(-) create mode 100644 explorer/src/components/instruction/TokenLendingDetailsCard.tsx create mode 100644 explorer/src/components/instruction/token-lending/types.ts diff --git a/explorer/.gitignore b/explorer/.gitignore index 00ec607c83..df50e4b884 100644 --- a/explorer/.gitignore +++ b/explorer/.gitignore @@ -10,6 +10,7 @@ # production /build +/wasm/target # misc .DS_Store diff --git a/explorer/src/components/SearchBar.tsx b/explorer/src/components/SearchBar.tsx index a25d4a5840..c23866f079 100644 --- a/explorer/src/components/SearchBar.tsx +++ b/explorer/src/components/SearchBar.tsx @@ -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) && diff --git a/explorer/src/components/account/TokenHistoryCard.tsx b/explorer/src/components/account/TokenHistoryCard.tsx index 19a4673aca..5cffac57ff 100644 --- a/explorer/src/components/account/TokenHistoryCard.tsx +++ b/explorer/src/components/account/TokenHistoryCard.tsx @@ -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) => diff --git a/explorer/src/components/instruction/TokenLendingDetailsCard.tsx b/explorer/src/components/instruction/TokenLendingDetailsCard.tsx new file mode 100644 index 0000000000..933c2a7c1a --- /dev/null +++ b/explorer/src/components/instruction/TokenLendingDetailsCard.tsx @@ -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 ( + + ); +} diff --git a/explorer/src/components/instruction/token-lending/types.ts b/explorer/src/components/instruction/token-lending/types.ts new file mode 100644 index 0000000000..a635ed4c50 --- /dev/null +++ b/explorer/src/components/instruction/token-lending/types.ts @@ -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]; +} diff --git a/explorer/src/pages/TransactionDetailsPage.tsx b/explorer/src/pages/TransactionDetailsPage.tsx index e52e4c6a46..db3ef59d1d 100644 --- a/explorer/src/pages/TransactionDetailsPage.tsx +++ b/explorer/src/pages/TransactionDetailsPage.tsx @@ -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 ; } else if (isTokenSwapInstruction(transactionIx)) { return ; + } else if (isTokenLendingInstruction(transactionIx)) { + return ; } else { return ; } diff --git a/explorer/src/utils/tx.ts b/explorer/src/utils/tx.ts index 06eaa44766..96ae8f46c3 100644 --- a/explorer/src/utils/tx.ts +++ b/explorer/src/utils/tx.ts @@ -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] ||