From 290a22a0843cb9164e39ed7f4c023cb9084edc18 Mon Sep 17 00:00:00 2001 From: A5 Pickle Date: Tue, 1 Aug 2023 19:10:48 -0500 Subject: [PATCH] solana: wormhole-gatway tests in progress --- cross-chain/solana/.gitignore | 3 +- cross-chain/solana/.yarnrc.yml | 1 - cross-chain/solana/Anchor.toml | 70 ++++++++++- cross-chain/solana/Makefile | 26 ++++ cross-chain/solana/package-lock.json | 3 +- cross-chain/solana/programs/tbtc/Cargo.toml | 4 +- .../src/processor/deposit_wormhole_tbtc.rs | 6 +- .../src/processor/initialize.rs | 8 +- cross-chain/solana/tests/01__tbtc.ts | 24 ++-- .../solana/tests/02__wormholeGateway.ts | 116 ++++++++++++++++++ .../solana/tests/accounts/core_bridge.json | 13 ++ .../tests/accounts/core_emitter_sequence.json | 13 ++ .../tests/accounts/core_fee_collector.json | 13 ++ .../tests/accounts/core_guardian_set.json | 13 ++ .../tests/accounts/ethereum_token_bridge.json | 13 ++ .../tests/accounts/token_bridge_config.json | 13 ++ .../tests/accounts/wrapped_tbtc_asset.json | 13 ++ .../tests/accounts/wrapped_tbtc_mint.json | 13 ++ cross-chain/solana/tests/helpers/utils.ts | 53 ++++++++ 19 files changed, 395 insertions(+), 23 deletions(-) delete mode 100644 cross-chain/solana/.yarnrc.yml create mode 100644 cross-chain/solana/Makefile create mode 100644 cross-chain/solana/tests/02__wormholeGateway.ts create mode 100644 cross-chain/solana/tests/accounts/core_bridge.json create mode 100644 cross-chain/solana/tests/accounts/core_emitter_sequence.json create mode 100644 cross-chain/solana/tests/accounts/core_fee_collector.json create mode 100644 cross-chain/solana/tests/accounts/core_guardian_set.json create mode 100644 cross-chain/solana/tests/accounts/ethereum_token_bridge.json create mode 100644 cross-chain/solana/tests/accounts/token_bridge_config.json create mode 100644 cross-chain/solana/tests/accounts/wrapped_tbtc_asset.json create mode 100644 cross-chain/solana/tests/accounts/wrapped_tbtc_mint.json create mode 100644 cross-chain/solana/tests/helpers/utils.ts diff --git a/cross-chain/solana/.gitignore b/cross-chain/solana/.gitignore index 8d401163f..3db15d86f 100644 --- a/cross-chain/solana/.gitignore +++ b/cross-chain/solana/.gitignore @@ -5,4 +5,5 @@ target **/*.rs.bk node_modules test-ledger -.yarn +artifacts-mainnet +artifacts-testnet \ No newline at end of file diff --git a/cross-chain/solana/.yarnrc.yml b/cross-chain/solana/.yarnrc.yml deleted file mode 100644 index 3186f3f07..000000000 --- a/cross-chain/solana/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/cross-chain/solana/Anchor.toml b/cross-chain/solana/Anchor.toml index 789ca54fd..6025a94f1 100644 --- a/cross-chain/solana/Anchor.toml +++ b/cross-chain/solana/Anchor.toml @@ -1,6 +1,14 @@ [features] seeds = false skip-lint = false + +[workspace] +members = [ + "programs/tbtc", + "programs/wormhole-gateway", +] + + [programs.localnet] tbtc = "HksEtDgsXJV1BqcuhzbLRTmXp5gHgHJktieJCtQd3pG" wormhole-gateway = "8H9F5JGbEMyERycwaGuzLS5MQnV7dn2wm2h6egJ3Leiu" @@ -10,7 +18,65 @@ url = "https://api.apr.dev" [provider] cluster = "Localnet" -wallet = "/home/eth/.config/solana/id.json" +wallet = "~/.config/solana/id.json" [scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" +test = "npx ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[test] +startup_wait = 10000 + +[test.validator] +url = "https://api.mainnet-beta.solana.com" + +### MPL Token Metadata +[[test.validator.clone]] +address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" + +### Wormhole Core Bridge +[[test.validator.clone]] +address = "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth" + +### Wormhole Token Bridge +[[test.validator.clone]] +address = "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb" + +### Token Bridge -- Wrapped tBTC Mint +[[test.validator.account]] +address = "25rXTx9zDZcHyTav5sRqM6YBvTGu9pPH9yv83uAEqbgG" +filename = "tests/accounts/wrapped_tbtc_mint.json" + +### Token Bridge -- Wrapped tBTC Asset +[[test.validator.account]] +address = "5LEUZpBxUQmoxoNGqmYmFEGAPDuhWbAY5CGt519UixLo" +filename = "tests/accounts/wrapped_tbtc_asset.json" + +### Token Bridge -- Ethereum Foreign Endpoint +[[test.validator.account]] +address = "DujfLgMKW71CT2W8pxknf42FT86VbcK5PjQ6LsutjWKC" +filename = "tests/accounts/ethereum_token_bridge.json" + +### Token Bridge -- Config +[[test.validator.account]] +address = "DapiQYH3BGonhN8cngWcXQ6SrqSm3cwysoznoHr6Sbsx" +filename = "tests/accounts/token_bridge_config.json" + +### Core Bridge -- Bridge +[[test.validator.clone]] +address = "2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn" +filename = "tests/accounts/core_bridge.json" + +### Core Bridge -- Emitter Sequence (Token Bridge's) +[[test.validator.account]] +address = "GF2ghkjwsR9CHkGk1RvuZrApPZGBZynxMm817VNi51Nf" +filename = "tests/accounts/core_emitter_sequence.json" + +### Core Bridge -- Fee Collector +[[test.validator.account]] +address = "9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy" +filename = "tests/accounts/core_fee_collector.json" + +### Core Bridge -- Guardian Set (index == 3) +[[test.validator.account]] +address = "6d3w8mGjJauf6gCAg7WfLezbaPmUHYGuoNutnfYF1RYM" +filename = "tests/accounts/core_guardian_set.json" diff --git a/cross-chain/solana/Makefile b/cross-chain/solana/Makefile new file mode 100644 index 000000000..6fe96802c --- /dev/null +++ b/cross-chain/solana/Makefile @@ -0,0 +1,26 @@ + +out_solana-devnet=artifacts-testnet +out_mainnet=artifacts-mainnet + +.PHONY: all clean build test + +all: test + +clean: + anchor clean + rm -rf node_modules artifacts-mainnet artifacts-testnet + +node_modules: + npm ci + +build: $(out_$(NETWORK)) + +$(out_$(NETWORK)): +ifdef out_$(NETWORK) + anchor build --arch sbf -- --features "$(NETWORK)" -- --no-default-features + mkdir -p $(out_$(NETWORK)) + cp target/deploy/*.so $(out_$(NETWORK))/ +endif + +test: node_modules + anchor test --arch sbf diff --git a/cross-chain/solana/package-lock.json b/cross-chain/solana/package-lock.json index e0307d458..ddcd6cb2d 100644 --- a/cross-chain/solana/package-lock.json +++ b/cross-chain/solana/package-lock.json @@ -1861,8 +1861,9 @@ }, "node_modules/@solana/spl-token": { "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.8.tgz", + "integrity": "sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", diff --git a/cross-chain/solana/programs/tbtc/Cargo.toml b/cross-chain/solana/programs/tbtc/Cargo.toml index 127bfb608..18e88c859 100644 --- a/cross-chain/solana/programs/tbtc/Cargo.toml +++ b/cross-chain/solana/programs/tbtc/Cargo.toml @@ -9,11 +9,13 @@ crate-type = ["cdylib", "lib"] name = "tbtc" [features] +default = [] +mainnet = [] +solana-devnet = [] no-entrypoint = [] no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] -default = [] [dependencies] anchor-lang = { version = "=0.28.0", features = ["derive", "init-if-needed"] } diff --git a/cross-chain/solana/programs/wormhole-gateway/src/processor/deposit_wormhole_tbtc.rs b/cross-chain/solana/programs/wormhole-gateway/src/processor/deposit_wormhole_tbtc.rs index c36fb3e93..28ccdc59a 100644 --- a/cross-chain/solana/programs/wormhole-gateway/src/processor/deposit_wormhole_tbtc.rs +++ b/cross-chain/solana/programs/wormhole-gateway/src/processor/deposit_wormhole_tbtc.rs @@ -84,6 +84,8 @@ pub fn deposit_wormhole_tbtc(ctx: Context, amount: u64) -> amount, )?; + let custodian = &ctx.accounts.custodian; + // Now mint. tbtc::cpi::mint( CpiContext::new_with_signer( @@ -92,11 +94,11 @@ pub fn deposit_wormhole_tbtc(ctx: Context, amount: u64) -> mint: ctx.accounts.tbtc_mint.to_account_info(), config: ctx.accounts.tbtc_config.to_account_info(), minter_info: ctx.accounts.minter_info.to_account_info(), - minter: ctx.accounts.custodian.to_account_info(), + minter: custodian.to_account_info(), recipient_token: ctx.accounts.recipient_token.to_account_info(), token_program: ctx.accounts.token_program.to_account_info(), }, - &[&[Custodian::SEED_PREFIX, &[ctx.bumps["custodian"]]]], + &[&[Custodian::SEED_PREFIX, &[custodian.bump]]], ), amount, ) diff --git a/cross-chain/solana/programs/wormhole-gateway/src/processor/initialize.rs b/cross-chain/solana/programs/wormhole-gateway/src/processor/initialize.rs index c26b191d2..f73831546 100644 --- a/cross-chain/solana/programs/wormhole-gateway/src/processor/initialize.rs +++ b/cross-chain/solana/programs/wormhole-gateway/src/processor/initialize.rs @@ -7,15 +7,15 @@ const TBTC_FOREIGN_TOKEN_CHAIN: u8 = 2; #[cfg(feature = "mainnet")] const TBTC_FOREIGN_TOKEN_ADDRESS: [u8; 32] = [ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8d, 0xae, 0xba, 0xde, 0x92, 0x2d, - 0xf7, 0x35, 0xc3, 0x8c, 0x80, 0xc7, 0xeb, 0xd7, 0x08, 0xaf, 0x50, 0x81, 0x5f, 0xaa, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x08, 0x4f, 0xbA, 0x66, 0x6a, + 0x33, 0xd3, 0x75, 0x92, 0xfA, 0x26, 0x33, 0xfD, 0x49, 0xa7, 0x4D, 0xD9, 0x3a, 0x88, ]; /// TODO: Fix this to reflect testnet contract address. #[cfg(feature = "solana-devnet")] const TBTC_FOREIGN_TOKEN_ADDRESS: [u8; 32] = [ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8d, 0xae, 0xba, 0xde, 0x92, 0x2d, - 0xf7, 0x35, 0xc3, 0x8c, 0x80, 0xc7, 0xeb, 0xd7, 0x08, 0xaf, 0x50, 0x81, 0x5f, 0xaa, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x08, 0x4f, 0xbA, 0x66, 0x6a, + 0x33, 0xd3, 0x75, 0x92, 0xfA, 0x26, 0x33, 0xfD, 0x49, 0xa7, 0x4D, 0xD9, 0x3a, 0x88, ]; #[derive(Accounts)] diff --git a/cross-chain/solana/tests/01__tbtc.ts b/cross-chain/solana/tests/01__tbtc.ts index f69c0a820..0a70e3d93 100644 --- a/cross-chain/solana/tests/01__tbtc.ts +++ b/cross-chain/solana/tests/01__tbtc.ts @@ -5,6 +5,7 @@ import * as web3 from '@solana/web3.js'; import { Tbtc } from "../target/types/tbtc"; import { expect } from 'chai'; import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; +import { transferLamports } from "./helpers/utils"; function maybeAuthorityAnd( signer, @@ -446,17 +447,18 @@ describe("tbtc", () => { await checkState(program, authority, 1, 0, 0); // Transfer lamports to imposter. - await web3.sendAndConfirmTransaction( - program.provider.connection, - new web3.Transaction().add( - web3.SystemProgram.transfer({ - fromPubkey: authority.publicKey, - toPubkey: impostorKeys.publicKey, - lamports: 1000000000, - }) - ), - [authority.payer] - ); + await transferLamports(program.provider.connection, authority.payer, impostorKeys.publicKey, 1000000000); + // await web3.sendAndConfirmTransaction( + // program.provider.connection, + // new web3.Transaction().add( + // web3.SystemProgram.transfer({ + // fromPubkey: authority.publicKey, + // toPubkey: impostorKeys.publicKey, + // lamports: 1000000000, + // }) + // ), + // [authority.payer] + // ); try { await addMinter(program, impostorKeys, minter2Keys, authority); diff --git a/cross-chain/solana/tests/02__wormholeGateway.ts b/cross-chain/solana/tests/02__wormholeGateway.ts new file mode 100644 index 000000000..69eb14fe3 --- /dev/null +++ b/cross-chain/solana/tests/02__wormholeGateway.ts @@ -0,0 +1,116 @@ +import * as mock 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 } 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 { web3 } from "@coral-xyz/anchor"; + +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; + +function getCustodianPDA( + program: Program, +): [anchor.web3.PublicKey, number] { + return web3.PublicKey.findProgramAddressSync( + [ + Buffer.from('custodian'), + ], + program.programId + ); +} + + +describe("wormhole-gateway", () => { + // Configure the client to use the local cluster. + anchor.setProvider(anchor.AnchorProvider.env()); + + const program = anchor.workspace.WormholeGateway as Program; + const connection = program.provider.connection; + + 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(); + const impostorKeys = anchor.web3.Keypair.generate(); + const guardianKeys = anchor.web3.Keypair.generate(); + const guardian2Keys = anchor.web3.Keypair.generate(); + + const recipientKeys = anchor.web3.Keypair.generate(); + + const ethereumTokenBridge = new mock.MockEthereumTokenBridge(ETHEREUM_TOKEN_BRIDGE_ADDRESS); + + 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); + 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); + expect(mintData.decimals).to.equal(8); + expect(mintData.supply).to.equal(BigInt(90)); + + const wrappedTbtcToken = await getOrCreateTokenAccount(connection, payer, wrappedTbtcMint, payer.publicKey); + + // Bridge tbtc to token account. + const published = ethereumTokenBridge.publishTransferTokens( + tryNativeToHexString(ETHEREUM_TBTC_ADDRESS, "ethereum"), + 2, + BigInt("100000000000"), + 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]); + }); + + it('setup', async () => { + // await setup(program, authority); + // await checkState(program, authority, 0, 0, 0); + }); +}); + +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.json new file mode 100644 index 000000000..5c14d521c --- /dev/null +++ b/cross-chain/solana/tests/accounts/core_bridge.json @@ -0,0 +1,13 @@ +{ + "pubkey": "2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn", + "account": { + "lamports": 1057920, + "data": [ + "AwAAAPhmKAUAAAAAgFEBAGQAAAAAAAAA", + "base64" + ], + "owner": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", + "executable": false, + "rentEpoch": 361 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/core_emitter_sequence.json b/cross-chain/solana/tests/accounts/core_emitter_sequence.json new file mode 100644 index 000000000..966d215e5 --- /dev/null +++ b/cross-chain/solana/tests/accounts/core_emitter_sequence.json @@ -0,0 +1,13 @@ +{ + "pubkey": "GF2ghkjwsR9CHkGk1RvuZrApPZGBZynxMm817VNi51Nf", + "account": { + "lamports": 946560, + "data": [ + "4KMEAAAAAAA=", + "base64" + ], + "owner": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", + "executable": false, + "rentEpoch": 361 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/core_fee_collector.json b/cross-chain/solana/tests/accounts/core_fee_collector.json new file mode 100644 index 000000000..6f355d442 --- /dev/null +++ b/cross-chain/solana/tests/accounts/core_fee_collector.json @@ -0,0 +1,13 @@ +{ + "pubkey": "9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy", + "account": { + "lamports": 86533780, + "data": [ + "", + "base64" + ], + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 361 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/core_guardian_set.json b/cross-chain/solana/tests/accounts/core_guardian_set.json new file mode 100644 index 000000000..d503f9049 --- /dev/null +++ b/cross-chain/solana/tests/accounts/core_guardian_set.json @@ -0,0 +1,13 @@ +{ + "pubkey": "6d3w8mGjJauf6gCAg7WfLezbaPmUHYGuoNutnfYF1RYM", + "account": { + "lamports": 3647040, + "data": [ + "AwAAAAEAAAC++kKdV80Yt/ik2RotqatK8F0PvgAAAAAAAAAA", + "base64" + ], + "owner": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", + "executable": false, + "rentEpoch": 0 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/ethereum_token_bridge.json b/cross-chain/solana/tests/accounts/ethereum_token_bridge.json new file mode 100644 index 000000000..e5c5ef3ad --- /dev/null +++ b/cross-chain/solana/tests/accounts/ethereum_token_bridge.json @@ -0,0 +1,13 @@ +{ + "pubkey": "DujfLgMKW71CT2W8pxknf42FT86VbcK5PjQ6LsutjWKC", + "account": { + "lamports": 1127520, + "data": [ + "AgAAAAAAAAAAAAAAAAA+4YsiFK/5cADZdM9kfnw0fo+lhQ==", + "base64" + ], + "owner": "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb", + "executable": false, + "rentEpoch": 361 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/token_bridge_config.json b/cross-chain/solana/tests/accounts/token_bridge_config.json new file mode 100644 index 000000000..c4f8fb6c3 --- /dev/null +++ b/cross-chain/solana/tests/accounts/token_bridge_config.json @@ -0,0 +1,13 @@ +{ + "pubkey": "DapiQYH3BGonhN8cngWcXQ6SrqSm3cwysoznoHr6Sbsx", + "account": { + "lamports": 1113600, + "data": [ + "DgpYmkGlX71mxSpHXy2SptPcm0dHEUy5r4JamLVF084=", + "base64" + ], + "owner": "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb", + "executable": false, + "rentEpoch": 361 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/wrapped_tbtc_asset.json b/cross-chain/solana/tests/accounts/wrapped_tbtc_asset.json new file mode 100644 index 000000000..423cf3610 --- /dev/null +++ b/cross-chain/solana/tests/accounts/wrapped_tbtc_asset.json @@ -0,0 +1,13 @@ +{ + "pubkey": "5LEUZpBxUQmoxoNGqmYmFEGAPDuhWbAY5CGt519UixLo", + "account": { + "lamports": 1134480, + "data": [ + "AgAAAAAAAAAAAAAAAAAYCE+6Zmoz03WS+iYz/UmnTdk6iBI=", + "base64" + ], + "owner": "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb", + "executable": false, + "rentEpoch": 0 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/accounts/wrapped_tbtc_mint.json b/cross-chain/solana/tests/accounts/wrapped_tbtc_mint.json new file mode 100644 index 000000000..0dd211691 --- /dev/null +++ b/cross-chain/solana/tests/accounts/wrapped_tbtc_mint.json @@ -0,0 +1,13 @@ +{ + "pubkey": "25rXTx9zDZcHyTav5sRqM6YBvTGu9pPH9yv83uAEqbgG", + "account": { + "lamports": 1461600, + "data": [ + "AQAAAJdz8e+O65iVdMpxBFsTrA0MEUaiThB5GX5TF1UZTdCWWgAAAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 0 + } +} \ No newline at end of file diff --git a/cross-chain/solana/tests/helpers/utils.ts b/cross-chain/solana/tests/helpers/utils.ts new file mode 100644 index 000000000..b87a0a948 --- /dev/null +++ b/cross-chain/solana/tests/helpers/utils.ts @@ -0,0 +1,53 @@ +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] + ); +} + +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; + } +} \ No newline at end of file