sdk/js: Aptos transfer with payload support

This commit is contained in:
Kevin Peters 2023-08-28 09:03:19 -05:00 committed by Evan Gray
parent 451f7d2c63
commit 04b7afeb62
3 changed files with 164 additions and 6 deletions

View File

@ -227,11 +227,19 @@ export const transferTokensWithPayload = (
amount: string,
recipientChain: ChainId | ChainName,
recipient: Uint8Array,
relayerFee: string,
nonce: number,
payload: string
payload: Uint8Array
): Types.EntryFunctionPayload => {
throw new Error("Transfer with payload are not yet supported in the sdk");
if (!tokenBridgeAddress) throw new Error("Need token bridge address.");
if (!isValidAptosType(fullyQualifiedType)) {
throw new Error("Invalid qualified type");
}
const recipientChainId = coalesceChainId(recipientChain);
return {
function: `${tokenBridgeAddress}::transfer_tokens::transfer_tokens_with_payload_entry`,
type_arguments: [fullyQualifiedType],
arguments: [amount, recipientChainId, recipient, nonce, payload],
};
};
// Created wrapped coin

View File

@ -32,6 +32,7 @@ import {
getOriginalAssetAptos,
getSignedVAAWithRetry,
hexToUint8Array,
parseTokenTransferVaa,
redeemOnAptos,
redeemOnEth,
transferFromAptos,
@ -376,6 +377,156 @@ describe("Aptos SDK tests", () => {
balanceBeforeTransferEth.sub(balanceAfterTransferEth).toString()
).toEqual(amount.toString());
// clean up
provider.destroy();
});
test("Transfer native token with payload 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
const client = new AptosClient(APTOS_NODE_URL);
const sender = new AptosAccount();
const faucet = new FaucetClient(APTOS_NODE_URL, APTOS_FAUCET_URL);
await faucet.fundAccount(sender.address(), 100_000_000);
// attest native aptos token
const attestPayload = attestFromAptos(
APTOS_TOKEN_BRIDGE,
CHAIN_ID_APTOS,
COIN_TYPE
);
let tx = (await generateSignAndSubmitEntryFunction(
client,
sender,
attestPayload
)) as Types.UserTransaction;
await client.waitForTransaction(tx.hash);
// get signed attest vaa
let sequence = parseSequenceFromLogAptos(APTOS_CORE_BRIDGE, tx);
expect(sequence).toBeTruthy();
const { vaaBytes: attestVAA } = await getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
CHAIN_ID_APTOS,
APTOS_TOKEN_BRIDGE_EMITTER_ADDRESS,
sequence!,
{
transport: NodeHttpTransport(),
},
1000,
5
);
expect(attestVAA).toBeTruthy();
// setup ethereum
const provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
const recipient = new ethers.Wallet(ETH_PRIVATE_KEY6, provider);
const recipientAddress = await recipient.getAddress();
const ethTokenBridge = CONTRACTS.DEVNET.ethereum.token_bridge;
try {
await createWrappedOnEth(ethTokenBridge, recipient, attestVAA);
} catch (e) {
// this could fail because the token is already attested (in an unclean env)
}
// check attestation on ethereum
const externalAddress = hexToUint8Array(
await getExternalAddressFromType(COIN_TYPE)
);
const address = getForeignAssetEth(
ethTokenBridge,
provider,
CHAIN_ID_APTOS,
externalAddress
);
expect(address).toBeTruthy();
expect(address).not.toBe(ethers.constants.AddressZero);
// transfer from aptos
const balanceBeforeTransferAptos = ethers.BigNumber.from(
await getBalanceAptos(client, COIN_TYPE, sender.address())
);
const payload = Buffer.from("All your base are belong to us");
const transferPayload = transferFromAptos(
APTOS_TOKEN_BRIDGE,
COIN_TYPE,
(10_000_000).toString(),
CHAIN_ID_ETH,
tryNativeToUint8Array(recipientAddress, CHAIN_ID_ETH),
"0",
payload
);
tx = (await generateSignAndSubmitEntryFunction(
client,
sender,
transferPayload
)) as Types.UserTransaction;
await client.waitForTransaction(tx.hash);
const balanceAfterTransferAptos = ethers.BigNumber.from(
await getBalanceAptos(client, COIN_TYPE, sender.address())
);
expect(
balanceBeforeTransferAptos
.sub(balanceAfterTransferAptos)
.gt((10_000_000).toString())
).toBe(true);
// get signed transfer vaa
sequence = parseSequenceFromLogAptos(APTOS_CORE_BRIDGE, tx);
expect(sequence).toBeTruthy();
const { vaaBytes: transferVAA } = await getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
CHAIN_ID_APTOS,
APTOS_TOKEN_BRIDGE_EMITTER_ADDRESS,
sequence!,
{
transport: NodeHttpTransport(),
},
1000,
5
);
expect(transferVAA).toBeTruthy();
const { tokenTransferPayload } = parseTokenTransferVaa(transferVAA);
expect(tokenTransferPayload.toString()).toBe(payload.toString());
// get balance on eth
const originAssetHex = tryNativeToUint8Array(COIN_TYPE, CHAIN_ID_APTOS);
if (!originAssetHex) {
throw new Error("originAssetHex is null");
}
const foreignAsset = await getForeignAssetEth(
ethTokenBridge,
provider,
CHAIN_ID_APTOS,
originAssetHex
);
if (!foreignAsset) {
throw new Error("foreignAsset is null");
}
const balanceBeforeTransferEth = await getBalanceEth(
foreignAsset,
recipient
);
// redeem on eth
await redeemOnEth(ethTokenBridge, recipient, transferVAA);
expect(
await getIsTransferCompletedEth(ethTokenBridge, provider, transferVAA)
).toBe(true);
const balanceAfterTransferEth = await getBalanceEth(
foreignAsset,
recipient
);
expect(
balanceAfterTransferEth.sub(balanceBeforeTransferEth).toNumber()
).toEqual(10_000_000);
// clean up
provider.destroy();
});

View File

@ -885,7 +885,7 @@ export async function transferNearFromNear(
* @param recipientChain Target chain
* @param recipient Recipient's address on target chain
* @param relayerFee Fee to pay relayer
* @param payload Payload3 data, leave undefined for basic token transfers
* @param payload Payload3 data, leave null for basic token transfers
* @returns Transaction payload
*/
export function transferFromAptos(
@ -895,7 +895,7 @@ export function transferFromAptos(
recipientChain: ChainId | ChainName,
recipient: Uint8Array,
relayerFee: string = "0",
payload: string = ""
payload: Uint8Array | null = null
): Types.EntryFunctionPayload {
if (payload) {
// Currently unsupported
@ -905,7 +905,6 @@ export function transferFromAptos(
amount,
recipientChain,
recipient,
relayerFee,
createNonce().readUInt32LE(0),
payload
);