UNPKG

@layerzerolabs/lz-sui-sdk-v2

Version:

158 lines (141 loc) 5.34 kB
import { SuiClient, SuiExecutionResult, SuiTransactionBlockResponse } from '@mysten/sui/client' import { Keypair } from '@mysten/sui/cryptography' import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519' import { Transaction } from '@mysten/sui/transactions' import { fromBase64 } from '@mysten/sui/utils' import { MoveAbortError, SimulateResult, UnclassifiedError } from '../types' export async function simulateTransaction( suiClient: SuiClient, tx: Transaction, sender?: string ): Promise<SimulateResult[]> { const resolvedSender = determineSenderAddress(tx, sender) const { results, error } = await suiClient.devInspectTransactionBlock({ transactionBlock: tx, sender: resolvedSender, }) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (error != undefined && error != null) { throw handleError(error) } if (results === undefined || results === null) { throw new Error('No results found') } const lastCommandResults: SuiExecutionResult = results[results.length - 1] if (lastCommandResults.returnValues === undefined) { throw new Error('No return values found') } return lastCommandResults.returnValues.map((result) => { return { value: typeof result[0] === 'string' ? fromBase64(result[0]) : new Uint8Array(result[0]), type: result[1], } }) } export async function simulateTransactionMultiResult( suiClient: SuiClient, tx: Transaction, resultIndex: number[], sender?: string ): Promise<SimulateResult[][]> { const resolvedSender = determineSenderAddress(tx, sender) const { results, error } = await suiClient.devInspectTransactionBlock({ transactionBlock: tx, sender: resolvedSender, }) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (error != undefined && error != null) { throw handleError(error) } if (results === undefined || results === null) { throw new Error('No results found') } const selectedResults: SuiExecutionResult[] = resultIndex.map((idx) => { return results[idx] }) return selectedResults.map((result) => { if (result.returnValues === undefined) { throw new Error('No return values found') } return result.returnValues.map((result) => { return { value: typeof result[0] === 'string' ? fromBase64(result[0]) : new Uint8Array(result[0]), type: result[1], } }) }) } export async function validateTransaction( client: SuiClient, signer: Keypair, tx: Transaction ): Promise<SuiTransactionBlockResponse> { tx.setSenderIfNotSet(signer.getPublicKey().toSuiAddress()) const result = await client.signAndExecuteTransaction({ signer, transaction: tx, options: { showEffects: true, showObjectChanges: true, showEvents: true, }, }) if (result.effects?.status.status !== 'success') { throw new Error(result.effects?.status.error) } await client.waitForTransaction({ digest: result.digest }) return result } /** * Execute simulate using a moveCall function * @param client - The Sui client * @param moveCallFn - Function that adds moveCall to the transaction (supports both sync and async, any return type) * @param parser - Function to parse the result * @returns The parsed result */ export async function executeSimulate<T>( client: SuiClient, moveCallFn: (tx: Transaction) => unknown, parser: (result: { value: Uint8Array }[]) => T ): Promise<T> { const tx = new Transaction() await moveCallFn(tx) const result = await simulateTransaction(client, tx) return parser(result) } /** * Determines the appropriate sender address using fallback hierarchy: * 1. Use provided sender if available * 2. Use transaction's existing sender if valid * 3. Generate a random sender as last resort * * @param tx - The transaction to get sender from * @param preferredSender - Optional sender address to prioritize * @returns The determined sender address */ function determineSenderAddress(tx: Transaction, preferredSender?: string): string { //eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (preferredSender) return preferredSender const existingSender = tx.getData().sender if (typeof existingSender === 'string') return existingSender return Ed25519Keypair.generate().toSuiAddress() } export function handleError(e: string): unknown { if (e.includes('ExecutionError')) { const majorStatusMatch = e.match(/major_status:\s([A-Z_]+)/) if (majorStatusMatch !== null) { const major_status = majorStatusMatch[1] if (major_status === 'ABORTED') { const subStatusMatch = e.match(/sub_status:\sSome\((\d+)\)/) if (subStatusMatch === null) { return new MoveAbortError(Number.NEGATIVE_INFINITY, e) } else { return new MoveAbortError(Number(subStatusMatch[1]), e) } } } else { return new UnclassifiedError(e) } } return new Error(e) }