From 753c7eadcc8e2e3dce92813f381e5e9136d71b2f Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Thu, 3 Aug 2023 12:51:43 -0500 Subject: [PATCH] solana: clean up test dir --- cross-chain/solana/.gitignore | 1 + cross-chain/solana/Anchor.toml | 4 +- cross-chain/solana/tests/01__tbtc.ts | 359 ++++++++++-------- .../solana/tests/02__wormholeGateway.ts | 218 ++++++----- ...core_bridge.json => core_bridge_data.json} | 0 cross-chain/solana/tests/helpers/consts.ts | 50 +++ cross-chain/solana/tests/helpers/index.ts | 2 + cross-chain/solana/tests/helpers/tbtc.ts | 108 ++++++ .../solana/tests/helpers/tbtcHelpers.ts | 134 ------- cross-chain/solana/tests/helpers/utils.ts | 235 +++++++++--- .../solana/tests/helpers/wormholeGateway.ts | 39 ++ .../tests/helpers/wormholeGatewayHelpers.ts | 67 ---- 12 files changed, 689 insertions(+), 528 deletions(-) rename cross-chain/solana/tests/accounts/{core_bridge.json => core_bridge_data.json} (100%) create mode 100644 cross-chain/solana/tests/helpers/consts.ts create mode 100644 cross-chain/solana/tests/helpers/index.ts create mode 100644 cross-chain/solana/tests/helpers/tbtc.ts delete mode 100644 cross-chain/solana/tests/helpers/tbtcHelpers.ts create mode 100644 cross-chain/solana/tests/helpers/wormholeGateway.ts delete mode 100644 cross-chain/solana/tests/helpers/wormholeGatewayHelpers.ts diff --git a/cross-chain/solana/.gitignore b/cross-chain/solana/.gitignore index 3db15d86f..0f1813a21 100644 --- a/cross-chain/solana/.gitignore +++ b/cross-chain/solana/.gitignore @@ -1,5 +1,6 @@ .anchor +.prettierrc.json .DS_Store target **/*.rs.bk diff --git a/cross-chain/solana/Anchor.toml b/cross-chain/solana/Anchor.toml index 0801ceacc..127ca5360 100644 --- a/cross-chain/solana/Anchor.toml +++ b/cross-chain/solana/Anchor.toml @@ -61,10 +61,10 @@ filename = "tests/accounts/ethereum_token_bridge.json" address = "DapiQYH3BGonhN8cngWcXQ6SrqSm3cwysoznoHr6Sbsx" filename = "tests/accounts/token_bridge_config.json" -### Core Bridge -- Bridge +### Core Bridge -- Bridge Data [[test.validator.clone]] address = "2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn" -filename = "tests/accounts/core_bridge.json" +filename = "tests/accounts/core_bridge_data.json" ### Core Bridge -- Emitter Sequence (Token Bridge's) [[test.validator.account]] diff --git a/cross-chain/solana/tests/01__tbtc.ts b/cross-chain/solana/tests/01__tbtc.ts index 6bc20e51b..d40a34df1 100644 --- a/cross-chain/solana/tests/01__tbtc.ts +++ b/cross-chain/solana/tests/01__tbtc.ts @@ -1,21 +1,27 @@ import * as anchor from "@coral-xyz/anchor"; -import { Program, AnchorError } from "@coral-xyz/anchor"; +import { AnchorError, Program } from "@coral-xyz/anchor"; import * as spl from "@solana/spl-token"; -import * as web3 from '@solana/web3.js'; +import * as web3 from "@solana/web3.js"; +import { expect } from "chai"; import { Tbtc } from "../target/types/tbtc"; -import { expect } from 'chai'; -import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; +import { + addMinter, + checkState, + getConfigPDA, + getGuardianPDA, + getGuardiansPDA, + getMinterPDA, + getMintersPDA, + getTokenPDA, + maybeAuthorityAnd, +} from "./helpers/tbtc"; import { transferLamports } from "./helpers/utils"; -import { maybeAuthorityAnd, getConfigPDA, getTokenPDA, getMinterPDA, getGuardianPDA, getMintersPDA, getGuardiansPDA, checkState, addMinter } from "./helpers/tbtcHelpers"; -async function setup( - program: Program, - authority -) { - const [config,] = getConfigPDA(program); - const [guardians,] = getGuardiansPDA(program); - const [minters,] = getMintersPDA(program); - const [tbtcMintPDA, _] = getTokenPDA(program); +async function setup(program: Program, authority) { + const config = getConfigPDA(); + const guardians = getGuardiansPDA(); + const minters = getMintersPDA(); + const tbtcMintPDA = getTokenPDA(); await program.methods .initialize() @@ -24,7 +30,7 @@ async function setup( config, guardians, minters, - authority: authority.publicKey + authority: authority.publicKey, }) .rpc(); } @@ -32,9 +38,9 @@ async function setup( async function changeAuthority( program: Program, authority, - newAuthority, + newAuthority ) { - const [config,] = getConfigPDA(program); + const config = getConfigPDA(); await program.methods .changeAuthority() .accounts({ @@ -46,11 +52,8 @@ async function changeAuthority( .rpc(); } -async function takeAuthority( - program: Program, - newAuthority, -) { - const [config,] = getConfigPDA(program); +async function takeAuthority(program: Program, newAuthority) { + const config = getConfigPDA(); await program.methods .takeAuthority() .accounts({ @@ -61,11 +64,8 @@ async function takeAuthority( .rpc(); } -async function cancelAuthorityChange( - program: Program, - authority, -) { - const [config,] = getConfigPDA(program); +async function cancelAuthorityChange(program: Program, authority) { + const config = getConfigPDA(); await program.methods .cancelAuthorityChange() .accounts({ @@ -76,41 +76,29 @@ async function cancelAuthorityChange( .rpc(); } -async function checkPendingAuthority( - program: Program, - pendingAuthority, -) { - const [config,] = getConfigPDA(program); +async function checkPendingAuthority(program: Program, pendingAuthority) { + const config = getConfigPDA(); let configState = await program.account.config.fetch(config); expect(configState.pendingAuthority).to.eql(pendingAuthority.publicKey); } -async function checkNoPendingAuthority( - program: Program, -) { - const [config,] = getConfigPDA(program); +async function checkNoPendingAuthority(program: Program) { + const config = getConfigPDA(); let configState = await program.account.config.fetch(config); expect(configState.pendingAuthority).to.equal(null); } -async function checkPaused( - program: Program, - paused: boolean -) { - const [config,] = getConfigPDA(program); +async function checkPaused(program: Program, paused: boolean) { + const config = getConfigPDA(); let configState = await program.account.config.fetch(config); expect(configState.paused).to.equal(paused); } -async function checkMinter( - program: Program, - minter -) { - const [minterInfoPDA, bump] = getMinterPDA(program, minter.publicKey); +async function checkMinter(program: Program, minter) { + const minterInfoPDA = getMinterPDA(minter.publicKey); let minterInfo = await program.account.minterInfo.fetch(minterInfoPDA); expect(minterInfo.minter).to.eql(minter.publicKey); - expect(minterInfo.bump).to.equal(bump); } async function removeMinter( @@ -119,8 +107,8 @@ async function removeMinter( minter, minterInfo ) { - const [config,] = getConfigPDA(program); - const [minters,] = getMintersPDA(program); + const config = getConfigPDA(); + const minters = getMintersPDA(); await program.methods .removeMinter() .accounts({ @@ -128,7 +116,7 @@ async function removeMinter( authority: authority.publicKey, minters, minterInfo: minterInfo, - minter: minter.publicKey + minter: minter.publicKey, }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); @@ -140,9 +128,9 @@ async function addGuardian( guardian, payer ): Promise { - const [config,] = getConfigPDA(program); - const [guardians,] = getGuardiansPDA(program); - const [guardianInfoPDA, _] = getGuardianPDA(program, guardian); + const config = getConfigPDA(); + const guardians = getGuardiansPDA(); + const guardianInfoPDA = getGuardianPDA(guardian); await program.methods .addGuardian() .accounts({ @@ -157,15 +145,11 @@ async function addGuardian( return guardianInfoPDA; } -async function checkGuardian( - program: Program, - guardian -) { - const [guardianInfoPDA, bump] = getGuardianPDA(program, guardian); +async function checkGuardian(program: Program, guardian) { + const guardianInfoPDA = getGuardianPDA(guardian); let guardianInfo = await program.account.guardianInfo.fetch(guardianInfoPDA); expect(guardianInfo.guardian).to.eql(guardian.publicKey); - expect(guardianInfo.bump).to.equal(bump); } async function removeGuardian( @@ -174,8 +158,8 @@ async function removeGuardian( guardian, guardianInfo ) { - const [config,] = getConfigPDA(program); - const [guardians,] = getGuardiansPDA(program); + const config = getConfigPDA(); + const guardians = getGuardiansPDA(); await program.methods .removeGuardian() .accounts({ @@ -183,39 +167,33 @@ async function removeGuardian( authority: authority.publicKey, guardians, guardianInfo: guardianInfo, - guardian: guardian.publicKey + guardian: guardian.publicKey, }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); } -async function pause( - program: Program, - guardian -) { - const [config,] = getConfigPDA(program); - const [guardianInfoPDA, _] = getGuardianPDA(program, guardian); +async function pause(program: Program, guardian) { + const config = getConfigPDA(); + const guardianInfoPDA = getGuardianPDA(guardian); await program.methods .pause() .accounts({ config, guardianInfo: guardianInfoPDA, - guardian: guardian.publicKey + guardian: guardian.publicKey, }) .signers([guardian]) .rpc(); } -async function unpause( - program: Program, - authority -) { - const [config,] = getConfigPDA(program); +async function unpause(program: Program, authority) { + const config = getConfigPDA(); await program.methods .unpause() .accounts({ config, - authority: authority.publicKey + authority: authority.publicKey, }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); @@ -227,21 +205,26 @@ async function mint( minterInfoPDA, recipient, amount, - payer, + payer ) { const connection = program.provider.connection; - const [config,] = getConfigPDA(program); - const [tbtcMintPDA, _] = getTokenPDA(program); - const recipientToken = spl.getAssociatedTokenAddressSync(tbtcMintPDA, recipient.publicKey); - - const tokenData = await spl.getAccount(connection, recipientToken).catch((err) => { - if (err instanceof spl.TokenAccountNotFoundError) { - return null; - } else { - throw err; - }; - }); + const config = getConfigPDA(); + const tbtcMintPDA = getTokenPDA(); + const recipientToken = spl.getAssociatedTokenAddressSync( + tbtcMintPDA, + recipient.publicKey + ); + + const tokenData = await spl + .getAccount(connection, recipientToken) + .catch((err) => { + if (err instanceof spl.TokenAccountNotFoundError) { + return null; + } else { + throw err; + } + }); if (tokenData === null) { const tx = await web3.sendAndConfirmTransaction( @@ -251,14 +234,13 @@ async function mint( payer.publicKey, recipientToken, recipient.publicKey, - tbtcMintPDA, + tbtcMintPDA ) ), [payer.payer] ); } - await program.methods .mint(new anchor.BN(amount)) .accounts({ @@ -278,7 +260,8 @@ describe("tbtc", () => { const program = anchor.workspace.Tbtc as Program; - const authority = (program.provider as anchor.AnchorProvider).wallet as anchor.Wallet; + const authority = (program.provider as anchor.AnchorProvider) + .wallet as anchor.Wallet; const newAuthority = anchor.web3.Keypair.generate(); const minterKeys = anchor.web3.Keypair.generate(); const minter2Keys = anchor.web3.Keypair.generate(); @@ -288,13 +271,13 @@ describe("tbtc", () => { const recipientKeys = anchor.web3.Keypair.generate(); - it('setup', async () => { + it("setup", async () => { await setup(program, authority); - await checkState(program, authority, 0, 0, 0); + await checkState(authority, 0, 0, 0); }); - it('change authority', async () => { - await checkState(program, authority, 0, 0, 0); + it("change authority", async () => { + await checkState(authority, 0, 0, 0); await checkNoPendingAuthority(program); try { await cancelAuthorityChange(program, authority); @@ -302,7 +285,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('NoPendingAuthorityChange'); + expect(err.error.errorCode.code).to.equal("NoPendingAuthorityChange"); expect(err.program.equals(program.programId)).is.true; } try { @@ -311,7 +294,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('NoPendingAuthorityChange'); + expect(err.error.errorCode.code).to.equal("NoPendingAuthorityChange"); expect(err.program.equals(program.programId)).is.true; } @@ -319,7 +302,7 @@ describe("tbtc", () => { await checkPendingAuthority(program, newAuthority); await takeAuthority(program, newAuthority); await checkNoPendingAuthority(program); - await checkState(program, newAuthority, 0, 0, 0); + await checkState(newAuthority, 0, 0, 0); await changeAuthority(program, newAuthority, authority.payer); try { await takeAuthority(program, impostorKeys); @@ -327,7 +310,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotPendingAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotPendingAuthority"); expect(err.program.equals(program.programId)).is.true; } try { @@ -336,7 +319,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotPendingAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotPendingAuthority"); expect(err.program.equals(program.programId)).is.true; } try { @@ -345,22 +328,27 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotAuthority"); expect(err.program.equals(program.programId)).is.true; } await takeAuthority(program, authority); - await checkState(program, authority, 0, 0, 0); - }) + await checkState(authority, 0, 0, 0); + }); - it('add minter', async () => { - await checkState(program, authority, 0, 0, 0); - await addMinter(program, authority, minterKeys.publicKey); + it("add minter", async () => { + await checkState(authority, 0, 0, 0); + await addMinter(authority, minterKeys.publicKey); await checkMinter(program, minterKeys); - await checkState(program, authority, 1, 0, 0); + await checkState(authority, 1, 0, 0); // Transfer lamports to imposter. - await transferLamports(program.provider.connection, authority.payer, impostorKeys.publicKey, 1000000000); + await transferLamports( + program.provider.connection, + authority.payer, + impostorKeys.publicKey, + 1000000000 + ); // await web3.sendAndConfirmTransaction( // program.provider.connection, // new web3.Transaction().add( @@ -374,25 +362,32 @@ describe("tbtc", () => { // ); try { - await addMinter(program, impostorKeys, minter2Keys.publicKey); + await addMinter(impostorKeys, minter2Keys.publicKey); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotAuthority"); expect(err.program.equals(program.programId)).is.true; } }); - it('mint', async () => { - await checkState(program, authority, 1, 0, 0); - const [minterInfoPDA, _] = getMinterPDA(program, minterKeys.publicKey); + it("mint", async () => { + await checkState(authority, 1, 0, 0); + const minterInfoPDA = getMinterPDA(minterKeys.publicKey); await checkMinter(program, minterKeys); // await setupMint(program, authority, recipientKeys); - await mint(program, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); + await mint( + program, + minterKeys, + minterInfoPDA, + recipientKeys, + 1000, + authority + ); - await checkState(program, authority, 1, 0, 1000); + await checkState(authority, 1, 0, 1000); // // Burn for next test. // const ix = spl.createBurnCheckedInstruction( @@ -402,44 +397,57 @@ describe("tbtc", () => { // BURN_QUANTITY * (10**MINT_DECIMALS), // Number of tokens to burn // MINT_DECIMALS // Number of Decimals of the Token Mint // ) - }); - it('won\'t mint', async () => { - await checkState(program, authority, 1, 0, 1000); - const [minterInfoPDA, _] = getMinterPDA(program, minterKeys.publicKey); + it("won't mint", async () => { + await checkState(authority, 1, 0, 1000); + const minterInfoPDA = getMinterPDA(minterKeys.publicKey); await checkMinter(program, minterKeys); // await setupMint(program, authority, recipientKeys); try { - await mint(program, impostorKeys, minterInfoPDA, recipientKeys, 1000, authority); + await mint( + program, + impostorKeys, + minterInfoPDA, + recipientKeys, + 1000, + authority + ); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('ConstraintSeeds'); + expect(err.error.errorCode.code).to.equal("ConstraintSeeds"); expect(err.program.equals(program.programId)).is.true; } }); - it('use two minters', async () => { - await checkState(program, authority, 1, 0, 1000); - const [minterInfoPDA, _] = getMinterPDA(program, minterKeys.publicKey); + it("use two minters", async () => { + await checkState(authority, 1, 0, 1000); + const minterInfoPDA = getMinterPDA(minterKeys.publicKey); await checkMinter(program, minterKeys); - const minter2InfoPDA = await addMinter(program, authority, minter2Keys.publicKey); + const minter2InfoPDA = await addMinter(authority, minter2Keys.publicKey); await checkMinter(program, minter2Keys); - await checkState(program, authority, 2, 0, 1000); + await checkState(authority, 2, 0, 1000); // await setupMint(program, authority, recipientKeys); // cannot mint with wrong keys try { - await mint(program, minter2Keys, minterInfoPDA, recipientKeys, 1000, authority); + await mint( + program, + minter2Keys, + minterInfoPDA, + recipientKeys, + 1000, + authority + ); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('ConstraintSeeds'); + expect(err.error.errorCode.code).to.equal("ConstraintSeeds"); expect(err.program.equals(program.programId)).is.true; } @@ -450,25 +458,32 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('ConstraintSeeds'); + expect(err.error.errorCode.code).to.equal("ConstraintSeeds"); expect(err.program.equals(program.programId)).is.true; } - await mint(program, minterKeys, minterInfoPDA, recipientKeys, 500, authority); - await checkState(program, authority, 2, 0, 1500); + await mint( + program, + minterKeys, + minterInfoPDA, + recipientKeys, + 500, + authority + ); + await checkState(authority, 2, 0, 1500); }); - it('remove minter', async () => { - await checkState(program, authority, 2, 0, 1500); - const [minter2InfoPDA, _] = getMinterPDA(program, minter2Keys.publicKey); + it("remove minter", async () => { + await checkState(authority, 2, 0, 1500); + const minter2InfoPDA = getMinterPDA(minter2Keys.publicKey); await checkMinter(program, minter2Keys); await removeMinter(program, authority, minter2Keys, minter2InfoPDA); - await checkState(program, authority, 1, 0, 1500); + await checkState(authority, 1, 0, 1500); }); - it('won\'t remove minter', async () => { - await checkState(program, authority, 1, 0, 1500); - const [minterInfoPDA, _] = getMinterPDA(program, minterKeys.publicKey); + it("won't remove minter", async () => { + await checkState(authority, 1, 0, 1500); + const minterInfoPDA = getMinterPDA(minterKeys.publicKey); await checkMinter(program, minterKeys); try { @@ -477,12 +492,12 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotAuthority"); expect(err.program.equals(program.programId)).is.true; } await removeMinter(program, authority, minterKeys, minterInfoPDA); - await checkState(program, authority, 0, 0, 1500); + await checkState(authority, 0, 0, 1500); try { await removeMinter(program, authority, minterKeys, minterInfoPDA); @@ -490,16 +505,16 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('AccountNotInitialized'); + expect(err.error.errorCode.code).to.equal("AccountNotInitialized"); expect(err.program.equals(program.programId)).is.true; } }); - it('add guardian', async () => { - await checkState(program, authority, 0, 0, 1500); + it("add guardian", async () => { + await checkState(authority, 0, 0, 1500); await addGuardian(program, authority, guardianKeys, authority); await checkGuardian(program, guardianKeys); - await checkState(program, authority, 0, 1, 1500); + await checkState(authority, 0, 1, 1500); try { await addGuardian(program, impostorKeys, guardian2Keys, authority); @@ -507,28 +522,33 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotAuthority"); expect(err.program.equals(program.programId)).is.true; } }); - it('remove guardian', async () => { - await checkState(program, authority, 0, 1, 1500); - const [guardianInfoPDA, _] = getGuardianPDA(program, guardianKeys); + it("remove guardian", async () => { + await checkState(authority, 0, 1, 1500); + const guardianInfoPDA = getGuardianPDA(guardianKeys); await checkGuardian(program, guardianKeys); try { - await removeGuardian(program, impostorKeys, guardianKeys, guardianInfoPDA); + await removeGuardian( + program, + impostorKeys, + guardianKeys, + guardianInfoPDA + ); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotAuthority'); + expect(err.error.errorCode.code).to.equal("IsNotAuthority"); expect(err.program.equals(program.programId)).is.true; } await removeGuardian(program, authority, guardianKeys, guardianInfoPDA); - await checkState(program, authority, 0, 0, 1500); + await checkState(authority, 0, 0, 1500); try { await removeGuardian(program, authority, guardianKeys, guardianInfoPDA); @@ -536,21 +556,21 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('AccountNotInitialized'); + expect(err.error.errorCode.code).to.equal("AccountNotInitialized"); expect(err.program.equals(program.programId)).is.true; } }); - it('pause', async () => { - await checkState(program, authority, 0, 0, 1500); + it("pause", async () => { + await checkState(authority, 0, 0, 1500); await addGuardian(program, authority, guardianKeys, authority); await checkPaused(program, false); await pause(program, guardianKeys); await checkPaused(program, true); }); - it('unpause', async () => { - await checkState(program, authority, 0, 1, 1500); + it("unpause", async () => { + await checkState(authority, 0, 1, 1500); await checkPaused(program, true); await unpause(program, authority); await checkPaused(program, false); @@ -562,34 +582,41 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsNotPaused'); + expect(err.error.errorCode.code).to.equal("IsNotPaused"); expect(err.program.equals(program.programId)).is.true; } }); - it('won\'t mint when paused', async () => { - await checkState(program, authority, 0, 1, 1500); - const minterInfoPDA = await addMinter(program, authority, minterKeys.publicKey); + it("won't mint when paused", async () => { + await checkState(authority, 0, 1, 1500); + const minterInfoPDA = await addMinter(authority, minterKeys.publicKey); await pause(program, guardianKeys); // await setupMint(program, authority, recipientKeys); try { - await mint(program, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); + await mint( + program, + minterKeys, + minterInfoPDA, + recipientKeys, + 1000, + authority + ); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsPaused'); + expect(err.error.errorCode.code).to.equal("IsPaused"); expect(err.program.equals(program.programId)).is.true; } await unpause(program, authority); await checkPaused(program, false); - }) + }); - it('use two guardians', async () => { - await checkState(program, authority, 1, 1, 1500); - const [guardianInfoPDA, _] = getGuardianPDA(program, guardianKeys); + it("use two guardians", async () => { + await checkState(authority, 1, 1, 1500); + const guardianInfoPDA = getGuardianPDA(guardianKeys); await checkGuardian(program, guardianKeys); await addGuardian(program, authority, guardian2Keys, authority); await checkGuardian(program, guardian2Keys); @@ -602,7 +629,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('IsPaused'); + expect(err.error.errorCode.code).to.equal("IsPaused"); expect(err.program.equals(program.programId)).is.true; } @@ -618,7 +645,7 @@ describe("tbtc", () => { } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('ConstraintSeeds'); + expect(err.error.errorCode.code).to.equal("ConstraintSeeds"); expect(err.program.equals(program.programId)).is.true; } }); diff --git a/cross-chain/solana/tests/02__wormholeGateway.ts b/cross-chain/solana/tests/02__wormholeGateway.ts index 9b4d712a7..964e46260 100644 --- a/cross-chain/solana/tests/02__wormholeGateway.ts +++ b/cross-chain/solana/tests/02__wormholeGateway.ts @@ -1,26 +1,32 @@ -import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock"; +import { redeemOnSolana, tryNativeToHexString } from "@certusone/wormhole-sdk"; +import { MockEthereumTokenBridge } from "@certusone/wormhole-sdk/lib/cjs/mock"; import * as tokenBridge from "@certusone/wormhole-sdk/lib/cjs/solana/tokenBridge"; import * as coreBridge from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; -import { NodeWallet } from "@certusone/wormhole-sdk/lib/cjs/solana"; -import { parseTokenTransferVaa, postVaaSolana, redeemOnSolana, tryNativeToHexString } from "@certusone/wormhole-sdk"; import * as anchor from "@coral-xyz/anchor"; -import { Program, AnchorError } from "@coral-xyz/anchor"; -import * as spl from "@solana/spl-token"; -import { expect } from 'chai'; -import { WormholeGateway } from "../target/types/wormhole_gateway"; -import { generatePayer, getOrCreateTokenAccount } from "./helpers/utils"; -import { getCustodianPDA, getTokenBridgeRedeemerPDA, getTokenBridgeSenderPDA, getWrappedTbtcTokenPDA } from "./helpers/wormholeGatewayHelpers"; -import * as tbtc from "./helpers/tbtcHelpers"; -import { web3 } from "@coral-xyz/anchor"; +import { AnchorError, Program, web3 } from "@coral-xyz/anchor"; +import { getMint } from "@solana/spl-token"; +import { expect } from "chai"; import { Tbtc } from "../target/types/tbtc"; - -const SOLANA_CORE_BRIDGE_ADDRESS = "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"; -const SOLANA_TOKEN_BRIDGE_ADDRESS = "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"; -const ETHEREUM_TOKEN_BRIDGE_ADDRESS = "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"; -const ETHEREUM_TBTC_ADDRESS = "0x18084fbA666a33d37592fA2633fD49a74DD93a88"; - -const GUARDIAN_SET_INDEX = 3; - +import { WormholeGateway } from "../target/types/wormhole_gateway"; +import { + ETHEREUM_TBTC_ADDRESS, + ETHEREUM_TOKEN_BRIDGE_ADDRESS, + GUARDIAN_SET_INDEX, + SOLANA_CORE_BRIDGE_ADDRESS, + SOLANA_TOKEN_BRIDGE_ADDRESS, + WRAPPED_TBTC_MINT, + generatePayer, + getOrCreateTokenAccount, + mockSignAndPostVaa, + preloadWrappedTbtc, +} from "./helpers"; +import * as tbtc from "./helpers/tbtc"; +import { + getCustodianPDA, + getTokenBridgeRedeemerPDA, + getTokenBridgeSenderPDA, + getWrappedTbtcTokenPDA, +} from "./helpers/wormholeGateway"; async function setup( program: Program, @@ -28,14 +34,18 @@ async function setup( authority, mintingLimit: number ) { - const [custodian,] = getCustodianPDA(program); - const [tbtcMint,] = tbtc.getTokenPDA(tbtcProgram); - const [gatewayWrappedTbtcToken,] = getWrappedTbtcTokenPDA(program); - const [tokenBridgeSender,] = getTokenBridgeSenderPDA(program); - const [tokenBridgeRedeemer,] = getTokenBridgeRedeemerPDA(program); - - const wrappedTbtcMint = tokenBridge.deriveWrappedMintKey(SOLANA_TOKEN_BRIDGE_ADDRESS, 2, ETHEREUM_TBTC_ADDRESS); - + const custodian = getCustodianPDA(); + const tbtcMint = tbtc.getTokenPDA(); + const gatewayWrappedTbtcToken = getWrappedTbtcTokenPDA(); + const tokenBridgeSender = getTokenBridgeSenderPDA(); + const tokenBridgeRedeemer = getTokenBridgeRedeemerPDA(); + + const wrappedTbtcMint = tokenBridge.deriveWrappedMintKey( + SOLANA_TOKEN_BRIDGE_ADDRESS, + 2, + ETHEREUM_TBTC_ADDRESS + ); + await program.methods .initialize(new anchor.BN(mintingLimit)) .accounts({ @@ -53,13 +63,14 @@ async function setup( async function checkState( program: Program, expectedAuthority, - expectedMintingLimit, + expectedMintingLimit // expectedMintedAmount, ) { - const [custodian,] = getCustodianPDA(program); + const custodian = getCustodianPDA(); let custodianState = await program.account.custodian.fetch(custodian); - expect(custodianState.mintingLimit.eq(new anchor.BN(expectedMintingLimit))).to.be.true; + expect(custodianState.mintingLimit.eq(new anchor.BN(expectedMintingLimit))).to + .be.true; expect(custodianState.authority).to.eql(expectedAuthority.publicKey); } @@ -72,14 +83,15 @@ describe("wormhole-gateway", () => { const tbtcProgram = anchor.workspace.Tbtc as Program; - const [custodian,] = getCustodianPDA(program); - const [tbtcMint,] = tbtc.getTokenPDA(tbtcProgram); - const [tbtcConfig,] = tbtc.getConfigPDA(tbtcProgram); - const [gatewayWrappedTbtcToken,] = getWrappedTbtcTokenPDA(program); - const [tokenBridgeSender,] = getTokenBridgeSenderPDA(program); - const [tokenBridgeRedeemer,] = getTokenBridgeRedeemerPDA(program); + const custodian = getCustodianPDA(); + const tbtcMint = tbtc.getTokenPDA(); + const tbtcConfig = tbtc.getConfigPDA(); + const gatewayWrappedTbtcToken = getWrappedTbtcTokenPDA(); + const tokenBridgeSender = getTokenBridgeSenderPDA(); + const tokenBridgeRedeemer = getTokenBridgeRedeemerPDA(); - const authority = (program.provider as anchor.AnchorProvider).wallet as anchor.Wallet; + const authority = (program.provider as anchor.AnchorProvider) + .wallet as anchor.Wallet; const newAuthority = anchor.web3.Keypair.generate(); const minterKeys = anchor.web3.Keypair.generate(); const minter2Keys = anchor.web3.Keypair.generate(); @@ -89,23 +101,38 @@ describe("wormhole-gateway", () => { const recipientKeys = anchor.web3.Keypair.generate(); - const ethereumTokenBridge = new mock.MockEthereumTokenBridge(ETHEREUM_TOKEN_BRIDGE_ADDRESS); + const ethereumTokenBridge = new MockEthereumTokenBridge( + ETHEREUM_TOKEN_BRIDGE_ADDRESS + ); - it('check core bridge and token bridge', async () => { + it("check core bridge and token bridge", async () => { // Check core bridge guardian set. - const guardianSetData = await coreBridge.getGuardianSet(connection, SOLANA_CORE_BRIDGE_ADDRESS, GUARDIAN_SET_INDEX); + const guardianSetData = await coreBridge.getGuardianSet( + connection, + SOLANA_CORE_BRIDGE_ADDRESS, + GUARDIAN_SET_INDEX + ); expect(guardianSetData.keys).has.length(1); // Set up new wallet const payer = await generatePayer(connection, authority.payer); // Check wrapped tBTC mint. - const wrappedTbtcMint = tokenBridge.deriveWrappedMintKey(SOLANA_TOKEN_BRIDGE_ADDRESS, 2, ETHEREUM_TBTC_ADDRESS); - const mintData = await spl.getMint(connection, wrappedTbtcMint); + const wrappedTbtcMint = tokenBridge.deriveWrappedMintKey( + SOLANA_TOKEN_BRIDGE_ADDRESS, + 2, + ETHEREUM_TBTC_ADDRESS + ); + const mintData = await getMint(connection, wrappedTbtcMint); expect(mintData.decimals).to.equal(8); expect(mintData.supply).to.equal(BigInt(90)); - const wrappedTbtcToken = await getOrCreateTokenAccount(connection, payer, wrappedTbtcMint, payer.publicKey); + const wrappedTbtcToken = await getOrCreateTokenAccount( + connection, + payer, + wrappedTbtcMint, + payer.publicKey + ); // Bridge tbtc to token account. const published = ethereumTokenBridge.publishTransferTokens( @@ -126,72 +153,59 @@ describe("wormhole-gateway", () => { SOLANA_CORE_BRIDGE_ADDRESS, SOLANA_TOKEN_BRIDGE_ADDRESS, payer.publicKey, - signedVaa, + signedVaa ); await web3.sendAndConfirmTransaction(connection, tx, [payer]); }); - it('setup', async () => { + it("setup", async () => { await setup(program, tbtcProgram, authority, 10000); await checkState(program, authority, 10000); - await tbtc.checkState(tbtcProgram, authority, 1, 2, 1500); + await tbtc.checkState(authority, 1, 2, 1500); }); - it('update minting limit', async () => { + it("update minting limit", async () => { await program.methods .updateMintingLimit(new anchor.BN(20000)) .accounts({ custodian, - authority: authority.publicKey + authority: authority.publicKey, }) .rpc(); await checkState(program, authority, 20000); }); - it('deposit wrapped tokens', async () => { - const [custodian,] = getCustodianPDA(program); - const minterInfo = await tbtc.addMinter(tbtcProgram, authority, custodian); + it("deposit wrapped tokens", async () => { + const custodian = getCustodianPDA(); + const minterInfo = await tbtc.addMinter(authority, custodian); // Set up new wallet const payer = await generatePayer(connection, authority.payer); // Check wrapped tBTC mint. - const wrappedTbtcMint = tokenBridge.deriveWrappedMintKey(SOLANA_TOKEN_BRIDGE_ADDRESS, 2, ETHEREUM_TBTC_ADDRESS); - const wrappedTbtcToken = await getOrCreateTokenAccount(connection, payer, wrappedTbtcMint, payer.publicKey); - - // Bridge tbtc to token account. - const published = ethereumTokenBridge.publishTransferTokens( - tryNativeToHexString(ETHEREUM_TBTC_ADDRESS, "ethereum"), - 2, + const wrappedTbtcToken = await preloadWrappedTbtc( + connection, + payer, + ethereumTokenBridge, BigInt("100000000000"), - 1, - wrappedTbtcToken.address.toBuffer().toString("hex"), - BigInt(0), - 0, - 0 + payer.publicKey ); - const signedVaa = await mockSignAndPostVaa(connection, payer, published); - - const tx = await redeemOnSolana( + const recipientToken = await getOrCreateTokenAccount( connection, - SOLANA_CORE_BRIDGE_ADDRESS, - SOLANA_TOKEN_BRIDGE_ADDRESS, - payer.publicKey, - signedVaa, + payer, + tbtcMint, + payer.publicKey ); - await web3.sendAndConfirmTransaction(connection, tx, [payer]); - - const recipientToken = await getOrCreateTokenAccount(connection, payer, tbtcMint, payer.publicKey); await program.methods .depositWormholeTbtc(new anchor.BN(500)) .accounts({ custodian, wrappedTbtcToken: gatewayWrappedTbtcToken, - wrappedTbtcMint, + wrappedTbtcMint: WRAPPED_TBTC_MINT, tbtcMint, - recipientWrappedToken: wrappedTbtcToken.address, + recipientWrappedToken: wrappedTbtcToken, recipientToken: recipientToken.address, recipient: payer.publicKey, tbtcConfig, @@ -201,51 +215,31 @@ describe("wormhole-gateway", () => { .signers(tbtc.maybeAuthorityAnd(payer, [])) .rpc(); - await tbtc.checkState(tbtcProgram, authority, 2, 2, 2000); + await tbtc.checkState(authority, 2, 2, 2000); try { await program.methods - .depositWormholeTbtc(new anchor.BN(50000)) - .accounts({ - custodian, - wrappedTbtcToken: gatewayWrappedTbtcToken, - wrappedTbtcMint, - tbtcMint, - recipientWrappedToken: wrappedTbtcToken.address, - recipientToken: recipientToken.address, - recipient: payer.publicKey, - tbtcConfig, - minterInfo, - tbtcProgram: tbtcProgram.programId, - }) - .signers(tbtc.maybeAuthorityAnd(payer, [])) - .rpc(); + .depositWormholeTbtc(new anchor.BN(50000)) + .accounts({ + custodian, + wrappedTbtcToken: gatewayWrappedTbtcToken, + wrappedTbtcMint: WRAPPED_TBTC_MINT, + tbtcMint, + recipientWrappedToken: wrappedTbtcToken, + recipientToken: recipientToken.address, + recipient: payer.publicKey, + tbtcConfig, + minterInfo, + tbtcProgram: tbtcProgram.programId, + }) + .signers(tbtc.maybeAuthorityAnd(payer, [])) + .rpc(); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal('MintingLimitExceeded'); + expect(err.error.errorCode.code).to.equal("MintingLimitExceeded"); expect(err.program.equals(program.programId)).is.true; } }); }); - -async function mockSignAndPostVaa(connection: web3.Connection, payer: web3.Keypair, published: Buffer) { - const guardians = new mock.MockGuardians( - GUARDIAN_SET_INDEX, - ["cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"] - ); - - // Add guardian signature. - const signedVaa = guardians.addSignatures(published, [0]); - - // Verify and post VAA. - await postVaaSolana(connection, - new NodeWallet(payer).signTransaction, - SOLANA_CORE_BRIDGE_ADDRESS, - payer.publicKey, - signedVaa - ); - - return signedVaa; -} diff --git a/cross-chain/solana/tests/accounts/core_bridge.json b/cross-chain/solana/tests/accounts/core_bridge_data.json similarity index 100% rename from cross-chain/solana/tests/accounts/core_bridge.json rename to cross-chain/solana/tests/accounts/core_bridge_data.json diff --git a/cross-chain/solana/tests/helpers/consts.ts b/cross-chain/solana/tests/helpers/consts.ts new file mode 100644 index 000000000..e27570ec2 --- /dev/null +++ b/cross-chain/solana/tests/helpers/consts.ts @@ -0,0 +1,50 @@ +import { PublicKey } from "@solana/web3.js"; + +export const TBTC_PROGRAM_ID = new PublicKey( + "HksEtDgsXJV1BqcuhzbLRTmXp5gHgHJktieJCtQd3pG" +); +export const WORMHOLE_GATEWAY_PROGRAM_ID = new PublicKey( + "8H9F5JGbEMyERycwaGuzLS5MQnV7dn2wm2h6egJ3Leiu" +); + +export const SOLANA_CORE_BRIDGE_ADDRESS = new PublicKey( + "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth" +); +export const SOLANA_TOKEN_BRIDGE_ADDRESS = new PublicKey( + "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb" +); + +export const ETHEREUM_TOKEN_BRIDGE_ADDRESS = + "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"; +export const ETHEREUM_TBTC_ADDRESS = + "0x18084fbA666a33d37592fA2633fD49a74DD93a88"; + +export const GUARDIAN_SET_INDEX = 3; +export const GUARDIAN_DEVNET_PRIVATE_KEYS = [ + "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0", +]; + +// relevant core bridge PDAs +export const CORE_BRIDGE_DATA = new PublicKey( + "2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn" +); +export const CORE_EMITTER_SEQUENCE = new PublicKey( + "GF2ghkjwsR9CHkGk1RvuZrApPZGBZynxMm817VNi51Nf" +); +export const CORE_FEE_COLLECTOR = new PublicKey( + "9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy" +); + +// relevant token bridge PDAs +export const WRAPPED_TBTC_MINT = new PublicKey( + "25rXTx9zDZcHyTav5sRqM6YBvTGu9pPH9yv83uAEqbgG" +); +export const WRAPPED_TBTC_ASSET = new PublicKey( + "5LEUZpBxUQmoxoNGqmYmFEGAPDuhWbAY5CGt519UixLo" +); +export const ETHEREUM_ENDPOINT = new PublicKey( + "DujfLgMKW71CT2W8pxknf42FT86VbcK5PjQ6LsutjWKC" +); +export const TOKEN_BRIDGE_CONFIG = new PublicKey( + "DapiQYH3BGonhN8cngWcXQ6SrqSm3cwysoznoHr6Sbsx" +); diff --git a/cross-chain/solana/tests/helpers/index.ts b/cross-chain/solana/tests/helpers/index.ts new file mode 100644 index 000000000..cb1b44601 --- /dev/null +++ b/cross-chain/solana/tests/helpers/index.ts @@ -0,0 +1,2 @@ +export * from "./consts"; +export * from "./utils"; \ No newline at end of file diff --git a/cross-chain/solana/tests/helpers/tbtc.ts b/cross-chain/solana/tests/helpers/tbtc.ts new file mode 100644 index 000000000..93cd9194e --- /dev/null +++ b/cross-chain/solana/tests/helpers/tbtc.ts @@ -0,0 +1,108 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { getMint } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; +import { expect } from "chai"; +import { Tbtc } from "../../target/types/tbtc"; +import { TBTC_PROGRAM_ID } from "./consts"; + +export function maybeAuthorityAnd(signer, signers) { + return signers.concat( + signer instanceof (anchor.Wallet as any) ? [] : [signer] + ); +} + +export function getConfigPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("config")], + TBTC_PROGRAM_ID + )[0]; +} + +export function getTokenPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("tbtc-mint")], + TBTC_PROGRAM_ID + )[0]; +} + +export function getMinterPDA(minter: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("minter-info"), minter.toBuffer()], + TBTC_PROGRAM_ID + )[0]; +} + +export function getGuardianPDA(guardian): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("guardian-info"), guardian.publicKey.toBuffer()], + TBTC_PROGRAM_ID + )[0]; +} + +export function getGuardiansPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("guardians")], + TBTC_PROGRAM_ID + )[0]; +} + +export function getMintersPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("minters")], + TBTC_PROGRAM_ID + )[0]; +} + +export async function checkState( + expectedAuthority, + expectedMinters: number, + expectedGuardians: number, + expectedTokensSupply +) { + const program = anchor.workspace.Tbtc as Program; + + const config = getConfigPDA(); + let configState = await program.account.config.fetch(config); + + expect(configState.authority).to.eql(expectedAuthority.publicKey); + expect(configState.numMinters).to.equal(expectedMinters); + expect(configState.numGuardians).to.equal(expectedGuardians); + + let tbtcMint = configState.mint; + + let mintState = await getMint(program.provider.connection, tbtcMint); + + expect(mintState.supply).to.equal(BigInt(expectedTokensSupply)); + + const guardians = getGuardiansPDA(); + let guardiansState = await program.account.guardians.fetch(guardians); + expect(guardiansState.keys).has.length(expectedGuardians); + + const minters = getMintersPDA(); + let mintersState = await program.account.minters.fetch(minters); + expect(mintersState.keys).has.length(expectedMinters); +} + +export async function addMinter( + authority, + minter +): Promise { + const program = anchor.workspace.Tbtc as Program; + + const config = getConfigPDA(); + const minters = getMintersPDA(); + const minterInfoPDA = getMinterPDA(minter); + await program.methods + .addMinter() + .accounts({ + config, + authority: authority.publicKey, + minters, + minter, + minterInfo: minterInfoPDA, + }) + .signers(maybeAuthorityAnd(authority, [])) + .rpc(); + return minterInfoPDA; +} diff --git a/cross-chain/solana/tests/helpers/tbtcHelpers.ts b/cross-chain/solana/tests/helpers/tbtcHelpers.ts deleted file mode 100644 index a0918c1d1..000000000 --- a/cross-chain/solana/tests/helpers/tbtcHelpers.ts +++ /dev/null @@ -1,134 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import * as spl from "@solana/spl-token"; -import * as web3 from '@solana/web3.js'; -import { Tbtc } from "../../target/types/tbtc"; -import { expect } from 'chai'; - -export function maybeAuthorityAnd( - signer, - signers - ) { - return signers.concat(signer instanceof (anchor.Wallet as any) ? [] : [signer]); - } - -export function getConfigPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('config'), - ], - program.programId - ); -} - -export function getTokenPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('tbtc-mint'), - ], - program.programId - ); -} - -export function getMinterPDA( - program: Program, - minter -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('minter-info'), - minter.toBuffer(), - ], - program.programId - ); -} - -export function getGuardianPDA( - program: Program, - guardian -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('guardian-info'), - guardian.publicKey.toBuffer(), - ], - program.programId - ); -} - -export function getGuardiansPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('guardians'), - ], - program.programId - ); -} - -export function getMintersPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('minters'), - ], - program.programId - ); -} - -export async function checkState( - program: Program, - expectedAuthority, - expectedMinters, - expectedGuardians, - expectedTokensSupply - ) { - const [config,] = getConfigPDA(program); - let configState = await program.account.config.fetch(config); - - expect(configState.authority).to.eql(expectedAuthority.publicKey); - expect(configState.numMinters).to.equal(expectedMinters); - expect(configState.numGuardians).to.equal(expectedGuardians); - - let tbtcMint = configState.mint; - - let mintState = await spl.getMint(program.provider.connection, tbtcMint); - - expect(mintState.supply).to.equal(BigInt(expectedTokensSupply)); - - const [guardians,] = getGuardiansPDA(program); - let guardiansState = await program.account.guardians.fetch(guardians); - expect(guardiansState.keys).has.length(expectedGuardians); - - const [minters,] = getMintersPDA(program); - let mintersState = await program.account.minters.fetch(minters); - expect(mintersState.keys).has.length(expectedMinters); -} - -export async function addMinter( - program: Program, - authority, - minter - ): Promise { - const [config,] = getConfigPDA(program); - const [minters,] = getMintersPDA(program); - const [minterInfoPDA, _] = getMinterPDA(program, minter); - await program.methods - .addMinter() - .accounts({ - config, - authority: authority.publicKey, - minters, - minter, - minterInfo: minterInfoPDA, - }) - .signers(maybeAuthorityAnd(authority, [])) - .rpc(); - return minterInfoPDA; - } \ No newline at end of file diff --git a/cross-chain/solana/tests/helpers/utils.ts b/cross-chain/solana/tests/helpers/utils.ts index b87a0a948..901f2f980 100644 --- a/cross-chain/solana/tests/helpers/utils.ts +++ b/cross-chain/solana/tests/helpers/utils.ts @@ -1,53 +1,194 @@ +import { + MockEthereumTokenBridge, + MockGuardians, +} from "@certusone/wormhole-sdk/lib/cjs/mock"; import { web3 } from "@coral-xyz/anchor"; -import { Account, TokenAccountNotFoundError, createAssociatedTokenAccountIdempotentInstruction, getAccount, getAssociatedTokenAddressSync } from "@solana/spl-token"; -import { Connection, Keypair, PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; - -export async function transferLamports(connection: web3.Connection, fromSigner: web3.Keypair, toPubkey: web3.PublicKey, lamports: number) { - return sendAndConfirmTransaction( - connection, - new Transaction().add( - SystemProgram.transfer({ - fromPubkey: fromSigner.publicKey, - toPubkey, - lamports, - }) - ), - [fromSigner] - ); +import { + Account, + TokenAccountNotFoundError, + createAssociatedTokenAccountIdempotentInstruction, + getAccount, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; +import { + Connection, + Keypair, + PublicKey, + SystemProgram, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + ETHEREUM_TBTC_ADDRESS, + GUARDIAN_SET_INDEX, + SOLANA_CORE_BRIDGE_ADDRESS, + SOLANA_TOKEN_BRIDGE_ADDRESS, + WRAPPED_TBTC_MINT, +} from "./consts"; +import { + postVaaSolana, + redeemOnSolana, + tryNativeToHexString, +} from "@certusone/wormhole-sdk"; +import { NodeWallet } from "@certusone/wormhole-sdk/lib/cjs/solana"; + +export async function transferLamports( + connection: web3.Connection, + fromSigner: web3.Keypair, + toPubkey: web3.PublicKey, + lamports: number +) { + return sendAndConfirmTransaction( + connection, + new Transaction().add( + SystemProgram.transfer({ + fromPubkey: fromSigner.publicKey, + toPubkey, + lamports, + }) + ), + [fromSigner] + ); } -export async function generatePayer(connection: web3.Connection, payer: Keypair, lamports?: number) { - const newPayer = Keypair.generate(); - await transferLamports(connection, payer, newPayer.publicKey, lamports === undefined ? 1000000000 : lamports); - return newPayer; +export async function generatePayer( + connection: web3.Connection, + payer: Keypair, + lamports?: number +) { + const newPayer = Keypair.generate(); + await transferLamports( + connection, + payer, + newPayer.publicKey, + lamports === undefined ? 1000000000 : lamports + ); + return newPayer; } -export async function getOrCreateTokenAccount(connection: Connection, payer: Keypair, mint: PublicKey, owner: PublicKey) { - const token = getAssociatedTokenAddressSync(mint, owner); - const tokenData: Account = await getAccount(connection, token).catch((err) => { - if (err instanceof TokenAccountNotFoundError) { - return null; - } else { - throw err; - }; - }); - - if (tokenData === null) { - await web3.sendAndConfirmTransaction( - connection, - new web3.Transaction().add( - createAssociatedTokenAccountIdempotentInstruction( - payer.publicKey, - token, - owner, - mint, - ) - ), - [payer] - ); - - return getAccount(connection, token); - } else { - return tokenData; +export async function getOrCreateTokenAccount( + connection: Connection, + payer: Keypair, + mint: PublicKey, + owner: PublicKey +) { + const token = getAssociatedTokenAddressSync(mint, owner); + const tokenData: Account = await getAccount(connection, token).catch( + (err) => { + if (err instanceof TokenAccountNotFoundError) { + return null; + } else { + throw err; + } } -} \ No newline at end of file + ); + + if (tokenData === null) { + await web3.sendAndConfirmTransaction( + connection, + new web3.Transaction().add( + createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, + token, + owner, + mint + ) + ), + [payer] + ); + + return getAccount(connection, token); + } else { + return tokenData; + } +} + +export async function preloadWrappedTbtc( + connection: Connection, + payer: Keypair, + ethereumTokenBridge: MockEthereumTokenBridge, + amount: bigint, + tokenOwner: PublicKey +) { + const wrappedTbtcToken = await getOrCreateTokenAccount( + connection, + payer, + WRAPPED_TBTC_MINT, + tokenOwner + ); + + // Bridge tbtc to token account. + const published = ethereumTokenBridge.publishTransferTokens( + tryNativeToHexString(ETHEREUM_TBTC_ADDRESS, "ethereum"), + 2, + amount, + 1, + wrappedTbtcToken.address.toBuffer().toString("hex"), + BigInt(0), + 0, + 0 + ); + + const signedVaa = await mockSignAndPostVaa(connection, payer, published); + + const tx = await redeemOnSolana( + connection, + SOLANA_CORE_BRIDGE_ADDRESS, + SOLANA_TOKEN_BRIDGE_ADDRESS, + payer.publicKey, + signedVaa + ); + await web3.sendAndConfirmTransaction(connection, tx, [payer]); + + return wrappedTbtcToken.address; +} + +export async function mockSignAndPostVaa( + connection: web3.Connection, + payer: web3.Keypair, + published: Buffer +) { + const guardians = new MockGuardians(GUARDIAN_SET_INDEX, [ + "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0", + ]); + + // Add guardian signature. + const signedVaa = guardians.addSignatures(published, [0]); + + // Verify and post VAA. + await postVaaSolana( + connection, + new NodeWallet(payer).signTransaction, + SOLANA_CORE_BRIDGE_ADDRESS, + payer.publicKey, + signedVaa + ); + + return signedVaa; +} + +// export function ethereumGatewaySendTbtc( +// ethereumTokenBridge: MockEthereumTokenBridge, +// amount: bigint, +// recipient: Buffer +// ) { +// const wrappedTbtcMint = getWrappedTbtcMintPDA(); +// const custodianWrappedTbtcToken = getWrappedTbtcTokenPDA; +// const published = ethereumTokenBridge.publishTransferTokens( +// tryNativeToHexString(ETHEREUM_TBTC_ADDRESS, "ethereum"), +// 2, +// BigInt("100000000000"), +// 1, +// wrappedTbtcToken.address.toBuffer().toString("hex"), +// BigInt(0), +// 0, +// 0 +// ); + +// const guardians = new mock.MockGuardians(GUARDIAN_SET_INDEX, [ +// "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0", +// ]); + +// // Add guardian signature. +// const signedVaa = guardians.addSignatures(published, [0]); +// } diff --git a/cross-chain/solana/tests/helpers/wormholeGateway.ts b/cross-chain/solana/tests/helpers/wormholeGateway.ts new file mode 100644 index 000000000..747da5a10 --- /dev/null +++ b/cross-chain/solana/tests/helpers/wormholeGateway.ts @@ -0,0 +1,39 @@ +import { PublicKey } from "@solana/web3.js"; +import { WORMHOLE_GATEWAY_PROGRAM_ID } from "./consts"; + +export function getCustodianPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("custodian")], + WORMHOLE_GATEWAY_PROGRAM_ID + )[0]; +} + +export function getGatewayInfoPDA(targetChain): PublicKey { + const encodedChain = Buffer.alloc(2); + encodedChain.writeUInt16LE(targetChain); + return PublicKey.findProgramAddressSync( + [Buffer.from("gateway-info"), encodedChain], + WORMHOLE_GATEWAY_PROGRAM_ID + )[0]; +} + +export function getWrappedTbtcTokenPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("wrapped-token")], + WORMHOLE_GATEWAY_PROGRAM_ID + )[0]; +} + +export function getTokenBridgeSenderPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("sender")], + WORMHOLE_GATEWAY_PROGRAM_ID + )[0]; +} + +export function getTokenBridgeRedeemerPDA(): PublicKey { + return PublicKey.findProgramAddressSync( + [Buffer.from("redeemer")], + WORMHOLE_GATEWAY_PROGRAM_ID + )[0]; +} diff --git a/cross-chain/solana/tests/helpers/wormholeGatewayHelpers.ts b/cross-chain/solana/tests/helpers/wormholeGatewayHelpers.ts deleted file mode 100644 index 90d2ee31d..000000000 --- a/cross-chain/solana/tests/helpers/wormholeGatewayHelpers.ts +++ /dev/null @@ -1,67 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import * as web3 from '@solana/web3.js'; -import { WormholeGateway } from "../../target/types/wormhole_gateway"; - -export function getCustodianPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('custodian'), - ], - program.programId - ); -} - -export function getGatewayInfoPDA( - program: Program, - targetChain -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('gateway-info'), - toBytesLE(targetChain), - ], - program.programId - ); -} - -export function getWrappedTbtcTokenPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('wrapped-token'), - ], - program.programId - ); -} - -export function getTokenBridgeSenderPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('sender'), - ], - program.programId - ); -} - -export function getTokenBridgeRedeemerPDA( - program: Program, -): [anchor.web3.PublicKey, number] { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from('redeemer'), - ], - program.programId - ); -} - -function toBytesLE(x): Buffer { - const buf = Buffer.alloc(2); - buf.writeUint16LE(x); - return buf; -} \ No newline at end of file