Skip to content

Commit

Permalink
[view] Add BCS view functionality
Browse files Browse the repository at this point in the history
This makes view functions use the same input parsing logic as entry
functions.  It simplifies the whole process to be exactly the same
dead simple ABI flow.
  • Loading branch information
gregnazario committed Feb 29, 2024
1 parent 13796f7 commit 60eea49
Show file tree
Hide file tree
Showing 18 changed files with 379 additions and 100 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T

# Unreleased

- [`Breaking`] Change `ViewFunctionPayload` to `ViewFunctionData`, allowing view functions to take advantage of automatic ABI type conversion
-
# 1.9.1 (2024-02-28)

- [`Fix`] Remove decimals field from `CurrentTokenOwnershipFields` gql fragement
Expand Down
22 changes: 15 additions & 7 deletions examples/javascript/simple_transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const {
} = require("@aptos-labs/ts-sdk");

const APTOS_COIN = "0x1::aptos_coin::AptosCoin";
const COIN_STORE = `0x1::coin::CoinStore<${APTOS_COIN}>`;
const ALICE_INITIAL_BALANCE = 100_000_000;
const BOB_INITIAL_BALANCE = 100;
const TRANSFER_AMOUNT = 100;
Expand All @@ -29,12 +28,21 @@ const APTOS_NETWORK = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network
*
*/
const balance = async (sdk, name, address) => {
let balance = await sdk.getAccountResource({ accountAddress: address, resourceType: COIN_STORE });

let amount = Number(balance.coin.value);

console.log(`${name}'s balance is: ${amount}`);
return amount;
try {
const response = await sdk.view({
payload: {
function: "0x1::coin::balance",
typeArguments: [APTOS_COIN],
functionArguments: [address],
},
});
const amount = Number(response[0]);

console.log(`${name}'s balance is: ${amount}`);
return amount;
} catch (e) {
return 0;
}
};

const example = async () => {
Expand Down
4 changes: 2 additions & 2 deletions examples/typescript-esm/multisig_v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
Account,
Aptos,
AptosConfig,
InputViewRequestData,
Network,
NetworkToNetworkName,
MoveString,
Expand All @@ -30,6 +29,7 @@ import {
TransactionPayloadMultiSig,
MultiSig,
AccountAddress,
InputViewFunctionData,
} from "@aptos-labs/ts-sdk";

// Default to devnet, but allow for overriding
Expand Down Expand Up @@ -90,7 +90,7 @@ const settingUpMultiSigAccount = async () => {
// ===========================================================================================
// Get the next multisig account address. This will be the same as the account address of the multisig account we'll
// be creating.
const payload: InputViewRequestData = {
const payload: InputViewFunctionData = {
function: "0x1::multisig_account::get_next_multisig_account_address",
functionArguments: [owner1.accountAddress.toString()],
};
Expand Down
17 changes: 9 additions & 8 deletions examples/typescript-esm/simple_transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import {
Account,
AccountAddress,
Aptos,
APTOS_COIN,
AptosConfig,
Network,
NetworkToNetworkName,
parseTypeTag,
} from "@aptos-labs/ts-sdk";

// TODO: There currently isn't a way to use the APTOS_COIN in the COIN_STORE due to a regex
const APTOS_COIN = "0x1::aptos_coin::AptosCoin";
const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>";
const ALICE_INITIAL_BALANCE = 100_000_000;
const BOB_INITIAL_BALANCE = 100;
const TRANSFER_AMOUNT = 100;
Expand All @@ -33,12 +31,15 @@ const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] |
*
*/
const balance = async (aptos: Aptos, name: string, address: AccountAddress) => {
type Coin = { coin: { value: string } };
const resource = await aptos.getAccountResource<Coin>({
accountAddress: address,
resourceType: COIN_STORE,
const [amountStr] = await aptos.view<[string]>({
payload: {
function: "0x1::coin::balance",
typeArguments: [APTOS_COIN],
functionArguments: [address],
},
});
const amount = Number(resource.coin.value);

const amount = Number(amountStr);

console.log(`${name}'s balance is: ${amount}`);
return amount;
Expand Down
29 changes: 18 additions & 11 deletions examples/typescript/simple_transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
* This example shows how to use the Aptos client to create accounts, fund them, and transfer between them.
*/

import { Account, AccountAddress, Aptos, AptosConfig, Network, NetworkToNetworkName } from "@aptos-labs/ts-sdk";
import {
Account,
AccountAddress,
Aptos,
APTOS_COIN,
AptosConfig,
Network,
NetworkToNetworkName,
} from "@aptos-labs/ts-sdk";

// TODO: There currently isn't a way to use the APTOS_COIN in the COIN_STORE due to a regex
const APTOS_COIN = "0x1::aptos_coin::AptosCoin";
const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>";
const ALICE_INITIAL_BALANCE = 100_000_000;
const BOB_INITIAL_BALANCE = 100;
const TRANSFER_AMOUNT = 100;
Expand All @@ -21,16 +26,18 @@ const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] |
* @param aptos
* @param name
* @param address
* @returns {Promise<*>}
* @returns {Promise<number>}
*
*/
const balance = async (aptos: Aptos, name: string, address: AccountAddress) => {
type Coin = { coin: { value: string } };
const resource = await aptos.getAccountResource<Coin>({
accountAddress: address,
resourceType: COIN_STORE,
const balance = async (aptos: Aptos, name: string, address: AccountAddress): Promise<number> => {
const response = await aptos.view<[string]>({
payload: {
function: "0x1::coin::balance",
typeArguments: [APTOS_COIN],
functionArguments: [address],
},
});
const amount = Number(resource.coin.value);
const amount = Number(response[0]);

console.log(`${name}'s balance is: ${amount}`);
return amount;
Expand Down
6 changes: 3 additions & 3 deletions examples/typescript/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
Ed25519PrivateKey,
Network,
NetworkToNetworkName,
InputViewRequestData,
InputViewFunctionData,
} from "@aptos-labs/ts-sdk";
import { createInterface } from "readline";
// Default to devnet, but allow for overriding
Expand All @@ -38,11 +38,11 @@ const getOptimalLpAmount = async (
token1Addr: AccountAddress,
token2Addr: AccountAddress,
): Promise<void> => {
const payload: InputViewRequestData = {
const payload: InputViewFunctionData = {
function: `${swap.toString()}::router::optimal_liquidity_amounts`,
functionArguments: [token1Addr, token2Addr, false, "200000", "300000", "200", "300"],
};
const result = await aptos.view({ payload });
const result = await aptos.view<[]>({ payload });
console.log("Optimal LP amount: ", result);
};

Expand Down
4 changes: 2 additions & 2 deletions examples/typescript/your_fungible_asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
AnyNumber,
Aptos,
AptosConfig,
InputViewRequestData,
InputViewFunctionData,
Network,
NetworkToNetworkName,
} from "@aptos-labs/ts-sdk";
Expand Down Expand Up @@ -127,7 +127,7 @@ const getFaBalance = async (owner: Account, assetType: string): Promise<number>

/** Return the address of the managed fungible asset that's created when this module is deployed */
async function getMetadata(admin: Account): Promise<string> {
const payload: InputViewRequestData = {
const payload: InputViewFunctionData = {
function: `${admin.accountAddress}::fa_coin::get_metadata`,
functionArguments: [],
};
Expand Down
6 changes: 3 additions & 3 deletions src/api/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
getProcessorStatus,
getTableItem,
queryIndexer,
view,
} from "../internal/general";
import { view } from "../internal/view";
import {
AnyNumber,
Block,
Expand All @@ -23,9 +23,9 @@ import {
LedgerVersionArg,
MoveValue,
TableItemRequest,
InputViewRequestData,
} from "../types";
import { ProcessorType } from "../utils/const";
import { InputViewFunctionData } from "../transactions";

/**
* A class to query all `General` Aptos related queries
Expand Down Expand Up @@ -137,7 +137,7 @@ export class General {
* @returns an array of Move values
*/
async view<T extends Array<MoveValue>>(args: {
payload: InputViewRequestData;
payload: InputViewFunctionData;
options?: LedgerVersionArg;
}): Promise<T> {
return view<T>({ aptosConfig: this.config, ...args });
Expand Down
17 changes: 6 additions & 11 deletions src/internal/ans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import { AptosConfig } from "../api/aptosConfig";
import { Account, AccountAddress, AccountAddressInput } from "../core";
import { InputGenerateTransactionOptions, SimpleTransaction } from "../transactions/types";
import { GetANSNameResponse, MoveAddressType, MoveValue, OrderByArg, PaginationArgs, WhereArg } from "../types";
import { GetANSNameResponse, MoveAddressType, OrderByArg, PaginationArgs, WhereArg } from "../types";
import { GetNamesQuery } from "../types/generated/operations";
import { GetNames } from "../types/generated/queries";
import { CurrentAptosNamesBoolExp } from "../types/generated/types";
import { Network } from "../utils/apiEndpoints";
import { queryIndexer, view } from "./general";
import { queryIndexer } from "./general";
import { generateTransaction } from "./transactionSubmission";
import { view } from "./view";

export const VALIDATION_RULES_DESCRIPTION = [
"A name must be between 3 and 63 characters long,",
Expand Down Expand Up @@ -85,12 +86,6 @@ function getRouterAddress(aptosConfig: AptosConfig): string {
return address;
}

const Some = <T>(value: T): MoveValue => ({ vec: [value] });
const None = (): MoveValue => ({ vec: [] });
// != here is intentional, we want to check for null and undefined
// eslint-disable-next-line eqeqeq
const Option = <T>(value: T | undefined | null): MoveValue => (value != undefined ? Some(value) : None());

const unwrapOption = <T>(option: any): T | undefined => {
if (!!option && typeof option === "object" && "vec" in option && Array.isArray(option.vec)) {
return option.vec[0];
Expand All @@ -108,7 +103,7 @@ export async function getOwnerAddress(args: { aptosConfig: AptosConfig; name: st
aptosConfig,
payload: {
function: `${routerAddress}::router::get_owner_addr`,
functionArguments: [domainName, Option(subdomainName)],
functionArguments: [domainName, subdomainName],
},
});

Expand Down Expand Up @@ -219,7 +214,7 @@ export async function getExpiration(args: { aptosConfig: AptosConfig; name: stri
aptosConfig,
payload: {
function: `${routerAddress}::router::get_expiration`,
functionArguments: [domainName, Option(subdomainName)],
functionArguments: [domainName, subdomainName],
},
});

Expand Down Expand Up @@ -303,7 +298,7 @@ export async function getTargetAddress(args: {
aptosConfig,
payload: {
function: `${routerAddress}::router::get_target_addr`,
functionArguments: [domainName, Option(subdomainName)],
functionArguments: [domainName, subdomainName],
},
});

Expand Down
24 changes: 0 additions & 24 deletions src/internal/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ import {
GraphqlQuery,
LedgerInfo,
LedgerVersionArg,
MoveValue,
TableItemRequest,
ViewRequest,
InputViewRequestData,
} from "../types";
import { GetChainTopUserTransactionsQuery, GetProcessorStatusQuery } from "../types/generated/operations";
import { GetChainTopUserTransactions, GetProcessorStatus } from "../types/generated/queries";
Expand Down Expand Up @@ -84,27 +81,6 @@ export async function getTableItem<T>(args: {
return response.data as T;
}

export async function view<T extends Array<MoveValue> = Array<MoveValue>>(args: {
aptosConfig: AptosConfig;
payload: InputViewRequestData;
options?: LedgerVersionArg;
}): Promise<T> {
const { aptosConfig, payload, options } = args;
const { data } = await postAptosFullNode<ViewRequest, MoveValue[]>({
aptosConfig,
originMethod: "view",
path: "view",
params: { ledger_version: options?.ledgerVersion },
body: {
function: payload.function,
type_arguments: payload.typeArguments ?? [],
arguments: payload.functionArguments ?? [],
},
});

return data as T;
}

export async function getChainTopUserTransactions(args: {
aptosConfig: AptosConfig;
limit: number;
Expand Down
32 changes: 32 additions & 0 deletions src/internal/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

import { AptosConfig } from "../api/aptosConfig";
import { Serializer } from "../bcs";
import { postAptosFullNode } from "../client";
import { LedgerVersionArg, MimeType, MoveValue, ViewRequest } from "../types";
import { generateViewFunctionPayload, InputViewFunctionData } from "../transactions";

export async function view<T extends Array<MoveValue> = Array<MoveValue>>(args: {
aptosConfig: AptosConfig;
payload: InputViewFunctionData;
options?: LedgerVersionArg;
}): Promise<T> {
const { aptosConfig, payload, options } = args;

const viewFunction = await generateViewFunctionPayload({ ...payload, aptosConfig });
const serializer = new Serializer();
viewFunction.serialize(serializer);
const body = serializer.toUint8Array();

const { data } = await postAptosFullNode<ViewRequest, MoveValue[]>({
aptosConfig,
originMethod: "view",
path: "view",
contentType: MimeType.BCS_VIEW_FUNCTION,
params: { ledger_version: options?.ledgerVersion },
body,
});

return data as T;
}
Loading

0 comments on commit 60eea49

Please sign in to comment.