sdk/js: getForeignAssetAptos takes in tokenId for foreign chains

This commit is contained in:
heyitaki 2023-02-06 23:45:00 +00:00 committed by Evan Gray
parent 291051c5fb
commit dc924313b9
3 changed files with 124 additions and 12 deletions

View File

@ -22,6 +22,7 @@ import {
deriveTokenHashFromTokenId,
ensureHexPrefix,
generateSignAndSubmitEntryFunction,
hexToUint8Array,
tryNativeToHexString,
tryNativeToUint8Array,
} from "../../utils";
@ -145,7 +146,8 @@ describe("Aptos NFT SDK tests", () => {
aptosClient,
APTOS_NFT_BRIDGE_ADDRESS,
CHAIN_ID_ETH,
tryNativeToUint8Array(ethNft.address, CHAIN_ID_ETH)
tryNativeToUint8Array(ethNft.address, CHAIN_ID_ETH),
hexToUint8Array(BigInt(10).toString(16).padStart(64, "0"))
);
assertIsNotNull(tokenId);
expect(
@ -422,6 +424,90 @@ describe("Aptos NFT SDK tests", () => {
tryNativeToHexString(TEST_SOLANA_TOKEN3, CHAIN_ID_SOLANA)
);
});
test.only("Transfer multiple tokens from same collection from Ethereum to Aptos", async () => {
const ETH_COLLECTION_NAME = "Test APE 🐒";
// create NFTs on Ethereum
const ethNfts = await deployTestNftOnEthereum(
web3,
ethSigner,
ETH_COLLECTION_NAME,
"APE🐒",
"https://cloudflare-ipfs.com/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/",
2
);
// transfer 2 NFTs from Ethereum to Aptos
const ethTransferTx1 = await transferFromEth(
ETH_NFT_BRIDGE_ADDRESS,
ethSigner,
ethNfts.address,
0,
CHAIN_ID_APTOS,
aptosAccount.address().toUint8Array()
);
const ethTransferTx2 = await transferFromEth(
ETH_NFT_BRIDGE_ADDRESS,
ethSigner,
ethNfts.address,
1,
CHAIN_ID_APTOS,
aptosAccount.address().toUint8Array()
);
// observe txs and get vaas
const ethTransferVaa1 = await getSignedVaaEthereum(ethTransferTx1);
const ethTransferVaa2 = await getSignedVaaEthereum(ethTransferTx2);
// redeem NFTs on Aptos
const aptosRedeemPayload1 = await redeemOnAptos(
APTOS_NFT_BRIDGE_ADDRESS,
ethTransferVaa1
);
const aptosRedeemTx1 = await generateSignAndSubmitEntryFunction(
aptosClient,
aptosAccount,
aptosRedeemPayload1
);
await aptosClient.waitForTransactionWithResult(aptosRedeemTx1.hash);
const aptosRedeemPayload2 = await redeemOnAptos(
APTOS_NFT_BRIDGE_ADDRESS,
ethTransferVaa2
);
const aptosRedeemTx2 = await generateSignAndSubmitEntryFunction(
aptosClient,
aptosAccount,
aptosRedeemPayload2
);
await aptosClient.waitForTransactionWithResult(aptosRedeemTx2.hash);
// get token ids
const tokenId1 = await getForeignAssetAptos(
aptosClient,
APTOS_NFT_BRIDGE_ADDRESS,
CHAIN_ID_ETH,
tryNativeToUint8Array(ethNfts.address, CHAIN_ID_ETH),
hexToUint8Array(BigInt(0).toString(16).padStart(64, "0"))
);
const tokenId2 = await getForeignAssetAptos(
aptosClient,
APTOS_NFT_BRIDGE_ADDRESS,
CHAIN_ID_ETH,
tryNativeToUint8Array(ethNfts.address, CHAIN_ID_ETH),
hexToUint8Array(BigInt(1).toString(16).padStart(64, "0"))
);
assertIsNotNull(tokenId1);
assertIsNotNull(tokenId2);
expect(tokenId1.property_version).toBe("0");
expect(tokenId2.property_version).toBe("0");
expect(tokenId1.token_data_id.collection).toBe(
tokenId2.token_data_id.collection
);
expect(tokenId1.token_data_id.creator).toBe(tokenId2.token_data_id.creator);
expect(tokenId1.token_data_id.name).not.toBe(tokenId2.token_data_id.name);
});
});
// https://github.com/microsoft/TypeScript/issues/34523

View File

@ -1,7 +1,7 @@
import { BN } from "@project-serum/anchor";
import { PublicKeyInitData } from "@solana/web3.js";
import { LCDClient } from "@terra-money/terra.js";
import { AptosClient, HexString, TokenTypes } from "aptos";
import { AptosClient, HexString, TokenTypes, Types } from "aptos";
import { ethers } from "ethers";
import { isBytes } from "ethers/lib/utils";
import { fromUint8Array } from "js-base64";
@ -15,6 +15,7 @@ import {
CHAIN_ID_APTOS,
coalesceChainId,
deriveResourceAccountAddress,
ensureHexPrefix,
} from "../utils";
/**
@ -120,13 +121,16 @@ export const getForeignAssetSol = getForeignAssetSolana;
* @param originChain
* @param originAddress External address of token on origin chain, or token hash
* if origin chain is Aptos
* @param tokenId Token id of token on origin chain, unnecessary if origin
* chain is Aptos
* @returns Unique token identifier on Aptos
*/
export async function getForeignAssetAptos(
client: AptosClient,
nftBridgeAddress: string,
originChain: ChainId | ChainName,
originAddress: Uint8Array
originAddress: Uint8Array,
tokenId?: Uint8Array
): Promise<TokenTypes.TokenId | null> {
const originChainId = coalesceChainId(originChain);
if (originChainId === CHAIN_ID_APTOS) {
@ -150,6 +154,10 @@ export async function getForeignAssetAptos(
return { token_data_id, property_version };
}
if (!tokenId) {
throw new Error("Invalid token ID");
}
const creatorAddress = await deriveResourceAccountAddress(
nftBridgeAddress,
originChainId,
@ -159,16 +167,32 @@ export async function getForeignAssetAptos(
throw new Error("Could not derive creator account address");
}
// Each creator account should contain a single collection and a single token
// creation event. The latter contains the token id that we're looking for.
const event = (
await client.getEventsByEventHandle(
// Each creator account should contain a single collection that contains the
// token creation event with the token id that we're looking for.
const PAGE_SIZE = 25;
let curr = 0;
let numEvents = PAGE_SIZE;
let event: Types.Event | undefined = undefined;
while (numEvents === PAGE_SIZE && !event) {
const events = await client.getEventsByEventHandle(
creatorAddress,
"0x3::token::Collections",
"create_token_data_events",
{ limit: 1 }
)
)[0] as CreateTokenDataEvent;
{ limit: PAGE_SIZE, start: curr }
);
event = events.find(
(e) =>
ensureHexPrefix((e as CreateTokenDataEvent).data.id.name) ===
HexString.fromUint8Array(tokenId).hex()
);
numEvents = events.length;
curr += numEvents;
}
if (!event) {
throw new Error("Invalid token ID");
}
return {
token_data_id: event.data.id,
property_version: "0",

View File

@ -63,8 +63,10 @@ export const nativeTerraHexToDenom = (h: string): string =>
export const uint8ArrayToHex = (a: Uint8Array): string =>
Buffer.from(a).toString("hex");
export const hexToUint8Array = (h: string): Uint8Array =>
new Uint8Array(Buffer.from(h, "hex"));
export const hexToUint8Array = (h: string): Uint8Array => {
if (h.startsWith("0x")) h = h.slice(2);
return new Uint8Array(Buffer.from(h, "hex"));
}
/**
*