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] ||