diff --git a/platforms/solana/protocols/core/src/core.ts b/platforms/solana/protocols/core/src/core.ts index fe0e543c56..b14a53afb0 100644 --- a/platforms/solana/protocols/core/src/core.ts +++ b/platforms/solana/protocols/core/src/core.ts @@ -143,7 +143,7 @@ export class SolanaWormholeCore signatureSet.publicKey, ); - // Create a new transaction for every 2 signatures we have to Verify + // Create a new transaction for every 2 instructions for (let i = 0; i < verifySignaturesInstructions.length; i += 2) { const verifySigTx = new Transaction().add( ...verifySignaturesInstructions.slice(i, i + 2), @@ -155,9 +155,6 @@ export class SolanaWormholeCore yield this.createUnsignedTx(verifySigTx, 'Core.VerifySignature', true); } - // TODO: if VAA is already posted, just, like, dont post it again man - // if(this.connection.getAccountInfo(postedVaaAddress)) - // Finally create the VAA posting transaction const postVaaTx = new Transaction().add( createPostVaaInstruction( diff --git a/platforms/solana/src/platform.ts b/platforms/solana/src/platform.ts index 7e2f022d69..70c1e2f600 100644 --- a/platforms/solana/src/platform.ts +++ b/platforms/solana/src/platform.ts @@ -165,14 +165,14 @@ export class SolanaPlatform extends PlatformContext< rpc: Connection, stxns: SignedTx[], ): Promise { + const { blockhash, lastValidBlockHeight } = await this.latestBlock(rpc); + const txhashes = await Promise.all( stxns.map((stxn) => { return rpc.sendRawTransaction(stxn); }), ); - const { blockhash, lastValidBlockHeight } = await this.latestBlockhash(rpc); - await Promise.all( txhashes.map((txid) => { const bhs: BlockheightBasedTransactionConfirmationStrategy = { @@ -187,7 +187,7 @@ export class SolanaPlatform extends PlatformContext< return txhashes; } - static async latestBlockhash( + static async latestBlock( rpc: Connection, commitment?: Commitment, ): Promise<{ blockhash: string; lastValidBlockHeight: number }> { @@ -195,15 +195,12 @@ export class SolanaPlatform extends PlatformContext< } static async getLatestBlock(rpc: Connection): Promise { - const { lastValidBlockHeight } = await this.latestBlockhash(rpc); + const { lastValidBlockHeight } = await this.latestBlock(rpc); return lastValidBlockHeight; } static async getLatestFinalizedBlock(rpc: Connection): Promise { - const { lastValidBlockHeight } = await this.latestBlockhash( - rpc, - 'finalized', - ); + const { lastValidBlockHeight } = await this.latestBlock(rpc, 'finalized'); return lastValidBlockHeight; } diff --git a/platforms/solana/src/testing/index.ts b/platforms/solana/src/testing/index.ts index af5d8964d6..79099ebb86 100644 --- a/platforms/solana/src/testing/index.ts +++ b/platforms/solana/src/testing/index.ts @@ -1 +1,2 @@ export * from './signer'; +export * from './sendSigner'; diff --git a/platforms/solana/src/testing/sendSigner.ts b/platforms/solana/src/testing/sendSigner.ts new file mode 100644 index 0000000000..aed79e38c3 --- /dev/null +++ b/platforms/solana/src/testing/sendSigner.ts @@ -0,0 +1,94 @@ +import { Connection, Keypair, Transaction } from '@solana/web3.js'; +import { + SignAndSendSigner, + Signer, + UnsignedTransaction, + encoding, +} from '@wormhole-foundation/connect-sdk'; +import { Network } from '@wormhole-foundation/sdk-base/src'; +import { SolanaPlatform } from '../platform'; +import { SolanaChains } from '../types'; +import { SolanaUnsignedTransaction } from '../unsignedTransaction'; + +// returns a SignOnlySigner for the Solana platform +export async function getSolanaSignAndSendSigner( + rpc: Connection, + privateKey: string, +): Promise { + const [_, chain] = await SolanaPlatform.chainFromRpc(rpc); + return new SolanaSendSigner( + rpc, + chain, + Keypair.fromSecretKey(encoding.b58.decode(privateKey)), + ); +} + +export class SolanaSendSigner< + N extends Network, + C extends SolanaChains = 'Solana', +> implements SignAndSendSigner +{ + constructor( + private _rpc: Connection, + private _chain: C, + private _keypair: Keypair, + private _debug: boolean = false, + ) {} + + chain(): C { + return this._chain; + } + + address(): string { + return this._keypair.publicKey.toBase58(); + } + + async signAndSend(tx: UnsignedTransaction[]): Promise { + const txids: string[] = []; + + const { blockhash, lastValidBlockHeight } = + await SolanaPlatform.latestBlock(this._rpc); + + for (const txn of tx) { + const { description, transaction } = txn as SolanaUnsignedTransaction< + N, + C + >; + 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('Program', ix.programId.toBase58()); + console.log('Data: ', ix.data.toString('hex')); + console.log( + 'Keys: ', + ix.keys.map((k) => [k, k.pubkey.toBase58()]), + ); + }); + } + + 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()}`); + + await this._rpc.confirmTransaction({ + signature: txid, + blockhash, + lastValidBlockHeight, + }); + + txids.push(txid); + } + + return txids; + } +}