import * as types from "."; import * as web3 from "@solana/web3.js"; import * as wh from "@certusone/wormhole-sdk"; export function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } export async function waitForAccountToExist( solanaConnection: web3.Connection, key: web3.PublicKey, retryOptions = { backoff: 200, silent: true, retries: 30 }, ) { await retry(retryOptions, async () => { return await solanaConnection.getAccountInfo(key, "confirmed"); }); } export function _undef(x: T | undefined | null, errorMsg: string = ""): T { if (x === undefined) { throw new Error(`Found undefined ${errorMsg}`); } if (x === null) { throw new Error(`Found null ${errorMsg}`); } return x; } export function dropNull(xs: Array): Array { // @ts-ignore return xs.filter(x => x !== null && x !== undefined); } export type RetryOptions = { retries: number; backoff?: number; silent?: boolean; }; export async function retry( { backoff = 10, silent = false, retries }: RetryOptions = { backoff: 10, silent: false, retries: 5, }, f: (retry?: number) => Promise, ): Promise { for (let i = 0; i < retries; i++) { try { const t = await f(i); if (t) { if (i > 0 && !silent) { dbg(`Retry ${i + 1} succeeded`); } return t; } else { throw new Error( "Expected return value to be defined, retrying", ); } } catch (e) { if (!silent) { dbg(`${i + 1}-th retry failed`); console.error(e); } } if (backoff) { await sleep(backoff * i); } } throw new Error("retries exhausted"); } export const setShouldLog = (val: boolean) => (shouldLog = val); export let shouldLog = true; export function txLogsFact(connection: web3.Connection) { return async ( tx: string | web3.VersionedTransactionResponse, ): Promise => { if (!shouldLog) { // @ts-ignore return null; } let response: web3.VersionedTransactionResponse | null; if (typeof tx === "string") { response = await retry( { retries: 4, backoff: 200, silent: false }, () => connection.getTransaction(tx as string, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }), ); } else { response = tx as web3.TransactionResponse; } console.log(response?.meta?.logMessages); return _undef(response, "Expected getTx to return a TxResponse"); }; } //////// LOGGING //////// export async function asyncFilter( arr: Array, predicate: (x: T) => Promise, ): Promise> { return Promise.all(arr.map(predicate)).then(results => arr.filter((_v, index) => results[index]), ); } export function dbg( x: T, message?: string, toString?: (x: T) => string | T, ): T { _log("[DEBUG]", x, message, toString); return x; } export function info( x: T, message?: string, toString?: (x: T) => string | T, ): T { _log("[INFO]", x, message, toString); return x; } export function warn( x: T, message?: string, toString?: (x: T) => string | T, ): T { _log("[WARN]", x, message, toString); return x; } export function err( x: T, message?: string, toString?: (x: T) => string | T, ): T { _log("[ERROR]", x, message, toString); return x; } function _log( level: string, x: T, message?: string, formatter = (x: T) => { try { // @ts-ignore return x.toBase58(); } catch (e) { return x; } }, ) { if (message) { console.log(level, message); console.log(formatter(x)); } else { console.log(level, formatter(x)); } } export function debug_pk_obj>( obj: T, msg?: string, ): T { if (msg) { info(msg); } Object.entries(obj).forEach(([k, v]) => { try { console.log(k, v.toBase58()); } catch (e) { console.log(k, v); } }); return obj; } export function debug_ix( ix: web3.TransactionInstruction, name?: string, ): web3.TransactionInstruction { debug_ix_keys(ix.keys, name); return ix; } export function debug_ix_keys( keys: Array, name?: string, ): Array { if (name) { dbg(name); } keys.forEach((key, i) => { try { console.log( `${i}: ${key.pubkey.toBase58()} w: ${key.isWritable} s: ${ key.isSigner }`, ); } catch (e) { console.log(key); } }); return keys; } export function debug_xTokenLocator(token: types.XTokenLocator, msg?: string) { if (msg) { dbg(msg); } console.log("ChainId: ", token.chainId); console.log( "Address: ", wh.tryUint8ArrayToNative( Buffer.from(token.address.inner), token.chainId as wh.ChainId, ), ); }