sdk/aptos: documentation
This commit is contained in:
parent
41fb6ba756
commit
9ae43b3dd9
|
@ -122,6 +122,28 @@ export const completeTransferWithPayload = (
|
|||
throw new Error("Completing transfers with payload is not yet supported in the sdk");
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a payload for a transaction that registers a coin defined by the given origin chain
|
||||
* ID and address to the sender's account.
|
||||
*
|
||||
* The bytecode was compiled from the following Move code:
|
||||
* ```move
|
||||
* script {
|
||||
* use aptos_framework::coin;
|
||||
* use aptos_framework::signer;
|
||||
*
|
||||
* fun main<CoinType>(user: &signer) {
|
||||
* if (!coin::is_account_registered<CoinType>(signer::address_of(user))) {
|
||||
* coin::register<CoinType>(user);
|
||||
* };
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param originChain Origin chain ID of asset
|
||||
* @param originAddress Asset address on origin chain
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export const registerCoin = (
|
||||
tokenBridgeAddress: string,
|
||||
originChain: ChainId | ChainName,
|
||||
|
|
|
@ -30,8 +30,8 @@ import {
|
|||
hexToUint8Array,
|
||||
redeemOnAptos,
|
||||
redeemOnEth,
|
||||
signAndSubmitEntryFunction,
|
||||
signAndSubmitScript,
|
||||
generateSignAndSubmitEntryFunction,
|
||||
generateSignAndSubmitScript,
|
||||
TokenImplementation__factory,
|
||||
transferFromAptos,
|
||||
transferFromEth,
|
||||
|
@ -85,7 +85,7 @@ describe("Aptos SDK tests", () => {
|
|||
CHAIN_ID_APTOS,
|
||||
COIN_TYPE
|
||||
);
|
||||
let tx = (await signAndSubmitEntryFunction(
|
||||
let tx = (await generateSignAndSubmitEntryFunction(
|
||||
client,
|
||||
sender,
|
||||
attestPayload
|
||||
|
@ -143,7 +143,7 @@ describe("Aptos SDK tests", () => {
|
|||
CHAIN_ID_ETH,
|
||||
tryNativeToUint8Array(recipientAddress, CHAIN_ID_ETH)
|
||||
);
|
||||
tx = (await signAndSubmitEntryFunction(
|
||||
tx = (await generateSignAndSubmitEntryFunction(
|
||||
client,
|
||||
sender,
|
||||
transferPayload
|
||||
|
@ -251,7 +251,7 @@ describe("Aptos SDK tests", () => {
|
|||
attestVAA
|
||||
);
|
||||
try {
|
||||
await signAndSubmitEntryFunction(
|
||||
await generateSignAndSubmitEntryFunction(
|
||||
client,
|
||||
recipient,
|
||||
createWrappedCoinTypePayload
|
||||
|
@ -273,7 +273,7 @@ describe("Aptos SDK tests", () => {
|
|||
attestVAA
|
||||
);
|
||||
try {
|
||||
await signAndSubmitEntryFunction(
|
||||
await generateSignAndSubmitEntryFunction(
|
||||
client,
|
||||
recipient,
|
||||
createWrappedCoinPayload
|
||||
|
@ -359,7 +359,7 @@ describe("Aptos SDK tests", () => {
|
|||
|
||||
// register token on aptos
|
||||
const script = registerCoin(aptosTokenBridge, CHAIN_ID_ETH, TEST_ERC20);
|
||||
await signAndSubmitScript(client, recipient, script);
|
||||
await generateSignAndSubmitScript(client, recipient, script);
|
||||
|
||||
// redeem on aptos
|
||||
const balanceBeforeTransferAptos = ethers.BigNumber.from(
|
||||
|
@ -370,7 +370,7 @@ describe("Aptos SDK tests", () => {
|
|||
aptosTokenBridge,
|
||||
transferVAA
|
||||
);
|
||||
await signAndSubmitEntryFunction(client, recipient, redeemPayload);
|
||||
await generateSignAndSubmitEntryFunction(client, recipient, redeemPayload);
|
||||
expect(
|
||||
await getIsTransferCompletedAptos(client, aptosTokenBridge, transferVAA)
|
||||
).toBe(true);
|
||||
|
|
|
@ -323,8 +323,13 @@ export async function attestNearFromNear(
|
|||
};
|
||||
}
|
||||
|
||||
// TODO: do we want to pass in a single assetAddress (instead of tokenChain and tokenAddress) as
|
||||
// with other APIs above and let user derive the wrapped asset address themselves?
|
||||
/**
|
||||
* Attest given token from Aptos.
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param tokenChain Origin chain ID
|
||||
* @param tokenAddress Address of token on origin chain
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export function attestFromAptos(
|
||||
tokenBridgeAddress: string,
|
||||
tokenChain: ChainId,
|
||||
|
|
|
@ -109,13 +109,35 @@ export async function createWrappedOnNear(
|
|||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs payload to create wrapped asset type. The type is of form `{{address}}::coin::T`,
|
||||
* where address is `sha256_hash(tokenBridgeAddress | chainID | "::" | originAddress | 0xFF)`.
|
||||
*
|
||||
* Note that the typical createWrapped call is broken into two parts on Aptos because we must first
|
||||
* create the CoinType that is used by `create_wrapped_coin<CoinType>`. Since it's not possible to
|
||||
* create a resource and use it in the same transaction, this is broken into separate transactions.
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param attestVAA Bytes of attest VAA
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export function createWrappedTypeOnAptos(
|
||||
tokenBridgeAddress: string,
|
||||
signedVAA: Uint8Array
|
||||
attestVAA: Uint8Array
|
||||
): Types.EntryFunctionPayload {
|
||||
return createWrappedCoinTypeAptos(tokenBridgeAddress, signedVAA);
|
||||
return createWrappedCoinTypeAptos(tokenBridgeAddress, attestVAA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs payload to create wrapped asset.
|
||||
*
|
||||
* Note that this function is typically called in tandem with `createWrappedTypeOnAptos` because
|
||||
* we must first create the CoinType that is used by `create_wrapped_coin<CoinType>`. Since it's
|
||||
* not possible to create a resource and use it in the same transaction, this is broken into
|
||||
* separate transactions.
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param attestVAA Bytes of attest VAA
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export function createWrappedOnAptos(
|
||||
tokenBridgeAddress: string,
|
||||
attestVAA: Uint8Array
|
||||
|
|
|
@ -191,6 +191,14 @@ export async function getForeignAssetNear(
|
|||
return ret !== "" ? ret : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get native module address of asset given its origin info.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param originChain Chain ID of chain asset is originally from
|
||||
* @param originAddress Asset address on origin chain
|
||||
* @returns Asset module address on Aptos
|
||||
*/
|
||||
export async function getForeignAssetAptos(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
|
@ -198,14 +206,21 @@ export async function getForeignAssetAptos(
|
|||
originAddress: string
|
||||
): Promise<string | null> {
|
||||
const originChainId = coalesceChainId(originChain);
|
||||
const assetAddress = getForeignAssetAddress(tokenBridgeAddress, originChainId, originAddress);
|
||||
const assetAddress = getForeignAssetAddress(
|
||||
tokenBridgeAddress,
|
||||
originChainId,
|
||||
originAddress
|
||||
);
|
||||
if (!assetAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// check if asset exists and throw if it doesn't
|
||||
await client.getAccountResource(assetAddress, `0x1::coin::CoinInfo<${ensureHexPrefix(assetAddress)}::coin::T>`);
|
||||
await client.getAccountResource(
|
||||
assetAddress,
|
||||
`0x1::coin::CoinInfo<${ensureHexPrefix(assetAddress)}::coin::T>`
|
||||
);
|
||||
return assetAddress;
|
||||
} catch (e) {
|
||||
return null;
|
||||
|
|
|
@ -249,10 +249,17 @@ export async function getIsTransferCompletedNear(
|
|||
)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the transfer in the given VAA has completed on Aptos.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param transferVAA Bytes of transfer VAA
|
||||
* @returns True if transfer is completed
|
||||
*/
|
||||
export async function getIsTransferCompletedAptos(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
signedVAA: Uint8Array,
|
||||
transferVAA: Uint8Array,
|
||||
): Promise<boolean> {
|
||||
// get handle
|
||||
tokenBridgeAddress = ensureHexPrefix(tokenBridgeAddress);
|
||||
|
@ -262,13 +269,13 @@ export async function getIsTransferCompletedAptos(
|
|||
const handle = state.consumed_vaas.elems.handle;
|
||||
|
||||
// check if vaa hash is in consumed_vaas
|
||||
const signedVAAHash = await getSignedVAAHash(signedVAA);
|
||||
const transferVAAHash = await getSignedVAAHash(transferVAA);
|
||||
try {
|
||||
// when accessing Set<T>, key is type T and value is 0
|
||||
await client.getTableItem(handle, {
|
||||
key_type: "vector<u8>",
|
||||
value_type: "u8",
|
||||
key: signedVAAHash,
|
||||
key: transferVAAHash,
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
|
|
|
@ -116,7 +116,13 @@ export function getIsWrappedAssetNear(
|
|||
return asset.endsWith("." + tokenBridge);
|
||||
}
|
||||
|
||||
// TODO: do we need to check if token is registered in bridge?
|
||||
/**
|
||||
* Determines whether or not given address is wrapped or native to Aptos.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param assetAddress Module address of asset
|
||||
* @returns True if asset is wrapped
|
||||
*/
|
||||
export async function getIsWrappedAssetAptos(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
|
|
|
@ -313,6 +313,13 @@ export async function getOriginalAssetNear(
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the origin chain ID and address of an asset on Aptos, given its fully qualified type.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param fullyQualifiedType Fully qualified type of asset
|
||||
* @returns Original chain ID and address of asset
|
||||
*/
|
||||
export async function getOriginalAssetAptos(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
|
|
|
@ -385,6 +385,14 @@ export async function redeemOnNear(
|
|||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the token specified in the given VAA in the transfer recipient's account if necessary
|
||||
* and complete the transfer.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param transferVAA Bytes of transfer VAA
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export function redeemOnAptos(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
|
|
|
@ -961,6 +961,17 @@ export async function transferNearFromNear(
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer an asset on Aptos to another chain.
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param fullyQualifiedType Full qualified type of asset to transfer
|
||||
* @param amount Amount to send to recipient
|
||||
* @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
|
||||
* @returns Transaction payload
|
||||
*/
|
||||
export function transferFromAptos(
|
||||
tokenBridgeAddress: string,
|
||||
fullyQualifiedType: string,
|
||||
|
|
|
@ -4,7 +4,20 @@ import { ChainId, CHAIN_ID_APTOS, ensureHexPrefix, hex } from "../utils";
|
|||
import { AptosAccount, AptosClient, TxnBuilderTypes, Types } from "aptos";
|
||||
import { State } from "../aptos/types";
|
||||
|
||||
export const signAndSubmitEntryFunction = (
|
||||
/**
|
||||
* Generate, sign, and submit a transaction calling the given entry function with the given
|
||||
* arguments. Prevents transaction submission and throws if the transaction fails.
|
||||
*
|
||||
* This is separated from `generateSignAndSubmitScript` because it makes use of `AptosClient`'s
|
||||
* `generateTransaction` which pulls ABIs from the node and uses them to encode arguments
|
||||
* automatically.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param sender Account that will submit transaction
|
||||
* @param payload Payload containing unencoded fully qualified entry function, types, and arguments
|
||||
* @param opts Override default transaction options
|
||||
* @returns Data from transaction after is has been successfully submitted to mempool
|
||||
*/
|
||||
export const generateSignAndSubmitEntryFunction = (
|
||||
client: AptosClient,
|
||||
sender: AptosAccount,
|
||||
payload: Types.EntryFunctionPayload,
|
||||
|
@ -32,7 +45,20 @@ export const signAndSubmitEntryFunction = (
|
|||
);
|
||||
};
|
||||
|
||||
export const signAndSubmitScript = async (
|
||||
/**
|
||||
* Generate, sign, and submit a transaction containing given bytecode. Prevents transaction
|
||||
* submission and throws if the transaction fails.
|
||||
*
|
||||
* Unlike `generateSignAndSubmitEntryFunction`, this function must construct a `RawTransaction`
|
||||
* manually because `generateTransaction` does not have support for scripts for which there are
|
||||
* no corresponding on-chain ABIs. Type/argument encoding is left to the caller.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param sender Account that will submit transaction
|
||||
* @param payload Payload containing compiled bytecode and encoded types/arguments
|
||||
* @param opts Override default transaction options
|
||||
* @returns Data from transaction after is has been successfully submitted to mempool
|
||||
*/
|
||||
export const generateSignAndSubmitScript = async (
|
||||
client: AptosClient,
|
||||
sender: AptosAccount,
|
||||
payload: TxnBuilderTypes.TransactionPayloadScript,
|
||||
|
@ -66,31 +92,16 @@ export const signAndSubmitScript = async (
|
|||
return signAndSubmitTransaction(client, sender, rawTx);
|
||||
};
|
||||
|
||||
const signAndSubmitTransaction = async (
|
||||
client: AptosClient,
|
||||
sender: AptosAccount,
|
||||
rawTx: TxnBuilderTypes.RawTransaction
|
||||
): Promise<Types.Transaction> => {
|
||||
// simulate transaction
|
||||
await client.simulateTransaction(sender, rawTx).then((sims) =>
|
||||
sims.forEach((tx) => {
|
||||
if (!tx.success) {
|
||||
throw new Error(
|
||||
`Transaction failed: ${tx.vm_status}\n${JSON.stringify(tx, null, 2)}`
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// sign & submit transaction
|
||||
return client
|
||||
.signTransaction(sender, rawTx)
|
||||
.then((signedTx) => client.submitTransaction(signedTx))
|
||||
.then((pendingTx) => client.waitForTransactionWithResult(pendingTx.hash));
|
||||
};
|
||||
|
||||
/**
|
||||
* Derives the fully qualified type of the asset defined by the given origin chain and address.
|
||||
* @param tokenBridgeAddress Address of token bridge (32 bytes)
|
||||
* @param originChain Chain ID of chain that original asset is from
|
||||
* @param originAddress Native address of asset; if origin chain ID is 22 (Aptos), this is the
|
||||
* asset's fully qualified type
|
||||
* @returns The fully qualified type on Aptos for the given asset
|
||||
*/
|
||||
export const getAssetFullyQualifiedType = (
|
||||
tokenBridgeAddress: string, // 32 bytes
|
||||
tokenBridgeAddress: string,
|
||||
originChain: ChainId,
|
||||
originAddress: string
|
||||
): string | null => {
|
||||
|
@ -116,8 +127,15 @@ export const getAssetFullyQualifiedType = (
|
|||
: null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive the module address for an asset defined by the given origin chain and address.
|
||||
* @param tokenBridgeAddress Address of token bridge (32 bytes)
|
||||
* @param originChain Chain ID of chain that original asset is from
|
||||
* @param originAddress Native address of asset
|
||||
* @returns The module address for the given asset
|
||||
*/
|
||||
export const getForeignAssetAddress = (
|
||||
tokenBridgeAddress: string, // 32 bytes
|
||||
tokenBridgeAddress: string,
|
||||
originChain: ChainId,
|
||||
originAddress: string
|
||||
): string | null => {
|
||||
|
@ -142,9 +160,20 @@ export const getForeignAssetAddress = (
|
|||
);
|
||||
};
|
||||
|
||||
export const isValidAptosType = (address: string): boolean =>
|
||||
/^(0x)?[0-9a-fA-F]+::\w+::\w+$/.test(address);
|
||||
/**
|
||||
* Test if given string is a valid fully qualified type of moduleAddress::moduleName::structName.
|
||||
* @param str String to test
|
||||
* @returns Whether or not given string is a valid type
|
||||
*/
|
||||
export const isValidAptosType = (str: string): boolean =>
|
||||
/^(0x)?[0-9a-fA-F]+::\w+::\w+$/.test(str);
|
||||
|
||||
/**
|
||||
* Hashes the given type. Because fully qualified types are a concept unique to Aptos, this
|
||||
* output acts as the address on other chains.
|
||||
* @param fullyQualifiedType Fully qualified type on Aptos
|
||||
* @returns External address corresponding to given type
|
||||
*/
|
||||
export const getExternalAddressFromType = (
|
||||
fullyQualifiedType: string
|
||||
): string => {
|
||||
|
@ -152,6 +181,13 @@ export const getExternalAddressFromType = (
|
|||
return sha3_256(fullyQualifiedType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a hash, returns the fully qualified type by querying the corresponding TypeInfo.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param tokenBridgeAddress Address of token bridge
|
||||
* @param fullyQualifiedTypeHash Hash of fully qualified type
|
||||
* @returns The fully qualified type associated with the given hash
|
||||
*/
|
||||
export async function getTypeFromExternalAddress(
|
||||
client: AptosClient,
|
||||
tokenBridgeAddress: string,
|
||||
|
@ -193,3 +229,34 @@ export async function getTypeFromExternalAddress(
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates given raw transaction and either returns the resulting transaction that was submitted
|
||||
* to the mempool, or throws if it fails.
|
||||
* @param client Client used to transfer data to/from Aptos node
|
||||
* @param sender Account that will submit transaction
|
||||
* @param rawTx Raw transaction to sign & submit
|
||||
* @returns Transaction data
|
||||
*/
|
||||
const signAndSubmitTransaction = async (
|
||||
client: AptosClient,
|
||||
sender: AptosAccount,
|
||||
rawTx: TxnBuilderTypes.RawTransaction
|
||||
): Promise<Types.Transaction> => {
|
||||
// simulate transaction
|
||||
await client.simulateTransaction(sender, rawTx).then((sims) =>
|
||||
sims.forEach((tx) => {
|
||||
if (!tx.success) {
|
||||
throw new Error(
|
||||
`Transaction failed: ${tx.vm_status}\n${JSON.stringify(tx, null, 2)}`
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// sign & submit transaction
|
||||
return client
|
||||
.signTransaction(sender, rawTx)
|
||||
.then((signedTx) => client.submitTransaction(signedTx))
|
||||
.then((pendingTx) => client.waitForTransactionWithResult(pendingTx.hash));
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue