Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solana: Provide ability to resign transactions with ephemeral keys #195

Merged
merged 5 commits into from
Jan 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/src/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ import {
import { getAlgorandSigner } from "@wormhole-foundation/connect-sdk-algorand/src/testing";
import { getCosmwasmSigner } from "@wormhole-foundation/connect-sdk-cosmwasm/src/testing";
import { getEvmSigner } from "@wormhole-foundation/connect-sdk-evm/src/testing";
import { getSolanaSigner } from "@wormhole-foundation/connect-sdk-solana/src/testing";
import { getSolanaSignAndSendSigner } from "@wormhole-foundation/connect-sdk-solana/src/testing";

// Use .env.example as a template for your .env file and populate it with secrets
// for funded accounts on the relevant chain+network combos to run the example
@@ -56,7 +56,7 @@ export async function getStuff<
const platform = chain.platform.utils()._platform;
switch (platform) {
case "Solana":
signer = await getSolanaSigner(await chain.getRpc(), getEnv("SOL_PRIVATE_KEY"));
signer = await getSolanaSignAndSendSigner(await chain.getRpc(), getEnv("SOL_PRIVATE_KEY"));
break;
case "Cosmwasm":
signer = await getCosmwasmSigner(await chain.getRpc(), getEnv("COSMOS_MNEMONIC"));
18 changes: 9 additions & 9 deletions platforms/solana/__tests__/integration/tokenBridge.test.ts
Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ describe('TokenBridge Tests', () => {
expect(attestTx.chain).toEqual(chain);

const { transaction } = attestTx;
expect(transaction.instructions).toHaveLength(2);
expect(transaction.transaction.instructions).toHaveLength(2);
});

test('Submit Attestation', async () => {
@@ -229,17 +229,17 @@ describe('TokenBridge Tests', () => {
});
const submitAttestation = tb.submitAttestation(vaa, sender);

const allTxns = [];
const allTxns: SolanaUnsignedTransaction<TNet>[] = [];
for await (const atx of submitAttestation) {
allTxns.push(atx);
}
expect(allTxns).toHaveLength(3);

const [verifySig, postVaa, create] = allTxns;
//
expect(verifySig.transaction.instructions).toHaveLength(2);
expect(postVaa.transaction.instructions).toHaveLength(1);
expect(create.transaction.instructions).toHaveLength(1);
expect(verifySig.transaction.transaction.instructions).toHaveLength(2);
expect(postVaa.transaction.transaction.instructions).toHaveLength(1);
expect(create.transaction.transaction.instructions).toHaveLength(1);
});
});

@@ -260,7 +260,7 @@ describe('TokenBridge Tests', () => {
const xfer = tb.transfer(sender, recipient, token, amount, payload);
expect(xfer).toBeTruthy();

const allTxns = [];
const allTxns: SolanaUnsignedTransaction<TNet>[] = [];
for await (const tx of xfer) {
allTxns.push(tx);
}
@@ -271,7 +271,7 @@ describe('TokenBridge Tests', () => {
expect(xferTx!.chain).toEqual(chain);

const { transaction } = xferTx;
expect(transaction.instructions).toHaveLength(6);
expect(transaction.transaction.instructions).toHaveLength(6);
// ...
});

@@ -285,7 +285,7 @@ describe('TokenBridge Tests', () => {
);
expect(xfer).toBeTruthy();

const allTxns = [];
const allTxns: SolanaUnsignedTransaction<TNet>[] = [];
for await (const tx of xfer) {
allTxns.push(tx);
}
@@ -296,7 +296,7 @@ describe('TokenBridge Tests', () => {
expect(xferTx.chain).toEqual(chain);

const { transaction } = xferTx;
expect(transaction.instructions).toHaveLength(2);
expect(transaction.transaction.instructions).toHaveLength(2);
});
});
});
12 changes: 4 additions & 8 deletions platforms/solana/protocols/cctp/src/circleBridge.ts
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import {
SolanaChains,
SolanaPlatform,
SolanaPlatformType,
SolanaTransaction,
SolanaUnsignedTransaction,
} from '@wormhole-foundation/connect-sdk-solana';
import { MessageTransmitter, TokenMessenger } from '.';
@@ -105,13 +106,10 @@ export class SolanaCircleBridge<N extends Network, C extends SolanaChains>
senderPk,
);

const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPk;
transaction.add(ix);

yield this.createUnsignedTx(transaction, 'CircleBridge.Redeem');
yield this.createUnsignedTx({ transaction }, 'CircleBridge.Redeem');
}

async *transfer(
@@ -140,13 +138,11 @@ export class SolanaCircleBridge<N extends Network, C extends SolanaChains>
amount,
);

const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderPk;
transaction.add(ix);

yield this.createUnsignedTx(transaction, 'CircleBridge.Transfer');
yield this.createUnsignedTx({ transaction }, 'CircleBridge.Transfer');
}

async isTransferCompleted(message: CircleBridge.Message): Promise<boolean> {
@@ -216,7 +212,7 @@ export class SolanaCircleBridge<N extends Network, C extends SolanaChains>
}

private createUnsignedTx(
txReq: Transaction,
txReq: SolanaTransaction,
description: string,
parallelizable: boolean = false,
): SolanaUnsignedTransaction<N, C> {
34 changes: 16 additions & 18 deletions platforms/solana/protocols/core/src/core.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import {
SolanaPlatform,
SolanaPlatformType,
SolanaUnsignedTransaction,
SolanaTransaction,
} from '@wormhole-foundation/connect-sdk-solana';
import {
ChainId,
@@ -30,14 +31,14 @@ import {
} from '@wormhole-foundation/connect-sdk';
import { Wormhole as WormholeCoreContract } from './types';
import {
BridgeData,
createBridgeFeeTransferInstruction,
createPostMessageInstruction,
createPostVaaInstruction,
createReadOnlyWormholeProgramInterface,
createVerifySignaturesInstructions,
createBridgeFeeTransferInstruction,
derivePostedVaaKey,
getWormholeBridgeData,
BridgeData,
} from './utils';

const SOLANA_SEQ_LOG = 'Program log: Sequence: ';
@@ -82,6 +83,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
throw new Error(
`Network mismatch for chain ${chain}: ${conf.network} != ${network}`,
);

return new SolanaWormholeCore(
network as N,
chain,
@@ -126,24 +128,20 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
fee,
);

const { blockhash } = await SolanaPlatform.latestBlock(this.connection);
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = payer;
transaction.add(feeTransferIx, postMsgIx);
transaction.partialSign(messageAccount);

yield this.createUnsignedTx(transaction, 'Core.PublishMessage');
yield this.createUnsignedTx(
{ transaction, signers: [messageAccount] },
'Core.PublishMessage',
);
}

async *verifyMessage(sender: AnySolanaAddress, vaa: VAA) {
yield* this.postVaa(sender, vaa);
}

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

async *postVaa(sender: AnySolanaAddress, vaa: VAA) {
const postedVaaAddress = derivePostedVaaKey(
this.coreBridge.programId,
Buffer.from(vaa.hash),
@@ -170,11 +168,12 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
const verifySigTx = new Transaction().add(
...verifySignaturesInstructions.slice(i, i + 2),
);
verifySigTx.recentBlockhash = blockhash;
verifySigTx.feePayer = senderAddr;
verifySigTx.partialSign(signatureSet);

yield this.createUnsignedTx(verifySigTx, 'Core.VerifySignature', true);
yield this.createUnsignedTx(
{ transaction: verifySigTx, signers: [signatureSet] },
'Core.VerifySignature',
true,
);
}

// Finally create the VAA posting transaction
@@ -187,10 +186,9 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
signatureSet.publicKey,
),
);
postVaaTx.recentBlockhash = blockhash;
postVaaTx.feePayer = senderAddr;

yield this.createUnsignedTx(postVaaTx, 'Core.PostVAA');
yield this.createUnsignedTx({ transaction: postVaaTx }, 'Core.PostVAA');
}

static parseSequenceFromLog(
@@ -327,7 +325,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
}

private createUnsignedTx(
txReq: Transaction,
txReq: SolanaTransaction,
description: string,
parallelizable: boolean = false,
): SolanaUnsignedTransaction<N, C> {
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import {
SolanaChains,
SolanaPlatform,
SolanaPlatformType,
SolanaTransaction,
SolanaUnsignedTransaction,
} from '@wormhole-foundation/connect-sdk-solana';

@@ -173,18 +174,18 @@ export class SolanaAutomaticTokenBridge<
nonce,
);

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

transaction.add(transferIx);
transaction.recentBlockhash = blockhash;
transaction.feePayer = senderAddress;

yield this.createUnsignedTx(transaction, 'AutomaticTokenBridge.Transfer');
yield this.createUnsignedTx(
{ transaction },
'AutomaticTokenBridge.Transfer',
);
}

async *redeem(sender: AccountAddress<C>, vaa: AutomaticTokenBridge.VAA) {
const redeemTx = new Transaction();
yield this.createUnsignedTx(redeemTx, 'AutomaticTokenBridge.Redeem');
const transaction = new Transaction();
yield this.createUnsignedTx({ transaction }, 'AutomaticTokenBridge.Redeem');
throw new Error('Method not implemented.');
}

@@ -327,7 +328,7 @@ export class SolanaAutomaticTokenBridge<
}

private createUnsignedTx(
txReq: Transaction,
txReq: SolanaTransaction,
description: string,
parallelizable: boolean = false,
): SolanaUnsignedTransaction<N, C> {
Loading