Skip to content

Commit

Permalink
[transactions] Allow multisig address to be a string
Browse files Browse the repository at this point in the history
  • Loading branch information
gregnazario committed Nov 29, 2023
1 parent df2d88e commit 86134f2
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 35 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T
- Add `transferFungibleAsset` function to easily generate a transaction to transfer a fungible asset from sender's primary store to recipient's primary store
- [`Breaking`] `AccountAddress.fromRelaxed` is now `AccountAddress.from`, and a new `AccountAddress.fromStrict` has the old functionality.
- Implement transaction management worker layer to manage transaction submission for a single account with a high throughput
- [`Fixed`] Allow for Uint8Array to be passed as a `vector<u8>` argument on entry functions
- [`Fixed`] Allow for raw vectors to be passed as arguments with encoded types within them for Remote ABI on entry functions e.g. [AccountAddress]

## 0.0.7 (2023-11-16)

Expand Down
29 changes: 28 additions & 1 deletion src/transactions/transactionBuilder/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
InputScriptData,
SimpleEntryFunctionArgumentTypes,
} from "../types";
import { Bool, FixedBytes, MoveString, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { Bool, FixedBytes, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
import { AccountAddress } from "../../core";
import { MoveFunction, MoveFunctionId } from "../../types";

Expand All @@ -32,36 +32,63 @@ export function isNull(arg: SimpleEntryFunctionArgumentTypes): arg is null | und
return arg === null || arg === undefined;
}

export function isEncodedEntryFunctionArgument(
arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes,
): arg is EntryFunctionArgumentTypes {
return (
isBcsBool(arg) ||
isBcsU8(arg) ||
isBcsU16(arg) ||
isBcsU32(arg) ||
isBcsU64(arg) ||
isBcsU128(arg) ||
isBcsU256(arg) ||
isBcsAddress(arg) ||
isBcsString(arg) ||
isBcsFixedBytes(arg) ||
arg instanceof MoveVector ||
arg instanceof MoveOption
);
}

export function isBcsBool(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is Bool {
return arg instanceof Bool;
}

export function isBcsAddress(
arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes,
): arg is AccountAddress {
return arg instanceof AccountAddress;
}

export function isBcsString(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is MoveString {
return arg instanceof MoveString;
}

export function isBcsFixedBytes(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is FixedBytes {
return arg instanceof FixedBytes;
}

export function isBcsU8(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U8 {
return arg instanceof U8;
}

export function isBcsU16(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U16 {
return arg instanceof U16;
}

export function isBcsU32(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U32 {
return arg instanceof U32;
}

export function isBcsU64(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U64 {
return arg instanceof U64;
}

export function isBcsU128(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U128 {
return arg instanceof U128;
}

export function isBcsU256(arg: EntryFunctionArgumentTypes | SimpleEntryFunctionArgumentTypes): arg is U256 {
return arg instanceof U256;
}
Expand Down
59 changes: 33 additions & 26 deletions src/transactions/transactionBuilder/remoteAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
findFirstNonSignerArg,
isBcsAddress,
isBcsBool,
isBcsFixedBytes,
isBcsString,
isBcsU128,
isBcsU16,
Expand All @@ -21,6 +20,7 @@ import {
isBcsU64,
isBcsU8,
isBool,
isEncodedEntryFunctionArgument,
isLargeNumber,
isNull,
isNumber,
Expand Down Expand Up @@ -104,28 +104,25 @@ export function convertArgument(
throw new Error(`Too many arguments for '${functionName}', expected ${functionAbi.parameters.length}`);
}

const param = functionAbi.parameters[position];
return checkOrConvertArgument(arg, param, position, genericTypeParams);
}

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 (
isBcsBool(arg) ||
isBcsU8(arg) ||
isBcsU16(arg) ||
isBcsU32(arg) ||
isBcsU64(arg) ||
isBcsU128(arg) ||
isBcsU256(arg) ||
isBcsAddress(arg) ||
isBcsString(arg) ||
isBcsFixedBytes(arg) ||
arg instanceof MoveVector ||
arg instanceof MoveOption
) {
if (isEncodedEntryFunctionArgument(arg)) {
// Ensure the type matches the ABI
checkType(functionAbi, arg, position);
checkType(param, arg, position);
return arg;
}

// If it is not BCS encoded, we will need to convert it with the ABI
return parseArg(arg, functionAbi.parameters[position], position, genericTypeParams);
return parseArg(arg, param, position, genericTypeParams);
}

/**
Expand Down Expand Up @@ -198,19 +195,30 @@ function parseArg(
throw new Error(`Generic argument ${param.toString()} is invalid for argument ${position}`);
}

parseArg(arg, genericTypeParams[genericIndex], position, genericTypeParams);
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() && isString(arg)) {
// TODO: Improve message when hex is invalid
return MoveVector.U8(Hex.fromHexInput(arg).toUint8Array());
if (param.value.isU8()) {
if (isString(arg)) {
try {
return MoveVector.U8(Hex.fromHexInput(arg).toUint8Array());
} catch (e: any) {
// This is a bit of a hack to not reuse code, but also have a better error message
throw new Error(`vector<u8> must be passed in as a hex string or a Uint8array, type '${param.toString()}'`);
}
}
if (arg instanceof Uint8Array) {
return MoveVector.U8(arg);
}
}

// TODO: Support Uint16Array, Uint32Array, BigUint64Array?

if (Array.isArray(arg)) {
return new MoveVector(arg.map((item) => parseArg(item, param.value, position, genericTypeParams)));
return new MoveVector(arg.map((item) => checkOrConvertArgument(item, param.value, position, genericTypeParams)));
}

throw new Error(`Type mismatch for argument ${position}, type '${param.toString()}'`);
Expand Down Expand Up @@ -239,7 +247,7 @@ function parseArg(
return new MoveOption<U8>(null);
}

return new MoveOption(parseArg(arg, param.value.typeArgs[0], position, genericTypeParams));
return new MoveOption(checkOrConvertArgument(arg, param.value.typeArgs[0], position, genericTypeParams));
}

throw new Error(`Unsupported struct input type for argument ${position}, type '${param.toString()}'`);
Expand All @@ -250,12 +258,11 @@ function parseArg(

/**
* Checks that the type of an already BCS encoded argument matches the ABI
* @param functionAbi
* @param param
* @param arg
* @param position
*/
function checkType(functionAbi: EntryFunctionABI, arg: EntryFunctionArgumentTypes, position: number) {
const param = functionAbi.parameters[position];
function checkType(param: TypeTag, arg: EntryFunctionArgumentTypes, position: number) {
if (param.isBool()) {
if (isBcsBool(arg)) {
return;
Expand Down
7 changes: 1 addition & 6 deletions src/transactions/transactionBuilder/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,7 @@ export function generateTransactionPayloadWithABI(

// Send it as multi sig if it's a multisig payload
if ("multisigAddress" in args) {
let multisigAddress: AccountAddress;
if (typeof args.multisigAddress === "string") {
multisigAddress = AccountAddress.from(args.multisigAddress);
} else {
multisigAddress = args.multisigAddress;
}
const multisigAddress = AccountAddress.from(args.multisigAddress);
return new TransactionPayloadMultisig(
new MultiSig(multisigAddress, new MultisigTransactionPayload(entryFunctionPayload)),
);
Expand Down
4 changes: 2 additions & 2 deletions src/transactions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type SimpleEntryFunctionArgumentTypes =
| null // To support optional empty
| undefined // To support optional empty
| Uint8Array
| Array<SimpleEntryFunctionArgumentTypes>;
| Array<SimpleEntryFunctionArgumentTypes | EntryFunctionArgumentTypes>;

/**
* Entry function arguments to be used when building a raw transaction using BCS serialized arguments
Expand Down Expand Up @@ -115,7 +115,7 @@ export type InputEntryFunctionDataWithRemoteABI = InputEntryFunctionData & { apt
* The data needed to generate a Multi Sig payload
*/
export type InputMultiSigData = {
multisigAddress: AccountAddress;
multisigAddress: AccountAddressInput;
} & InputEntryFunctionData;

/**
Expand Down

0 comments on commit 86134f2

Please sign in to comment.