import { AccountInfo, Commitment, Connection, GetAccountInfoConfig, PublicKey } from '@solana/web3.js' import BN from 'bn.js' import { SimpleMessageLibProgram, UlnProgram } from '.' import * as EndpointProgram from './endpoint' import { COUNT_SEED, LZ_COMPOSE_TYPES_SEED, LZ_RECEIVE_TYPES_SEED, OAppBasePDADeriver } from './pda-deriver' import { AddressType } from './types' // TODO - replace default seed to a convention ID Seed /** * Derives the OApp ID PDA. * * @param {PublicKey} program - The program public key. * @param {string} [seed=COUNT_SEED] - The seed. * @param {number} [id] - The ID. * @returns {[PublicKey, number]} The derived PDA and bump. */ export function oappIDPDA(program: PublicKey, seed = COUNT_SEED, id?: number): [PublicKey, number] { if (id != undefined) { return PublicKey.findProgramAddressSync( [Buffer.from(seed, 'utf8'), new BN(id).toArrayLike(Buffer, 'be', 1)], program ) } else { return PublicKey.findProgramAddressSync([Buffer.from(seed, 'utf8')], program) } } /** * Derives the LZ receive types accounts PDA. * * @param {PublicKey} program - The program public key. * @param {PublicKey} [oappId] - The OApp ID. * @returns {[PublicKey, number]} The derived PDA and bump. */ export function deriveLzReceiveTypesAccountsPDA(program: PublicKey, oappId?: PublicKey): [PublicKey, number] { if (oappId != undefined) { return PublicKey.findProgramAddressSync([Buffer.from(LZ_RECEIVE_TYPES_SEED, 'utf8'), oappId.toBytes()], program) } return PublicKey.findProgramAddressSync([Buffer.from(LZ_RECEIVE_TYPES_SEED, 'utf8')], program) } /** * Derives the LZ compose types accounts PDA. * * @param {PublicKey} program - The program public key. * @param {PublicKey} [oappId] - The OApp ID. * @returns {[PublicKey, number]} The derived PDA and bump. */ export function deriveLzComposeTypesAccountsPDA(program: PublicKey, oappId?: PublicKey): [PublicKey, number] { if (oappId != undefined) { return PublicKey.findProgramAddressSync([Buffer.from(LZ_COMPOSE_TYPES_SEED, 'utf8'), oappId.toBytes()], program) } return PublicKey.findProgramAddressSync([Buffer.from(LZ_COMPOSE_TYPES_SEED, 'utf8')], program) } /** * Abstract class representing a base OApp. */ export abstract class BaseOApp { oappBaseDeriver: OAppBasePDADeriver /** * Creates an instance of the BaseOApp class. * * @param {PublicKey} program - The program public key. */ constructor(public program: PublicKey) { this.oappBaseDeriver = new OAppBasePDADeriver(program) } /** * Gets the endpoint. * * @param {Connection} connection - The connection. * @returns {Promise} The endpoint. */ abstract getEndpoint(connection: Connection): Promise /** * Gets the send library program. * * @param {Connection} connection - The connection. * @param {PublicKey} payer - The payer public key. * @param {number} dstEid - The destination endpoint ID. * @param {EndpointProgram.Endpoint} [endpoint] - The endpoint. * @returns {Promise} The send library program. */ abstract getSendLibraryProgram( connection: Connection, payer: PublicKey, dstEid: number, endpoint?: EndpointProgram.Endpoint ): Promise /** * Queries the ID PDA info. * * @param {Connection} connection - The connection. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment or config. * @returns {Promise | null>} The account info. */ async queryIDPDAInfo( connection: Connection, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise | null> { return this.queryPDAInfo(connection, this.idPDA()[0], commitmentOrConfig) } /** * Queries the PDA info. * * @param {Connection} connection - The connection. * @param {PublicKey} pda - The PDA public key. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment or config. * @returns {Promise | null>} The account info. */ async queryPDAInfo( connection: Connection, pda: PublicKey, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise | null> { return connection.getAccountInfo(pda, commitmentOrConfig) } /** * Gets the ID PDA. * * @returns {[PublicKey, number]} The ID PDA and bump. */ idPDA(): [PublicKey, number] { return oappIDPDA(this.program) } /** * Gets the remote. * * @param {Connection} connection - The connection. * @param {number} dstEid - The destination endpoint ID. * @param {Commitment | GetAccountInfoConfig} [commitmentOrConfig] - The commitment or config. * @returns {Promise} The remote data. */ async getRemote( connection: Connection, dstEid: number, commitmentOrConfig?: Commitment | GetAccountInfoConfig ): Promise { const [remotePDA] = this.oappBaseDeriver.remote(dstEid) const info = await this.queryPDAInfo(connection, remotePDA, commitmentOrConfig) if (info) { // the first 8 bytes is account discriminator, so skip it. The next 32 bytes is the address. const result = AddressType.read(info.data, 8) return Uint8Array.from(result) } return null } }