UNPKG

@xswap-link/sdk

Version:
329 lines (302 loc) 9.39 kB
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck import { PublicKey } from "@solana/web3.js"; import { uint64ToLE } from "./common"; import { Connection } from "@solana/web3.js"; import { tokenAdminRegistry } from "../../bindings/accounts"; /** * CCIP Router seeds for PDA derivation */ export const ROUTER_SEEDS = { CONFIG: "config", FEE_BILLING_SIGNER: "fee_billing_signer", TOKEN_ADMIN_REGISTRY: "token_admin_registry", DEST_CHAIN_STATE: "dest_chain_state", NONCE: "nonce", ALLOWED_OFFRAMP: "allowed_offramp", EXTERNAL_TOKEN_POOLS_SIGNER: "external_token_pools_signer", APPROVED_CCIP_SENDER: "approved_ccip_sender", EXTERNAL_EXECUTION_CONFIG: "external_execution_config", TOKEN_POOL_CHAIN_CONFIG: "ccip_tokenpool_chainconfig", } as const; /** * CCIP Router PDA utilities */ /** * Finds the Config PDA for a program * @param programId Router program ID * @returns [PDA, bump] */ export function findConfigPDA(programId: PublicKey): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.CONFIG)], programId, ); } /** * Finds the Fee Billing Signer PDA for a program * @param programId Router program ID * @returns [PDA, bump] */ export function findFeeBillingSignerPDA( programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.FEE_BILLING_SIGNER)], programId, ); } /** * Finds the Token Admin Registry PDA for a mint * @param mint Token mint * @param programId Router program ID * @returns [PDA, bump] */ export function findTokenAdminRegistryPDA( mint: PublicKey, programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.TOKEN_ADMIN_REGISTRY), mint.toBuffer()], programId, ); } /** * Finds the Destination Chain State PDA for a chain selector * @param chainSelector Chain selector * @param programId Router program ID * @returns [PDA, bump] */ export function findDestChainStatePDA( chainSelector: bigint, programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.DEST_CHAIN_STATE), uint64ToLE(chainSelector)], programId, ); } /** * Finds the Nonce PDA for a chain selector and authority * @param chainSelector Chain selector * @param authority User authority * @param programId Router program ID * @returns [PDA, bump] */ export function findNoncePDA( chainSelector: bigint, authority: PublicKey, programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.NONCE), uint64ToLE(chainSelector), authority.toBuffer(), ], programId, ); } /** * Finds the Approved Sender PDA for a chain selector and source sender * @param chainSelector Chain selector * @param sourceSender Source chain sender address * @param receiverProgram Receiver program ID * @returns [PDA, bump] */ export function findApprovedSenderPDA( chainSelector: bigint, sourceSender: Buffer, receiverProgram: PublicKey, ): [PublicKey, number] { const lenPrefix = Buffer.from([sourceSender.length]); return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.APPROVED_CCIP_SENDER), uint64ToLE(chainSelector), lenPrefix, sourceSender, ], receiverProgram, ); } /** * Finds the Allowed Offramp PDA for a chain selector and offramp * @param chainSelector Chain selector * @param offramp Offramp program ID * @param programId Router program ID * @returns [PDA, bump] */ export function findAllowedOfframpPDA( chainSelector: bigint, offramp: PublicKey, programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.ALLOWED_OFFRAMP), uint64ToLE(chainSelector), offramp.toBuffer(), ], programId, ); } /** * Finds the Token Pool Chain Config PDA for a chain selector and token mint * @param chainSelector Chain selector * @param tokenMint Token mint * @param programId Pool program ID * @returns [PDA, bump] */ export function findTokenPoolChainConfigPDA( chainSelector: bigint, tokenMint: PublicKey, programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.TOKEN_POOL_CHAIN_CONFIG), uint64ToLE(chainSelector), tokenMint.toBuffer(), ], programId, ); } /** * Finds the External Token Pools Signer PDA for a program * @param programId Router program ID * @returns [PDA, bump] */ export function findExternalTokenPoolsSignerPDA( programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.EXTERNAL_TOKEN_POOLS_SIGNER)], programId, ); } /** * Dynamically finds the correct token pool signer PDA for a specific token by retrieving * its admin registry and pool program from the lookup table. * * This function performs on-chain lookups to determine the exact PDA used for token transfers * in the CCIP protocol, which requires both the external_token_pools_signer seed and the * pool program ID from the token's lookup table. * * @param mint Token mint public key * @param routerProgramId CCIP Router program ID * @param connection Solana connection * @returns Promise with [PDA, bump] */ export async function findDynamicTokenPoolsSignerPDA( mint: PublicKey, routerProgramId: PublicKey, connection: Connection, ): Promise<[PublicKey, number]> { // First find the token admin registry PDA const [tokenAdminRegistryPDA] = findTokenAdminRegistryPDA( mint, routerProgramId, ); // Fetch the token admin registry account const tokenAdminRegistryAccount = await connection.getAccountInfo( tokenAdminRegistryPDA, ); if (!tokenAdminRegistryAccount) { throw new Error( `Token admin registry not found for mint: ${mint.toString()}`, ); } // Decode the token admin registry to get the lookup table const tokenRegistry = tokenAdminRegistry.decode( tokenAdminRegistryAccount.data, ); const lookupTableAddress = tokenRegistry.lookupTable; // Fetch the lookup table const { value: lookupTableAccount } = await connection.getAddressLookupTable( lookupTableAddress, ); if (!lookupTableAccount) { throw new Error(`Lookup table not found: ${lookupTableAddress.toString()}`); } // Get the addresses from the lookup table const lookupTableAddresses = lookupTableAccount.state.addresses; // The pool program is at index 2 in the lookup table if (lookupTableAddresses.length <= 2) { throw new Error( "Lookup table doesn't have enough entries to determine pool program", ); } // Extract the pool program from the lookup table (index 2) const poolProgram = lookupTableAddresses[2]; // Now create the correct PDA using both the external_token_pools_signer seed and the pool program return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.EXTERNAL_TOKEN_POOLS_SIGNER), poolProgram.toBuffer(), ], routerProgramId, ); } /** * Finds the External Execution Config PDA for a program * @param programId Router program ID * @returns [PDA, bump] */ export function findExternalExecutionConfigPDA( programId: PublicKey, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from(ROUTER_SEEDS.EXTERNAL_EXECUTION_CONFIG)], programId, ); } /** * Finds the correct token pool signer PDA using the CCIPAccountReader * * This version uses the CCIPAccountReader which already has methods to retrieve * token admin registry accounts, making the process more reliable and consistent * with the rest of the SDK. * * @param mint Token mint public key * @param routerProgramId CCIP Router program ID * @param accountReader CCIPAccountReader instance * @param connection Solana connection * @returns Promise with [PDA, bump] */ export async function findTokenPoolsSignerWithAccountReader( mint: PublicKey, routerProgramId: PublicKey, accountReader: import("../../core/client/accounts").CCIPAccountReader, connection: Connection, ): Promise<[PublicKey, number]> { // Use the account reader to get the token admin registry const tokenRegistry = await accountReader.getTokenAdminRegistry(mint); // Fetch the lookup table const { value: lookupTableAccount } = await connection.getAddressLookupTable( tokenRegistry.lookupTable, ); if (!lookupTableAccount) { throw new Error( `Lookup table not found: ${tokenRegistry.lookupTable.toString()}`, ); } // Get the addresses from the lookup table const lookupTableAddresses = lookupTableAccount.state.addresses; // The pool program is at index 2 in the lookup table if (lookupTableAddresses.length <= 2) { throw new Error( "Lookup table doesn't have enough entries to determine pool program", ); } // Extract the pool program from the lookup table (index 2) const poolProgram = lookupTableAddresses[2]; // Now create the correct PDA using both the external_token_pools_signer seed and the pool program return PublicKey.findProgramAddressSync( [ Buffer.from(ROUTER_SEEDS.EXTERNAL_TOKEN_POOLS_SIGNER), poolProgram.toBuffer(), ], routerProgramId, ); }