import { AccountMeta, Commitment, Connection, GetAccountInfoConfig, PublicKey, TransactionInstruction, } from '@solana/web3.js' import BN from 'bn.js' import invariant from 'tiny-invariant' import { keccak_256 } from '@layerzerolabs/lz-foundation' import { arrayify } from '@layerzerolabs/lz-utilities' import { PacketV1Codec, addressToBytes32 } from '@layerzerolabs/lz-v2-utilities' import { BlockedMessageLibProgram, MessageLibInterface, SetConfigType, SimpleMessageLibProgram, SolanaPacketPath, UlnProgram, simulateTransaction, } from '.' import * as pdaDeriver from './pda-deriver' import { EventPDADeriver, MessageLibPDADeriver, OAPP_SEED } from './pda-deriver' import { ComposeMessageState, EndpointSettings, MessageLibInfo, PayloadHash } from '../generated/solita/endpoint' import * as accounts from '../generated/solita/endpoint/accounts' import * as errors from '../generated/solita/endpoint/errors' import * as events from '../generated/solita/endpoint/events' import * as instructions from '../generated/solita/endpoint/instructions' import * as types from '../generated/solita/endpoint/types' import { Version } from '../generated/solita/simple_messagelib' export { accounts, instructions, types, events, errors } export { PROGRAM_ID } from '../generated/solita/endpoint' // https://github.com/coral-xyz/anchor/blob/c7ccbb8f620aed3ffecf8ab2f9c9007946f0f5cc/lang/src/event.rs#L2 export const EventEmitDiscriminator = 'e445a52e51cb9a1d' // Sha256(anchor:event)[..8] then evert export const DefaultMessageLib = PublicKey.default // [0u8; 32] /** * Class representing an Endpoint. */ export class Endpoint { deriver: pdaDeriver.EndpointPDADeriver eventAuthorityPDA: PublicKey /** * Creates an instance of the Endpoint class. * * @param {PublicKey} program - The program public key. */ constructor(public program: PublicKey) { this.deriver = new pdaDeriver.EndpointPDADeriver(program) const [eventAuthorityPDA] = new EventPDADeriver(program).eventAuthority() this.eventAuthorityPDA = eventAuthorityPDA } /** * Initializes the endpoint settings, including eid and admin, and registers the blocked message library. * * @param {number} endpointId - The endpoint ID. * @param {PublicKey} payer - The payer public key. * @param {PublicKey} admin - The admin public key. * @returns {TransactionInstruction} The transaction instruction. */ initEndpoint(endpointId: number, payer: PublicKey, admin: PublicKey): TransactionInstruction { const [settingPDA] = this.deriver.setting() return instructions.createInitEndpointInstruction( { payer, endpoint: settingPDA, } satisfies instructions.InitEndpointInstructionAccounts, { params: { eid: endpointId, admin, } satisfies types.InitEndpointParams, } satisfies instructions.InitEndpointInstructionArgs, this.program ) } // async initOrUpdateConfig(connection:Connection,) /** * Registers a library. * call this function after endpoint initialized. Only admin can call this function. * * @param {PublicKey} admin - The admin public key. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {types.MessageLibType} [libType=types.MessageLibType.SendAndReceive] - The library type. * @returns {TransactionInstruction} The transaction instruction. */ registerLibrary( admin: PublicKey, messageLibProgram: PublicKey, libType: types.MessageLibType = types.MessageLibType.SendAndReceive ): TransactionInstruction { const [msgLibPda] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const [msgLibInfoPda] = this.deriver.messageLibraryInfo(msgLibPda) const [settingPDA] = this.deriver.setting() return instructions.createRegisterLibraryInstruction( { admin, endpoint: settingPDA, messageLibInfo: msgLibInfoPda, eventAuthority: this.eventAuthorityPDA, program: this.program, }, { params: { libProgram: messageLibProgram, libType, }, }, this.program ) } /** * Sets the default send library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} admin - The admin public key. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {number} dstEid - The destination endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the transaction instruction or null if not needed. */ async setDefaultSendLibrary( connection: Connection, admin: PublicKey, messageLibProgram: PublicKey, dstEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [msgLib] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const [defaultSendLibConfig] = this.deriver.defaultSendLibraryConfig(dstEid) const [settingPDA] = this.deriver.setting() const [msgLibInfo] = this.deriver.messageLibraryInfo(msgLib) const info = await connection.getAccountInfo(defaultSendLibConfig, commitmentOrConfig) if (!info) { return instructions.createInitDefaultSendLibraryInstruction( { admin, endpoint: settingPDA, defaultSendLibraryConfig: defaultSendLibConfig, messageLibInfo: msgLibInfo, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.InitDefaultSendLibraryInstructionAccounts, { params: { eid: dstEid, newLib: msgLib, } satisfies types.InitDefaultSendLibraryParams, } satisfies instructions.InitDefaultSendLibraryInstructionArgs, this.program ) } else { // get current default send library const sendLibConfig = await accounts.SendLibraryConfig.fromAccountAddress( connection, defaultSendLibConfig, commitmentOrConfig ) if (sendLibConfig.messageLib.toBase58() === msgLib.toBase58()) { return null } return instructions.createSetDefaultSendLibraryInstruction( { admin, endpoint: settingPDA, defaultSendLibraryConfig: defaultSendLibConfig, messageLibInfo: msgLibInfo, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.SetDefaultSendLibraryInstructionAccounts, { params: { eid: dstEid, newLib: msgLib, } satisfies types.SetDefaultSendLibraryParams, } satisfies instructions.SetDefaultSendLibraryInstructionArgs, this.program ) } } /** * Sets the default receive library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} admin - The admin public key. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {number} srcEid - The source endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the transaction instruction or null if not needed. */ async setDefaultReceiveLibrary( connection: Connection, admin: PublicKey, messageLibProgram: PublicKey, srcEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [msgLib] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const [defaultReceiveLibConfig] = this.deriver.defaultReceiveLibraryConfig(srcEid) const [settingPDA] = this.deriver.setting() const [defaultMsgLibInfo] = this.deriver.messageLibraryInfo(msgLib) const info = await connection.getAccountInfo(defaultReceiveLibConfig, commitmentOrConfig) if (!info) { return instructions.createInitDefaultReceiveLibraryInstruction( { admin, endpoint: settingPDA, defaultReceiveLibraryConfig: defaultReceiveLibConfig, messageLibInfo: defaultMsgLibInfo, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.InitDefaultReceiveLibraryInstructionAccounts, { params: { eid: srcEid, newLib: msgLib, } satisfies types.InitDefaultReceiveLibraryParams, } satisfies instructions.InitDefaultReceiveLibraryInstructionArgs, this.program ) } else { // get current default receive library const receiveLibConfig = await accounts.ReceiveLibraryConfig.fromAccountAddress( connection, defaultReceiveLibConfig, commitmentOrConfig ) if (receiveLibConfig.messageLib.toBase58() === msgLib.toBase58()) { return null } return instructions.createSetDefaultReceiveLibraryInstruction( { admin, endpoint: settingPDA, defaultReceiveLibraryConfig: defaultReceiveLibConfig, messageLibInfo: defaultMsgLibInfo, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.SetDefaultReceiveLibraryInstructionAccounts, { params: { eid: srcEid, newLib: msgLib, gracePeriod: 0, // TODO: set grace period } satisfies types.SetDefaultReceiveLibraryParams, } satisfies instructions.SetDefaultReceiveLibraryInstructionArgs, this.program ) } } /** * Initializes the OApp configuration. * * @param {PublicKey} delegate - The delegate public key. * @param {MessageLibInterface} msgLibSDK - The message library SDK. * @param {PublicKey} payer - The payer public key. * @param {PublicKey} oappID - The OApp ID public key. * @param {number} eid - The endpoint ID. * @returns {TransactionInstruction} The transaction instruction. */ initOAppConfig( delegate: PublicKey, msgLibSDK: MessageLibInterface, payer: PublicKey, oappID: PublicKey, eid: number ): TransactionInstruction { const [msgLib] = new MessageLibPDADeriver(msgLibSDK.program).messageLib() const [oappRegistry] = this.deriver.oappRegistry(oappID) const [msgLibInfo] = this.deriver.messageLibraryInfo(msgLib) // if (!(await uln.getOappConfig(connection, oappID, eid, commitmentOrConfig))) { // init oapp config return instructions.createInitConfigInstruction( { delegate, oappRegistry, messageLib: msgLib, messageLibInfo: msgLibInfo, messageLibProgram: msgLibSDK.program, anchorRemainingAccounts: msgLibSDK.getInitConfigIXAccountMetaForCPI(payer, oappID, eid), } satisfies instructions.InitConfigInstructionAccounts, { params: { eid: eid, oapp: oappID, } satisfies types.InitConfigParams, } satisfies instructions.InitConfigInstructionArgs, this.program ) } /** * Initializes the OApp nonce. * * @param {PublicKey} delegate - The delegate public key. * @param {number} dstEid - The destination endpoint ID. * @param {PublicKey} oappIDPDA - The OApp ID PDA. * @param {Uint8Array} remoteOappAddr - The remote OApp address. * @returns {TransactionInstruction} The transaction instruction. */ initOAppNonce( delegate: PublicKey, // payer dstEid: number, oappIDPDA: PublicKey, remoteOappAddr: Uint8Array ): TransactionInstruction { const [nonce] = this.deriver.nonce(oappIDPDA, dstEid, remoteOappAddr) const [pendingNonce] = this.deriver.pendingNonce(oappIDPDA, dstEid, remoteOappAddr) const [oappRegistry] = this.deriver.oappRegistry(oappIDPDA) return instructions.createInitNonceInstruction( { nonce, pendingInboundNonce: pendingNonce, oappRegistry, delegate, } satisfies instructions.InitNonceInstructionAccounts, { params: { localOapp: oappIDPDA, remoteOapp: Array.from(remoteOappAddr), remoteEid: dstEid, } satisfies types.InitNonceParams, } satisfies instructions.InitNonceInstructionArgs, this.program ) } /** * Initializes the send library. * * @param {PublicKey} delegate - The delegate public key. * @param {PublicKey} sender - The sender public key. * @param {number} dstEid - The destination endpoint ID. * @returns {TransactionInstruction} The transaction instruction. */ initSendLibrary(delegate: PublicKey, sender: PublicKey, dstEid: number): TransactionInstruction { const [oappRegistry] = this.deriver.oappRegistry(sender) const [sendLibraryConfig] = this.deriver.sendLibraryConfig(sender, dstEid) return instructions.createInitSendLibraryInstruction( { delegate, oappRegistry, sendLibraryConfig: sendLibraryConfig, } satisfies instructions.InitSendLibraryInstructionAccounts, { params: { sender, eid: dstEid, } satisfies types.InitSendLibraryParams, } satisfies instructions.InitSendLibraryInstructionArgs, this.program ) } /** * Sets the send library. * * @param {PublicKey} oappAdmin - The OApp admin public key. * @param {PublicKey} oappIDPDA - The OApp ID PDA. * @param {PublicKey} newSendLibProgram - The new send library program public key. * @param {number} dstEid - The destination endpoint ID. * @returns {TransactionInstruction} The transaction instruction. */ setSendLibrary( oappAdmin: PublicKey, oappIDPDA: PublicKey, newSendLibProgram: PublicKey, dstEid: number ): TransactionInstruction { const [newSendLib] = new MessageLibPDADeriver(newSendLibProgram).messageLib() const [sendLibraryConfig] = this.deriver.sendLibraryConfig(oappIDPDA, dstEid) const [sendLibraryInfo] = this.deriver.messageLibraryInfo(newSendLib) const [oappRegistry] = this.deriver.oappRegistry(oappIDPDA) const ix = instructions.createSetSendLibraryInstruction( { signer: oappAdmin, sendLibraryConfig: sendLibraryConfig, messageLibInfo: sendLibraryInfo, oappRegistry, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.SetSendLibraryInstructionAccounts, { params: { sender: oappIDPDA, eid: dstEid, newLib: newSendLib, } satisfies types.SetSendLibraryParams, } satisfies instructions.SetSendLibraryInstructionArgs, this.program ) return ix } /** * Initializes the receive library. * * @param {PublicKey} delegate - The delegate public key. * @param {PublicKey} receiver - The receiver public key. * @param {number} srcEid - The source endpoint ID. * @returns {TransactionInstruction} The transaction instruction. */ initReceiveLibrary(delegate: PublicKey, receiver: PublicKey, srcEid: number): TransactionInstruction { const [oappRegistry] = this.deriver.oappRegistry(receiver) const [receiveLibraryConfig] = this.deriver.receiveLibraryConfig(receiver, srcEid) return instructions.createInitReceiveLibraryInstruction( { delegate, oappRegistry, receiveLibraryConfig: receiveLibraryConfig, } satisfies instructions.InitReceiveLibraryInstructionAccounts, { params: { receiver, eid: srcEid, } satisfies types.InitReceiveLibraryParams, } satisfies instructions.InitReceiveLibraryInstructionArgs, this.program ) } /** * Sets the receive library. * * @param {PublicKey} oappAdmin - The OApp admin public key. * @param {PublicKey} oappIDPDA - The OApp ID PDA. * @param {PublicKey} newReceiveLibProgram - The new receive library program public key. * @param {number} srcEid - The source endpoint ID. * @param {bigint | number} [gracePeriod] - The grace period. * @returns {TransactionInstruction} The transaction instruction. */ setReceiveLibrary( oappAdmin: PublicKey, oappIDPDA: PublicKey, newReceiveLibProgram: PublicKey, srcEid: number, gracePeriod?: bigint | number ): TransactionInstruction { const [newReceiveLib] = new MessageLibPDADeriver(newReceiveLibProgram).messageLib() const [receiveLibraryConfig] = this.deriver.receiveLibraryConfig(oappIDPDA, srcEid) const [receiveLibraryInfo] = this.deriver.messageLibraryInfo(newReceiveLib) const [oappRegistry] = this.deriver.oappRegistry(oappIDPDA) const ix = instructions.createSetReceiveLibraryInstruction( { signer: oappAdmin, receiveLibraryConfig: receiveLibraryConfig, messageLibInfo: receiveLibraryInfo, oappRegistry, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.SetReceiveLibraryInstructionAccounts, { params: { receiver: oappIDPDA, eid: srcEid, newLib: newReceiveLib, gracePeriod: gracePeriod === undefined ? 0 : new BN(gracePeriod.toString()), } satisfies types.SetReceiveLibraryParams, } satisfies instructions.SetReceiveLibraryInstructionArgs, this.program ) return ix } /** * Sets the OApp configuration. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} oappDelegate - The OApp delegate public key. * @param {PublicKey} oappID - The OApp ID public key. * @param {PublicKey} msgLibProgram - The message library program public key. * @param {number} eid - The endpoint ID. * @param {object} config - The configuration object. * @param {SetConfigType} config.configType - The configuration type. * @param {UlnProgram.types.ExecutorConfig | UlnProgram.types.UlnConfig} config.value - The configuration value. * @param {Commitment} [commitment='confirmed'] - The commitment level. * @returns {Promise} A promise that resolves to the transaction instruction. * @throws {Error} If the message library version is unsupported. */ async setOappConfig( connection: Connection, oappDelegate: PublicKey, oappID: PublicKey, msgLibProgram: PublicKey, eid: number, config: { configType: SetConfigType value: UlnProgram.types.ExecutorConfig | UlnProgram.types.UlnConfig }, commitment: Commitment = 'confirmed' ): Promise { const [msgLib] = new MessageLibPDADeriver(msgLibProgram).messageLib() const [msgLibInfo] = this.deriver.messageLibraryInfo(msgLib) const msgLibVersion = await this.getMessageLibVersion(connection, oappDelegate, msgLibProgram, commitment) let msgLibClient: MessageLibInterface if (msgLibVersion?.major.toString() === '0' && msgLibVersion.minor == 0 && msgLibVersion.endpointVersion == 2) { msgLibClient = new SimpleMessageLibProgram.SimpleMessageLib(msgLibProgram) } else if ( msgLibVersion?.major.toString() === '3' && msgLibVersion.minor == 0 && msgLibVersion.endpointVersion == 2 ) { msgLibClient = new UlnProgram.Uln(msgLibProgram) } else { throw new Error( `unsupported message lib version ${msgLibVersion?.major.toString()}.${msgLibVersion?.minor.toString()}` ) } const [oappRegistry] = this.deriver.oappRegistry(oappID) const ix = instructions.createSetConfigInstruction( { signer: oappDelegate, messageLibInfo: msgLibInfo, messageLib: msgLib, messageLibProgram: msgLibProgram, oappRegistry, anchorRemainingAccounts: await msgLibClient.getSetConfigIXAccountMetaForCPI(this.program, oappID, eid), }, { params: { eid, configType: config.configType, config: msgLibProgram.toBase58() === SimpleMessageLibProgram.PROGRAM_ID.toBase58() ? new Uint8Array(10) : UlnProgram.Uln.constructSetConfigData(config.configType, config.value), oapp: oappID, } satisfies types.SetConfigParams, } satisfies instructions.SetConfigInstructionArgs, this.program ) return ix } /// send a simulated transaction to the endpoint to get the fee for sending a message /** * * Gets the account metadata for the CPI (Cross-Program Invocation) of the quote instruction. * * caculate the fee for sending a message with ULN: * 1. executorFee: feeForGas + feeForOptionType * * feeForGas: ((gas + gasForCalldata) * gasPriceInUnit * priceRatio / priceRatioDenominator) * multiplier_bps / 10000 * * feeForOptionType: nativeDrop/lzReceive/lzCompose * 2. oracleFee: feeForGas * * feeForGas: same above * * The priceRatioDenominator is 10^20 * totalFee = executorFee + oracleFee * numOracles(requiredOracles + optionalOracles) * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} payer - The payer public key. * @param {PacketPath} path - The packet path. * @param {MessageLibInterface} msgLibProgram - The message library program. * @returns {Promise} A promise that resolves to an array of account metadata. * @throws {Error} If the default send library is not initialized or the message library is blocked. */ async getQuoteIXAccountMetaForCPI( connection: Connection, payer: PublicKey, path: SolanaPacketPath, msgLibProgram: MessageLibInterface ): Promise { const { sender: sender_, dstEid, receiver: receiver_ } = path const sender = new PublicKey(arrayify(sender_)) const receiver = addressToBytes32(receiver_) const sendLibInfo = await this.getSendLibrary(connection, sender, dstEid) const { msgLib, programId: owner } = sendLibInfo const [sendLibraryInfo] = this.deriver.messageLibraryInfo(msgLib) const remainingAccounts = await msgLibProgram.getQuoteIXAccountMetaForCPI(connection, payer, path) const [setting] = this.deriver.setting() const [defaultSendLibraryConfig] = this.deriver.defaultSendLibraryConfig(dstEid) const [sendLibraryConfig] = this.deriver.sendLibraryConfig(sender, dstEid) const [nonce] = this.deriver.nonce(sender, dstEid, receiver) const accounts = instructions.createQuoteInstructionAccounts( { sendLibraryConfig: sendLibraryConfig, sendLibraryProgram: owner, defaultSendLibraryConfig: defaultSendLibraryConfig, endpoint: setting, nonce, sendLibraryInfo, anchorRemainingAccounts: remainingAccounts, } satisfies instructions.QuoteInstructionAccounts, this.program ) return Promise.resolve( [ { pubkey: this.program, isSigner: false, isWritable: false, }, ].concat(accounts) ) } /** * Gets the account metadata for the CPI (Cross-Program Invocation) of the send instruction. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} payer - The payer public key. * @param {PacketPath} path - The packet path. * @param {MessageLibInterface} msgLibProgram - The message library program. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to an array of account metadata. * @throws {Error} If the default send library is not initialized or the message library is blocked. */ async getSendIXAccountMetaForCPI( connection: Connection, payer: PublicKey, path: SolanaPacketPath, msgLibProgram: MessageLibInterface, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const { sender: sender_, dstEid, receiver: receiver_ } = path const sender = new PublicKey(arrayify(sender_)) const receiver = addressToBytes32(receiver_) const info = await this.getSendLibrary(connection, sender, dstEid, commitmentOrConfig) const sendLibrary = info.msgLib const [sendLibraryInfo] = this.deriver.messageLibraryInfo(sendLibrary) const remainingAccounts = await msgLibProgram.getSendIXAccountMetaForCPI(connection, payer, path) const [defaultSendLibraryConfig] = this.deriver.defaultSendLibraryConfig(dstEid) const [sendLibraryConfig] = this.deriver.sendLibraryConfig(sender, dstEid) const [nonce] = this.deriver.nonce(sender, dstEid, receiver) const accounts = instructions.createSendInstructionAccounts( { sender, //signer /// this account should be derived from message lib sendLibraryProgram: info.programId, sendLibraryConfig, defaultSendLibraryConfig: defaultSendLibraryConfig, sendLibraryInfo, endpoint: this.deriver.setting()[0], program: this.program, nonce: nonce, eventAuthority: this.eventAuthorityPDA, anchorRemainingAccounts: remainingAccounts, } satisfies instructions.SendInstructionAccounts, this.program ) accounts.forEach((item: AccountMeta) => { item.isSigner = false }) return [ { pubkey: this.program, isSigner: false, isWritable: false, }, ].concat(accounts) } /** * Skips a message. * * @param {PublicKey} payer - The payer public key. * @param {PublicKey} sender - The sender public key. * @param {PublicKey} receiver - The receiver public key. * @param {number} srcEid - The source endpoint ID. * @param {string} nonce - The nonce. * @returns {Promise} A promise that resolves to the transaction instruction or null if not needed. */ async skip( payer: PublicKey, sender: PublicKey, receiver: PublicKey, srcEid: number, nonce: string ): Promise { const [nonceAccount] = this.deriver.nonce(receiver, srcEid, addressToBytes32(sender.toBase58())) const [payloadHash] = this.deriver.payloadHash(receiver, srcEid, sender.toBytes(), parseInt(nonce)) const [pendingInboundNonce] = this.deriver.pendingNonce(receiver, srcEid, sender.toBytes()) // get the delegate from endpoint const [oAppRegistry] = PublicKey.findProgramAddressSync( [Buffer.from(OAPP_SEED, 'utf-8'), receiver.toBuffer()], this.program ) const ix = instructions.createSkipInstruction( { signer: payer, oappRegistry: oAppRegistry, nonce: nonceAccount, pendingInboundNonce: pendingInboundNonce, payloadHash: payloadHash, endpoint: this.deriver.setting()[0], eventAuthority: this.eventAuthorityPDA, program: this.program, } satisfies instructions.SkipInstructionAccounts, { params: { receiver: receiver, srcEid: srcEid, sender: Array.from(sender.toBytes()), nonce: new BN(nonce), } satisfies types.SkipParams, } satisfies instructions.SkipInstructionArgs, this.program ) return Promise.resolve(ix) } /** * Initializes the verification process. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} payer - The payer public key. * @param {PublicKey} sender - The sender public key. * @param {PublicKey} receiver - The receiver public key. * @param {number} srcEid - The source endpoint ID. * @param {string} nonce - The nonce. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the transaction instruction or null if not needed. */ async initVerify( connection: Connection, payer: PublicKey, sender: PublicKey, receiver: PublicKey, srcEid: number, nonce: string, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [nonceAccount] = this.deriver.nonce(receiver, srcEid, addressToBytes32(sender.toBase58())) const [payloadHash] = this.deriver.payloadHash(receiver, srcEid, sender.toBytes(), parseInt(nonce)) const payloadHashInfo = await connection.getAccountInfo(payloadHash, commitmentOrConfig) if (payloadHashInfo) { // throw new Error('init verify already executed') return null } return instructions.createInitVerifyInstruction( { payer, nonce: nonceAccount, payloadHash, } satisfies instructions.InitVerifyInstructionAccounts, { params: { srcEid, sender: Array.from(sender.toBytes()), receiver, nonce: new BN(nonce), } satisfies types.InitVerifyParams, } satisfies instructions.InitVerifyInstructionArgs, this.program ) } /** * Gets the account metadata for the CPI (Cross-Program Invocation) of the verify instruction. * * @param {PacketV1Codec} packet - The packet. * @param {PublicKey} receiveLibrary - The receive library public key. * @returns {AccountMeta[]} An array of account metadata. */ getVerifyIXAccountMetaForCPI(packet: PacketV1Codec, receiveLibrary: PublicKey): AccountMeta[] { const receiver = new PublicKey(Buffer.from(packet.receiver().slice(2), 'hex')) const srcEid = packet.srcEid() const sender = addressToBytes32(packet.sender()) const [payloadHash] = this.deriver.payloadHash(receiver, srcEid, sender, parseInt(packet.nonce())) const [defaultReceiveLibraryConfig] = this.deriver.defaultReceiveLibraryConfig(srcEid) const [receiveLibraryConfig] = this.deriver.receiveLibraryConfig(receiver, srcEid) const [nonce] = this.deriver.nonce(receiver, srcEid, sender) const [pendingNonce] = this.deriver.pendingNonce(receiver, srcEid, sender) // const [receiveLibraryPDA] = this.endpointDeriver.receiveLibrary(receiver, srcEid) const accounts = instructions.createVerifyInstructionAccounts( { receiveLibrary: receiveLibrary, receiveLibraryConfig: receiveLibraryConfig, defaultReceiveLibraryConfig: defaultReceiveLibraryConfig, nonce: nonce, pendingInboundNonce: pendingNonce, payloadHash: payloadHash, program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.VerifyInstructionAccounts, this.program ) accounts.forEach((key) => { key.isSigner = false }) const program: AccountMeta = { pubkey: this.program, isSigner: false, isWritable: false, } return [program, ...accounts] } /*** * resetConfig is only supported by ULN */ // async getResetConfigIXAccountMetaForCPI( // connection: Connection, // msgLibProgram: PublicKey, // oappID: PublicKey, // eid: number, // commitmentOrConfig: Commitment | GetAccountInfoConfig = 'confirmed' // ): Promise { // const uln = new UlnProgram.Uln(msgLibProgram) // const [msgLib] = uln.ulnDeriver.messageLib() // const [msgLibInfo] = this.endpointDeriver.messageLibraryAuthority(msgLib) // const ix = instructions.createResetConfigInstruction( // { // oapp: oappID, // messageLibAuthority: msgLibInfo, // messageLib: msgLib, // anchorRemainingAccounts: await uln.getResetConfigIXAccountMetaForCPI( // connection, // this.program, // oappID, // eid, // commitmentOrConfig // ), // } as instructions.ResetConfigInstructionAccounts, // // Fake params. Just to get the ix.keys // { // params: { // eid: eid, // messageLib: msgLib, // } as types.ResetConfigParams, // } as instructions.ResetConfigInstructionArgs // ) // ix.keys.forEach((key) => { // key.isSigner = false // }) // return [ // { // pubkey: this.program, // isWritable: false, // isSigner: false, // } as AccountMeta, // ].concat(ix.keys) // } /** * snapshotConfig is only supported by ULN */ // async getSnapshotConfigIXAccountMetaForCPI( // connection: Connection, // msgLibProgram: PublicKey, // oappID: PublicKey, // eid: number, // commitmentOrConfig: Commitment | GetAccountInfoConfig = 'confirmed' // ): Promise { // const uln = new UlnProgram.Uln(msgLibProgram) // const [msgLib] = uln.ulnDeriver.messageLib() // const [msgLibInfo] = this.endpointDeriver.messageLibraryAuthority(msgLib) // const ix = instructions.createSnapshotConfigInstruction( // { // oapp: oappID, // messageLibAuthority: msgLibInfo, // messageLib: msgLib, // anchorRemainingAccounts: await uln.getSnapshotConfigIXAccountMetaForCPI( // connection, // this.program, // oappID, // eid, // commitmentOrConfig // ), // } satisfies instructions.SnapshotConfigInstructionAccounts, // // Fake params. Just to get the ix.keys // { // params: { // eid: eid, // messageLib: msgLib, // } as types.SnapshotConfigParams, // } as instructions.SnapshotConfigInstructionArgs // ) // ix.keys.forEach((key) => { // key.isSigner = false // }) // return [ // { // pubkey: this.program, // isWritable: false, // isSigner: false, // }, // ].concat(ix.keys) // } /** * @param composer. The composer is usually the PDA of the app's ID. It depends on the oapp's implementation. */ // getDeliverComposedMessageIXAccountMetaForCPI( // payer: PublicKey, // composer: PublicKey, // packet: PacketV1Codec // ): AccountMeta[] { // const receiver = new PublicKey(Buffer.from(packet.receiver().slice(2), 'hex')) // const guid = Uint8Array.from(Buffer.from(packet.guid().slice(2), 'hex')) // const message = ethers.utils.arrayify(ethers.utils.keccak256(packet.message())) // const ix = instructions.createDeliverComposedMessageInstruction( // { // payer, // receiver, // composedMessage: this.endpointDeriver.composedMessage(receiver, guid, composer, message)[0], // endpoint: this.endpointDeriver.setting()[0], // endpointProgram: this.program, // } satisfies instructions.DeliverComposedMessageInstructionAccounts, // // Fake params. Just to get the ix.keys // { // params: { // composer: payer, // guid: Array.from(guid), // message: Uint8Array.from([]), // } as types.DeliverComposedMessageParams, // } as instructions.DeliverComposedMessageInstructionArgs // ) // ix.keys.forEach((key) => { // key.isSigner = false // }) // return [ // { // pubkey: this.program, // isWritable: false, // isSigner: false, // }, // ].concat(ix.keys) // } /** * @param composer. The composer is usually the PDA of the app's ID. It depends on the oapp's implementation. */ // getClearComposedMessageIXAccountMetaForCPI(composer: PublicKey, packet: PacketV1Codec): AccountMeta[] { // const receiver = new PublicKey(Buffer.from(packet.receiver().slice(2), 'hex')) // const guid = Uint8Array.from(Buffer.from(packet.guid().slice(2), 'hex')) // const message = ethers.utils.arrayify(ethers.utils.keccak256(packet.message())) // const ix = instructions.createClearComposedMessageInstruction( // { // composer, // composedMessage: this.endpointDeriver.composedMessage(receiver, guid, composer, message)[0], // endpoint: this.endpointDeriver.setting()[0], // endpointProgram: this.program, // } satisfies instructions.ClearComposedMessageInstructionAccounts, // // Fake params. Just to get the ix.keys // { // params: { // receiver: composer, // guid: Array.from(guid), // message: Uint8Array.from([]), // } as types.ClearComposedMessageParams, // } as instructions.ClearComposedMessageInstructionArgs // ) // ix.keys.forEach((key) => { // key.isSigner = false // }) // return [ // { // pubkey: this.program, // isWritable: false, // isSigner: false, // } as AccountMeta, // ].concat(ix.keys) // } /** * Gets the account metadata for the CPI (Cross-Program Invocation) of the register OApp instruction. * * @param {PublicKey} payer - The payer public key. * @param {PublicKey} oapp - The OApp public key. * @returns {AccountMeta[]} An array of account metadata. */ getRegisterOappIxAccountMetaForCPI(payer: PublicKey, oapp: PublicKey): AccountMeta[] { const [oappRegistry] = this.deriver.oappRegistry(oapp) const eventAuthority = this.eventAuthorityPDA const keys = instructions.createRegisterOappInstructionAccounts( { payer, oapp, oappRegistry, eventAuthority, program: this.program, } satisfies instructions.RegisterOappInstructionAccounts, this.program ) keys.forEach((key) => { key.isSigner = false }) return [ { pubkey: this.program, isSigner: false, isWritable: false, }, ].concat(keys) } /** * Gets the account metadata for the CPI (Cross-Program Invocation) of the skip instruction. * * @param {PublicKey} receiver - The receiver public key. * @param {Uint8Array} sender - The sender address. * @param {number} srcEid - The source endpoint ID. * @param {number} nonce - The nonce. * @returns {AccountMeta[]} An array of account metadata. */ getSkipIxAccountMetaForCPI(receiver: PublicKey, sender: Uint8Array, srcEid: number, nonce: number): AccountMeta[] { const [noncePDA] = this.deriver.nonce(receiver, srcEid, sender) const [pendingNonce] = this.deriver.pendingNonce(receiver, srcEid, sender) const [payloadHash] = this.deriver.payloadHash(receiver, srcEid, sender, nonce) const [oappRegistry] = this.deriver.oappRegistry(receiver) const keys = instructions.createSkipInstructionAccounts( { signer: receiver, oappRegistry, nonce: noncePDA, pendingInboundNonce: pendingNonce, payloadHash, endpoint: this.deriver.setting()[0], program: this.program, eventAuthority: this.eventAuthorityPDA, } satisfies instructions.SkipInstructionAccounts, this.program ) keys.forEach((key) => { key.isSigner = false }) return [ { pubkey: this.program, isSigner: false, isWritable: false, }, ].concat(keys) } /** * Checks if the given message library program is the default send library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {number} dstEid - The destination endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to true if the message library program is the default send library, false otherwise. */ async isDefaultSendLibrary( connection: Connection, messageLibProgram: PublicKey, dstEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [msgLib] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const info = await this.getDefaultSendLibrary(connection, dstEid, commitmentOrConfig) if (info) { // const [defaultSendLib] = new MessageLibPDADeriver(messageLibProgram).messageLib() return info.msgLib.equals(msgLib) } return false } /** * Checks if the given message library program is the default receive library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {number} srcEid - The source endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to true if the message library program is the default receive library, false otherwise. */ async isDefaultReceiveLibrary( connection: Connection, messageLibProgram: PublicKey, srcEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [msgLib] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const info = await this.getDefaultReceiveLibrary(connection, srcEid, commitmentOrConfig) if (info) { return info.msgLib.equals(msgLib) } return false } // all of below functions are retrieving accounts state /** * Gets the endpoint settings. * * @param {Connection} connection - The connection to the Solana cluster. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the endpoint settings or null if not found. */ async getSetting( connection: Connection, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [setting] = this.deriver.setting() try { return await accounts.EndpointSettings.fromAccountAddress(connection, setting, commitmentOrConfig) } catch (e) { return null } } /** * Gets the message library information. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} messageLibProgram - The message library program public key, It is a PDA of the message library program * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the message library information or null if not found. */ async getMessageLibInfo( connection: Connection, messageLibProgram: PublicKey, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [msgLib] = new pdaDeriver.MessageLibPDADeriver(messageLibProgram).messageLib() const [messageLibInfo] = this.deriver.messageLibraryInfo(msgLib) try { return await accounts.MessageLibInfo.fromAccountAddress(connection, messageLibInfo, commitmentOrConfig) } catch (e) { return null } } /** * Gets the default receive library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {number} srcEid - The source endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise<{ msgLib: PublicKey; owner: PublicKey } | null>} A promise that resolves to the default receive library or null if not found. */ async getDefaultReceiveLibrary( connection: Connection, srcEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise<{ msgLib: PublicKey; owner: PublicKey } | null> { const [defaultReceiveLibConfig] = this.deriver.defaultReceiveLibraryConfig(srcEid) try { const defaultInfo = await accounts.ReceiveLibraryConfig.fromAccountAddress( connection, defaultReceiveLibConfig, commitmentOrConfig ) let msgLibProgram: PublicKey | undefined if (Endpoint.checkIfBlockedMessageLib(defaultInfo.messageLib)) { msgLibProgram = BlockedMessageLibProgram.PROGRAM_ID } else { const messageLibInfo = await connection.getAccountInfo(defaultInfo.messageLib, commitmentOrConfig) invariant(messageLibInfo, 'messageLibInfo should not be null') msgLibProgram = messageLibInfo.owner } return { owner: msgLibProgram, msgLib: defaultInfo.messageLib, } } catch (e) { return null } } /** * Gets the default send library. * * @param {Connection} connection - The connection to the Solana cluster. * @param {number} dstEid - The destination endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise<{ msgLib: PublicKey; owner: PublicKey } | null>} A promise that resolves to the default send library or null if not found. */ async getDefaultSendLibrary( connection: Connection, dstEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise<{ msgLib: PublicKey; owner: PublicKey } | null> { const [defaultSendLibConfig] = this.deriver.defaultSendLibraryConfig(dstEid) try { const defaultInfo = await accounts.SendLibraryConfig.fromAccountAddress( connection, defaultSendLibConfig, commitmentOrConfig ) let msgLibProgram: PublicKey | undefined if (Endpoint.checkIfBlockedMessageLib(defaultInfo.messageLib)) { msgLibProgram = BlockedMessageLibProgram.PROGRAM_ID } else { const messageLibInfo = await connection.getAccountInfo(defaultInfo.messageLib, commitmentOrConfig) invariant(messageLibInfo, 'messageLibInfo should not be null') msgLibProgram = messageLibInfo.owner } return { owner: msgLibProgram, msgLib: defaultInfo.messageLib, } } catch (e) { return null } } /** * Gets the configured send library for the app. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} oappPda - The OApp PDA. * @param {number} dstEid - The destination endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig='confirmed'] - The commitment level or account info configuration. * @returns {Promise<{ msgLib: PublicKey; programId: PublicKey; isDefault: boolean } | null>} A promise that resolves to the configured send library or null if not found. */ async getSendLibrary( connection: Connection, oappPda: PublicKey, dstEid: number, commitmentOrConfig: Commitment | GetAccountInfoConfig = 'confirmed' ): Promise<{ msgLib: PublicKey; programId: PublicKey; isDefault: boolean }> { const [sendLibConfig] = this.deriver.sendLibraryConfig(oappPda, dstEid) const [defaultSendLibConfig] = this.deriver.defaultSendLibraryConfig(dstEid) const [defaultSendLibConfigBuf, sendLibConfigBuf] = await connection.getMultipleAccountsInfo( [defaultSendLibConfig, sendLibConfig], commitmentOrConfig ) if (!defaultSendLibConfigBuf || !sendLibConfigBuf) { throw new Error( `Unable to find defaultSendLibraryConfig/sendLibraryConfig account at ${defaultSendLibConfig.toString()}/${sendLibConfig.toString()}` ) } const [sendLibConfigInfo] = accounts.SendLibraryConfig.fromAccountInfo(sendLibConfigBuf, 0) const [defaultSendLibConfigInfo] = accounts.SendLibraryConfig.fromAccountInfo(defaultSendLibConfigBuf, 0) const msgLib = sendLibConfigInfo.messageLib.toString() === DefaultMessageLib.toString() ? defaultSendLibConfigInfo.messageLib : sendLibConfigInfo.messageLib const isDefault = sendLibConfigInfo.messageLib.toString() === DefaultMessageLib.toString() let msgLibProgram: PublicKey | undefined if (Endpoint.checkIfBlockedMessageLib(msgLib)) { msgLibProgram = BlockedMessageLibProgram.PROGRAM_ID } else { const msgLibAccountInfo = await connection.getAccountInfo(msgLib, commitmentOrConfig) invariant(msgLibAccountInfo, 'msgLibAccountInfo should not be null') msgLibProgram = msgLibAccountInfo.owner } return { programId: msgLibProgram, msgLib, isDefault, } } /** * Gets the configured receive library for the app. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} oappPda - The OApp PDA. * @param {number} srcEid - The source endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise<{ msgLib: PublicKey; programId?: PublicKey; isDefault: boolean; timeout: { msgLib: PublicKey; expiry: bigint } | null } | null>} A promise that resolves to the configured receive library or null if not found. */ async getReceiveLibrary( connection: Connection, oappPda: PublicKey, srcEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise<{ msgLib: PublicKey // message library pda, it might be null if the message lib is blocked programId: PublicKey isDefault: boolean timeout?: { msgLib: PublicKey; expiry: bigint } }> { const [receiveLibConfig] = this.deriver.receiveLibraryConfig(oappPda, srcEid) const [defaultReceiveLibConfig] = this.deriver.defaultReceiveLibraryConfig(srcEid) // const accountInfo = await connection.getAccountInfo(receiveLibConfig, commitmentOrConfig) const [defaultReceiveLibConfigBuf, receiveLibConfigBuf] = await connection.getMultipleAccountsInfo( [defaultReceiveLibConfig, receiveLibConfig], commitmentOrConfig ) if (!defaultReceiveLibConfigBuf || !receiveLibConfigBuf) { throw new Error( `Unable to find defaultReceiveLibraryConfig/receiveLibraryConfig account at ${defaultReceiveLibConfig.toString()}/${receiveLibConfig.toString()}` ) } const [receiveLibConfigInfo] = accounts.ReceiveLibraryConfig.fromAccountInfo(receiveLibConfigBuf, 0) const [defaultReceiveLibConfigInfo] = accounts.ReceiveLibraryConfig.fromAccountInfo( defaultReceiveLibConfigBuf, 0 ) const finalReceiveLibConfigInfo = receiveLibConfigInfo.messageLib.toString() === DefaultMessageLib.toString() ? defaultReceiveLibConfigInfo : receiveLibConfigInfo const { messageLib, timeout } = finalReceiveLibConfigInfo let msgLibProgram: PublicKey | undefined if (Endpoint.checkIfBlockedMessageLib(messageLib)) { msgLibProgram = BlockedMessageLibProgram.PROGRAM_ID } else { const msgLibAccountInfo = await connection.getAccountInfo(messageLib, commitmentOrConfig) invariant(msgLibAccountInfo, 'msgLibAccountInfo should not be null') msgLibProgram = msgLibAccountInfo.owner } return { programId: msgLibProgram, msgLib: messageLib, isDefault: receiveLibConfigInfo.messageLib.toString() === DefaultMessageLib.toString(), timeout: timeout === null ? undefined : { msgLib: timeout.messageLib, expiry: BigInt(timeout.expiry.toString()) }, } } // rename to a more generic name /** * Gets the inbound payload hash. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} receiver - The receiver public key. * @param {number} srcEid - The source endpoint ID. * @param {Uint8Array} sender - The sender address. * @param {number} nonce - The nonce. * @param {Uint8Array} _payloadHash - The payload hash. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the payload hash or null if not found. */ async getInboundPayloadHash( connection: Connection, receiver: PublicKey, srcEid: number, sender: Uint8Array, nonce: number, _payloadHash: Uint8Array, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [payloadHashPDA] = this.deriver.payloadHash(receiver, srcEid, sender, nonce) const accountInfo = await connection.getAccountInfo(payloadHashPDA, commitmentOrConfig) if (!accountInfo) { return null } return PayloadHash.fromAccountInfo(accountInfo, 0)[0] } /** * Gets the composed message state. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} from - The sender public key. * @param {types.SendComposeParams} params - The send compose parameters. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the composed message state or null if not found. */ async getComposedMessageState( connection: Connection, from: PublicKey, params: types.SendComposeParams, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const message = keccak_256(params.message) const [composedMessagePDA] = this.deriver.composedMessage( from, Uint8Array.from(params.guid), params.index, params.to, message ) const accountInfo = await connection.getAccountInfo(composedMessagePDA, commitmentOrConfig) if (!accountInfo) { return null } return ComposeMessageState.fromAccountInfo(accountInfo, 0)[0] } /** * Gets the nonce. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} oappIDPDA - The OApp ID PDA. * @param {number} remoteEid - The remote endpoint ID. * @param {Uint8Array} remoteOappAddr - The remote OApp address. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the nonce or null if not found. */ async getNonce( connection: Connection, oappIDPDA: PublicKey, remoteEid: number, remoteOappAddr: Uint8Array, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [nonce] = this.deriver.nonce(oappIDPDA, remoteEid, remoteOappAddr) try { return await accounts.Nonce.fromAccountAddress(connection, nonce, commitmentOrConfig) } catch (e) { return null } } /** * Gets the pending inbound nonce. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} oappIDPDA - The OApp ID PDA. * @param {number} remoteEid - The remote endpoint ID. * @param {Uint8Array} remoteOappAddr - The remote OApp address. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment level or account info configuration. * @returns {Promise} A promise that resolves to the pending inbound nonce or null if not found. */ async getPendingInboundNonce( connection: Connection, oappIDPDA: PublicKey, remoteEid: number, remoteOappAddr: Uint8Array, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [nonce] = this.deriver.pendingNonce(oappIDPDA, remoteEid, remoteOappAddr) try { return await accounts.PendingInboundNonce.fromAccountAddress(connection, nonce, commitmentOrConfig) } catch (e) { return null } } /** * Gets the message library version. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} payer - The payer public key. * @param {PublicKey} messageLibProgram - The message library program public key. * @param {Commitment} [commitment='confirmed'] - The commitment level. * @returns {Promise} A promise that resolves to the version or null if not found. */ async getMessageLibVersion( connection: Connection, payer: PublicKey, messageLibProgram: PublicKey, commitment: Commitment = 'confirmed' ): Promise { const ix = SimpleMessageLibProgram.instructions.createVersionInstruction(messageLibProgram) const simulateResp = await simulateTransaction(connection, [ix], messageLibProgram, payer, commitment) const version = SimpleMessageLibProgram.types.versionBeet.read(simulateResp, 0) return version } /** * Transfers the admin role to a new admin. * * @param {Connection} connection - The connection to the Solana cluster. * @param {PublicKey} admin - The current admin public key. * @param {PublicKey} newAdmin - The new admin public key. * @param {Commitment} [commitment='confirmed'] - The commitment level. * @returns {Promise} A promise that resolves to the transaction instruction or null if not needed. */ async transferAdmin( connection: Connection, admin: PublicKey, newAdmin: PublicKey, commitment: Commitment = 'confirmed' ): Promise { const [settingPDA] = this.deriver.setting() const endpointSettings = await accounts.EndpointSettings.fromAccountAddress(connection, settingPDA, commitment) if (endpointSettings.admin.toBase58() === newAdmin.toBase58()) { console.warn('endpoint admin not need change') return null } return instructions.createTransferAdminInstruction( { admin, endpoint: settingPDA, eventAuthority: this.eventAuthorityPDA, program: this.program, }, { params: { admin: newAdmin, }, }, this.program ) } static checkIfBlockedMessageLib(msgLib: PublicKey): boolean { const [blockedMessageLib] = new MessageLibPDADeriver(BlockedMessageLibProgram.PROGRAM_ID).messageLib() return msgLib.toString() === blockedMessageLib.toString() } }