@xswap-link/sdk
Version:
JavaScript SDK for XSwap platform
329 lines (302 loc) • 9.39 kB
text/typescript
// 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,
);
}