Skip to content

Commit

Permalink
[abi] Address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
gregnazario committed Mar 26, 2024
1 parent f910014 commit 58e22cb
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 38 deletions.
11 changes: 10 additions & 1 deletion src/transactions/transactionBuilder/remoteAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export function standardizeTypeTags(typeArguments?: Array<TypeTag | string>): Ar
);
}

/**
* Fetches a function ABI from the on-chain module ABI. It doesn't validate whether it's a view or entry function.
* @param moduleAddress
* @param moduleName
* @param functionName
* @param aptosConfig
*/
export async function fetchFunctionAbi(
moduleAddress: string,
moduleName: string,
Expand Down Expand Up @@ -129,16 +136,18 @@ export async function fetchViewFunctionAbi(
throw new Error(`Could not find view function ABI for '${moduleAddress}::${moduleName}::${functionName}'`);
}

// Non-view functions also can't be used
// Non-view functions can't be used
if (!functionAbi.is_view) {
throw new Error(`'${moduleAddress}::${moduleName}::${functionName}' is not an view function`);
}

// Type tag parameters for the function
const params: TypeTag[] = [];
for (let i = 0; i < functionAbi.params.length; i += 1) {
params.push(parseTypeTag(functionAbi.params[i], { allowGenerics: true }));
}

// The return types of the view function
const returnTypes: TypeTag[] = [];
for (let i = 0; i < functionAbi.return.length; i += 1) {
returnTypes.push(parseTypeTag(functionAbi.return[i], { allowGenerics: true }));
Expand Down
82 changes: 60 additions & 22 deletions src/transactions/transactionBuilder/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
InputMultiSigDataWithABI,
InputViewFunctionDataWithRemoteABI,
InputViewFunctionDataWithABI,
FunctionABI,
} from "../types";
import { convertArgument, fetchEntryFunctionAbi, fetchViewFunctionAbi, standardizeTypeTags } from "./remoteAbi";
import { memoizeAsync } from "../../utils/memoize";
Expand Down Expand Up @@ -107,18 +108,17 @@ export async function generateTransactionPayload(
if (isScriptDataInput(args)) {
return generateTransactionPayloadScript(args);
}

const { moduleAddress, moduleName, functionName } = getFunctionParts(args.function);

let functionAbi = args.abi;
if (!functionAbi) {
// We fetch the entry function ABI, and then pretend that we already had the ABI
functionAbi = await memoizeAsync(
async () => fetchEntryFunctionAbi(moduleAddress, moduleName, functionName, args.aptosConfig),
`entry-function-${args.aptosConfig.network}-${moduleAddress}-${moduleName}-${functionName}`,
1000 * 60 * 5, // 5 minutes
)();
}
const functionAbi = await fetchAbi({
key: "entry-function",
moduleAddress,
moduleName,
functionName,
aptosConfig: args.aptosConfig,
abi: args.abi,
fetch: fetchEntryFunctionAbi,
});

// Fill in the ABI
return generateTransactionPayloadWithABI({ abi: functionAbi, ...args });
Expand Down Expand Up @@ -178,15 +178,15 @@ export function generateTransactionPayloadWithABI(
export async function generateViewFunctionPayload(args: InputViewFunctionDataWithRemoteABI): Promise<EntryFunction> {
const { moduleAddress, moduleName, functionName } = getFunctionParts(args.function);

let functionAbi = args.abi;
// If ABI was not provided, we fetch the entry function ABI, and then pretend that we already had the ABI
if (!functionAbi) {
functionAbi = await memoizeAsync(
async () => fetchViewFunctionAbi(moduleAddress, moduleName, functionName, args.aptosConfig),
`view-function-${args.aptosConfig.network}-${moduleAddress}-${moduleName}-${functionName}`,
1000 * 60 * 5, // 5 minutes
)();
}
const functionAbi = await fetchAbi({
key: "view-function",
moduleAddress,
moduleName,
functionName,
aptosConfig: args.aptosConfig,
abi: args.abi,
fetch: fetchViewFunctionAbi,
});

// Fill in the ABI
return generateViewFunctionPayloadWithABI({ abi: functionAbi, ...args });
Expand All @@ -207,9 +207,8 @@ export function generateViewFunctionPayloadWithABI(args: InputViewFunctionDataWi
}

// Check all BCS types, and convert any non-BCS types
const functionArguments: Array<EntryFunctionArgumentTypes> = args.functionArguments.map((arg, i) =>
convertArgument(args.function, functionAbi, arg, i, typeArguments),
);
const functionArguments: Array<EntryFunctionArgumentTypes> =
args?.functionArguments?.map((arg, i) => convertArgument(args.function, functionAbi, arg, i, typeArguments)) ?? [];

// Check that all arguments are accounted for
if (functionArguments.length !== functionAbi.parameters.length) {
Expand Down Expand Up @@ -623,3 +622,42 @@ export function generateSigningMessage(transaction: AnyRawTransaction): Uint8Arr

return mergedArray;
}

/**
* Fetches and caches ABIs with allowing for pass-through on provided ABIs
* @param key
* @param moduleAddress
* @param moduleName
* @param functionName
* @param aptosConfig
* @param abi
* @param fetch
*/
async function fetchAbi<T extends FunctionABI>({
key,
moduleAddress,
moduleName,
functionName,
aptosConfig,
abi,
fetch,
}: {
key: string;
moduleAddress: string;
moduleName: string;
functionName: string;
aptosConfig: AptosConfig;
abi?: T;
fetch: (moduleAddress: string, moduleName: string, functionName: string, aptosConfig: AptosConfig) => Promise<T>;
}): Promise<T> {
if (abi) {
return abi;
}

// We fetch the entry function ABI, and then pretend that we already had the ABI
return memoizeAsync(
async () => fetch(moduleAddress, moduleName, functionName, aptosConfig),
`${key}-${aptosConfig.network}-${moduleAddress}-${moduleName}-${functionName}`,
1000 * 60 * 5, // 5 minutes
)();
}
18 changes: 13 additions & 5 deletions src/transactions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,28 @@ export type InputScriptData = {
};

/**
* The data needed to generate an View Function payload
* The data needed to generate a View Function payload
*/
export type InputViewFunctionData = {
function: MoveFunctionId;
typeArguments?: Array<TypeTag | string>;
functionArguments: Array<EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes>;
functionArguments?: Array<EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes>;
abi?: ViewFunctionABI;
};

/**
* Data needed to generate a view function payload and fetch the remote ABI
*/
export type InputViewFunctionDataWithRemoteABI = InputViewFunctionData & { aptosConfig: AptosConfig };

export type InputViewFunctionDataWithABI = Omit<InputViewFunctionData, "abi"> & {
abi: ViewFunctionABI;
};
/**
* Data needed to generate a view function, with an already fetched ABI
*/
export type InputViewFunctionDataWithABI = InputViewFunctionData & { abi: ViewFunctionABI };

/**
* Data need for a generic function ABI, both view and entry
*/
export type FunctionABI = {
typeParameters: Array<MoveFunctionGenericTypeParam>;
parameters: Array<TypeTag>;
Expand Down
10 changes: 0 additions & 10 deletions tests/e2e/api/general.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ describe("general api", () => {

const payload: InputViewFunctionData = {
function: "0x1::chain_id::get",
typeArguments: [],
functionArguments: [],
};

const chainId = (await aptos.view({ payload }))[0];
Expand All @@ -118,8 +116,6 @@ describe("general api", () => {

const payload: InputViewFunctionData = {
function: "0x1::chain_id::get",
typeArguments: [],
functionArguments: [],
};

const chainId = (await aptos.view<[number]>({ payload }))[0];
Expand All @@ -133,7 +129,6 @@ describe("general api", () => {

const payload: InputViewFunctionData = {
function: "0x1::account::exists_at",
typeArguments: [],
functionArguments: ["0x1"],
};

Expand All @@ -143,7 +138,6 @@ describe("general api", () => {

const payload2: InputViewFunctionData = {
function: "0x1::account::exists_at",
typeArguments: [],
functionArguments: ["0x12345"],
};

Expand All @@ -158,7 +152,6 @@ describe("general api", () => {

const payload: InputViewFunctionData = {
function: "0x1::account::get_sequence_number",
typeArguments: [],
functionArguments: ["0x1"],
};

Expand All @@ -168,7 +161,6 @@ describe("general api", () => {

const payload2: InputViewFunctionData = {
function: "0x1::account::get_authentication_key",
typeArguments: [],
functionArguments: ["0x1"],
};

Expand All @@ -184,7 +176,6 @@ describe("general api", () => {
const payload: InputViewFunctionData = {
function: "0x1::coin::symbol",
typeArguments: ["0x1::aptos_coin::AptosCoin"],
functionArguments: [],
};

const symbol = (await aptos.view<[string]>({ payload }))[0];
Expand All @@ -202,7 +193,6 @@ describe("general api", () => {
const payload3: InputViewFunctionData = {
function: "0x1::coin::supply",
typeArguments: ["0x1::aptos_coin::AptosCoin"],
functionArguments: [],
};

const supply = (await aptos.view<[{ vec: [string] }]>({ payload: payload3 }))[0].vec[0];
Expand Down

0 comments on commit 58e22cb

Please sign in to comment.