sdk/js: add tests for aptos sdk
This commit is contained in:
parent
7c1721d199
commit
3bb0562f45
|
@ -3,6 +3,7 @@
|
||||||
"^.+\\.(t|j)sx?$": "ts-jest"
|
"^.+\\.(t|j)sx?$": "ts-jest"
|
||||||
},
|
},
|
||||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||||
|
"testPathIgnorePatterns": ["__tests__/utils"],
|
||||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
|
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||||
"testTimeout": 60000
|
"testTimeout": 60000
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
import { beforeAll, describe, expect, jest, test } from "@jest/globals";
|
||||||
|
import { AptosAccount, AptosClient, FaucetClient, Types } from "aptos";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import Web3 from "web3";
|
||||||
|
import {
|
||||||
|
CHAIN_ID_APTOS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CONTRACTS,
|
||||||
|
deriveResourceAccountAddress,
|
||||||
|
generateSignAndSubmitEntryFunction,
|
||||||
|
tryNativeToUint8Array,
|
||||||
|
} from "../../utils";
|
||||||
|
import { parseNftTransferVaa } from "../../vaa";
|
||||||
|
import { getIsTransferCompletedAptos } from "../getIsTransferCompleted";
|
||||||
|
import { getIsWrappedAssetAptos } from "../getIsWrappedAsset";
|
||||||
|
import { getOriginalAssetAptos } from "../getOriginalAsset";
|
||||||
|
import { redeemOnAptos } from "../redeem";
|
||||||
|
import { transferFromAptos, transferFromEth } from "../transfer";
|
||||||
|
import {
|
||||||
|
APTOS_FAUCET_URL,
|
||||||
|
APTOS_NODE_URL,
|
||||||
|
ETH_NODE_URL,
|
||||||
|
ETH_PRIVATE_KEY,
|
||||||
|
} from "./consts";
|
||||||
|
import { deployTestNftOnEthereum } from "./utils/deployTestNft";
|
||||||
|
import { getSignedVaaAptos, getSignedVaaEthereum } from "./utils/getSignedVaa";
|
||||||
|
|
||||||
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
const APTOS_NFT_BRIDGE_ADDRESS = CONTRACTS.DEVNET.aptos.nft_bridge;
|
||||||
|
const ETH_NFT_BRIDGE_ADDRESS = CONTRACTS.DEVNET.ethereum.nft_bridge;
|
||||||
|
|
||||||
|
let aptosClient: AptosClient;
|
||||||
|
let aptosAccount: AptosAccount;
|
||||||
|
let web3: Web3;
|
||||||
|
let ethProvider: ethers.providers.WebSocketProvider;
|
||||||
|
let ethSigner: ethers.Wallet;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
// aptos setup
|
||||||
|
aptosClient = new AptosClient(APTOS_NODE_URL);
|
||||||
|
aptosAccount = new AptosAccount();
|
||||||
|
const faucet = new FaucetClient(APTOS_NODE_URL, APTOS_FAUCET_URL);
|
||||||
|
await faucet.fundAccount(aptosAccount.address(), 100_000_000);
|
||||||
|
|
||||||
|
// ethereum setup
|
||||||
|
web3 = new Web3(ETH_NODE_URL);
|
||||||
|
ethProvider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
ethSigner = new ethers.Wallet(ETH_PRIVATE_KEY, ethProvider); // corresponds to accounts[1]
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Aptos NFT SDK tests", () => {
|
||||||
|
test("Transfer ERC-721 from Ethereum to Aptos and back", async () => {
|
||||||
|
const ETH_COLLECTION_NAME = "Not an APE 🐒";
|
||||||
|
|
||||||
|
// create NFT on Ethereum
|
||||||
|
const ethNft = await deployTestNftOnEthereum(
|
||||||
|
web3,
|
||||||
|
ethSigner,
|
||||||
|
ETH_COLLECTION_NAME,
|
||||||
|
"APE🐒",
|
||||||
|
"https://cloudflare-ipfs.com/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/",
|
||||||
|
11
|
||||||
|
);
|
||||||
|
|
||||||
|
// transfer NFT from Ethereum to Aptos
|
||||||
|
const ethTransferTx = await transferFromEth(
|
||||||
|
ETH_NFT_BRIDGE_ADDRESS,
|
||||||
|
ethSigner,
|
||||||
|
ethNft.address,
|
||||||
|
10,
|
||||||
|
CHAIN_ID_APTOS,
|
||||||
|
aptosAccount.address().toUint8Array()
|
||||||
|
);
|
||||||
|
|
||||||
|
// observe tx and get vaa
|
||||||
|
const ethTransferVaa = await getSignedVaaEthereum(ethTransferTx);
|
||||||
|
const ethTransferVaaParsed = parseNftTransferVaa(ethTransferVaa);
|
||||||
|
expect(ethTransferVaaParsed.name).toBe(ETH_COLLECTION_NAME);
|
||||||
|
|
||||||
|
// redeem NFT on Aptos
|
||||||
|
const aptosRedeemPayload = await redeemOnAptos(
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
ethTransferVaa
|
||||||
|
);
|
||||||
|
const aptosRedeemTx = await generateSignAndSubmitEntryFunction(
|
||||||
|
aptosClient,
|
||||||
|
aptosAccount,
|
||||||
|
aptosRedeemPayload
|
||||||
|
);
|
||||||
|
const aptosRedeemTxResult = (await aptosClient.waitForTransactionWithResult(
|
||||||
|
aptosRedeemTx.hash
|
||||||
|
)) as Types.UserTransaction;
|
||||||
|
expect(aptosRedeemTxResult.success).toBe(true);
|
||||||
|
expect(
|
||||||
|
await getIsTransferCompletedAptos(
|
||||||
|
aptosClient,
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
ethTransferVaa
|
||||||
|
)
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
// get creator address
|
||||||
|
const creatorAddress = await deriveResourceAccountAddress(
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
ethNft.address
|
||||||
|
);
|
||||||
|
expect(creatorAddress).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
await getIsWrappedAssetAptos(
|
||||||
|
aptosClient,
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
creatorAddress!
|
||||||
|
)
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
await getOriginalAssetAptos(
|
||||||
|
aptosClient,
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
creatorAddress!
|
||||||
|
)
|
||||||
|
).toMatchObject({
|
||||||
|
isWrapped: true,
|
||||||
|
chainId: CHAIN_ID_ETH,
|
||||||
|
assetAddress: Uint8Array.from(ethTransferVaaParsed.tokenAddress),
|
||||||
|
});
|
||||||
|
|
||||||
|
// transfer NFT from Aptos back to Ethereum
|
||||||
|
const aptosTransferPayload = await transferFromAptos(
|
||||||
|
APTOS_NFT_BRIDGE_ADDRESS,
|
||||||
|
creatorAddress!,
|
||||||
|
ethTransferVaaParsed.name, // TODO(aki): derive this properly
|
||||||
|
ethTransferVaaParsed.tokenId.toString(16).padStart(64, "0"), // TODO(aki): derive this properly
|
||||||
|
0,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
tryNativeToUint8Array(ethSigner.address, CHAIN_ID_ETH)
|
||||||
|
);
|
||||||
|
const aptosTransferTx = await generateSignAndSubmitEntryFunction(
|
||||||
|
aptosClient,
|
||||||
|
aptosAccount,
|
||||||
|
aptosTransferPayload
|
||||||
|
);
|
||||||
|
const aptosTransferTxResult =
|
||||||
|
(await aptosClient.waitForTransactionWithResult(
|
||||||
|
aptosTransferTx.hash
|
||||||
|
)) as Types.UserTransaction;
|
||||||
|
expect(aptosTransferTxResult.success).toBe(true);
|
||||||
|
|
||||||
|
// observe tx and get vaa
|
||||||
|
const aptosTransferVaa = await getSignedVaaAptos(
|
||||||
|
aptosClient,
|
||||||
|
aptosTransferTxResult
|
||||||
|
);
|
||||||
|
const aptosTransferVaaParsed = parseNftTransferVaa(aptosTransferVaa);
|
||||||
|
expect(aptosTransferVaaParsed.name).toBe(ETH_COLLECTION_NAME);
|
||||||
|
expect(aptosTransferVaaParsed.tokenAddress.toString("hex")).toBe(
|
||||||
|
ethTransferVaaParsed.tokenAddress.toString("hex")
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(aki): make this work
|
||||||
|
// // redeem NFT on Ethereum & check NFT is the same
|
||||||
|
// const ethRedeemTxResult = await redeemOnEth(
|
||||||
|
// ETH_NFT_BRIDGE_ADDRESS,
|
||||||
|
// ethSigner,
|
||||||
|
// aptosTransferVaa,
|
||||||
|
// { gasLimit: 3e7 }
|
||||||
|
// );
|
||||||
|
// console.log(
|
||||||
|
// "ethRedeemTxResult",
|
||||||
|
// JSON.stringify(ethRedeemTxResult, null, 2)
|
||||||
|
// );
|
||||||
|
// expect(ethRedeemTxResult.status).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
function afterAll(arg0: () => Promise<void>) {
|
||||||
|
throw new Error("Function not implemented.");
|
||||||
|
}
|
|
@ -33,6 +33,15 @@ export const WORMHOLE_RPC_HOSTS = ci
|
||||||
? ["http://guardian:7071"]
|
? ["http://guardian:7071"]
|
||||||
: ["http://localhost:7071"];
|
: ["http://localhost:7071"];
|
||||||
|
|
||||||
|
export const APTOS_PRIVATE_KEY =
|
||||||
|
"537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d";
|
||||||
|
export const APTOS_NODE_URL = ci
|
||||||
|
? "http://aptos:8080/v1"
|
||||||
|
: "http://0.0.0.0:8080/v1";
|
||||||
|
export const APTOS_FAUCET_URL = ci
|
||||||
|
? "http://aptos:8081"
|
||||||
|
: "http://0.0.0.0:8081";
|
||||||
|
|
||||||
describe("consts should exist", () => {
|
describe("consts should exist", () => {
|
||||||
it("has Solana test token", () => {
|
it("has Solana test token", () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
|
|
|
@ -32,10 +32,7 @@ import {
|
||||||
SOLANA_PRIVATE_KEY,
|
SOLANA_PRIVATE_KEY,
|
||||||
TEST_SOLANA_TOKEN,
|
TEST_SOLANA_TOKEN,
|
||||||
} from "./consts";
|
} from "./consts";
|
||||||
import {
|
import { getSignedVaaEthereum, getSignedVaaSolana } from "./utils/getSignedVaa";
|
||||||
waitUntilTransactionObservedEthereum,
|
|
||||||
waitUntilTransactionObservedSolana,
|
|
||||||
} from "./utils/waitUntilTransactionObserved";
|
|
||||||
|
|
||||||
jest.setTimeout(60000);
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
@ -59,72 +56,6 @@ afterEach(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Integration Tests", () => {
|
describe("Integration Tests", () => {
|
||||||
// TODO: figure out why this isn't working
|
|
||||||
// test("Send Ethereum ERC-721 to Solana and back", (done) => {
|
|
||||||
// (async () => {
|
|
||||||
// try {
|
|
||||||
// const erc721 = await deployNFTOnEth(
|
|
||||||
// "Not an APE 🐒",
|
|
||||||
// "APE🐒",
|
|
||||||
// "https://cloudflare-ipfs.com/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/",
|
|
||||||
// 11 // mint ids 0..10 (inclusive)
|
|
||||||
// );
|
|
||||||
// const sol_addr = await nft_bridge.getForeignAssetSol(
|
|
||||||
// CONTRACTS.DEVNET.solana.nft_bridge,
|
|
||||||
// CHAIN_ID_ETH,
|
|
||||||
// tryNativeToUint8Array(erc721.address, CHAIN_ID_ETH),
|
|
||||||
// arrayify(BigNumber.from("10"))
|
|
||||||
// );
|
|
||||||
// const fromAddress = await Token.getAssociatedTokenAddress(
|
|
||||||
// ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
||||||
// TOKEN_PROGRAM_ID,
|
|
||||||
// new PublicKey(sol_addr),
|
|
||||||
// keypair.publicKey
|
|
||||||
// );
|
|
||||||
// const transaction = await _transferFromEth(
|
|
||||||
// erc721.address,
|
|
||||||
// 10,
|
|
||||||
// fromAddress.toString(),
|
|
||||||
// CHAIN_ID_SOLANA
|
|
||||||
// );
|
|
||||||
// let signedVAA = await waitUntilEthTxObserved(transaction);
|
|
||||||
// await _redeemOnSolana(signedVAA);
|
|
||||||
|
|
||||||
// let ownerEth = await erc721.ownerOf(10);
|
|
||||||
// expect(ownerEth).not.toBe(signer.address);
|
|
||||||
|
|
||||||
// // TODO: check wrapped balance
|
|
||||||
|
|
||||||
// // Send back the NFT to ethereum
|
|
||||||
// const transaction2 = await _transferFromSolana(
|
|
||||||
// fromAddress,
|
|
||||||
// sol_addr,
|
|
||||||
// signer.address,
|
|
||||||
// CHAIN_ID_ETH,
|
|
||||||
// tryNativeToUint8Array(erc721.address, CHAIN_ID_ETH),
|
|
||||||
// CHAIN_ID_ETH,
|
|
||||||
// arrayify(BigNumber.from("10"))
|
|
||||||
// );
|
|
||||||
// signedVAA = await waitUntilSolanaTxObserved(transaction2);
|
|
||||||
// (await expectReceivedOnEth(signedVAA)).toBe(false);
|
|
||||||
// await _redeemOnEth(signedVAA);
|
|
||||||
// (await expectReceivedOnEth(signedVAA)).toBe(true);
|
|
||||||
|
|
||||||
// // ensure that the transaction roundtrips back to the original native asset
|
|
||||||
// ownerEth = await erc721.ownerOf(10);
|
|
||||||
// expect(ownerEth).toBe(signer.address);
|
|
||||||
|
|
||||||
// // TODO: the wrapped token should no longer exist
|
|
||||||
|
|
||||||
// done();
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error(e);
|
|
||||||
// done(
|
|
||||||
// `An error occured while trying to transfer from Ethereum to Solana and back: ${e}`
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// })();
|
|
||||||
// });
|
|
||||||
test("Send Solana SPL to Ethereum and back", (done) => {
|
test("Send Solana SPL to Ethereum and back", (done) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -140,7 +71,7 @@ describe("Integration Tests", () => {
|
||||||
signer.address,
|
signer.address,
|
||||||
CHAIN_ID_ETH
|
CHAIN_ID_ETH
|
||||||
);
|
);
|
||||||
let signedVAA = await waitUntilTransactionObservedSolana(transaction1);
|
let signedVAA = await getSignedVaaSolana(transaction1);
|
||||||
|
|
||||||
// we get the solana token id from the VAA
|
// we get the solana token id from the VAA
|
||||||
const { tokenId } = parseNftTransferVaa(signedVAA);
|
const { tokenId } = parseNftTransferVaa(signedVAA);
|
||||||
|
@ -162,7 +93,7 @@ describe("Integration Tests", () => {
|
||||||
fromAddress.toString(),
|
fromAddress.toString(),
|
||||||
CHAIN_ID_SOLANA
|
CHAIN_ID_SOLANA
|
||||||
);
|
);
|
||||||
signedVAA = await waitUntilTransactionObservedEthereum(transaction3);
|
signedVAA = await getSignedVaaEthereum(transaction3);
|
||||||
|
|
||||||
const { name, symbol } = parseNftTransferVaa(signedVAA);
|
const { name, symbol } = parseNftTransferVaa(signedVAA);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { AptosAccount, AptosClient, TokenClient } from "aptos";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import Web3 from "web3";
|
import Web3 from "web3";
|
||||||
import {
|
import {
|
||||||
|
@ -6,6 +7,31 @@ import {
|
||||||
} from "../../../ethers-contracts";
|
} from "../../../ethers-contracts";
|
||||||
const ERC721 = require("@openzeppelin/contracts/build/contracts/ERC721PresetMinterPauserAutoId.json");
|
const ERC721 = require("@openzeppelin/contracts/build/contracts/ERC721PresetMinterPauserAutoId.json");
|
||||||
|
|
||||||
|
export const deployTestNftOnAptos = async (
|
||||||
|
client: AptosClient,
|
||||||
|
account: AptosAccount
|
||||||
|
) => {
|
||||||
|
const tokenClient = new TokenClient(client);
|
||||||
|
const collectionName = "testCollection";
|
||||||
|
const collectionHash = await tokenClient.createCollection(
|
||||||
|
account,
|
||||||
|
collectionName,
|
||||||
|
"test collection",
|
||||||
|
"https://www.wormhole.com"
|
||||||
|
);
|
||||||
|
await client.waitForTransaction(collectionHash);
|
||||||
|
|
||||||
|
const tokenHash = await tokenClient.createToken(
|
||||||
|
account,
|
||||||
|
collectionName,
|
||||||
|
"testToken",
|
||||||
|
"test token",
|
||||||
|
1,
|
||||||
|
"https://wormhole.com/static/a9281881f58cc7fbe4df796a9ba684ac/90d9d/s2.webp"
|
||||||
|
);
|
||||||
|
await client.waitForTransaction(tokenHash);
|
||||||
|
};
|
||||||
|
|
||||||
export async function deployTestNftOnEthereum(
|
export async function deployTestNftOnEthereum(
|
||||||
web3: Web3,
|
web3: Web3,
|
||||||
signer: ethers.Wallet,
|
signer: ethers.Wallet,
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { GetSignedVAAResponse } from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
|
||||||
|
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||||
|
import { TransactionResponse } from "@solana/web3.js";
|
||||||
|
import { AptosClient, Types } from "aptos";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { NftBridgeState } from "../../../aptos/types";
|
||||||
|
import {
|
||||||
|
getEmitterAddressEth,
|
||||||
|
getEmitterAddressSolana,
|
||||||
|
parseSequenceFromLogAptos,
|
||||||
|
parseSequenceFromLogEth,
|
||||||
|
parseSequenceFromLogSolana,
|
||||||
|
} from "../../../bridge";
|
||||||
|
import { getSignedVAAWithRetry } from "../../../rpc";
|
||||||
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_APTOS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
CONTRACTS,
|
||||||
|
} from "../../../utils";
|
||||||
|
import { WORMHOLE_RPC_HOSTS } from "../consts";
|
||||||
|
|
||||||
|
// TODO(aki): implement getEmitterAddressAptos and sub here
|
||||||
|
export async function getSignedVaaAptos(
|
||||||
|
client: AptosClient,
|
||||||
|
result: Types.UserTransaction
|
||||||
|
): Promise<Uint8Array> {
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogAptos(
|
||||||
|
CONTRACTS.DEVNET.aptos.core,
|
||||||
|
result
|
||||||
|
);
|
||||||
|
if (sequence === null) {
|
||||||
|
throw new Error("aptos: Could not parse sequence from logs");
|
||||||
|
}
|
||||||
|
|
||||||
|
const nftBridgeAddress = CONTRACTS.DEVNET.aptos.nft_bridge;
|
||||||
|
const state = (await client.getAccountResources(nftBridgeAddress)).find(
|
||||||
|
(r) => r.type === `${nftBridgeAddress}::state::State`
|
||||||
|
);
|
||||||
|
if (!state) {
|
||||||
|
throw new Error("aptos: Could not find State resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: ensure 0x is stripped if exists
|
||||||
|
const emitterAddress = (
|
||||||
|
state.data as NftBridgeState
|
||||||
|
).emitter_cap.emitter.padStart(64, "0");
|
||||||
|
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
return getSignedVaa(CHAIN_ID_APTOS, emitterAddress, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSignedVaaEthereum(
|
||||||
|
receipt: ethers.ContractReceipt
|
||||||
|
): Promise<Uint8Array> {
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
CONTRACTS.DEVNET.ethereum.core
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.nft_bridge
|
||||||
|
);
|
||||||
|
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
return getSignedVaa(CHAIN_ID_ETH, emitterAddress, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSignedVaaSolana(
|
||||||
|
response: TransactionResponse
|
||||||
|
): Promise<Uint8Array> {
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogSolana(response);
|
||||||
|
const emitterAddress = getEmitterAddressSolana(
|
||||||
|
CONTRACTS.DEVNET.solana.nft_bridge
|
||||||
|
);
|
||||||
|
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
return getSignedVaa(CHAIN_ID_SOLANA, emitterAddress, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSignedVaa = async (
|
||||||
|
chain: ChainId,
|
||||||
|
emitterAddress: string,
|
||||||
|
sequence: string
|
||||||
|
): Promise<Uint8Array> => {
|
||||||
|
const { vaaBytes: signedVAA }: GetSignedVAAResponse =
|
||||||
|
await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
chain,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return signedVAA;
|
||||||
|
};
|
|
@ -1,57 +0,0 @@
|
||||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
|
||||||
import { TransactionResponse } from "@solana/web3.js";
|
|
||||||
import { ethers } from "ethers";
|
|
||||||
import {
|
|
||||||
getEmitterAddressEth,
|
|
||||||
getEmitterAddressSolana,
|
|
||||||
parseSequenceFromLogEth,
|
|
||||||
parseSequenceFromLogSolana,
|
|
||||||
} from "../../../bridge";
|
|
||||||
import { getSignedVAAWithRetry } from "../../../rpc";
|
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, CONTRACTS } from "../../../utils";
|
|
||||||
import { WORMHOLE_RPC_HOSTS } from "../consts";
|
|
||||||
|
|
||||||
export async function waitUntilTransactionObservedEthereum(
|
|
||||||
receipt: ethers.ContractReceipt
|
|
||||||
): Promise<Uint8Array> {
|
|
||||||
// get the sequence from the logs (needed to fetch the vaa)
|
|
||||||
let sequence = parseSequenceFromLogEth(
|
|
||||||
receipt,
|
|
||||||
CONTRACTS.DEVNET.ethereum.core
|
|
||||||
);
|
|
||||||
let emitterAddress = getEmitterAddressEth(
|
|
||||||
CONTRACTS.DEVNET.ethereum.nft_bridge
|
|
||||||
);
|
|
||||||
// poll until the guardian(s) witness and sign the vaa
|
|
||||||
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
|
||||||
WORMHOLE_RPC_HOSTS,
|
|
||||||
CHAIN_ID_ETH,
|
|
||||||
emitterAddress,
|
|
||||||
sequence,
|
|
||||||
{
|
|
||||||
transport: NodeHttpTransport(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return signedVAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function waitUntilTransactionObservedSolana(
|
|
||||||
response: TransactionResponse
|
|
||||||
): Promise<Uint8Array> {
|
|
||||||
// get the sequence from the logs (needed to fetch the vaa)
|
|
||||||
const sequence = parseSequenceFromLogSolana(response);
|
|
||||||
const emitterAddress = await getEmitterAddressSolana(
|
|
||||||
CONTRACTS.DEVNET.solana.nft_bridge
|
|
||||||
);
|
|
||||||
// poll until the guardian(s) witness and sign the vaa
|
|
||||||
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
|
||||||
WORMHOLE_RPC_HOSTS,
|
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
emitterAddress,
|
|
||||||
sequence,
|
|
||||||
{
|
|
||||||
transport: NodeHttpTransport(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return signedVAA;
|
|
||||||
}
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||||
import { describe, expect, jest, test } from "@jest/globals";
|
import { describe, expect, jest, test } from "@jest/globals";
|
||||||
import {
|
import {
|
||||||
AptosAccount,
|
AptosAccount,
|
||||||
|
@ -6,6 +7,8 @@ import {
|
||||||
HexString,
|
HexString,
|
||||||
Types,
|
Types,
|
||||||
} from "aptos";
|
} from "aptos";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { parseUnits } from "ethers/lib/utils";
|
||||||
import {
|
import {
|
||||||
approveEth,
|
approveEth,
|
||||||
APTOS_TOKEN_BRIDGE_EMITTER_ADDRESS,
|
APTOS_TOKEN_BRIDGE_EMITTER_ADDRESS,
|
||||||
|
@ -17,7 +20,8 @@ import {
|
||||||
createWrappedOnAptos,
|
createWrappedOnAptos,
|
||||||
createWrappedOnEth,
|
createWrappedOnEth,
|
||||||
createWrappedTypeOnAptos,
|
createWrappedTypeOnAptos,
|
||||||
getAssetFullyQualifiedType,
|
generateSignAndSubmitEntryFunction,
|
||||||
|
generateSignAndSubmitScript,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getExternalAddressFromType,
|
getExternalAddressFromType,
|
||||||
getForeignAssetAptos,
|
getForeignAssetAptos,
|
||||||
|
@ -30,55 +34,45 @@ import {
|
||||||
hexToUint8Array,
|
hexToUint8Array,
|
||||||
redeemOnAptos,
|
redeemOnAptos,
|
||||||
redeemOnEth,
|
redeemOnEth,
|
||||||
generateSignAndSubmitEntryFunction,
|
|
||||||
generateSignAndSubmitScript,
|
|
||||||
transferFromAptos,
|
transferFromAptos,
|
||||||
transferFromEth,
|
transferFromEth,
|
||||||
tryNativeToHexString,
|
tryNativeToHexString,
|
||||||
tryNativeToUint8Array,
|
tryNativeToUint8Array,
|
||||||
uint8ArrayToHex,
|
uint8ArrayToHex,
|
||||||
} from "../..";
|
} from "../..";
|
||||||
|
import { registerCoin } from "../../aptos";
|
||||||
|
import {
|
||||||
|
parseSequenceFromLogAptos,
|
||||||
|
parseSequenceFromLogEth,
|
||||||
|
} from "../../bridge/parseSequenceFromLog";
|
||||||
|
import { TokenImplementation__factory } from "../../ethers-contracts";
|
||||||
import {
|
import {
|
||||||
APTOS_FAUCET_URL,
|
APTOS_FAUCET_URL,
|
||||||
APTOS_NODE_URL,
|
APTOS_NODE_URL,
|
||||||
APTOS_PRIVATE_KEY,
|
|
||||||
ETH_NODE_URL,
|
ETH_NODE_URL,
|
||||||
ETH_PRIVATE_KEY6,
|
ETH_PRIVATE_KEY6,
|
||||||
TEST_ERC20,
|
TEST_ERC20,
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
} from "./consts";
|
} from "./consts";
|
||||||
import {
|
|
||||||
parseSequenceFromLogAptos,
|
|
||||||
parseSequenceFromLogEth,
|
|
||||||
} from "../../bridge/parseSequenceFromLog";
|
|
||||||
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
|
||||||
import { ethers } from "ethers";
|
|
||||||
import { parseUnits } from "ethers/lib/utils";
|
|
||||||
import { registerCoin } from "../../aptos";
|
|
||||||
import { TokenImplementation__factory } from "../../ethers-contracts";
|
|
||||||
|
|
||||||
const JEST_TEST_TIMEOUT = 60000;
|
const JEST_TEST_TIMEOUT = 60000;
|
||||||
jest.setTimeout(JEST_TEST_TIMEOUT);
|
jest.setTimeout(JEST_TEST_TIMEOUT);
|
||||||
|
|
||||||
describe("Aptos SDK tests", () => {
|
describe("Aptos SDK tests", () => {
|
||||||
test("Transfer native token from Aptos to Ethereum", async () => {
|
test("Transfer native token from Aptos to Ethereum", async () => {
|
||||||
|
const APTOS_TOKEN_BRIDGE = CONTRACTS.DEVNET.aptos.token_bridge;
|
||||||
|
const APTOS_CORE_BRIDGE = CONTRACTS.DEVNET.aptos.core;
|
||||||
|
const COIN_TYPE = "0x1::aptos_coin::AptosCoin";
|
||||||
|
|
||||||
// setup aptos
|
// setup aptos
|
||||||
const client = new AptosClient(APTOS_NODE_URL);
|
const client = new AptosClient(APTOS_NODE_URL);
|
||||||
|
const sender = new AptosAccount();
|
||||||
const faucet = new FaucetClient(APTOS_NODE_URL, APTOS_FAUCET_URL);
|
const faucet = new FaucetClient(APTOS_NODE_URL, APTOS_FAUCET_URL);
|
||||||
const sender = new AptosAccount(hexToUint8Array(APTOS_PRIVATE_KEY));
|
|
||||||
const aptosTokenBridge = CONTRACTS.DEVNET.aptos.token_bridge;
|
|
||||||
const aptosCoreBridge = CONTRACTS.DEVNET.aptos.core;
|
|
||||||
|
|
||||||
// sanity check funds in the account
|
|
||||||
const COIN_TYPE = "0x1::aptos_coin::AptosCoin";
|
|
||||||
const before = await getBalanceAptos(client, COIN_TYPE, sender.address());
|
|
||||||
await faucet.fundAccount(sender.address(), 100_000_000);
|
await faucet.fundAccount(sender.address(), 100_000_000);
|
||||||
const after = await getBalanceAptos(client, COIN_TYPE, sender.address());
|
|
||||||
expect(Number(after) - Number(before)).toEqual(100_000_000);
|
|
||||||
|
|
||||||
// attest native aptos token
|
// attest native aptos token
|
||||||
const attestPayload = attestFromAptos(
|
const attestPayload = attestFromAptos(
|
||||||
aptosTokenBridge,
|
APTOS_TOKEN_BRIDGE,
|
||||||
CHAIN_ID_APTOS,
|
CHAIN_ID_APTOS,
|
||||||
COIN_TYPE
|
COIN_TYPE
|
||||||
);
|
);
|
||||||
|
@ -90,7 +84,7 @@ describe("Aptos SDK tests", () => {
|
||||||
await client.waitForTransaction(tx.hash);
|
await client.waitForTransaction(tx.hash);
|
||||||
|
|
||||||
// get signed attest vaa
|
// get signed attest vaa
|
||||||
let sequence = parseSequenceFromLogAptos(aptosCoreBridge, tx);
|
let sequence = parseSequenceFromLogAptos(APTOS_CORE_BRIDGE, tx);
|
||||||
expect(sequence).toBeTruthy();
|
expect(sequence).toBeTruthy();
|
||||||
|
|
||||||
const { vaaBytes: attestVAA } = await getSignedVAAWithRetry(
|
const { vaaBytes: attestVAA } = await getSignedVAAWithRetry(
|
||||||
|
@ -135,7 +129,7 @@ describe("Aptos SDK tests", () => {
|
||||||
await getBalanceAptos(client, COIN_TYPE, sender.address())
|
await getBalanceAptos(client, COIN_TYPE, sender.address())
|
||||||
);
|
);
|
||||||
const transferPayload = transferFromAptos(
|
const transferPayload = transferFromAptos(
|
||||||
aptosTokenBridge,
|
APTOS_TOKEN_BRIDGE,
|
||||||
COIN_TYPE,
|
COIN_TYPE,
|
||||||
(10_000_000).toString(),
|
(10_000_000).toString(),
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
|
@ -157,7 +151,7 @@ describe("Aptos SDK tests", () => {
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
// get signed transfer vaa
|
// get signed transfer vaa
|
||||||
sequence = parseSequenceFromLogAptos(aptosCoreBridge, tx);
|
sequence = parseSequenceFromLogAptos(APTOS_CORE_BRIDGE, tx);
|
||||||
expect(sequence).toBeTruthy();
|
expect(sequence).toBeTruthy();
|
||||||
|
|
||||||
const { vaaBytes: transferVAA } = await getSignedVAAWithRetry(
|
const { vaaBytes: transferVAA } = await getSignedVAAWithRetry(
|
||||||
|
@ -243,7 +237,9 @@ describe("Aptos SDK tests", () => {
|
||||||
|
|
||||||
// setup aptos
|
// setup aptos
|
||||||
const client = new AptosClient(APTOS_NODE_URL);
|
const client = new AptosClient(APTOS_NODE_URL);
|
||||||
const recipient = new AptosAccount(hexToUint8Array(APTOS_PRIVATE_KEY));
|
const recipient = new AptosAccount();
|
||||||
|
const faucet = new FaucetClient(APTOS_NODE_URL, APTOS_FAUCET_URL);
|
||||||
|
await faucet.fundAccount(recipient.address(), 100_000_000);
|
||||||
const aptosTokenBridge = CONTRACTS.DEVNET.aptos.token_bridge;
|
const aptosTokenBridge = CONTRACTS.DEVNET.aptos.token_bridge;
|
||||||
const createWrappedCoinTypePayload = createWrappedTypeOnAptos(
|
const createWrappedCoinTypePayload = createWrappedTypeOnAptos(
|
||||||
aptosTokenBridge,
|
aptosTokenBridge,
|
||||||
|
|
|
@ -172,7 +172,8 @@ const MAINNET = {
|
||||||
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
||||||
token_bridge:
|
token_bridge:
|
||||||
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
||||||
nft_bridge: "0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
nft_bridge:
|
||||||
|
"0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
||||||
},
|
},
|
||||||
sui: {
|
sui: {
|
||||||
core: undefined,
|
core: undefined,
|
||||||
|
@ -328,7 +329,8 @@ const TESTNET = {
|
||||||
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
||||||
token_bridge:
|
token_bridge:
|
||||||
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
||||||
nft_bridge: undefined,
|
nft_bridge:
|
||||||
|
"0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
||||||
},
|
},
|
||||||
sui: {
|
sui: {
|
||||||
core: undefined,
|
core: undefined,
|
||||||
|
@ -484,7 +486,8 @@ const DEVNET = {
|
||||||
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
|
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
|
||||||
token_bridge:
|
token_bridge:
|
||||||
"0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
|
"0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
|
||||||
nft_bridge: undefined,
|
nft_bridge:
|
||||||
|
"0x46da3d4c569388af61f951bdd1153f4c875f90c2991f6b2d0a38e2161a40852c",
|
||||||
},
|
},
|
||||||
sui: {
|
sui: {
|
||||||
core: undefined,
|
core: undefined,
|
||||||
|
|
Loading…
Reference in New Issue