Skip to content

Commit

Permalink
sdk/js: Aptos transfer with payload support
Browse files Browse the repository at this point in the history
  • Loading branch information
kev1n-peters authored and evan-gray committed Aug 28, 2023
1 parent 451f7d2 commit 04b7afe
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 6 deletions.
14 changes: 11 additions & 3 deletions sdk/js/src/aptos/api/tokenBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
151 changes: 151 additions & 0 deletions sdk/js/src/token_bridge/__tests__/aptos-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
getOriginalAssetAptos,
getSignedVAAWithRetry,
hexToUint8Array,
parseTokenTransferVaa,
redeemOnAptos,
redeemOnEth,
transferFromAptos,
Expand Down Expand Up @@ -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();
});
Expand Down
5 changes: 2 additions & 3 deletions sdk/js/src/token_bridge/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -905,7 +905,6 @@ export function transferFromAptos(
amount,
recipientChain,
recipient,
relayerFee,
createNonce().readUInt32LE(0),
payload
);
Expand Down

0 comments on commit 04b7afe

Please sign in to comment.