222 lines
7.2 KiB
TypeScript
222 lines
7.2 KiB
TypeScript
import bs58 from "bs58";
|
|
import {
|
|
SystemProgram,
|
|
StakeProgram,
|
|
VOTE_PROGRAM_ID,
|
|
BPF_LOADER_PROGRAM_ID,
|
|
BPF_LOADER_DEPRECATED_PROGRAM_ID,
|
|
SYSVAR_CLOCK_PUBKEY,
|
|
SYSVAR_RENT_PUBKEY,
|
|
SYSVAR_REWARDS_PUBKEY,
|
|
SYSVAR_STAKE_HISTORY_PUBKEY,
|
|
ParsedTransaction,
|
|
TransactionInstruction,
|
|
Transaction,
|
|
PartiallyDecodedInstruction,
|
|
ParsedInstruction,
|
|
} from "@solana/web3.js";
|
|
import { Cluster } from "providers/cluster";
|
|
import { SerumMarketRegistry } from "serumMarketRegistry";
|
|
import { TokenInfoMap } from "@solana/spl-token-registry";
|
|
|
|
export type ProgramName = typeof PROGRAM_NAME_BY_ID[keyof typeof PROGRAM_NAME_BY_ID];
|
|
|
|
export enum PROGRAM_NAMES {
|
|
// native built-ins
|
|
CONFIG = "Config Program",
|
|
STAKE = "Stake Program",
|
|
SYSTEM = "System Program",
|
|
VOTE = "Vote Program",
|
|
|
|
// spl
|
|
ASSOCIATED_TOKEN = "Associated Token Program",
|
|
FEATURE_PROPOSAL = "Feature Proposal Program",
|
|
LENDING = "Lending Program",
|
|
MEMO = "Memo Program",
|
|
MEMO_2 = "Memo Program v2",
|
|
SWAP = "Swap Program",
|
|
TOKEN = "Token Program",
|
|
|
|
// other
|
|
BONFIDA_POOL = "Bonfida Pool Program",
|
|
BREAK_SOLANA = "Break Solana Program",
|
|
RAYDIUM_LIQUIDITY_1 = "Raydium Liquidity Pool Program v1",
|
|
RAYDIUM_LIQUIDITY_2 = "Raydium Liquidity Pool Program v2",
|
|
RAYDIUM_STAKING = "Raydium Staking Program",
|
|
SERUM_2 = "Serum Program v2",
|
|
SERUM_3 = "Serum Program v3",
|
|
}
|
|
|
|
const ALL_CLUSTERS = [
|
|
Cluster.Custom,
|
|
Cluster.Devnet,
|
|
Cluster.Testnet,
|
|
Cluster.MainnetBeta,
|
|
];
|
|
|
|
const LIVE_CLUSTERS = [Cluster.Devnet, Cluster.Testnet, Cluster.MainnetBeta];
|
|
|
|
const MAINNET_ONLY = [Cluster.MainnetBeta];
|
|
|
|
export const PROGRAM_DEPLOYMENTS = {
|
|
// native built-ins
|
|
[PROGRAM_NAMES.CONFIG]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.STAKE]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.SYSTEM]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.VOTE]: ALL_CLUSTERS,
|
|
|
|
// spl
|
|
[PROGRAM_NAMES.ASSOCIATED_TOKEN]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.FEATURE_PROPOSAL]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.LENDING]: LIVE_CLUSTERS,
|
|
[PROGRAM_NAMES.MEMO]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.MEMO_2]: ALL_CLUSTERS,
|
|
[PROGRAM_NAMES.SWAP]: LIVE_CLUSTERS,
|
|
[PROGRAM_NAMES.TOKEN]: ALL_CLUSTERS,
|
|
|
|
// other
|
|
[PROGRAM_NAMES.BONFIDA_POOL]: MAINNET_ONLY,
|
|
[PROGRAM_NAMES.BREAK_SOLANA]: LIVE_CLUSTERS,
|
|
[PROGRAM_NAMES.RAYDIUM_LIQUIDITY_1]: MAINNET_ONLY,
|
|
[PROGRAM_NAMES.RAYDIUM_LIQUIDITY_2]: MAINNET_ONLY,
|
|
[PROGRAM_NAMES.RAYDIUM_STAKING]: MAINNET_ONLY,
|
|
[PROGRAM_NAMES.SERUM_2]: MAINNET_ONLY,
|
|
[PROGRAM_NAMES.SERUM_3]: MAINNET_ONLY,
|
|
} as const;
|
|
|
|
export const PROGRAM_NAME_BY_ID = {
|
|
// native built-ins
|
|
Config1111111111111111111111111111111111111: PROGRAM_NAMES.CONFIG,
|
|
[StakeProgram.programId.toBase58()]: PROGRAM_NAMES.STAKE,
|
|
[SystemProgram.programId.toBase58()]: PROGRAM_NAMES.SYSTEM,
|
|
[VOTE_PROGRAM_ID.toBase58()]: PROGRAM_NAMES.VOTE,
|
|
|
|
// spl
|
|
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: PROGRAM_NAMES.ASSOCIATED_TOKEN,
|
|
Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse: PROGRAM_NAMES.FEATURE_PROPOSAL,
|
|
Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo: PROGRAM_NAMES.MEMO,
|
|
MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr: PROGRAM_NAMES.MEMO_2,
|
|
SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8: PROGRAM_NAMES.SWAP,
|
|
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: PROGRAM_NAMES.TOKEN,
|
|
LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi: PROGRAM_NAMES.LENDING,
|
|
|
|
// other
|
|
WvmTNLpGMVbwJVYztYL4Hnsy82cJhQorxjnnXcRm3b6: PROGRAM_NAMES.BONFIDA_POOL,
|
|
BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: PROGRAM_NAMES.BREAK_SOLANA,
|
|
RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr:
|
|
PROGRAM_NAMES.RAYDIUM_LIQUIDITY_1,
|
|
"27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv":
|
|
PROGRAM_NAMES.RAYDIUM_LIQUIDITY_2,
|
|
EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: PROGRAM_NAMES.RAYDIUM_STAKING,
|
|
EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o: PROGRAM_NAMES.SERUM_2,
|
|
"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin": PROGRAM_NAMES.SERUM_3,
|
|
} as const;
|
|
|
|
export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS];
|
|
export const LOADER_IDS = {
|
|
MoveLdr111111111111111111111111111111111111: "Move Loader",
|
|
NativeLoader1111111111111111111111111111111: "Native Loader",
|
|
[BPF_LOADER_DEPRECATED_PROGRAM_ID.toBase58()]: "BPF Loader",
|
|
[BPF_LOADER_PROGRAM_ID.toBase58()]: "BPF Loader 2",
|
|
BPFLoaderUpgradeab1e11111111111111111111111: "BPF Upgradeable Loader",
|
|
} as const;
|
|
|
|
const SYSVAR_ID: { [key: string]: string } = {
|
|
Sysvar1111111111111111111111111111111111111: "SYSVAR",
|
|
};
|
|
|
|
export const SYSVAR_IDS = {
|
|
[SYSVAR_CLOCK_PUBKEY.toBase58()]: "Sysvar: Clock",
|
|
SysvarEpochSchedu1e111111111111111111111111: "Sysvar: Epoch Schedule",
|
|
SysvarFees111111111111111111111111111111111: "Sysvar: Fees",
|
|
SysvarRecentB1ockHashes11111111111111111111: "Sysvar: Recent Blockhashes",
|
|
[SYSVAR_RENT_PUBKEY.toBase58()]: "Sysvar: Rent",
|
|
[SYSVAR_REWARDS_PUBKEY.toBase58()]: "Sysvar: Rewards",
|
|
SysvarS1otHashes111111111111111111111111111: "Sysvar: Slot Hashes",
|
|
SysvarS1otHistory11111111111111111111111111: "Sysvar: Slot History",
|
|
[SYSVAR_STAKE_HISTORY_PUBKEY.toBase58()]: "Sysvar: Stake History",
|
|
Sysvar1nstructions1111111111111111111111111: "Sysvar: Instructions",
|
|
};
|
|
|
|
export function programLabel(
|
|
address: string,
|
|
cluster: Cluster
|
|
): string | undefined {
|
|
const programName = PROGRAM_NAME_BY_ID[address];
|
|
if (programName && PROGRAM_DEPLOYMENTS[programName].includes(cluster)) {
|
|
return programName;
|
|
}
|
|
}
|
|
|
|
export function addressLabel(
|
|
address: string,
|
|
cluster: Cluster,
|
|
tokenRegistry?: TokenInfoMap
|
|
): string | undefined {
|
|
return (
|
|
programLabel(address, cluster) ||
|
|
LOADER_IDS[address] ||
|
|
SYSVAR_IDS[address] ||
|
|
SYSVAR_ID[address] ||
|
|
tokenRegistry?.get(address)?.name ||
|
|
SerumMarketRegistry.get(address, cluster)
|
|
);
|
|
}
|
|
|
|
export function displayAddress(
|
|
address: string,
|
|
cluster: Cluster,
|
|
tokenRegistry: TokenInfoMap
|
|
): string {
|
|
return addressLabel(address, cluster, tokenRegistry) || address;
|
|
}
|
|
|
|
export function intoTransactionInstruction(
|
|
tx: ParsedTransaction,
|
|
instruction: ParsedInstruction | PartiallyDecodedInstruction
|
|
): TransactionInstruction | undefined {
|
|
const message = tx.message;
|
|
if ("parsed" in instruction) return;
|
|
|
|
const keys = [];
|
|
for (const account of instruction.accounts) {
|
|
const accountKey = message.accountKeys.find(({ pubkey }) =>
|
|
pubkey.equals(account)
|
|
);
|
|
if (!accountKey) return;
|
|
keys.push({
|
|
pubkey: accountKey.pubkey,
|
|
isSigner: accountKey.signer,
|
|
isWritable: accountKey.writable,
|
|
});
|
|
}
|
|
|
|
return new TransactionInstruction({
|
|
data: bs58.decode(instruction.data),
|
|
keys: keys,
|
|
programId: instruction.programId,
|
|
});
|
|
}
|
|
|
|
export function intoParsedTransaction(tx: Transaction): ParsedTransaction {
|
|
const message = tx.compileMessage();
|
|
return {
|
|
signatures: tx.signatures.map((value) =>
|
|
bs58.encode(value.signature as any)
|
|
),
|
|
message: {
|
|
accountKeys: message.accountKeys.map((key, index) => ({
|
|
pubkey: key,
|
|
signer: tx.signatures.some(({ publicKey }) => publicKey.equals(key)),
|
|
writable: message.isAccountWritable(index),
|
|
})),
|
|
instructions: message.instructions.map((ix) => ({
|
|
programId: message.accountKeys[ix.programIdIndex],
|
|
accounts: ix.accounts.map((index) => message.accountKeys[index]),
|
|
data: ix.data,
|
|
})),
|
|
recentBlockhash: message.recentBlockhash,
|
|
},
|
|
};
|
|
}
|