wormhole/testing/solana-test-validator/sdk-tests/2_token_bridge.ts

2143 lines
72 KiB
TypeScript

import { expect } from "chai";
import * as web3 from "@solana/web3.js";
import {
createMint,
getAccount,
getAssociatedTokenAddressSync,
getMint,
getOrCreateAssociatedTokenAccount,
mintTo,
NATIVE_MINT,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import {
MockGuardians,
GovernanceEmitter,
MockEthereumTokenBridge,
} from "../../../sdk/js/src/mock";
import {
createApproveAuthoritySignerInstruction,
createAttestTokenInstruction,
createCompleteTransferNativeInstruction,
createCompleteTransferWrappedInstruction,
createCreateWrappedInstruction,
createRegisterChainInstruction,
createTransferNativeInstruction,
createTransferNativeWithPayloadInstruction,
createTransferWrappedInstruction,
createTransferWrappedWithPayloadInstruction,
deriveCustodyKey,
deriveEndpointKey,
deriveMintAuthorityKey,
deriveRedeemerAccountKey,
deriveWrappedMintKey,
getAttestTokenAccounts,
getCompleteTransferNativeAccounts,
getCompleteTransferWrappedAccounts,
getCreateWrappedAccounts,
getEndpointRegistration,
getInitializeAccounts,
getRegisterChainAccounts,
getTransferNativeAccounts,
getTransferNativeWithPayloadAccounts,
getTransferWrappedAccounts,
getTransferWrappedWithPayloadAccounts,
getUpgradeContractAccounts,
getWrappedMeta,
} from "../../../sdk/js/src/solana/tokenBridge";
import { postVaa } from "../../../sdk/js/src/solana/sendAndConfirmPostVaa";
import {
BpfLoaderUpgradeable,
getCompleteTransferNativeWithPayloadCpiAccounts,
getCompleteTransferWrappedWithPayloadCpiAccounts,
getTransferNativeWithPayloadCpiAccounts,
getTransferWrappedWithPayloadCpiAccounts,
NodeWallet,
signSendAndConfirmTransaction,
SplTokenMetadataProgram,
} from "../../../sdk/js/src/solana";
import {
deriveWormholeEmitterKey,
getPostedMessage,
getPostedVaa,
getProgramSequenceTracker,
} from "../../../sdk/js/src/solana/wormhole";
import {
parseGovernanceVaa,
parseAttestMetaVaa,
parseAttestMetaPayload,
parseTokenBridgeRegisterChainVaa,
parseTokenTransferPayload,
parseVaa,
} from "../../../sdk/js/src/vaa";
import {
CORE_BRIDGE_ADDRESS,
TOKEN_BRIDGE_ADDRESS,
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
GOVERNANCE_EMITTER_ADDRESS,
GUARDIAN_KEYS,
GUARDIAN_SET_INDEX,
LOCALHOST,
WETH_ADDRESS,
} from "./helpers/consts";
import { ethAddressToBuffer, now } from "./helpers/utils";
import {
getForeignAssetSolana,
getIsWrappedAssetSolana,
getOriginalAssetSolana,
} from "../../../sdk/js/src/token_bridge";
import { ChainId } from "../../../sdk/js/src";
import {
transferNativeSol,
redeemAndUnwrapOnSolana,
} from "../../../sdk/js/src/token_bridge";
describe("Token Bridge", () => {
const connection = new web3.Connection(LOCALHOST, "processed");
const wallet = new NodeWallet(web3.Keypair.generate());
// for signing wormhole messages
const guardians = new MockGuardians(GUARDIAN_SET_INDEX + 1, GUARDIAN_KEYS);
const localVariables: any = {};
before("Airdrop SOL", async () => {
await connection
.requestAirdrop(wallet.key(), 1000 * web3.LAMPORTS_PER_SOL)
.then(async (signature) => connection.confirmTransaction(signature));
});
before("Create Mint", async () => {
localVariables.mint = await createMint(
connection,
wallet.signer(),
wallet.key(),
null,
9
);
localVariables.mintAta = await getOrCreateAssociatedTokenAccount(
connection,
wallet.signer(),
localVariables.mint,
wallet.key()
).then((account) => account.address);
const mintToTx = await mintTo(
connection,
wallet.signer(),
localVariables.mint,
localVariables.mintAta,
wallet.key(),
1000 * web3.LAMPORTS_PER_SOL
);
});
before("Create Mint with Metadata", async () => {
// TODO
});
describe("Accounts", () => {
// for generating governance wormhole messages
const governance = new GovernanceEmitter(
GOVERNANCE_EMITTER_ADDRESS.toBuffer().toString("hex")
);
// token bridge on Ethereum
const ethereumTokenBridge = new MockEthereumTokenBridge(
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
const payer = new web3.PublicKey(
"6sbzC1eH4FTujJXWj51eQe25cYvr4xfXbJ1vAj7j2k5J"
);
it("Instruction 0: Initialize", () => {
const accounts = getInitializeAccounts(TOKEN_BRIDGE_ADDRESS, payer);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
});
it("Instruction 1: Attest Token", () => {
const mint = NATIVE_MINT;
const message = web3.Keypair.generate();
const accounts = getAttestTokenAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
mint,
message.publicKey
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.wrappedMeta.toString()).to.equal(
"8xGY7Bx9cWocPYpKRe3sjCYTdm3YchFJFHmJC5FenW6B"
);
expect(accounts.splMetadata.toString()).to.equal(
"6dM4TqWyWJsbx7obrdLcviBkTafD5E8av61zfU6jq57X"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.wormholeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.wormholeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 2: Complete Native", () => {
const mint = NATIVE_MINT;
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const amountEncoded = 42069n;
const fee = 0n;
const nonce = 420;
const timestamp = 23456789;
const message = ethereumTokenBridge.publishTransferTokens(
mint.toBuffer().toString("hex"),
1,
amountEncoded,
1,
mintAta.toBuffer().toString("hex"),
fee,
nonce,
timestamp
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getCompleteTransferNativeAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.vaa.toString()).to.equal(
"GMBEenvtgYkQrHZNkx3kYbdYEUYN1GFRGBFN172bk3cN"
);
expect(accounts.claim.toString()).to.equal(
"HzjTihvhEx7BbKnB2KHATNBwGFCEm2nnMG6c4Pwx6pPE"
);
expect(accounts.endpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.to.equals(mintAta)).is.true;
expect(accounts.toFees.equals(mintAta)).is.true;
expect(accounts.custody.toString()).to.equal(
"2Aczo4H847TNDsPradsVXQUZFquJ37ZoHhRmJ2MAqtiM"
);
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.custodySigner.toString()).to.equal(
"Eb8xqkMEZYeTnDse4BgWiHVByeUj3JDpgbuz98pWdgPE"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 3: Complete Wrapped", () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const amount = 4206942069n;
const recipientChain = 1;
const fee = 0n;
const nonce = 420;
const timestamp = 34567890;
const message = ethereumTokenBridge.publishTransferTokens(
tokenAddress.toString("hex"),
tokenChain,
amount,
recipientChain,
mintAta.toBuffer().toString("hex"),
fee,
nonce,
timestamp
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getCompleteTransferWrappedAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.vaa.toString()).to.equal(
"7NQnr9aG4xQCp9AjnF37L3CrBbM6GJ2gN98JeFjn7nnf"
);
expect(accounts.claim.toString()).to.equal(
"7Ae57QxvZMwCrknoDWpeaMTLbMP3LBeCJee6KaLEwxP6"
);
expect(accounts.endpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.to.equals(mintAta)).is.true;
expect(accounts.toFees.equals(mintAta)).is.true;
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.wrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.mintAuthority.toString()).to.equal(
"J2mhpFfGCwHtUjmeQGhJSa2yk5h3egRoSd1AUhaKx2WG"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 4: Transfer Wrapped", () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const message = web3.Keypair.generate();
const accounts = getTransferWrappedAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
payer,
tokenChain,
tokenAddress
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.from.equals(mintAta)).is.true;
expect(accounts.fromOwner.equals(payer)).is.true;
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.wrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.authoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.wormholeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.wormholeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 5: Transfer Native", () => {
const mint = NATIVE_MINT;
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const message = web3.Keypair.generate();
const accounts = getTransferNativeAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
mint
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.from.equals(mintAta)).is.true;
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.custody.toString()).to.equal(
"2Aczo4H847TNDsPradsVXQUZFquJ37ZoHhRmJ2MAqtiM"
);
expect(accounts.authoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.custodySigner.toString()).to.equal(
"Eb8xqkMEZYeTnDse4BgWiHVByeUj3JDpgbuz98pWdgPE"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.wormholeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.wormholeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 6: Register Chain", () => {
const timestamp = 45678901;
const message = governance.publishTokenBridgeRegisterChain(
timestamp,
2,
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getRegisterChainAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa
);
// verify accounts
const parsed = parseGovernanceVaa(signedVaa);
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.endpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.vaa.toString()).to.equal(
"92pVby4LJPSyQxSSHLYv3EdpqWjH5bBoLGBJAeQkunf8"
);
expect(accounts.claim.toString()).to.equal(
"J5LWxMcXo1xmdZq57VD4wrUgvw5taizQ9QEPHooHTwJv"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 7: Create Wrapped", () => {
const tokenAddress = WETH_ADDRESS;
const decimals = 18;
const symbol = "WETH";
const name = "Wrapped ETH";
const nonce = 420;
const message = ethereumTokenBridge.publishAttestMeta(
tokenAddress,
decimals,
symbol,
name,
nonce
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getCreateWrappedAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.endpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.vaa.toString()).to.equal(
"AaZqjqvg8QirKetuR799Smfw5gyAWobUooGsvxzr1aoX"
);
expect(accounts.claim.toString()).to.equal(
"4dyk94hhqektDX9wUBCL1ZkyQC1Xn3QaTSAdJeZzbTcJ"
);
expect(accounts.mint.toString()).to.equal(
"3tUXFuBNWzZZ8p2xNx5UoWCH664M2KHdDAWrdZAD1VQ3"
);
expect(accounts.wrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.splMetadata.toString()).to.equal(
"46nJp6UehY8XpgNsSZFTamdXcwiSEQpRGvbBCt2KvVUf"
);
expect(accounts.mintAuthority.toString()).to.equal(
"J2mhpFfGCwHtUjmeQGhJSa2yk5h3egRoSd1AUhaKx2WG"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 8: Upgrade Contract", () => {
const timestamp = 56789012;
const chain = 1;
const implementation = new web3.PublicKey(
"2B5wMnErS8oKWV1wPTNQQhM1WLyxee2obtBMDtsYeHgA"
);
const message = governance.publishTokenBridgeUpgradeContract(
timestamp,
chain,
implementation.toString()
);
const signedVaa = guardians.addSignatures(message, [0]);
const accounts = getUpgradeContractAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.vaa.toString()).to.equal(
"HM2U2HEfbjrkvYLvry8Sqfmd5cCVxq6RjdLUUrNW2ELR"
);
expect(accounts.claim.toString()).to.equal(
"9jDWWzAosaD6EWH9SMFT3ZwJnDZTeGcCdMU8H5Ba7dpx"
);
expect(accounts.upgradeAuthority.toString()).to.equal(
"B2LFmpNCkfBFpoorLy4BghGbZyi5sdRsbjxSSASpjUoA"
);
expect(accounts.spill.equals(payer)).is.true;
expect(accounts.implementation.equals(implementation)).is.true;
expect(accounts.programData.toString()).to.equal(
"3zHkdon6x9fUVqjxu6fCgdp3qMxLFZz59pj1H2NtnbGe"
);
expect(accounts.tokenBridgeProgram.equals(TOKEN_BRIDGE_ADDRESS)).to.be
.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(
accounts.bpfLoaderUpgradeable.equals(BpfLoaderUpgradeable.programId)
).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
});
it("Instruction 11: Transfer Wrapped With Payload", () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const message = web3.Keypair.generate();
const accounts = getTransferWrappedWithPayloadAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
payer,
tokenChain,
tokenAddress
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.from.equals(mintAta)).is.true;
expect(accounts.fromOwner.equals(payer)).is.true;
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.wrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.authoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.wormholeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.wormholeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.sender.equals(payer)).is.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("Instruction 12: Transfer Native With Payload", () => {
const mint = NATIVE_MINT;
const mintAta = getAssociatedTokenAddressSync(mint, payer);
const message = web3.Keypair.generate();
const accounts = getTransferNativeWithPayloadAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
mint
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.config.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.from.equals(mintAta)).is.true;
expect(accounts.custody.toString()).to.equal(
"2Aczo4H847TNDsPradsVXQUZFquJ37ZoHhRmJ2MAqtiM"
);
expect(accounts.authoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.custodySigner.toString()).to.equal(
"Eb8xqkMEZYeTnDse4BgWiHVByeUj3JDpgbuz98pWdgPE"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.wormholeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.wormholeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.sender.equals(payer)).is.true;
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
});
describe("CPI Accounts", () => {
// token bridge on Ethereum
const ethereumTokenBridge = new MockEthereumTokenBridge(
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
const payer = new web3.PublicKey(
"6sbzC1eH4FTujJXWj51eQe25cYvr4xfXbJ1vAj7j2k5J"
);
// mock program integrating token bridge
const cpiProgramId = new web3.PublicKey(
"pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ"
);
it("getCompleteTransferNativeWithPayloadCpiAccounts", () => {
const mint = NATIVE_MINT;
const redeemer = deriveRedeemerAccountKey(cpiProgramId);
const mintAta = getAssociatedTokenAddressSync(mint, redeemer, true);
const amountEncoded = 42069n;
const nonce = 420;
const timestamp = 23456789;
const message = ethereumTokenBridge.publishTransferTokensWithPayload(
mint.toBuffer().toString("hex"),
1,
amountEncoded,
1,
cpiProgramId.toBuffer().toString("hex"),
Buffer.alloc(32, 0),
Buffer.from("All your base are belong to us"),
nonce,
timestamp
);
expect(message[51]).to.equal(3);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getCompleteTransferNativeWithPayloadCpiAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa,
mintAta
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.tokenBridgeConfig.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.vaa.toString()).to.equal(
"GtiCPc4mxBVsrPQVgYnuVUzhuvh24A54KaDZhcP4mhDa"
);
expect(accounts.tokenBridgeClaim.toString()).to.equal(
"HzjTihvhEx7BbKnB2KHATNBwGFCEm2nnMG6c4Pwx6pPE"
);
expect(accounts.tokenBridgeForeignEndpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.toTokenAccount.equals(mintAta)).is.true;
expect(accounts.tokenBridgeRedeemer.toString()).to.equal(
"A2SNTmahH9ryK2PupNMfKibPPaMtcfYBSX4WjZchhatX"
);
expect(accounts.toFeesTokenAccount.equals(mintAta)).is.true;
expect(accounts.tokenBridgeCustody.toString()).to.equal(
"2Aczo4H847TNDsPradsVXQUZFquJ37ZoHhRmJ2MAqtiM"
);
expect(accounts.mint.equals(mint)).is.true;
expect(accounts.tokenBridgeCustodySigner.toString()).to.equal(
"Eb8xqkMEZYeTnDse4BgWiHVByeUj3JDpgbuz98pWdgPE"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("getCompleteTransferWrappedWithPayloadCpiAccounts", () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const redeemer = deriveRedeemerAccountKey(cpiProgramId);
const mintAta = getAssociatedTokenAddressSync(mint, redeemer, true);
const amount = 4206942069n;
const recipientChain = 1;
const nonce = 420;
const timestamp = 34567890;
const message = ethereumTokenBridge.publishTransferTokensWithPayload(
tokenAddress.toString("hex"),
tokenChain,
amount,
recipientChain,
cpiProgramId.toBuffer().toString("hex"),
Buffer.alloc(32, 0),
Buffer.from("All your base are belong to us"),
nonce,
timestamp
);
expect(message[51]).to.equal(3);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const accounts = getCompleteTransferWrappedWithPayloadCpiAccounts(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
signedVaa,
mintAta
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.tokenBridgeConfig.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.vaa.toString()).to.equal(
"9nFMaAfuXmE4FdJe8koZ4ScvYcJ5znoJPDgT29aVZM1x"
);
expect(accounts.tokenBridgeClaim.toString()).to.equal(
"7Ae57QxvZMwCrknoDWpeaMTLbMP3LBeCJee6KaLEwxP6"
);
expect(accounts.tokenBridgeForeignEndpoint.toString()).to.equal(
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
);
expect(accounts.toTokenAccount.equals(mintAta)).is.true;
expect(accounts.tokenBridgeRedeemer.toString()).to.equal(
"A2SNTmahH9ryK2PupNMfKibPPaMtcfYBSX4WjZchhatX"
);
expect(accounts.toFeesTokenAccount.equals(mintAta)).is.true;
expect(accounts.tokenBridgeWrappedMint.equals(mint)).is.true;
expect(accounts.tokenBridgeWrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.tokenBridgeMintAuthority.toString()).to.equal(
"J2mhpFfGCwHtUjmeQGhJSa2yk5h3egRoSd1AUhaKx2WG"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("getTransferWrappedWithPayloadCpiAccounts", () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, cpiProgramId, true);
const message = web3.Keypair.generate();
const accounts = getTransferWrappedWithPayloadCpiAccounts(
cpiProgramId,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
tokenChain,
tokenAddress
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.tokenBridgeConfig.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.fromTokenAccount.equals(mintAta)).is.true;
expect(accounts.fromTokenAccountOwner.equals(cpiProgramId)).is.true;
expect(accounts.tokenBridgeWrappedMint.equals(mint)).is.true;
expect(accounts.tokenBridgeWrappedMeta.toString()).to.equal(
"AWUK8RTEBvUNAWLz1VfagK3rnvJ9oLDZPBJEBCzpjqj7"
);
expect(accounts.tokenBridgeAuthoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.tokenBridgeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.tokenBridgeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.tokenBridgeSender.toString()).to.equal(
"7r3GbMGbRRp3cbPRPv9v5GBktxGpDmK5LBnvjDVxsEDN"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
it("getTransferNativeWithPayloadCpiAccounts", () => {
const mint = NATIVE_MINT;
const mintAta = getAssociatedTokenAddressSync(mint, cpiProgramId, true);
const message = web3.Keypair.generate();
const accounts = getTransferNativeWithPayloadCpiAccounts(
cpiProgramId,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
payer,
message.publicKey,
mintAta,
mint
);
// verify accounts
expect(accounts.payer.equals(payer)).is.true;
expect(accounts.tokenBridgeConfig.toString()).to.equal(
"GnQ6fGttTRnJpAJuy2XEg5TLgEMtbyU4HDJnBWmojsTv"
);
expect(accounts.fromTokenAccount.equals(mintAta)).is.true;
expect(accounts.tokenBridgeCustody.toString()).to.equal(
"2Aczo4H847TNDsPradsVXQUZFquJ37ZoHhRmJ2MAqtiM"
);
expect(accounts.tokenBridgeAuthoritySigner.toString()).to.equal(
"FDYbeBnX3rZnWM1jE6vTSxjxYdeGryxZtirXmzW71FTH"
);
expect(accounts.tokenBridgeCustodySigner.toString()).to.equal(
"Eb8xqkMEZYeTnDse4BgWiHVByeUj3JDpgbuz98pWdgPE"
);
expect(accounts.wormholeBridge.toString()).to.equal(
"DNN2VhmrGTGj6QVnPz4NVfsiSk64cRHzKBLP5kUaQrf8"
);
expect(accounts.tokenBridgeEmitter.toString()).to.equal(
"Ard2Zy4HckbJS2bL7y4361wbKSUH68JZqYBura5d4xtw"
);
expect(accounts.tokenBridgeSequence.toString()).to.equal(
"Gdeob8iLpTN4Fc8BEgRdFUWikdUsvrv9Rfc1rNQWy4b7"
);
expect(accounts.wormholeFeeCollector.toString()).to.equal(
"Cxt3Uka7X8vyHYjU6szcuYVPPFyg1fAtoeVy7eyzPjGV"
);
expect(accounts.clock.equals(web3.SYSVAR_CLOCK_PUBKEY)).is.true;
expect(accounts.tokenBridgeSender.toString()).to.equal(
"7r3GbMGbRRp3cbPRPv9v5GBktxGpDmK5LBnvjDVxsEDN"
);
expect(accounts.rent.equals(web3.SYSVAR_RENT_PUBKEY)).is.true;
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
.true;
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
});
});
describe("Token Bridge Program Interaction", () => {
// for generating governance wormhole messages
const governance = new GovernanceEmitter(
GOVERNANCE_EMITTER_ADDRESS.toBuffer().toString("hex"),
20
);
// token bridge on Ethereum
const ethereumTokenBridge = new MockEthereumTokenBridge(
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
describe("Setup Token Bridge", () => {
it("Register Ethereum Token Bridge", async () => {
const timestamp = now();
const message = governance.publishTokenBridgeRegisterChain(
timestamp,
2,
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const txSignatures = await postVaa(
connection,
wallet.signTransaction,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
).then((results) => results.map((result) => result.signature));
const postTx = txSignatures.pop()!;
for (const verifyTx of txSignatures) {
// console.log(`verifySignatures: ${verifyTx}`);
}
// console.log(`postVaa: ${postTx}`);
const registerChainTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(
createRegisterChainInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
)
),
[wallet.signer()]
);
// console.log(`registerChainTx: ${registerChainTx}`);
// verify data
const parsed = parseTokenBridgeRegisterChainVaa(signedVaa);
const endpoint = deriveEndpointKey(
TOKEN_BRIDGE_ADDRESS,
parsed.foreignChain,
parsed.foreignAddress
);
const endpointRegistration = await getEndpointRegistration(
connection,
endpoint
);
expect(endpointRegistration.chain).to.equal(2);
const expectedEmitter = ethAddressToBuffer(
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
expect(
Buffer.compare(endpointRegistration.contract, expectedEmitter)
).to.equal(0);
});
});
describe("Native Token Handling", () => {
it("Attest Mint Without Metadata", async () => {
const mint = localVariables.mint;
const message = web3.Keypair.generate();
const nonce = 69;
const attestTokenTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(
createAttestTokenInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
mint,
message.publicKey,
nonce
)
),
[wallet.signer(), message]
);
// console.log(`attestTokenTx: ${attestTokenTx}`);
// verify data
const messageData = await getPostedMessage(
connection,
message.publicKey
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(32);
expect(
Buffer.compare(
messageData.emitterAddress,
deriveWormholeEmitterKey(TOKEN_BRIDGE_ADDRESS).toBuffer()
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(1);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(0n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaSignatureAccount.equals(web3.PublicKey.default))
.is.true;
expect(messageData.vaaVersion).to.equal(0);
const assetMeta = parseAttestMetaPayload(messageData.payload);
expect(assetMeta.payloadType).to.equal(2);
expect(
Buffer.compare(assetMeta.tokenAddress, mint.toBuffer())
).to.equal(0);
expect(assetMeta.tokenChain).to.equal(1);
expect(assetMeta.decimals).to.equal(9);
expect(assetMeta.symbol).to.equal("");
expect(assetMeta.name).to.equal("");
const sequenceTracker = await getProgramSequenceTracker(
connection,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS
);
expect(sequenceTracker.value()).to.equal(messageData.sequence + 1n);
});
// it("Attest Mint With Metadata", async () => {
// // TODO
// });
it("Send Token", async () => {
const mint = localVariables.mint;
const mintAta = localVariables.mintAta;
const custodyAccount = deriveCustodyKey(TOKEN_BRIDGE_ADDRESS, mint);
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceBefore = 0n;
const nonce = 69;
const amount = BigInt(420 * web3.LAMPORTS_PER_SOL);
const fee = 0n;
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
const targetChain = 2;
const approveIx = createApproveAuthoritySignerInstruction(
TOKEN_BRIDGE_ADDRESS,
mintAta,
wallet.key(),
amount
);
const message = web3.Keypair.generate();
const transferNativeIx = createTransferNativeInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
message.publicKey,
mintAta,
mint,
nonce,
amount,
fee,
targetAddress,
targetChain
);
const approveAndTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(approveIx, transferNativeIx),
[wallet.signer(), message]
);
// console.log(`approveAndTransferTx: ${approveAndTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceAfter = await getAccount(
connection,
custodyAccount
).then((account) => account.amount);
// check balance changes
expect(walletBalanceBefore - walletBalanceAfter).to.equal(amount);
expect(custodyBalanceAfter - custodyBalanceBefore).to.equal(amount);
// verify data
const messageData = await getPostedMessage(
connection,
message.publicKey
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(32);
expect(
Buffer.compare(
messageData.emitterAddress,
deriveWormholeEmitterKey(TOKEN_BRIDGE_ADDRESS).toBuffer()
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(1);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(1n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaSignatureAccount.equals(web3.PublicKey.default))
.is.true;
expect(messageData.vaaVersion).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(1);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).greaterThan(8);
// decimals will be 8 on Ethereum token bridge
const amountEncoded =
amount / BigInt(Math.pow(10, mintInfo.decimals - 8));
expect(tokenTransfer.amount).to.equal(amountEncoded);
expect(tokenTransfer.fee).is.not.null;
expect(tokenTransfer.fee).to.equal(fee);
expect(tokenTransfer.fromAddress).is.null;
expect(Buffer.compare(tokenTransfer.to, targetAddress)).to.equal(0);
expect(tokenTransfer.toChain).to.equal(targetChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, mint.toBuffer())
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(1);
const sequenceTracker = await getProgramSequenceTracker(
connection,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS
);
expect(sequenceTracker.value()).to.equal(messageData.sequence + 1n);
});
it("Receive Token", async () => {
const mint = localVariables.mint;
const mintAta = localVariables.mintAta;
const custodyAccount = deriveCustodyKey(TOKEN_BRIDGE_ADDRESS, mint);
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceBefore = await getAccount(
connection,
custodyAccount
).then((account) => account.amount);
const amount = 420n * BigInt(web3.LAMPORTS_PER_SOL);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).greaterThan(8);
// decimals will be 8 on Ethereum token bridge
const amountEncoded =
amount / BigInt(Math.pow(10, mintInfo.decimals - 8));
const tokenChain = 1;
const recipientChain = 1;
const fee = 0n;
const nonce = 420;
const message = ethereumTokenBridge.publishTransferTokens(
mint.toBuffer().toString("hex"),
tokenChain,
amountEncoded,
recipientChain,
mintAta.toBuffer().toString("hex"),
fee,
nonce
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const txSignatures = await postVaa(
connection,
wallet.signTransaction,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
).then((results) => results.map((result) => result.signature));
const postTx = txSignatures.pop()!;
for (const verifyTx of txSignatures) {
// console.log(`verifySignatures: ${verifyTx}`);
}
// console.log(`postVaa: ${postTx}`);
const completeNativeTransferIx =
createCompleteTransferNativeInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
);
const completeNativeTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(completeNativeTransferIx),
[wallet.signer()]
);
// console.log(`completeNativeTransferTx: ${completeNativeTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceAfter = await getAccount(
connection,
custodyAccount
).then((account) => account.amount);
// check balance changes
expect(walletBalanceAfter - walletBalanceBefore).to.equal(amount);
expect(custodyBalanceBefore - custodyBalanceAfter).to.equal(amount);
// verify data
const messageData = await getPostedVaa(
connection,
CORE_BRIDGE_ADDRESS,
parseVaa(signedVaa).hash
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(
ethereumTokenBridge.consistencyLevel
);
expect(
Buffer.compare(
messageData.emitterAddress,
ethAddressToBuffer(ETHEREUM_TOKEN_BRIDGE_ADDRESS)
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(1n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaVersion).to.equal(1);
expect(
Buffer.compare(parseVaa(signedVaa).payload, messageData.payload)
).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(1);
expect(tokenTransfer.amount).to.equal(amountEncoded);
expect(tokenTransfer.fee).is.not.null;
expect(tokenTransfer.fee).to.equal(fee);
expect(tokenTransfer.fromAddress).is.null;
expect(Buffer.compare(tokenTransfer.to, mintAta.toBuffer())).to.equal(
0
);
expect(tokenTransfer.toChain).to.equal(recipientChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, mint.toBuffer())
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(tokenChain);
});
it("Send Token With Payload", async () => {
const mint = localVariables.mint;
const mintAta = localVariables.mintAta;
const custodyAccount = deriveCustodyKey(TOKEN_BRIDGE_ADDRESS, mint);
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceBefore = await getAccount(
connection,
custodyAccount
).then((account) => account.amount);
const nonce = 420;
const amount = BigInt(69 * web3.LAMPORTS_PER_SOL);
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
const targetChain = 2;
const approveIx = createApproveAuthoritySignerInstruction(
TOKEN_BRIDGE_ADDRESS,
mintAta,
wallet.key(),
amount
);
const message = web3.Keypair.generate();
const transferPayload = Buffer.from("All your base are belong to us");
const transferNativeIx = createTransferNativeWithPayloadInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
message.publicKey,
mintAta,
mint,
nonce,
amount,
targetAddress,
targetChain,
transferPayload
);
const approveAndTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(approveIx, transferNativeIx),
[wallet.signer(), message]
);
// console.log(`approveAndTransferTx: ${approveAndTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const custodyBalanceAfter = await getAccount(
connection,
custodyAccount
).then((account) => account.amount);
// check balance changes
expect(walletBalanceBefore - walletBalanceAfter).to.equal(amount);
expect(custodyBalanceAfter - custodyBalanceBefore).to.equal(amount);
// verify data
const messageData = await getPostedMessage(
connection,
message.publicKey
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(32);
expect(
Buffer.compare(
messageData.emitterAddress,
deriveWormholeEmitterKey(TOKEN_BRIDGE_ADDRESS).toBuffer()
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(1);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(2n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaSignatureAccount.equals(web3.PublicKey.default))
.is.true;
expect(messageData.vaaVersion).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(3);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).greaterThan(8);
// decimals will be 8 on Ethereum token bridge
const amountEncoded =
amount / BigInt(Math.pow(10, mintInfo.decimals - 8));
expect(tokenTransfer.amount).to.equal(amountEncoded);
expect(tokenTransfer.fee).is.null;
expect(tokenTransfer.fromAddress).is.not.null;
expect(
new web3.PublicKey(tokenTransfer.fromAddress!).equals(wallet.key())
).is.true;
expect(Buffer.compare(tokenTransfer.to, targetAddress)).to.equal(0);
expect(tokenTransfer.toChain).to.equal(targetChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, mint.toBuffer())
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(1);
expect(
Buffer.compare(tokenTransfer.tokenTransferPayload, transferPayload)
).to.equal(0);
const sequenceTracker = await getProgramSequenceTracker(
connection,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS
);
expect(sequenceTracker.value()).to.equal(messageData.sequence + 1n);
});
});
describe("Token Bridge Wrapped Token Handling", () => {
it("Create Wrapped with Metadata", async () => {
const tokenAddress = WETH_ADDRESS;
const decimals = 18;
const symbol = "WETH";
const name = "Wrapped ETH";
const nonce = 420;
const message = ethereumTokenBridge.publishAttestMeta(
tokenAddress,
decimals,
symbol,
name,
nonce
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const txSignatures = await postVaa(
connection,
wallet.signTransaction,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
).then((results) => results.map((result) => result.signature));
const postTx = txSignatures.pop()!;
for (const verifyTx of txSignatures) {
// console.log(`verifySignatures: ${verifyTx}`);
}
// console.log(`postVaa: ${postTx}`);
const createWrappedIx = createCreateWrappedInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
);
const createWrappedTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(createWrappedIx),
[wallet.signer()]
);
// console.log(`createWrappedTx: ${createWrappedTx}`);
// verify data
const parsed = parseAttestMetaVaa(signedVaa);
const messageData = await getPostedVaa(
connection,
CORE_BRIDGE_ADDRESS,
parsed.hash
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(
ethereumTokenBridge.consistencyLevel
);
const expectedEmitter = ethAddressToBuffer(
ETHEREUM_TOKEN_BRIDGE_ADDRESS
);
expect(
Buffer.compare(messageData.emitterAddress, expectedEmitter)
).to.equal(0);
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(2n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaVersion).to.equal(1);
expect(Buffer.compare(parsed.payload, messageData.payload)).to.equal(0);
const assetMeta = parseAttestMetaPayload(messageData.payload);
expect(assetMeta.payloadType).to.equal(2);
const expectedTokenAddress = ethAddressToBuffer(tokenAddress);
expect(
Buffer.compare(assetMeta.tokenAddress, expectedTokenAddress)
).to.equal(0);
expect(assetMeta.tokenChain).to.equal(ethereumTokenBridge.chain);
expect(assetMeta.decimals).to.equal(decimals);
expect(assetMeta.symbol).to.equal(symbol);
expect(assetMeta.name).to.equal(name);
// check wrapped mint
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
assetMeta.tokenChain,
assetMeta.tokenAddress
);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).to.equal(8);
expect(mintInfo.mintAuthority).is.not.null;
expect(
mintInfo.mintAuthority?.equals(
deriveMintAuthorityKey(TOKEN_BRIDGE_ADDRESS)
)
).is.true;
expect(mintInfo.supply).to.equal(0n);
// check wrapped meta
const wrappedMeta = await getWrappedMeta(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
expect(wrappedMeta.chain).to.equal(ethereumTokenBridge.chain);
expect(
Buffer.compare(wrappedMeta.tokenAddress, expectedTokenAddress)
).to.equal(0);
expect(wrappedMeta.originalDecimals).to.equal(decimals);
});
it("Receive Token", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = await getOrCreateAssociatedTokenAccount(
connection,
wallet.signer(),
mint,
wallet.key()
).then((account) => account.address);
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyBefore = await getMint(connection, mint).then(
(info) => info.supply
);
const amount = 2n * 4206942069n;
const recipientChain = 1;
const fee = 0n;
const nonce = 420;
const message = ethereumTokenBridge.publishTransferTokens(
tokenAddress.toString("hex"),
tokenChain,
amount,
recipientChain,
mintAta.toBuffer().toString("hex"),
fee,
nonce
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const txSignatures = await postVaa(
connection,
wallet.signTransaction,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
).then((results) => results.map((result) => result.signature));
const postTx = txSignatures.pop()!;
for (const verifyTx of txSignatures) {
// console.log(`verifySignatures: ${verifyTx}`);
}
// console.log(`postVaa: ${postTx}`);
const completeTransferWrappedIx =
createCompleteTransferWrappedInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
);
const completeWrappedTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(completeTransferWrappedIx),
[wallet.signer()]
);
// console.log(`completeWrappedTransferTx: ${completeWrappedTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyAfter = await getMint(connection, mint).then(
(info) => info.supply
);
// check balance and supply changes
expect(walletBalanceAfter - walletBalanceBefore).to.equal(amount);
expect(supplyAfter - supplyBefore).to.equal(amount);
// verify data
const parsed = parseVaa(signedVaa);
const messageData = await getPostedVaa(
connection,
CORE_BRIDGE_ADDRESS,
parsed.hash
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(
ethereumTokenBridge.consistencyLevel
);
expect(
Buffer.compare(
messageData.emitterAddress,
ethAddressToBuffer(ETHEREUM_TOKEN_BRIDGE_ADDRESS)
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(3n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaVersion).to.equal(1);
expect(
Buffer.compare(parseVaa(signedVaa).payload, messageData.payload)
).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(1);
expect(tokenTransfer.amount).to.equal(amount);
expect(tokenTransfer.fee).is.not.null;
expect(tokenTransfer.fee).to.equal(fee);
expect(tokenTransfer.fromAddress).is.null;
expect(Buffer.compare(tokenTransfer.to, mintAta.toBuffer())).to.equal(
0
);
expect(tokenTransfer.toChain).to.equal(recipientChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, tokenAddress)
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(tokenChain);
});
it("Send Token", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, wallet.key());
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyBefore = await getMint(connection, mint).then(
(info) => info.supply
);
const nonce = 69;
const amount = 4206942069n;
const fee = 0n;
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
const targetChain = 2;
const approveIx = createApproveAuthoritySignerInstruction(
TOKEN_BRIDGE_ADDRESS,
mintAta,
wallet.key(),
amount
);
const message = web3.Keypair.generate();
const transferNativeIx = createTransferWrappedInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
message.publicKey,
mintAta,
wallet.key(),
tokenChain,
tokenAddress,
nonce,
amount,
fee,
targetAddress,
targetChain
);
const approveAndTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(approveIx, transferNativeIx),
[wallet.signer(), message]
);
// console.log(`approveAndTransferTx: ${approveAndTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyAfter = await getMint(connection, mint).then(
(info) => info.supply
);
// check balance changes
expect(walletBalanceBefore - walletBalanceAfter).to.equal(amount);
expect(supplyBefore - supplyAfter).to.equal(amount);
// verify data
const messageData = await getPostedMessage(
connection,
message.publicKey
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(32);
expect(
Buffer.compare(
messageData.emitterAddress,
deriveWormholeEmitterKey(TOKEN_BRIDGE_ADDRESS).toBuffer()
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(1);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(3n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaSignatureAccount.equals(web3.PublicKey.default))
.is.true;
expect(messageData.vaaVersion).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(1);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).to.equal(8);
expect(tokenTransfer.amount).to.equal(amount);
expect(tokenTransfer.fee).is.not.null;
expect(tokenTransfer.fee).to.equal(fee);
expect(tokenTransfer.fromAddress).is.null;
expect(Buffer.compare(tokenTransfer.to, targetAddress)).to.equal(0);
expect(tokenTransfer.toChain).to.equal(targetChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, tokenAddress)
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(tokenChain);
const sequenceTracker = await getProgramSequenceTracker(
connection,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS
);
expect(sequenceTracker.value()).to.equal(messageData.sequence + 1n);
});
it("Send Token With Payload", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const mintAta = getAssociatedTokenAddressSync(mint, wallet.key());
const walletBalanceBefore = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyBefore = await getMint(connection, mint).then(
(info) => info.supply
);
const nonce = 69;
const amount = 4206942069n;
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
const targetChain = 2;
const approveIx = createApproveAuthoritySignerInstruction(
TOKEN_BRIDGE_ADDRESS,
mintAta,
wallet.key(),
amount
);
const message = web3.Keypair.generate();
const transferPayload = Buffer.from("All your base are belong to us");
const transferNativeIx = createTransferWrappedWithPayloadInstruction(
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS,
wallet.key(),
message.publicKey,
mintAta,
wallet.key(),
tokenChain,
tokenAddress,
nonce,
amount,
targetAddress,
targetChain,
transferPayload
);
const approveAndTransferTx = await web3.sendAndConfirmTransaction(
connection,
new web3.Transaction().add(approveIx, transferNativeIx),
[wallet.signer(), message]
);
// console.log(`approveAndTransferTx: ${approveAndTransferTx}`);
const walletBalanceAfter = await getAccount(connection, mintAta).then(
(account) => account.amount
);
const supplyAfter = await getMint(connection, mint).then(
(info) => info.supply
);
// check balance changes
expect(walletBalanceBefore - walletBalanceAfter).to.equal(amount);
expect(supplyBefore - supplyAfter).to.equal(amount);
// verify data
const messageData = await getPostedMessage(
connection,
message.publicKey
).then((posted) => posted.message);
expect(messageData.consistencyLevel).to.equal(32);
expect(
Buffer.compare(
messageData.emitterAddress,
deriveWormholeEmitterKey(TOKEN_BRIDGE_ADDRESS).toBuffer()
)
).to.equal(0);
expect(messageData.emitterChain).to.equal(1);
expect(messageData.nonce).to.equal(nonce);
expect(messageData.sequence).to.equal(4n);
expect(messageData.vaaTime).to.equal(0);
expect(messageData.vaaSignatureAccount.equals(web3.PublicKey.default))
.is.true;
expect(messageData.vaaVersion).to.equal(0);
const tokenTransfer = parseTokenTransferPayload(messageData.payload);
expect(tokenTransfer.payloadType).to.equal(3);
const mintInfo = await getMint(connection, mint);
expect(mintInfo.decimals).to.equal(8);
expect(tokenTransfer.amount).to.equal(amount);
expect(Buffer.compare(tokenTransfer.to, targetAddress)).to.equal(0);
expect(tokenTransfer.toChain).to.equal(targetChain);
expect(
Buffer.compare(tokenTransfer.tokenAddress, tokenAddress)
).to.equal(0);
expect(tokenTransfer.tokenChain).to.equal(tokenChain);
expect(
Buffer.compare(tokenTransfer.tokenTransferPayload, transferPayload)
).to.equal(0);
const sequenceTracker = await getProgramSequenceTracker(
connection,
TOKEN_BRIDGE_ADDRESS,
CORE_BRIDGE_ADDRESS
);
expect(sequenceTracker.value()).to.equal(messageData.sequence + 1n);
});
});
});
describe("SDK Methods", () => {
// nft bridge on Ethereum
const ethereumTokenBridge = new MockEthereumTokenBridge(
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
3 // startSequence
);
describe("getOriginalAssetSolana", () => {
it("Non-existent Token", async () => {
const mint = "wot m8?";
const asset = await getOriginalAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
// verify results
expect(asset.isWrapped).to.be.false;
expect(asset.chainId).to.equal(1);
expect(
Buffer.compare(Buffer.from(asset.assetAddress), Buffer.alloc(32))
).to.equal(0);
});
it("Native Token", async () => {
const mint = localVariables.mint;
const asset = await getOriginalAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
// verify results
expect(asset.isWrapped).to.be.false;
expect(asset.chainId).to.equal(1);
expect(
Buffer.compare(Buffer.from(asset.assetAddress), mint.toBuffer())
).to.equal(0);
});
it("Wrapped Token", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const asset = await getOriginalAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
// verify results
expect(asset.isWrapped).is.true;
expect(asset.chainId).to.equal(tokenChain);
expect(
Buffer.compare(Buffer.from(asset.assetAddress), tokenAddress)
).to.equal(0);
});
});
describe("getForeignAssetSolana", () => {
it("Wrapped Token", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const asset = await getForeignAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
tokenChain as ChainId,
tokenAddress
);
// verify results
expect(asset).to.equal("3tUXFuBNWzZZ8p2xNx5UoWCH664M2KHdDAWrdZAD1VQ3");
});
});
describe("getIsWrappedAsset", () => {
it("Non-existent Token", async () => {
const mint = null;
const isWrapped = await getIsWrappedAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
// @ts-ignore: mint is null
mint
);
// verify results
expect(isWrapped).to.be.false;
});
it("Native Token", async () => {
const mint = localVariables.mint;
const isWrapped = await getIsWrappedAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
// verify results
expect(isWrapped).to.be.false;
});
it("Wrapped Token", async () => {
const tokenAddress = ethAddressToBuffer(WETH_ADDRESS);
const tokenChain = ethereumTokenBridge.chain;
const mint = deriveWrappedMintKey(
TOKEN_BRIDGE_ADDRESS,
tokenChain,
tokenAddress
);
const isWrapped = await getIsWrappedAssetSolana(
connection,
TOKEN_BRIDGE_ADDRESS,
mint
);
// verify results
expect(isWrapped).is.true;
});
});
describe("transferNativeSol", () => {
it("Send SOL To Ethereum", async () => {
const balanceBefore = await connection
.getBalance(wallet.key())
.then((num) => BigInt(num));
const amount = 6969696969n;
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
const transferNativeSolTx = await transferNativeSol(
connection,
CORE_BRIDGE_ADDRESS,
TOKEN_BRIDGE_ADDRESS,
wallet.key(),
amount,
targetAddress,
"ethereum"
)
.then((transaction) =>
signSendAndConfirmTransaction(
connection,
wallet.key(),
wallet.signTransaction,
transaction
)
)
.then((response) => response.signature);
//console.log(`transferNativeSolTx: ${transferNativeSolTx}`);
const balanceAfter = await connection
.getBalance(wallet.key())
.then((num) => BigInt(num));
const transactionCost = 4601551n;
expect(balanceBefore - balanceAfter - transactionCost).to.equal(amount);
});
});
describe("redeemAndUnwrapOnSolana", () => {
it("Receive SOL From Ethereum", async () => {
const balanceBefore = await connection
.getBalance(wallet.key())
.then((num) => BigInt(num));
const tokenChain = 1;
const mintAta = await getOrCreateAssociatedTokenAccount(
connection,
wallet.signer(),
NATIVE_MINT,
wallet.key()
).then((account) => account.address);
const amount = 42042042n;
const recipientChain = 1;
const fee = 0n;
const nonce = 420;
const message = ethereumTokenBridge.publishTransferTokens(
NATIVE_MINT.toBuffer().toString("hex"),
tokenChain,
amount,
recipientChain,
mintAta.toBuffer().toString("hex"),
fee,
nonce
);
const signedVaa = guardians.addSignatures(
message,
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
);
const txSignatures = await postVaa(
connection,
wallet.signTransaction,
CORE_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
).then((results) => results.map((result) => result.signature));
const postTx = txSignatures.pop()!;
for (const verifyTx of txSignatures) {
// console.log(`verifySignatures: ${verifyTx}`);
}
// console.log(`postVaa: ${postTx}`);
const transferNativeSolTx = await redeemAndUnwrapOnSolana(
connection,
CORE_BRIDGE_ADDRESS,
TOKEN_BRIDGE_ADDRESS,
wallet.key(),
signedVaa
)
.then((transaction) =>
signSendAndConfirmTransaction(
connection,
wallet.key(),
wallet.signTransaction,
transaction
)
)
.then((response) => response.signature);
//console.log(`transferNativeSolTx: ${transferNativeSolTx}`);
const balanceAfter = await connection
.getBalance(wallet.key())
.then((num) => BigInt(num));
const transactionCost = 6821400n;
expect(balanceAfter - (balanceBefore - transactionCost)).to.equal(
amount * 10n
);
});
});
});
});