import { AccountMeta, Commitment, Connection, GetAccountInfoConfig, PublicKey, TransactionInstruction, } from '@solana/web3.js' import { PacketV1Codec } from '@layerzerolabs/lz-v2-utilities' import { EndpointProgram, MessageLibInterface, SolanaPacketPath } from '.' import { EndpointPDADeriver, MessageLibPDADeriver } from './pda-deriver' import * as accounts from '../generated/solita/simple_messagelib/accounts' import * as errors from '../generated/solita/simple_messagelib/errors' import * as instructions from '../generated/solita/simple_messagelib/instructions' import * as types from '../generated/solita/simple_messagelib/types' export { PROGRAM_ID } from '../generated/solita/simple_messagelib' export { accounts, instructions, types, errors } /** * Class representing the SimpleMessageLib. * @see {@link MessageLibInterface} */ export class SimpleMessageLib implements MessageLibInterface { /** * The PDA deriver for the message library. */ deriver: MessageLibPDADeriver /** * Creates an instance of the SimpleMessageLib class. * * @param {PublicKey} program - The program public key. */ constructor(public program: PublicKey) { this.deriver = new MessageLibPDADeriver(program) } /** * Initializes the SimpleMessageLib. * * @param {PublicKey} endpointProgram - The endpoint program public key. * @param {PublicKey} payer - The payer public key. * @param {PublicKey} admin - The admin public key. * @param {number} eid - The endpoint ID. * @param {number} nativeFee - The native fee. * @param {number} [lzTokenFee=0] - The LayerZero token fee. * * @returns {TransactionInstruction} The transaction instruction. */ initSimpleMessageLib( endpointProgram: PublicKey, payer: PublicKey, admin: PublicKey, eid: number, nativeFee: number, lzTokenFee = 0 ): TransactionInstruction { const [messageLibPda] = this.deriver.messageLib() const [endpointAuth] = new EndpointPDADeriver(endpointProgram).messageLibraryInfo(messageLibPda) return instructions.createInitMessageLibInstruction( { payer, messageLib: messageLibPda, } satisfies instructions.InitMessageLibInstructionAccounts, { params: { endpointProgram: endpointProgram, endpoint: endpointAuth, eid, admin, fee: nativeFee, lzTokenFee, } satisfies types.InitMessageLibParams, }, this.program ) } /** * Sets the whitelist caller. * * @param {PublicKey} admin - The admin public key. * @param {PublicKey} newCaller - The new caller public key. * * @returns {TransactionInstruction} The transaction instruction. */ setWhitelistCaller(admin: PublicKey, newCaller: PublicKey): TransactionInstruction { const [messageLibPDA] = this.deriver.messageLib() return instructions.createSetWlCallerInstruction( { admin, messageLib: messageLibPDA, } satisfies instructions.SetWlCallerInstructionAccounts, { params: { newCaller, } satisfies types.SetWlCallerParams, } satisfies instructions.SetWlCallerInstructionArgs, this.program ) } /** * Checks if a caller is whitelisted. * * @param {Connection} connection - The connection object. * @param {PublicKey} caller - The caller public key. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment or config. * * @returns {Promise} A promise that resolves to true if the caller is whitelisted, false otherwise. */ async isWhiteListed( connection: Connection, caller: PublicKey, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { try { const exceptedCaller = await this.getWhiteListCaller(connection, commitmentOrConfig) return exceptedCaller.equals(caller) } catch (e) { return false } } /** * Gets the whitelisted caller. * * @param {Connection} connection - The connection object. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment or config. * * @returns {Promise} A promise that resolves to the whitelisted caller public key. */ async getWhiteListCaller( connection: Connection, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [messageLibPDA] = this.deriver.messageLib() const messageLibInfo = await accounts.MessageLib.fromAccountAddress( connection, messageLibPDA, commitmentOrConfig ) return messageLibInfo.wlCaller } /** * Validates a packet. * * @param {Connection} _connection - The connection object. * @param {PublicKey} endpointProgram - The endpoint program public key. * @param {PublicKey} payer - The payer public key. * @param {Uint8Array} encodedPacket - The encoded packet. * * @returns {TransactionInstruction} The transaction instruction. */ validatePacket( _connection: Connection, endpointProgram: PublicKey, payer: PublicKey, encodedPacket: Uint8Array ): TransactionInstruction { const packet = PacketV1Codec.fromBytes(encodedPacket) const [receiveLibrary] = this.deriver.messageLib() const endpoint = new EndpointProgram.Endpoint(endpointProgram) return instructions.createValidatePacketInstruction( { payer: payer, receiveLibrary, anchorRemainingAccounts: endpoint.getVerifyIXAccountMetaForCPI(packet, receiveLibrary), } satisfies instructions.ValidatePacketInstructionAccounts, { params: { packet: encodedPacket } satisfies types.ValidatePacketParams, } satisfies instructions.ValidatePacketInstructionArgs, this.program ) } /** * Gets the account meta of the send instruction for CPI (Cross-Program Invocation). * * @param {Connection} _connection - The connection object. * @param {PublicKey} payer - The payer public key. * @param {PacketPath} _path - The packet path. * * @returns {Promise} A promise that resolves to an array of account meta. */ async getSendIXAccountMetaForCPI( _connection: Connection, payer: PublicKey, _path: SolanaPacketPath ): Promise { const [msgLib] = this.deriver.messageLib() const cpiAccounts = instructions.createSendInstructionAccounts( { endpoint: PublicKey.default, // useless messageLib: msgLib, payer, } satisfies instructions.SendInstructionAccounts, this.program ) cpiAccounts.forEach((key) => { if (!payer.equals(key.pubkey)) { key.isSigner = false } }) return Promise.resolve(cpiAccounts.slice(1)) } /** * Gets the account meta of the quote instruction for CPI (Cross-Program Invocation). * * @param {Connection} _connection - The connection object. * @param {PublicKey} _payer - The payer public key. * @param {PacketPath} _path - The packet path. * @param {Commitment} [commitment] - The commitment. * * @returns {Promise} A promise that resolves to an array of account meta. */ async getQuoteIXAccountMetaForCPI( _connection: Connection, _payer: PublicKey, _path: SolanaPacketPath, _commitment?: Commitment ): Promise { const [msgLib] = this.deriver.messageLib() const keys = instructions.createQuoteInstructionAccounts( { endpoint: PublicKey.default, // useless messageLib: msgLib, } satisfies instructions.QuoteInstructionAccounts, this.program ) return Promise.resolve(keys.slice(1)) } /** * Gets the account meta of the init config instruction for CPI (Cross-Program Invocation). * * @param {PublicKey} payer - The payer public key. * @param {PublicKey} oappID - The oapp ID public key. * @param {number} eid - The endpoint ID. * * @returns {AccountMeta[]} An array of account meta. */ getInitConfigIXAccountMetaForCPI(payer: PublicKey, oappID: PublicKey, eid: number): AccountMeta[] { const [sendConfig] = this.deriver.sendConfig(eid, oappID) const [receiveConfig] = this.deriver.receiveConfig(eid, oappID) const [messageLib] = this.deriver.messageLib() const keys = instructions.createInitConfigInstructionAccounts( { endpoint: PublicKey.default, // useless payer, messageLib, sendConfig, receiveConfig, } satisfies instructions.InitConfigInstructionAccounts, this.program ) keys.forEach((key) => { if (!payer.equals(key.pubkey)) { key.isSigner = false } }) return keys.slice(1) } /** * Gets the account meta of the set config instruction for CPI (Cross-Program Invocation). * * @param {PublicKey} endpointProgram - The endpoint program public key. * @param {PublicKey} oappID - The oapp ID public key. * @param {number} eid - The endpoint ID. * * @returns {Promise} A promise that resolves to an array of account meta. */ async getSetConfigIXAccountMetaForCPI( endpointProgram: PublicKey, oappID: PublicKey, eid: number ): Promise { const [sendConfig] = this.deriver.sendConfig(eid, oappID) const [receiveConfig] = this.deriver.receiveConfig(eid, oappID) const [msgLib] = this.deriver.messageLib() const accounts = instructions.createSetConfigInstructionAccounts( { endpoint: PublicKey.default, // useless sendConfig, receiveConfig, messageLib: msgLib, } satisfies instructions.SetConfigInstructionAccounts, this.program ) return Promise.resolve(accounts.slice(1)) } }