PHP WebShell
Текущая директория: /opt/BitGoJS/node_modules/@aptos-labs/ts-sdk/src/transactions/transactionBuilder
Просмотр файла: remoteAbi.ts
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
import { parseTypeTag } from "../typeTag/parser";
import {
TypeTag,
TypeTagAddress,
TypeTagBool,
TypeTagStruct,
TypeTagU128,
TypeTagU16,
TypeTagU256,
TypeTagU32,
TypeTagU64,
TypeTagU8,
} from "../typeTag";
import { AptosConfig } from "../../api/aptosConfig";
import {
EntryFunctionArgumentTypes,
SimpleEntryFunctionArgumentTypes,
EntryFunctionABI,
ViewFunctionABI,
FunctionABI,
TypeArgument,
} from "../types";
import { Bool, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { AccountAddress } from "../../core";
import { getModule } from "../../internal/account";
import {
findFirstNonSignerArg,
isBcsAddress,
isBcsBool,
isBcsString,
isBcsU128,
isBcsU16,
isBcsU256,
isBcsU32,
isBcsU64,
isBcsU8,
isBool,
isEncodedEntryFunctionArgument,
isLargeNumber,
isEmptyOption,
isString,
throwTypeMismatch,
convertNumber,
} from "./helpers";
import { MoveFunction } from "../../types";
const TEXT_ENCODER = new TextEncoder();
/**
* Convert type arguments to only type tags, allowing for string representations of type tags.
*
* @param typeArguments - An optional array of type arguments that may include string representations.
* @returns An array of TypeTag objects derived from the provided type arguments.
*/
export function standardizeTypeTags(typeArguments?: Array<TypeArgument>): Array<TypeTag> {
return (
typeArguments?.map((typeArg: TypeArgument): TypeTag => {
// Convert to TypeTag if it's a string representation
if (isString(typeArg)) {
return parseTypeTag(typeArg);
}
return typeArg;
}) ?? []
);
}
/**
* Fetches the ABI of a specified function from the on-chain module ABI. This function allows you to access the details of a
* specific function within a module.
*
* @param moduleAddress - The address of the module from which to fetch the function ABI.
* @param moduleName - The name of the module containing the function.
* @param functionName - The name of the function whose ABI is to be fetched.
* @param aptosConfig - The configuration settings for Aptos.
*/
export async function fetchFunctionAbi(
moduleAddress: string,
moduleName: string,
functionName: string,
aptosConfig: AptosConfig,
): Promise<MoveFunction | undefined> {
// This fetch from the API is currently cached
const module = await getModule({ aptosConfig, accountAddress: moduleAddress, moduleName });
if (module.abi) {
return module.abi.exposed_functions.find((func) => func.name === functionName);
}
return undefined;
}
/**
* Fetches the ABI for an entry function from the specified module address.
* This function validates if the ABI corresponds to an entry function and retrieves its parameters.
*
* @param moduleAddress - The address of the module containing the entry function.
* @param moduleName - The name of the module containing the entry function.
* @param functionName - The name of the entry function to fetch the ABI for.
* @param aptosConfig - The configuration settings for Aptos.
* @returns An object containing the number of signers, type parameters, and function parameters.
* @throws Error if the ABI cannot be found or if the function is not an entry function.
*/
export async function fetchEntryFunctionAbi(
moduleAddress: string,
moduleName: string,
functionName: string,
aptosConfig: AptosConfig,
): Promise<EntryFunctionABI> {
const functionAbi = await fetchFunctionAbi(moduleAddress, moduleName, functionName, aptosConfig);
// If there's no ABI, then the function is invalid
if (!functionAbi) {
throw new Error(`Could not find entry function ABI for '${moduleAddress}::${moduleName}::${functionName}'`);
}
// Non-entry functions also can't be used
if (!functionAbi.is_entry) {
throw new Error(`'${moduleAddress}::${moduleName}::${functionName}' is not an entry function`);
}
// Remove the signer arguments
const numSigners = findFirstNonSignerArg(functionAbi);
const params: TypeTag[] = [];
for (let i = numSigners; i < functionAbi.params.length; i += 1) {
params.push(parseTypeTag(functionAbi.params[i], { allowGenerics: true }));
}
return {
signers: numSigners,
typeParameters: functionAbi.generic_type_params,
parameters: params,
};
}
/**
* Fetches the ABI for a view function from the specified module address.
* This function ensures that the ABI is valid and retrieves the type parameters, parameters, and return types for the view function.
*
* @param moduleAddress - The address of the module containing the view function.
* @param moduleName - The name of the module containing the view function.
* @param functionName - The name of the view function for which to fetch the ABI.
* @param aptosConfig - The configuration settings for Aptos.
* @returns An object containing the type parameters, parameters, and return types of the view function.
* @throws Error if the ABI cannot be found or if the function is not a view function.
*/
export async function fetchViewFunctionAbi(
moduleAddress: string,
moduleName: string,
functionName: string,
aptosConfig: AptosConfig,
): Promise<ViewFunctionABI> {
const functionAbi = await fetchFunctionAbi(moduleAddress, moduleName, functionName, aptosConfig);
// If there's no ABI, then the function is invalid
if (!functionAbi) {
throw new Error(`Could not find view function ABI for '${moduleAddress}::${moduleName}::${functionName}'`);
}
// 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 }));
}
return {
typeParameters: functionAbi.generic_type_params,
parameters: params,
returnTypes,
};
}
/**
* Converts a non-BCS encoded argument into BCS encoded, if necessary.
* This function checks the provided argument against the expected parameter type and converts it accordingly.
*
* @param functionName - The name of the function for which the argument is being converted.
* @param functionAbi - The ABI (Application Binary Interface) of the function, which defines its parameters.
* @param arg - The argument to be converted, which can be of various types.
* @param position - The index of the argument in the function's parameter list.
* @param genericTypeParams - An array of type tags for any generic type parameters.
*/
export function convertArgument(
functionName: string,
functionAbi: FunctionABI,
arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes,
position: number,
genericTypeParams: Array<TypeTag>,
) {
// Ensure not too many arguments
if (position >= functionAbi.parameters.length) {
throw new Error(`Too many arguments for '${functionName}', expected ${functionAbi.parameters.length}`);
}
const param = functionAbi.parameters[position];
return checkOrConvertArgument(arg, param, position, genericTypeParams);
}
/**
* Checks if the provided argument is BCS encoded and converts it if necessary, ensuring type compatibility with the ABI.
* This function helps in validating and converting arguments for entry functions based on their expected types.
*
* @param arg - The argument to check or convert, which can be either a simple or entry function argument type.
* @param param - The expected type tag for the argument.
* @param position - The position of the argument in the function call.
* @param genericTypeParams - An array of generic type parameters that may be used for conversion.
*/
export function checkOrConvertArgument(
arg: SimpleEntryFunctionArgumentTypes | EntryFunctionArgumentTypes,
param: TypeTag,
position: number,
genericTypeParams: Array<TypeTag>,
) {
// If the argument is bcs encoded, we can just use it directly
if (isEncodedEntryFunctionArgument(arg)) {
// Ensure the type matches the ABI
/**
* Checks the type of the provided argument against the expected type.
* This function helps validate that the argument conforms to the specified type requirements.
*
* @param typeArgs - The expected type arguments.
* @param arg - The argument to be checked.
* @param position - The position of the argument in the context of the check.
*/
checkType(param, arg, position);
return arg;
}
// If it is not BCS encoded, we will need to convert it with the ABI
return parseArg(arg, param, position, genericTypeParams);
}
/**
* Parses a non-BCS encoded argument into a BCS encoded argument recursively.
* This function helps convert various types of input arguments into their corresponding BCS encoded formats based on the
* specified parameter type.
*
* @param arg - The argument to be parsed, which can be of various types.
* @param param - The type tag that defines the expected type of the argument.
* @param position - The position of the argument in the function call, used for error reporting.
* @param genericTypeParams - An array of type tags for generic type parameters, used when the parameter type is generic.
*/
function parseArg(
arg: SimpleEntryFunctionArgumentTypes,
param: TypeTag,
position: number,
genericTypeParams: Array<TypeTag>,
): EntryFunctionArgumentTypes {
if (param.isBool()) {
if (isBool(arg)) {
return new Bool(arg);
}
if (isString(arg)) {
if (arg === "true") return new Bool(true);
if (arg === "false") return new Bool(false);
}
/**
* Throws a type mismatch error for the specified move option.
*
* @param moveOption - The name of the move option that caused the type mismatch.
* @param position - The position where the error occurred.
*/
throwTypeMismatch("boolean", position);
}
// TODO: support uint8array?
if (param.isAddress()) {
if (isString(arg)) {
return AccountAddress.fromString(arg);
}
throwTypeMismatch("string | AccountAddress", position);
}
if (param.isU8()) {
const num = convertNumber(arg);
if (num !== undefined) {
return new U8(num);
}
throwTypeMismatch("number | string", position);
}
if (param.isU16()) {
const num = convertNumber(arg);
if (num !== undefined) {
return new U16(num);
}
throwTypeMismatch("number | string", position);
}
if (param.isU32()) {
const num = convertNumber(arg);
if (num !== undefined) {
return new U32(num);
}
throwTypeMismatch("number | string", position);
}
if (param.isU64()) {
if (isLargeNumber(arg)) {
return new U64(BigInt(arg));
}
throwTypeMismatch("bigint | number | string", position);
}
if (param.isU128()) {
if (isLargeNumber(arg)) {
return new U128(BigInt(arg));
}
throwTypeMismatch("bigint | number | string", position);
}
if (param.isU256()) {
if (isLargeNumber(arg)) {
return new U256(BigInt(arg));
}
throwTypeMismatch("bigint | number | string", position);
}
// Generic needs to use the subtype
if (param.isGeneric()) {
const genericIndex = param.value;
if (genericIndex < 0 || genericIndex >= genericTypeParams.length) {
throw new Error(`Generic argument ${param.toString()} is invalid for argument ${position}`);
}
return checkOrConvertArgument(arg, genericTypeParams[genericIndex], position, genericTypeParams);
}
// We have to special case some vectors for Vector<u8>
if (param.isVector()) {
// Check special case for Vector<u8>
if (param.value.isU8()) {
// We don't allow vector<u8>, but we convert strings to UTF8 Uint8Array
// This is legacy behavior from the original SDK
if (isString(arg)) {
return MoveVector.U8(TEXT_ENCODER.encode(arg));
}
if (arg instanceof Uint8Array) {
return MoveVector.U8(arg);
}
if (arg instanceof ArrayBuffer) {
return MoveVector.U8(new Uint8Array(arg));
}
}
// TODO: Support Uint16Array, Uint32Array, BigUint64Array?
if (Array.isArray(arg)) {
return new MoveVector(arg.map((item) => checkOrConvertArgument(item, param.value, position, genericTypeParams)));
}
throw new Error(`Type mismatch for argument ${position}, type '${param.toString()}'`);
}
// Handle structs as they're more complex
if (param.isStruct()) {
if (param.isString()) {
if (isString(arg)) {
return new MoveString(arg);
}
throwTypeMismatch("string", position);
}
if (param.isObject()) {
// The inner type of Object doesn't matter, since it's just syntactic sugar
if (isString(arg)) {
return AccountAddress.fromString(arg);
}
throwTypeMismatch("string | AccountAddress", position);
}
if (param.isOption()) {
if (isEmptyOption(arg)) {
// Here we attempt to reconstruct the underlying type
// Note, for some reason the `isBool` etc. does not work with the compiler
const innerParam = param.value.typeArgs[0];
if (innerParam instanceof TypeTagBool) {
return new MoveOption<Bool>(null);
}
if (innerParam instanceof TypeTagAddress) {
return new MoveOption<AccountAddress>(null);
}
if (innerParam instanceof TypeTagU8) {
return new MoveOption<U8>(null);
}
if (innerParam instanceof TypeTagU16) {
return new MoveOption<U16>(null);
}
if (innerParam instanceof TypeTagU32) {
return new MoveOption<U32>(null);
}
if (innerParam instanceof TypeTagU64) {
return new MoveOption<U64>(null);
}
if (innerParam instanceof TypeTagU128) {
return new MoveOption<U128>(null);
}
if (innerParam instanceof TypeTagU256) {
return new MoveOption<U256>(null);
}
// In all other cases, we will use a placeholder, it doesn't actually matter what the type is, but it will be obvious
// Note: This is a placeholder U8 type, and does not match the actual type, as that can't be dynamically grabbed
return new MoveOption<MoveString>(null);
}
return new MoveOption(checkOrConvertArgument(arg, param.value.typeArgs[0], position, genericTypeParams));
}
throw new Error(`Unsupported struct input type for argument ${position}, type '${param.toString()}'`);
}
throw new Error(`Type mismatch for argument ${position}, type '${param.toString()}'`);
}
/**
* Checks that the type of the BCS encoded argument matches the ABI
* @param param
* @param arg
* @param position
*/
function checkType(param: TypeTag, arg: EntryFunctionArgumentTypes, position: number) {
if (param.isBool()) {
if (isBcsBool(arg)) {
return;
}
throwTypeMismatch("Bool", position);
}
if (param.isAddress()) {
if (isBcsAddress(arg)) {
return;
}
throwTypeMismatch("AccountAddress", position);
}
if (param.isU8()) {
if (isBcsU8(arg)) {
return;
}
throwTypeMismatch("U8", position);
}
if (param.isU16()) {
if (isBcsU16(arg)) {
return;
}
throwTypeMismatch("U16", position);
}
if (param.isU32()) {
if (isBcsU32(arg)) {
return;
}
throwTypeMismatch("U32", position);
}
if (param.isU64()) {
if (isBcsU64(arg)) {
return;
}
throwTypeMismatch("U64", position);
}
if (param.isU128()) {
if (isBcsU128(arg)) {
return;
}
throwTypeMismatch("U128", position);
}
if (param.isU256()) {
if (isBcsU256(arg)) {
return;
}
throwTypeMismatch("U256", position);
}
if (param.isVector()) {
if (arg instanceof MoveVector) {
// If there's anything in it, check that the inner types match
// Note that since it's typed, the first item should be the same as the rest
if (arg.values.length > 0) {
checkType(param.value, arg.values[0], position);
}
return;
}
throwTypeMismatch("MoveVector", position);
}
// Handle structs as they're more complex
if (param instanceof TypeTagStruct) {
if (param.isString()) {
if (isBcsString(arg)) {
return;
}
throwTypeMismatch("MoveString", position);
}
if (param.isObject()) {
if (isBcsAddress(arg)) {
return;
}
throwTypeMismatch("AccountAddress", position);
}
if (param.isOption()) {
if (arg instanceof MoveOption) {
// If there's a value, we can check the inner type (otherwise it doesn't really matter)
if (arg.value !== undefined) {
checkType(param.value.typeArgs[0], arg.value, position);
}
return;
}
throwTypeMismatch("MoveOption", position);
}
}
throw new Error(`Type mismatch for argument ${position}, expected '${param.toString()}'`);
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!