From 0744d50f49a6c9480c8f5c0f18a3ced62b5354d3 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Tue, 2 Jan 2024 12:12:09 -0500 Subject: [PATCH] allow opts to be passed to sendWait on solana platform --- examples/src/tokenBridge.ts | 5 ++- .../protocols/tokenBridge/src/tokenBridge.ts | 13 +++---- platforms/solana/src/platform.ts | 27 ++++++++------ platforms/solana/src/testing/sendSigner.ts | 35 +++++++++---------- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/examples/src/tokenBridge.ts b/examples/src/tokenBridge.ts index 3a9ce5edc..84079f1af 100644 --- a/examples/src/tokenBridge.ts +++ b/examples/src/tokenBridge.ts @@ -33,7 +33,7 @@ import "@wormhole-foundation/connect-sdk-algorand-tokenbridge"; const rcvChain = wh.getChain("Solana"); // Shortcut to allow transferring native gas token - //const token: TokenId | "native" = "native"; + const token: TokenId | "native" = "native"; // A TokenId is just a `{chain, address}` pair and an alias for ChainAddress // The `address` field must be a parsed address. @@ -42,9 +42,8 @@ import "@wormhole-foundation/connect-sdk-algorand-tokenbridge"; // e.g. // wAvax on Solana // const token = Wormhole.chainAddress("Solana", "3Ftc5hTz9sG4huk79onufGiebJNDMZNL8HYgdMJ9E7JR"); - // wSol on Avax - const token = Wormhole.chainAddress("Avalanche", "0xb10563644a6AB8948ee6d7f5b0a1fb15AaEa1E03"); + // const token = Wormhole.chainAddress("Avalanche", "0xb10563644a6AB8948ee6d7f5b0a1fb15AaEa1E03"); // Normalized given token decimals later but can just pass bigints as base units // Note: The Token bridge will dedust past 8 decimals diff --git a/platforms/solana/protocols/tokenBridge/src/tokenBridge.ts b/platforms/solana/protocols/tokenBridge/src/tokenBridge.ts index 3cd60ec43..e87638954 100644 --- a/platforms/solana/protocols/tokenBridge/src/tokenBridge.ts +++ b/platforms/solana/protocols/tokenBridge/src/tokenBridge.ts @@ -1,4 +1,5 @@ import { + Platform, Chain, ChainAddress, ChainId, @@ -49,7 +50,6 @@ import { TransactionInstruction, } from '@solana/web3.js'; -import { Platform } from '@wormhole-foundation/sdk-base/src'; import { TokenBridge as TokenBridgeContract } from './tokenBridgeType'; import { createApproveAuthoritySignerInstruction, @@ -210,6 +210,8 @@ export class SolanaTokenBridge payer?: AnySolanaAddress, ): AsyncGenerator> { if (!payer) throw new Error('Payer required to create attestation'); + + const { blockhash } = await SolanaPlatform.latestBlock(this.connection); const senderAddress = new SolanaAddress(payer).unwrap(); // TODO: createNonce().readUInt32LE(0); const nonce = 0; @@ -231,7 +233,6 @@ export class SolanaTokenBridge ); const transaction = new Transaction().add(transferIx, attestIx); - const { blockhash } = await SolanaPlatform.latestBlock(this.connection); transaction.recentBlockhash = blockhash; transaction.feePayer = senderAddress; transaction.partialSign(messageKey); @@ -244,9 +245,9 @@ export class SolanaTokenBridge payer?: AnySolanaAddress, ): AsyncGenerator> { if (!payer) throw new Error('Payer required to create attestation'); - const senderAddress = new SolanaAddress(payer).unwrap(); const { blockhash } = await SolanaPlatform.latestBlock(this.connection); + const senderAddress = new SolanaAddress(payer).unwrap(); // Yield transactions to verify sigs and post the VAA yield* this.coreBridge.postVaa(senderAddress, vaa, blockhash); @@ -275,7 +276,9 @@ export class SolanaTokenBridge ): Promise> { // https://github.com/wormhole-foundation/wormhole-connect/blob/development/sdk/src/contexts/solana/context.ts#L245 + const { blockhash } = await SolanaPlatform.latestBlock(this.connection); const senderAddress = new SolanaAddress(sender).unwrap(); + // TODO: the payer can actually be different from the sender. We need to allow the user to pass in an optional payer const payerPublicKey = senderAddress; @@ -362,7 +365,6 @@ export class SolanaTokenBridge payerPublicKey, //authority ); - const { blockhash } = await SolanaPlatform.latestBlock(this.connection); const transaction = new Transaction(); transaction.recentBlockhash = blockhash; transaction.feePayer = payerPublicKey; @@ -393,8 +395,8 @@ export class SolanaTokenBridge return; } + const { blockhash } = await SolanaPlatform.latestBlock(this.connection); const tokenAddress = new SolanaAddress(token).unwrap(); - const senderAddress = new SolanaAddress(sender).unwrap(); const senderTokenAddress = await getAssociatedTokenAddress( tokenAddress, @@ -493,7 +495,6 @@ export class SolanaTokenBridge tokenBridgeTransferIx, ); - const { blockhash } = await SolanaPlatform.latestBlock(this.connection); transaction.recentBlockhash = blockhash; transaction.feePayer = senderAddress; transaction.partialSign(message); diff --git a/platforms/solana/src/platform.ts b/platforms/solana/src/platform.ts index 70c1e2f60..db3a9dc91 100644 --- a/platforms/solana/src/platform.ts +++ b/platforms/solana/src/platform.ts @@ -17,11 +17,11 @@ import { SolanaChain } from './chain'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; import { - BlockheightBasedTransactionConfirmationStrategy, Commitment, Connection, ParsedAccountData, PublicKey, + SendOptions, } from '@solana/web3.js'; import { SolanaAddress, SolanaZeroAddress } from './address'; import { @@ -164,23 +164,30 @@ export class SolanaPlatform extends PlatformContext< chain: Chain, rpc: Connection, stxns: SignedTx[], + opts?: SendOptions, ): Promise { const { blockhash, lastValidBlockHeight } = await this.latestBlock(rpc); + // Set the commitment level to match the rpc commitment level + // otherwise, it defaults to finalized + if (!opts) opts = { preflightCommitment: rpc.commitment }; + const txhashes = await Promise.all( stxns.map((stxn) => { - return rpc.sendRawTransaction(stxn); + return rpc.sendRawTransaction(stxn, opts); }), ); await Promise.all( - txhashes.map((txid) => { - const bhs: BlockheightBasedTransactionConfirmationStrategy = { - signature: txid, - blockhash, - lastValidBlockHeight, - }; - return rpc.confirmTransaction(bhs, rpc.commitment); + txhashes.map((signature) => { + return rpc.confirmTransaction( + { + signature, + blockhash, + lastValidBlockHeight, + }, + rpc.commitment, + ); }), ); @@ -191,7 +198,7 @@ export class SolanaPlatform extends PlatformContext< rpc: Connection, commitment?: Commitment, ): Promise<{ blockhash: string; lastValidBlockHeight: number }> { - return rpc.getLatestBlockhash(commitment ?? rpc.commitment); + return rpc.getLatestBlockhash(commitment ?? 'finalized'); } static async getLatestBlock(rpc: Connection): Promise { diff --git a/platforms/solana/src/testing/sendSigner.ts b/platforms/solana/src/testing/sendSigner.ts index 5b0285bad..a0764dfc7 100644 --- a/platforms/solana/src/testing/sendSigner.ts +++ b/platforms/solana/src/testing/sendSigner.ts @@ -1,4 +1,4 @@ -import { Connection, Keypair, Transaction } from '@solana/web3.js'; +import { Connection, Keypair } from '@solana/web3.js'; import { SignAndSendSigner, UnsignedTransaction, @@ -29,10 +29,10 @@ export class SolanaSendSigner< } async signAndSend(tx: UnsignedTransaction[]): Promise { - const txids: string[] = []; - const { blockhash, lastValidBlockHeight } = - await SolanaPlatform.latestBlock(this._rpc); + await SolanaPlatform.latestBlock(this._rpc, 'finalized'); + + const txPromises: Promise[] = []; for (const txn of tx) { const { description, transaction } = txn as SolanaUnsignedTransaction< @@ -42,10 +42,9 @@ export class SolanaSendSigner< console.log(`Signing: ${description} for ${this.address()}`); if (this._debug) { - const st = transaction as Transaction; - console.log(st.signatures); - console.log(st.feePayer); - st.instructions.forEach((ix) => { + console.log(transaction.signatures); + console.log(transaction.feePayer); + transaction.instructions.forEach((ix) => { console.log('Program', ix.programId.toBase58()); console.log('Data: ', ix.data.toString('hex')); console.log( @@ -57,21 +56,21 @@ export class SolanaSendSigner< transaction.partialSign(this._keypair); - const txid = await this._rpc.sendRawTransaction(transaction.serialize(), { - // skipPreflight: true, - // preflightCommitment: this._rpc.commitment, - maxRetries: 5, - }); - - console.log(`Sent: ${description} for ${this.address()}`); + txPromises.push( + this._rpc.sendRawTransaction(transaction.serialize(), { + preflightCommitment: this._rpc.commitment, + }), + ); + } + const txids = await Promise.all(txPromises); + // Wait for finalization + for (const signature of txids) { await this._rpc.confirmTransaction({ - signature: txid, + signature, blockhash, lastValidBlockHeight, }); - - txids.push(txid); } return txids;