import * as wh from "@certusone/wormhole-sdk"; import * as web3 from "@solana/web3.js"; import { dbg, retry, RetryOptions, txLogsFact, _undef } from "./utilities"; import { providers, Signer } from "ethers"; type Provider = providers.Provider; export interface EvmProvider { provider: Provider; } export interface EvmSigner { signer: Signer; } export interface EvmContextNoSigner extends EvmProvider, PIDS { chainId: wh.ChainId; evmWalletAddr: string; weth: string; } export type EvmContext = EvmContextNoSigner & EvmSigner; export interface SolanaConnectionOverrides { commitment: web3.Commitment; skipPreflight: boolean; } export class SolanaContextNoSigner implements SolanaPIDS { public txLogs: ( tx: string | web3.VersionedTransactionResponse, ) => Promise; public readonly solanaProxy: web3.PublicKey; public readonly tokenBridgeSolana: web3.PublicKey; public readonly coreBridgeSolana: web3.PublicKey; public readonly LIQUIDITY_PROGRAM_ID_V4: web3.PublicKey; public readonly ROUTE_PROGRAM_ID: web3.PublicKey; public readonly SERUM_PROGRAM_ID_V3: web3.PublicKey; public readonly wormholeRPC: string; constructor( public readonly conn: web3.Connection, public connectionOverrides: SolanaConnectionOverrides, pids: SolanaPIDS, public readonly isMainnet: boolean, ) { this.txLogs = txLogsFact(this.conn); this.solanaProxy = pids.solanaProxy; this.tokenBridgeSolana = pids.tokenBridgeSolana; this.coreBridgeSolana = pids.coreBridgeSolana; this.LIQUIDITY_PROGRAM_ID_V4 = pids.LIQUIDITY_PROGRAM_ID_V4; this.SERUM_PROGRAM_ID_V3 = pids.SERUM_PROGRAM_ID_V3; this.ROUTE_PROGRAM_ID = pids.ROUTE_PROGRAM_ID; this.wormholeRPC = pids.wormholeRPC; this.conn.getLatestBlockhash(); } } export class SolanaContext extends SolanaContextNoSigner { constructor( solanaConnection: web3.Connection, solanaConnectionOverrides: SolanaConnectionOverrides, public readonly payer: web3.Keypair, pids: SolanaPIDS, isMainnet: boolean, ) { super(solanaConnection, solanaConnectionOverrides, pids, isMainnet); } // This is an arrow function so that we can pass the function object itself // while keeping the correct 'this' // This is less efficient than member funcs in general public signTx = async (tx: web3.Transaction) => { tx.partialSign(this.payer); return tx; }; public sendMessage( msg: web3.MessageV0, signers: web3.Signer[], overrides: web3.ConfirmOptions | {} = {}, retryOpt?: RetryOptions, ): Promise { const tx = new web3.VersionedTransaction(msg); tx.sign(signers); dbg( signers.map(s => s.publicKey.toBase58()), "Signer pubkeys", ); return retry({ retries: 3, backoff: 500, ...retryOpt }, () => { return web3 .sendAndConfirmRawTransaction( this.conn, Buffer.from(tx.serialize()), { ...this.connectionOverrides, ...overrides, }, ) .then(this.txLogs) .catch(e => { dbg("Sending tx failed, retrying..."); throw e; }); }); } public sendTransaction( tx: web3.Transaction, signers: web3.Signer[], overrides: web3.ConfirmOptions | {} = {}, retryOpt?: RetryOptions, ): Promise { dbg( signers.map(s => s.publicKey.toBase58()), "Signer pubkeys", ); return retry({ retries: 3, backoff: 500, ...retryOpt }, () => { return web3 .sendAndConfirmTransaction(this.conn, tx, signers, { ...this.connectionOverrides, ...overrides, }) .then(this.txLogs) .catch(e => { dbg("Sending tx failed, retrying..."); throw e; }); }); } public sendTransactionWithPayer( tx: web3.Transaction, retryOpt?: RetryOptions, ): Promise { return this.sendTransaction(tx, [this.payer], undefined, retryOpt); } public sendIxsWithPayer( ixs: Array, retryOpt?: RetryOptions, ): Promise { const tx = new web3.Transaction(); ixs.forEach(ix => tx.add(ix)); return this.sendTransactionWithPayer(tx, retryOpt); } } export interface Context< Evm extends EvmContext | EvmContextNoSigner = EvmContext, Sol extends SolanaContext | SolanaContextNoSigner = SolanaContext, > extends PIDS { sol: Sol; evm: Evm; } export function newContext< Evm extends EvmContext | EvmContextNoSigner, Sol extends SolanaContext | SolanaContextNoSigner, >(sol: Sol, evm: Evm): Context { return { sol, evm, ...evm, // really just care about PIDS here }; } /* * Globals used for easy prototyping * These should be removed before prod */ export interface SolanaPIDS { solanaProxy: web3.PublicKey; tokenBridgeSolana: web3.PublicKey; coreBridgeSolana: web3.PublicKey; wormholeRPC: string; LIQUIDITY_PROGRAM_ID_V4: web3.PublicKey; SERUM_PROGRAM_ID_V3: web3.PublicKey; ROUTE_PROGRAM_ID: web3.PublicKey; } export interface PIDS extends SolanaPIDS { xRaydiumEvmAddr: string; coreBridgeEvm: string; tokenBridgeEvm: string; } export const solanaChainId = 1;