UNPKG

@arcium-hq/reader

Version:

Reader SDK for fetching onchain data for Arcium network programs

232 lines (227 loc) 9.24 kB
import { ARCIUM_IDL, getMempoolAccData } from '@arcium-hq/client'; export { getArciumProgramReadonly, getArxNodeAccAddress, getClockAccAddress, getClusterAccAddress, getCompDefAccAddress, getComputationAccAddress, getExecutingPoolAccAddress, getMXEAccAddress, getMempoolAccAddress, getStakingPoolAccAddress } from '@arcium-hq/client'; import * as anchor from '@coral-xyz/anchor'; const ARCIUM_PROGRAM_ID_STRING = 'BKck65TgoKRokMjQM3datB9oRwJ8rAj2jxPXvHXUvcL6'; /** * Discriminator for the ArxNode account type. Used to filter and identify ArxNode accounts on-chain. */ const ARX_NODE_ACC_DISCRIMINATOR = [2, 207, 122, 223, 93, 97, 231, 199]; /** * Discriminator for the Cluster account type. Used to filter and identify Cluster accounts on-chain. */ const CLUSTER_ACC_DISCRIMINATOR = [236, 225, 118, 228, 173, 106, 18, 60]; /** * Discriminator for the MXE account type. Used to filter and identify MXE accounts on-chain. */ const MXE_ACC_DISCRIMINATOR = [103, 26, 85, 250, 179, 159, 17, 117]; /** * The public key of the deployed Arcium program on Solana. */ const ARCIUM_PROGRAM_ID = new anchor.web3.PublicKey(ARCIUM_PROGRAM_ID_STRING); /** * Anchor coder for encoding and decoding Arcium program instructions. */ new anchor.BorshInstructionCoder(ARCIUM_IDL); /** * Anchor event parser for parsing Arcium program events from transaction logs. */ const ARCIUM_EVENT_CODER = new anchor.EventParser(ARCIUM_PROGRAM_ID, new anchor.BorshCoder(ARCIUM_IDL)); /** * Returns all MXE account addresses. * @param conn - The Solana connection object. * @returns Array of MXE account public keys. */ async function getMXEAccAddresses(conn) { return getArciumAccPubkeys(conn, MXE_ACC_DISCRIMINATOR); } /** * Returns all Cluster account addresses. * @param conn - The Solana connection object. * @returns Array of Cluster account public keys. */ async function getClusterAccAddresses(conn) { return getArciumAccPubkeys(conn, CLUSTER_ACC_DISCRIMINATOR); } /** * Returns all ArxNode account addresses. * @param conn - The Solana connection object. * @returns Array of ArxNode account public keys. */ async function getArxNodeAccAddresses(conn) { return getArciumAccPubkeys(conn, ARX_NODE_ACC_DISCRIMINATOR); } /** * Fetches and parses a given MXE account. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the MXE account. * @param commitment - (Optional) RPC commitment level. * @returns The MXEAccount object. */ async function getMXEAccInfo(arciumProgram, address, commitment) { return arciumProgram.account.mxeAccount.fetch(address, commitment); } /** * Fetches and parses a given Cluster account. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the Cluster account. * @param commitment - (Optional) RPC commitment level. * @returns The ClusterAccount object. */ async function getClusterAccInfo(arciumProgram, address, commitment) { return arciumProgram.account.cluster.fetch(address, commitment); } /** * Fetches and parses a given ArxNode account. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the ArxNode account. * @param commitment - (Optional) RPC commitment level. * @returns The ArxNodeAccount object. */ async function getArxNodeAccInfo(arciumProgram, address, commitment) { return arciumProgram.account.arxNode.fetch(address, commitment); } /** * Fetches and parses a given ComputationDefinition account. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the ComputationDefinition account. * @param commitment - (Optional) RPC commitment level. * @returns The ComputationDefinitionAccount object. */ async function getCompDefAccInfo(arciumProgram, address, commitment) { return arciumProgram.account.computationDefinitionAccount.fetch(address, commitment); } /** * Returns all computation references in the mempool for a given account. * Only non-stake computations are included. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the mempool account. * @returns Array of ComputationReference objects. */ async function getComputationsInMempool(arciumProgram, address) { const mempool = await getMempoolAccData(arciumProgram.provider, address); const startIndex = mempool.inner.computations.startIndex; const length = mempool.inner.computations.length; const elems = mempool.inner.computations.elems; function isValid(validBits, idx) { const byte = idx >>> 3; const bit = idx & 7; if (byte >= validBits.length) { // This should never happen, so we'll want to know about it throw new Error(`isValid: byte ${byte} >= validBits.length ${validBits.length}`); } return (validBits[byte] & (1 << bit)) !== 0; } // Handle circular buffer wraparound const refs = []; for (let i = 0; i < length; i++) { const idx = (startIndex + i) % elems.length; // Only save non-stake computations if (isValid(mempool.inner.computations.validBits, idx)) { refs.push(...elems[idx].entries); } } return refs .flat() .filter((ref) => !isNullRef(ref)); } /** * Fetches and parses a given Computation account. * @param arciumProgram - The Anchor program instance. * @param address - The public key of the Computation account. * @param commitment - (Optional) RPC commitment level. * @returns The Computation object. */ async function getComputationAccInfo(arciumProgram, address, commitment) { return arciumProgram.account.computationAccount.fetch(address, commitment); } async function getArciumAccPubkeys(conn, discriminator) { const accs = await conn.getProgramAccounts(ARCIUM_PROGRAM_ID, { dataSlice: { offset: 0, length: 0 }, filters: [ { memcmp: { offset: 0, encoding: 'base64', bytes: Buffer.from(discriminator).toString('base64'), }, }, ], }); return accs.map((acc) => acc.pubkey); } function isNullRef(ref) { const bigZero = new anchor.BN(0); return (ref.computationDefinitionOffset === 0 && ref.computationOffset === bigZero && ref.priorityFee === bigZero); } const ArciumEventNames = [ 'QueueComputationEvent', 'InitComputationEvent', 'CallbackComputationEvent', 'FinalizeComputationEvent', ]; /** * Subscribes to computation-related events for a given MXE program ID. * @param conn - The Solana connection object. * @param mxeProgramId - The public key of the MXE program. * @param callback - Callback function to handle each computation event and its name. * @returns The subscription ID for the logs listener. */ async function subscribeComputations(conn, mxeProgramId, callback) { return conn.onLogs(mxeProgramId, (logs) => { const events = getComputationEventsFromLogs(logs.logs); for (const event of events) { callback(event.event, event.name); } }); } /** * Unsubscribes from computation-related events using the subscription ID. * @param conn - The Solana connection object. * @param subscriptionId - The subscription ID returned by subscribeComputations. */ async function unsubscribeComputations(conn, subscriptionId) { conn.removeOnLogsListener(subscriptionId); } /** * Gets the computation offset from a transaction. * @param tx - The transaction to get the computation offset from. * @returns The computation offset if one is found, otherwise undefined. * @throws Error if multiple computation offsets are found in the transaction. */ function getComputationOffset(tx) { const events = getComputationEventsFromLogs(tx.meta?.logMessages ?? []); if (events.length === 0) { return undefined; } const computationOffsets = events.map((e) => e.event.computationOffset); const computationOffset = computationOffsets[0]; if (computationOffsets.some((offset) => !offset.eq(computationOffset))) { throw new Error(`Multiple computation offsets found in computation: ${JSON.stringify(computationOffsets)}`); } return computationOffset; } /** * Get the events related to arcium computations from a transaction's logs * @param logs - The logs to get the events from * @returns The events in the logs. */ function getComputationEventsFromLogs(logs) { return Array.from(ARCIUM_EVENT_CODER.parseLogs(logs)) .filter((e) => ArciumEventNames.includes(e.name)) .map((e) => { const eventData = { computationOffset: e.data.computation_offset, ...e.data, }; if (e.data.mxe_program_id) { eventData.mxeProgramId = e.data.mxe_program_id; } return { event: eventData, name: e.name, }; }); } export { getArxNodeAccAddresses, getArxNodeAccInfo, getClusterAccAddresses, getClusterAccInfo, getCompDefAccInfo, getComputationAccInfo, getComputationOffset, getComputationsInMempool, getMXEAccAddresses, getMXEAccInfo, subscribeComputations, unsubscribeComputations };