Skip to content

Commit

Permalink
add sign and send signer for solana (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
barnjamin authored Jan 2, 2024
1 parent 46b01d5 commit bfb7a61
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 38 deletions.
4 changes: 2 additions & 2 deletions platforms/solana/protocols/cctp/src/circleBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class SolanaCircleBridge<N extends Network, C extends SolanaChains>
senderPk,
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPk;
Expand Down Expand Up @@ -140,7 +140,7 @@ export class SolanaCircleBridge<N extends Network, C extends SolanaChains>
amount,
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPk;
Expand Down
9 changes: 3 additions & 6 deletions platforms/solana/protocols/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
consistencyLevel,
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = payer;
Expand All @@ -120,7 +120,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>

async *postVaa(sender: AnySolanaAddress, vaa: VAA, blockhash?: string) {
if (!blockhash)
({ blockhash } = await SolanaPlatform.latestBlockhash(this.connection));
({ blockhash } = await SolanaPlatform.latestBlock(this.connection));

const postedVaaAddress = derivePostedVaaKey(
this.coreBridge.programId,
Expand All @@ -143,7 +143,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
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),
Expand All @@ -155,9 +155,6 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export class SolanaAutomaticTokenBridge<
nonce,
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);

transaction.add(transferIx);
transaction.recentBlockhash = blockhash;
Expand Down
10 changes: 5 additions & 5 deletions platforms/solana/protocols/tokenBridge/src/tokenBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export class SolanaTokenBridge<N extends Network, C extends SolanaChains>
);

const transaction = new Transaction().add(transferIx, attestIx);
const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderAddress;
transaction.partialSign(messageKey);
Expand All @@ -246,7 +246,7 @@ export class SolanaTokenBridge<N extends Network, C extends SolanaChains>
if (!payer) throw new Error('Payer required to create attestation');
const senderAddress = new SolanaAddress(payer).unwrap();

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);

// Yield transactions to verify sigs and post the VAA
yield* this.coreBridge.postVaa(senderAddress, vaa, blockhash);
Expand Down Expand Up @@ -362,7 +362,7 @@ export class SolanaTokenBridge<N extends Network, C extends SolanaChains>
payerPublicKey, //authority
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = payerPublicKey;
Expand Down Expand Up @@ -493,7 +493,7 @@ export class SolanaTokenBridge<N extends Network, C extends SolanaChains>
tokenBridgeTransferIx,
);

const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderAddress;
transaction.partialSign(message);
Expand Down Expand Up @@ -608,7 +608,7 @@ export class SolanaTokenBridge<N extends Network, C extends SolanaChains>
vaa: TokenBridge.TransferVAA,
unwrapNative: boolean = true,
) {
const { blockhash } = await SolanaPlatform.latestBlockhash(this.connection);
const { blockhash } = await SolanaPlatform.latestBlock(this.connection);

// Find the token address local to this chain
const nativeAddress =
Expand Down
13 changes: 5 additions & 8 deletions platforms/solana/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ export class SolanaPlatform<N extends Network> extends PlatformContext<
rpc: Connection,
stxns: SignedTx[],
): Promise<TxHash[]> {
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 = {
Expand All @@ -187,23 +187,20 @@ export class SolanaPlatform<N extends Network> extends PlatformContext<
return txhashes;
}

static async latestBlockhash(
static async latestBlock(
rpc: Connection,
commitment?: Commitment,
): Promise<{ blockhash: string; lastValidBlockHeight: number }> {
return rpc.getLatestBlockhash(commitment ?? rpc.commitment);
}

static async getLatestBlock(rpc: Connection): Promise<number> {
const { lastValidBlockHeight } = await this.latestBlockhash(rpc);
const { lastValidBlockHeight } = await this.latestBlock(rpc);
return lastValidBlockHeight;
}

static async getLatestFinalizedBlock(rpc: Connection): Promise<number> {
const { lastValidBlockHeight } = await this.latestBlockhash(
rpc,
'finalized',
);
const { lastValidBlockHeight } = await this.latestBlock(rpc, 'finalized');
return lastValidBlockHeight;
}

Expand Down
32 changes: 32 additions & 0 deletions platforms/solana/src/testing/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
import { Connection, Keypair } from '@solana/web3.js';
import { SolanaPlatform } from '../platform';
import { SolanaSigner } from './signer';
import { Signer, encoding } from '@wormhole-foundation/connect-sdk';
import { SolanaSendSigner } from './sendSigner';

// returns a SignOnlySigner for the Solana platform
export async function getSolanaSigner(
rpc: Connection,
privateKey: string,
): Promise<Signer> {
const [_, chain] = await SolanaPlatform.chainFromRpc(rpc);
return new SolanaSigner(
chain,
Keypair.fromSecretKey(encoding.b58.decode(privateKey)),
);
}

// returns a SignAndSendSigner for the Solana platform
export async function getSolanaSignAndSendSigner(
rpc: Connection,
privateKey: string,
): Promise<Signer> {
const [_, chain] = await SolanaPlatform.chainFromRpc(rpc);
return new SolanaSendSigner(
rpc,
chain,
Keypair.fromSecretKey(encoding.b58.decode(privateKey)),
);
}

export * from './signer';
export * from './sendSigner';
79 changes: 79 additions & 0 deletions platforms/solana/src/testing/sendSigner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Connection, Keypair, Transaction } from '@solana/web3.js';
import {
SignAndSendSigner,
UnsignedTransaction,
} 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';

export class SolanaSendSigner<
N extends Network,
C extends SolanaChains = 'Solana',
> implements SignAndSendSigner<N, C>
{
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<any[]> {
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;
}
}
17 changes: 1 addition & 16 deletions platforms/solana/src/testing/signer.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,11 @@
import { Connection, Keypair, Transaction } from '@solana/web3.js';
import { Keypair, Transaction } from '@solana/web3.js';
import {
SignOnlySigner,
Signer,
UnsignedTransaction,
encoding,
} from '@wormhole-foundation/connect-sdk';
import { Network } from '@wormhole-foundation/sdk-base/src';
import { SolanaPlatform } from '../platform';
import { SolanaChains } from '../types';

// returns a SignOnlySigner for the Solana platform
export async function getSolanaSigner(
rpc: Connection,
privateKey: string,
): Promise<Signer> {
const [_, chain] = await SolanaPlatform.chainFromRpc(rpc);
return new SolanaSigner(
chain,
Keypair.fromSecretKey(encoding.b58.decode(privateKey)),
);
}

export class SolanaSigner<N extends Network, C extends SolanaChains = 'Solana'>
implements SignOnlySigner<N, C>
{
Expand Down

0 comments on commit bfb7a61

Please sign in to comment.