Skip to content

Commit

Permalink
[TS SDK] Update CoinClient to address aptos-labs#5648 and aptos-labs#…
Browse files Browse the repository at this point in the history
  • Loading branch information
banool authored and areshand committed Dec 17, 2022
1 parent e0f3567 commit 417beaa
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 25 deletions.
9 changes: 6 additions & 3 deletions ecosystem/typescript/sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ All notable changes to the Aptos Node SDK will be captured in this file. This ch

## Unreleased

- add missing fields to TokenData class
- add PropertyMap and PropertyValue type to match on-chain data
- support token property map deseralizer to read the property map in the original data format.
- Add missing fields to TokenData class
- Add PropertyMap and PropertyValue type to match on-chain data
- Support token property map deseralizer to read the property map in the original data format.
- Allow `checkBalance` in `CoinClient` to take in a `MaybeHexString` as well as `AptosAccount`, since users might want to check the balance of accounts they don't own (which is generally how you use `AptosAccount`).
- Similar to `checkBalance`, allow `transfer` in `CoinClient` to take in a `MaybeHexString` for the `receiver` argument.
- Add a new `createReceiverIfMissing` argument to `transfer` in `CoinClient`. If set, the `0x1::aptos_account::transfer` function will be called instead of `0x1::coin::transfer`, which will create the account on chain if it doesn't exist instead of failing.


## 1.3.17 (2022-11-08)
Expand Down
25 changes: 22 additions & 3 deletions ecosystem/typescript/sdk/src/abis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,26 @@ export const TOKEN_TRANSFER_OPT_IN =

/*
Follow these steps to get the ABI strings:
1. Compile the Move packages with Aptos CLI. e.g. aptos move compile --package-dir ./aptos-move/framework/aptos-token
2. Find the ABI files under the `build` directory and convert the ABI files to hex strings. On Mac and Linux, this can
be done with `cat <ABI_FILE_PATH> | od -v -t x1 -A n | tr -d ' \n'`
Go to the package directory of the relevant Move module, e.g. if you're trying
to get the ABI for the `transfer` function of `aptos_account.move`, go to
the directory `aptos-move/framework/aptos-framework`.
Compile the Move packages with the Aptos CLI:
```
aptos move compile --included-artifacts all
```
This `--included-artifacts all` argument is necessary to generate ABIs.
Find the ABI files under the `build` directory and convert the ABI files to hex strings.
On Mac and Linux, this can be done with this command:
```
cat <ABI_FILE_PATH> | od -v -t x1 -A n | tr -d ' \n'
```
For example:
```
cat build/AptosFramework/abis/aptos_account/transfer.abi | od -v -t x1 -A n | tr -d ' \n'
```
*/
export const TOKEN_ABIS = [
// aptos-token/build/AptosToken/abis/token/create_collection_script.abi
Expand Down Expand Up @@ -39,4 +56,6 @@ export const TOKEN_ABIS = [
export const COIN_ABIS = [
// aptos-framework/build/AptosFramework/abis/coin/transfer.abi
"01087472616E73666572000000000000000000000000000000000000000000000000000000000000000104636F696E3C205472616E73666572732060616D6F756E7460206F6620636F696E732060436F696E54797065602066726F6D206066726F6D6020746F2060746F602E0109636F696E5F747970650202746F0406616D6F756E7402",
// aptos-framework/build/AptosFramework/abis/aptos_account/transfer.abi
"01087472616e7366657200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e7400000202746f0406616d6f756e7402",
];
12 changes: 11 additions & 1 deletion ecosystem/typescript/sdk/src/aptos_account.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

import { AptosAccount, AptosAccountObject } from "./aptos_account";
import { AptosAccount, AptosAccountObject, getAddressFromAccountOrAddress } from "./aptos_account";
import { HexString } from "./hex_string";

const aptosAccountObject: AptosAccountObject = {
address: "0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aa",
Expand Down Expand Up @@ -74,3 +75,12 @@ test("Gets the resource account address", () => {
"0xcbed05b37b6981a57f535c1f5d136734df822abaf4cd30c51c9b4d60eae79d5d",
);
});

test("Test getAddressFromAccountOrAddress", () => {
const account = AptosAccount.fromAptosAccountObject(aptosAccountObject);
expect(getAddressFromAccountOrAddress(aptosAccountObject.address!).toString()).toBe(aptosAccountObject.address);
expect(getAddressFromAccountOrAddress(HexString.ensure(aptosAccountObject.address!)).toString()).toBe(
aptosAccountObject.address,
);
expect(getAddressFromAccountOrAddress(account).toString()).toBe(aptosAccountObject.address);
});
5 changes: 5 additions & 0 deletions ecosystem/typescript/sdk/src/aptos_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,8 @@ export class AptosAccount {
};
}
}

// Returns an account address as a HexString given either an AptosAccount or a MaybeHexString.
export function getAddressFromAccountOrAddress(accountOrAddress: AptosAccount | MaybeHexString): HexString {
return accountOrAddress instanceof AptosAccount ? accountOrAddress.address() : HexString.ensure(accountOrAddress);
}
11 changes: 10 additions & 1 deletion ecosystem/typescript/sdk/src/coin_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AptosAccount } from "./aptos_account";
import { CoinClient } from "./coin_client";

test(
"transferCoins and checkBalance works",
"transfer and checkBalance works",
async () => {
const client = new AptosClient(NODE_URL);
const faucetClient = getFaucetClient();
Expand All @@ -21,6 +21,15 @@ test(
await client.waitForTransaction(await coinClient.transfer(alice, bob, 42), { checkSuccess: true });

expect(await coinClient.checkBalance(bob)).toBe(BigInt(42));

// Test that `createReceiverIfMissing` works.
const jemima = new AptosAccount();
await client.waitForTransaction(await coinClient.transfer(alice, jemima, 717, { createReceiverIfMissing: true }), {
checkSuccess: true,
});

// Check that using a string address instead of an account works with `checkBalance`.
expect(await coinClient.checkBalance(jemima.address().hex())).toBe(BigInt(717));
},
longTestTimeout,
);
54 changes: 37 additions & 17 deletions ecosystem/typescript/sdk/src/coin_client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

import { AptosAccount } from "./aptos_account";
import { AptosAccount, getAddressFromAccountOrAddress } from "./aptos_account";
import { AptosClient, OptionalTransactionArgs } from "./aptos_client";
import { HexString } from "./hex_string";
import { HexString, MaybeHexString } from "./hex_string";
import { TransactionBuilderABI } from "./transaction_builder";
import { COIN_ABIS } from "./abis";
import { APTOS_COIN } from "./utils";
Expand All @@ -28,7 +28,14 @@ export class CoinClient {

/**
* Generate, sign, and submit a transaction to the Aptos blockchain API to
* transfer AptosCoin from one account to another.
* transfer coins from one account to another. By default it transfers
* 0x1::aptos_coin::AptosCoin, but you can specify a different coin type
* with the `coinType` argument.
*
* You may set `createReceiverIfMissing` to true if you want to create the
* receiver account if it does not exist on chain yet. If you do not set
* this to true, the transaction will fail if the receiver account does not
* exist on-chain.
*
* @param from Account sending the coins
* @param to Account to receive the coins
Expand All @@ -40,44 +47,57 @@ export class CoinClient {
// :!:>transfer
async transfer(
from: AptosAccount,
to: AptosAccount,
to: AptosAccount | MaybeHexString,
amount: number | bigint,
extraArgs?: OptionalTransactionArgs & {
// The coin type to use, defaults to 0x1::aptos_coin::AptosCoin
coinType?: string;
// If set, create the `receiver` account if it doesn't exist on-chain.
// This is done by calling `0x1::aptos_account::transfer` instead, which
// will create the account on-chain first if it doesn't exist before
// transferring the coins to it.
createReceiverIfMissing?: boolean;
},
): Promise<string> {
// If none is explicitly given, use 0x1::aptos_coin::AptosCoin as the coin type.
const coinTypeToTransfer = extraArgs?.coinType ?? APTOS_COIN;
const payload = this.transactionBuilder.buildTransactionPayload(
"0x1::coin::transfer",
[coinTypeToTransfer],
[to.address(), amount],
);

// If we should create the receiver account if it doesn't exist on-chain,
// use the `0x1::aptos_account::transfer` function.
const func = extraArgs?.createReceiverIfMissing ? "0x1::aptos_account::transfer" : "0x1::coin::transfer";

// If we're using the `0x1::aptos_account::transfer` function, we don't
// need type args.
const typeArgs = extraArgs?.createReceiverIfMissing ? [] : [coinTypeToTransfer];

// Get the receiver address from the AptosAccount or MaybeHexString.
const toAddress = getAddressFromAccountOrAddress(to);

const payload = this.transactionBuilder.buildTransactionPayload(func, typeArgs, [toAddress, amount]);

return this.aptosClient.generateSignSubmitTransaction(from, payload, extraArgs);
} // <:!:transfer

/**
* Generate, submit, and wait for a transaction to transfer AptosCoin from
* one account to another.
*
* If the transaction is submitted successfully, it returns the response
* from the API indicating that the transaction was submitted.
* Get the balance of the account. By default it checks the balance of
* 0x1::aptos_coin::AptosCoin, but you can specify a different coin type.
*
* @param account Account that you want to check the balance of.
* @param account Account that you want to get the balance of.
* @param extraArgs Extra args for checking the balance.
* @returns Promise that resolves to the balance as a bigint.
*/
// :!:>checkBalance
async checkBalance(
account: AptosAccount,
account: AptosAccount | MaybeHexString,
extraArgs?: {
// The coin type to use, defaults to 0x1::aptos_coin::AptosCoin
coinType?: string;
},
): Promise<bigint> {
const coinType = extraArgs?.coinType ?? APTOS_COIN;
const typeTag = `0x1::coin::CoinStore<${coinType}>`;
const resources = await this.aptosClient.getAccountResources(account.address());
const address = getAddressFromAccountOrAddress(account);
const resources = await this.aptosClient.getAccountResources(address);
const accountResource = resources.find((r) => r.type === typeTag);
return BigInt((accountResource!.data as any).coin.value);
} // <:!:checkBalance
Expand Down

0 comments on commit 417beaa

Please sign in to comment.