From 0b86e5ce259b76bdd418bc0dee777086c11a9178 Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:31:50 -0700 Subject: [PATCH 01/11] First pass at transaction arguments --- src/api/transaction_submission.ts | 1 + src/bcs/serializable/fixed-bytes.ts | 14 +- src/bcs/serializable/move-primitives.ts | 103 ++++++++++-- src/bcs/serializable/move-structs.ts | 148 +++++++++++++++--- src/core/account_address.ts | 13 +- .../instances/scriptTransactionArguments.ts | 135 ++++++++++------ .../instances/transactionArgument.ts | 81 ++++++++++ .../instances/transactionPayload.ts | 41 +++-- src/transactions/typeTag/typeTag.ts | 4 +- src/transactions/types.ts | 7 +- tests/unit/bcs-helper.test.ts | 49 +++++- .../unit/script_transaction_arguments.test.ts | 125 +++++++++++++++ tests/unit/type_tag.test.ts | 3 +- 13 files changed, 609 insertions(+), 115 deletions(-) create mode 100644 src/transactions/instances/transactionArgument.ts create mode 100644 tests/unit/script_transaction_arguments.test.ts diff --git a/src/api/transaction_submission.ts b/src/api/transaction_submission.ts index c5f7b4682..edcf8a7d0 100644 --- a/src/api/transaction_submission.ts +++ b/src/api/transaction_submission.ts @@ -14,6 +14,7 @@ import { GenerateSingleSignerRawTransactionInput, SingleSignerTransaction, SimulateTransactionData, + GenerateSingleSignerRawTransactionArgs, } from "../transactions/types"; import { UserTransactionResponse, PendingTransactionResponse } from "../types"; import { diff --git a/src/bcs/serializable/fixed-bytes.ts b/src/bcs/serializable/fixed-bytes.ts index 7b51418ff..e6a927629 100644 --- a/src/bcs/serializable/fixed-bytes.ts +++ b/src/bcs/serializable/fixed-bytes.ts @@ -5,6 +5,10 @@ import { Serializable, Serializer } from "../serializer"; import { Deserializer } from "../deserializer"; import { HexInput } from "../../types"; import { Hex } from "../../core"; +import { + EntryFunctionArgument, + TransactionArgument, +} from "../../transactions/instances/transactionArgument"; /** * This class exists to represent a contiguous sequence of BCS bytes that when serialized @@ -30,7 +34,7 @@ import { Hex } from "../../core"; * const fixedBytes = FixedBytes.deserialize(deserializer, 32); * @see EntryFunction */ -export class FixedBytes extends Serializable { +export class FixedBytes extends Serializable implements TransactionArgument { public value: Uint8Array; constructor(value: HexInput) { @@ -42,6 +46,14 @@ export class FixedBytes extends Serializable { serializer.serializeFixedBytes(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + serializer.serialize(this); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); + } + static deserialize(deserializer: Deserializer, length: number): FixedBytes { const bytes = deserializer.deserializeFixedBytes(length); return new FixedBytes(bytes); diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index 9331f228e..5a57fd003 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -11,9 +11,24 @@ import { } from "../consts"; import { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; import { Deserializer } from "../deserializer"; -import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer"; - -export class Bool extends Serializable { +import { + Serializable, + Serializer, + ensureBoolean, + validateNumberInRange, +} from "../serializer"; +import { TransactionArgument } from "../../transactions/instances/transactionArgument"; +import { + ScriptTransactionArgumentBool, + ScriptTransactionArgumentU128, + ScriptTransactionArgumentU16, + ScriptTransactionArgumentU256, + ScriptTransactionArgumentU32, + ScriptTransactionArgumentU64, + ScriptTransactionArgumentU8, +} from "../../transactions/instances"; + +export class Bool extends Serializable implements TransactionArgument { public readonly value: boolean; constructor(value: boolean) { @@ -26,12 +41,22 @@ export class Bool extends Serializable { serializer.serializeBool(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentBool(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): Bool { return new Bool(deserializer.deserializeBool()); } } -export class U8 extends Serializable { +export class U8 extends Serializable implements TransactionArgument { public readonly value: Uint8; constructor(value: Uint8) { @@ -44,12 +69,22 @@ export class U8 extends Serializable { serializer.serializeU8(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU8(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U8 { return new U8(deserializer.deserializeU8()); } } -export class U16 extends Serializable { +export class U16 extends Serializable implements TransactionArgument { public readonly value: Uint16; constructor(value: Uint16) { @@ -62,12 +97,22 @@ export class U16 extends Serializable { serializer.serializeU16(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU16(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U16 { return new U16(deserializer.deserializeU16()); } } -export class U32 extends Serializable { +export class U32 extends Serializable implements TransactionArgument { public readonly value: Uint32; constructor(value: Uint32) { @@ -80,12 +125,22 @@ export class U32 extends Serializable { serializer.serializeU32(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU32(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U32 { return new U32(deserializer.deserializeU32()); } } -export class U64 extends Serializable { +export class U64 extends Serializable implements TransactionArgument { public readonly value: bigint; constructor(value: AnyNumber) { @@ -98,12 +153,22 @@ export class U64 extends Serializable { serializer.serializeU64(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU64(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U64 { return new U64(deserializer.deserializeU64()); } } -export class U128 extends Serializable { +export class U128 extends Serializable implements TransactionArgument { public readonly value: bigint; constructor(value: AnyNumber) { @@ -116,12 +181,22 @@ export class U128 extends Serializable { serializer.serializeU128(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU128(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U128 { return new U128(deserializer.deserializeU128()); } } -export class U256 extends Serializable { +export class U256 extends Serializable implements TransactionArgument { public readonly value: bigint; constructor(value: AnyNumber) { @@ -134,6 +209,16 @@ export class U256 extends Serializable { serializer.serializeU256(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentU256(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): U256 { return new U256(deserializer.deserializeU256()); } diff --git a/src/bcs/serializable/move-structs.ts b/src/bcs/serializable/move-structs.ts index 0f4629999..59093abe3 100644 --- a/src/bcs/serializable/move-structs.ts +++ b/src/bcs/serializable/move-structs.ts @@ -5,7 +5,15 @@ import { Serializable, Serializer } from "../serializer"; import { Deserializable, Deserializer } from "../deserializer"; import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { AnyNumber, HexInput } from "../../types"; -import { AccountAddress } from "../../core"; +import { AccountAddress, Hex } from "../../core"; +import { + EntryFunctionArgument, + TransactionArgument, +} from "../../transactions/instances/transactionArgument"; +import { + ScriptTransactionArgumentAddress, + ScriptTransactionArgumentU8Vector, +} from "../../transactions/instances"; /** * This class is the Aptos Typescript SDK representation of a Move `vector`, @@ -37,9 +45,9 @@ import { AccountAddress } from "../../core"; * MoveOption.U8(2), * ]); * - * // vector [ std::string::utf8(b"hello"), std::string::utf8(b"world") ]; + * // vector [ std::string::utf8(b"hello"), std::string::utf8(b"world") ]; * const vecOfStrings = new MoveVector([new MoveString("hello"), new MoveString("world")]); - * const vecOfStrings2 = MoveVector.String(["hello", "world"]); + * const vecOfStrings2 = MoveVector.MoveString(["hello", "world"]); * * // where MySerializableStruct is a class you've made that implements Serializable * const vecOfSerializableValues = new MoveVector([ @@ -50,7 +58,10 @@ import { AccountAddress } from "../../core"; * values: an Array of values where T is a class that implements Serializable * @returns a `MoveVector` with the values `values` */ -export class MoveVector extends Serializable { +export class MoveVector + extends Serializable + implements TransactionArgument +{ public values: Array; constructor(values: Array) { @@ -58,6 +69,27 @@ export class MoveVector extends Serializable { this.values = values; } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + /** + * NOTE: This function will only work when the inner values in the `MoveVector` are `U8`s. + * @param serializer + */ + serializeForScriptFunction(serializer: Serializer): void { + // runtime check to ensure that you can't serialize anything other than vector + // TODO: consider adding support for MoveString later? + const isU8 = this.values[0] instanceof U8; + if (!isU8) { + throw new Error("Script function arguments only accept u8 vectors"); + } + const bcsBytes = this.bcsToBytes(); + const scriptTxArg = new ScriptTransactionArgumentU8Vector(bcsBytes); + serializer.serialize(scriptTxArg); + } + /** * Factory method to generate a MoveVector of U8s from an array of numbers. * @@ -66,8 +98,21 @@ export class MoveVector extends Serializable { * @params values: an array of `numbers` to convert to U8s * @returns a `MoveVector` */ - static U8(values: Array): MoveVector { - return new MoveVector(values.map((v) => new U8(v))); + static U8(values: Array | HexInput): MoveVector { + let numbers: Array; + + if (Array.isArray(values) && typeof values[0] === "number") { + numbers = values; + } else if (typeof values === "string") { + const hex = Hex.fromHexInput({ hexInput: values }); + numbers = Array.from(hex.toUint8Array()); + } else if (values instanceof Uint8Array) { + numbers = Array.from(values); + } else { + throw new Error("Invalid input type"); + } + + return new MoveVector(numbers.map((v) => new U8(v))); } /** @@ -146,11 +191,11 @@ export class MoveVector extends Serializable { * Factory method to generate a MoveVector of MoveStrings from an array of strings. * * @example - * const v = MoveVector.String(["hello", "world"]); + * const v = MoveVector.MoveString(["hello", "world"]); * @params values: an array of `numbers` to convert to MoveStrings * @returns a `MoveVector` */ - static String(values: Array): MoveVector { + static MoveString(values: Array): MoveVector { return new MoveVector(values.map((v) => new MoveString(v))); } @@ -174,7 +219,10 @@ export class MoveVector extends Serializable { * @returns a MoveVector of the corresponding class T * * */ - static deserialize(deserializer: Deserializer, cls: Deserializable): MoveVector { + static deserialize( + deserializer: Deserializer, + cls: Deserializable + ): MoveVector { const length = deserializer.deserializeUleb128AsU32(); const values = new Array(); for (let i = 0; i < length; i += 1) { @@ -184,7 +232,7 @@ export class MoveVector extends Serializable { } } -export class MoveString extends Serializable { +export class MoveString extends Serializable implements TransactionArgument { public value: string; constructor(value: string) { @@ -196,12 +244,26 @@ export class MoveString extends Serializable { serializer.serializeStr(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + const scriptTxArg = new ScriptTransactionArgumentU8Vector(bcsBytes); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): MoveString { return new MoveString(deserializer.deserializeStr()); } } -export class MoveOption extends Serializable { +export class MoveOption + extends Serializable + implements EntryFunctionArgument +{ private vec: MoveVector; public readonly value?: T; @@ -217,6 +279,11 @@ export class MoveOption extends Serializable { [this.value] = this.vec.values; } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + /** * Retrieves the inner value of the MoveOption. * @@ -264,7 +331,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U8(value?: number | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U8(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U8(value) : undefined + ); } /** @@ -279,7 +348,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U16(value?: number | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U16(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U16(value) : undefined + ); } /** @@ -294,7 +365,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U32(value?: number | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U32(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U32(value) : undefined + ); } /** @@ -309,7 +382,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U64(value?: AnyNumber | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U64(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U64(value) : undefined + ); } /** @@ -324,7 +399,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U128(value?: AnyNumber | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U128(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U128(value) : undefined + ); } /** @@ -339,7 +416,9 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static U256(value?: AnyNumber | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new U256(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new U256(value) : undefined + ); } /** @@ -354,32 +433,39 @@ export class MoveOption extends Serializable { * @returns a MoveOption with an inner value `value` */ static Bool(value?: boolean | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new Bool(value) : undefined); + return new MoveOption( + value !== null && value !== undefined ? new Bool(value) : undefined + ); } /** * Factory method to generate a MoveOption from a `string` or `undefined`. * * @example - * MoveOption.String("hello").isSome() === true; - * MoveOption.String("").isSome() === true; - * MoveOption.String().isSome() === false; - * MoveOption.String(undefined).isSome() === false; + * MoveOption.MoveString("hello").isSome() === true; + * MoveOption.MoveString("").isSome() === true; + * MoveOption.MoveString().isSome() === false; + * MoveOption.MoveString(undefined).isSome() === false; * @params value: the value used to fill the MoveOption. If `value` is undefined * the resulting MoveOption's .isSome() method will return false. * @returns a MoveOption with an inner value `value` */ - static String(value?: string | null): MoveOption { - return new MoveOption(value !== null && value !== undefined ? new MoveString(value) : undefined); + static MoveString(value?: string | null): MoveOption { + return new MoveOption( + value !== null && value !== undefined ? new MoveString(value) : undefined + ); } - static deserialize(deserializer: Deserializer, cls: Deserializable): MoveOption { + static deserialize( + deserializer: Deserializer, + cls: Deserializable + ): MoveOption { const vector = MoveVector.deserialize(deserializer, cls); return new MoveOption(vector.values[0]); } } -export class MoveObject extends Serializable { +export class MoveObject extends Serializable implements TransactionArgument { public value: AccountAddress; constructor(value: HexInput | AccountAddress) { @@ -396,6 +482,16 @@ export class MoveObject extends Serializable { serializer.serialize(this.value); } + serializeForEntryFunction(serializer: Serializer): void { + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentAddress(this.value); + serializer.serialize(scriptTxArg); + } + static deserialize(deserializer: Deserializer): MoveObject { const address = deserializer.deserialize(AccountAddress); return new MoveObject(address); diff --git a/src/core/account_address.ts b/src/core/account_address.ts index ae9c4bf2f..170fb698d 100644 --- a/src/core/account_address.ts +++ b/src/core/account_address.ts @@ -5,6 +5,8 @@ import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; import { HexInput } from "../types"; import { ParsingError, ParsingResult } from "./common"; import { Deserializer, Serializable, Serializer } from "../bcs"; +import { TransactionArgument } from "../transactions/instances/transactionArgument"; +import { ScriptTransactionArgumentAddress } from "../transactions/instances"; /** * This enum is used to explain why an address was invalid. @@ -35,7 +37,7 @@ export enum AddressInvalidReason { * The comments in this class make frequent reference to the LONG and SHORT formats, * as well as "special" addresses. To learn what these refer to see AIP-40. */ -export class AccountAddress extends Serializable { +export class AccountAddress extends Serializable implements TransactionArgument { /* * This is the internal representation of an account address. */ @@ -181,6 +183,15 @@ export class AccountAddress extends Serializable { serializer.serializeFixedBytes(this.data); } + serializeForEntryFunction(serializer: Serializer): void { + serializer.serialize(this); + } + + serializeForScriptFunction(serializer: Serializer): void { + const scriptTxArg = new ScriptTransactionArgumentAddress(this); + serializer.serialize(scriptTxArg); + } + /** * Deserialize an AccountAddress from the byte buffer in a Deserializer instance. * @param deserializer The deserializer to deserialize the AccountAddress from. diff --git a/src/transactions/instances/scriptTransactionArguments.ts b/src/transactions/instances/scriptTransactionArguments.ts index 3fc01b5a4..4d1762e28 100644 --- a/src/transactions/instances/scriptTransactionArguments.ts +++ b/src/transactions/instances/scriptTransactionArguments.ts @@ -2,18 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; +import { MoveVector } from "../../bcs/serializable/move-structs"; import { AccountAddress } from "../../core"; -import { ScriptTransactionArgumentVariants } from "../../types"; +import { AnyNumber, HexInput, ScriptTransactionArgumentVariants, Uint16, Uint32, Uint8 } from "../../types"; +import { ScriptFunctionArgument } from "./transactionArgument"; /** * Representation of a Script Transaction Argument that can be serialized and deserialized */ -export abstract class ScriptTransactionArgument extends Serializable { +export abstract class ScriptTransactionArgument extends Serializable implements ScriptFunctionArgument { /** - * Serialize a Script Transaction Argument + * Serialize a Script Transaction Argument to its BCS byte representation. */ abstract serialize(serializer: Serializer): void; + /** + * Implemented to satisfy the ScriptFunctionArgument interface. + * This is exactly the same as `serialize` for all classes that extend ScriptTransactionArgument. + */ + abstract serializeForScriptFunction(serializer: Serializer): void; + /** * Deserialize a Script Transaction Argument */ @@ -45,17 +54,21 @@ export abstract class ScriptTransactionArgument extends Serializable { } } -export class ScriptTransactionArgumentU8 extends ScriptTransactionArgument { - public readonly value: number; +export class ScriptTransactionArgumentU8 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U8; - constructor(value: number) { + constructor(value: Uint8) { super(); - this.value = value; + this.value = new U8(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8); - serializer.serializeU8(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU8 { @@ -64,17 +77,21 @@ export class ScriptTransactionArgumentU8 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentU16 extends ScriptTransactionArgument { - public readonly value: number; +export class ScriptTransactionArgumentU16 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U16; - constructor(value: number) { + constructor(value: Uint16) { super(); - this.value = value; + this.value = new U16(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U16); - serializer.serializeU16(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU16 { @@ -83,17 +100,21 @@ export class ScriptTransactionArgumentU16 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentU32 extends ScriptTransactionArgument { - public readonly value: number; +export class ScriptTransactionArgumentU32 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U32; - constructor(value: number) { + constructor(value: Uint32) { super(); - this.value = value; + this.value = new U32(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U32); - serializer.serializeU32(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU32 { @@ -102,17 +123,21 @@ export class ScriptTransactionArgumentU32 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentU64 extends ScriptTransactionArgument { - public readonly value: bigint; +export class ScriptTransactionArgumentU64 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U64; - constructor(value: bigint) { + constructor(value: AnyNumber) { super(); - this.value = value; + this.value = new U64(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U64); - serializer.serializeU64(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU64 { @@ -121,17 +146,21 @@ export class ScriptTransactionArgumentU64 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentU128 extends ScriptTransactionArgument { - public readonly value: bigint; +export class ScriptTransactionArgumentU128 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U128; - constructor(value: bigint) { + constructor(value: AnyNumber) { super(); - this.value = value; + this.value = new U128(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U128); - serializer.serializeU128(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU128 { @@ -140,17 +169,21 @@ export class ScriptTransactionArgumentU128 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentU256 extends ScriptTransactionArgument { - public readonly value: bigint; +export class ScriptTransactionArgumentU256 extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: U256; - constructor(value: bigint) { + constructor(value: AnyNumber) { super(); - this.value = value; + this.value = new U256(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U256); - serializer.serializeU256(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU256 { @@ -159,7 +192,7 @@ export class ScriptTransactionArgumentU256 extends ScriptTransactionArgument { } } -export class ScriptTransactionArgumentAddress extends ScriptTransactionArgument { +export class ScriptTransactionArgumentAddress extends ScriptTransactionArgument implements ScriptFunctionArgument { public readonly value: AccountAddress; constructor(value: AccountAddress) { @@ -169,7 +202,11 @@ export class ScriptTransactionArgumentAddress extends ScriptTransactionArgument serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Address); - this.value.serialize(serializer); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentAddress { @@ -178,17 +215,21 @@ export class ScriptTransactionArgumentAddress extends ScriptTransactionArgument } } -export class ScriptTransactionArgumentU8Vector extends ScriptTransactionArgument { - public readonly value: Uint8Array; +export class ScriptTransactionArgumentU8Vector extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: MoveVector; - constructor(value: Uint8Array) { + constructor(values: Array | HexInput) { super(); - this.value = value; + this.value = MoveVector.U8(values); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8Vector); - serializer.serializeBytes(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentU8Vector { @@ -197,21 +238,25 @@ export class ScriptTransactionArgumentU8Vector extends ScriptTransactionArgument } } -export class ScriptTransactionArgumentBool extends ScriptTransactionArgument { - public readonly value: boolean; +export class ScriptTransactionArgumentBool extends ScriptTransactionArgument implements ScriptFunctionArgument { + public readonly value: Bool; constructor(value: boolean) { super(); - this.value = value; + this.value = new Bool(value); } serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Bool); - serializer.serializeBool(this.value); + serializer.serialize(this.value); + } + + serializeForScriptFunction(serializer: Serializer): void { + serializer.serialize(this); } static load(deserializer: Deserializer): ScriptTransactionArgumentBool { const value = deserializer.deserializeBool(); return new ScriptTransactionArgumentBool(value); } -} +} \ No newline at end of file diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts new file mode 100644 index 000000000..867dad34b --- /dev/null +++ b/src/transactions/instances/transactionArgument.ts @@ -0,0 +1,81 @@ +import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { HexInput } from "../../types"; +import { FixedBytes } from "../../bcs/serializable/fixed-bytes"; + +export interface TransactionArgument + extends EntryFunctionArgument, + ScriptFunctionArgument {} + +export interface EntryFunctionArgument { + /** + * Serialize an argument to BCS-serialized bytes. + */ + serialize(serializer: Serializer): void; + /** + * Serialize an argument as a type-agnostic, fixed byte sequence. The byte sequence contains + * the number of the following bytes followed by the BCS-serialized bytes for a typed argument. + * @example asfasdf + */ + serializeForEntryFunction(serializer: Serializer): void; +} +export interface ScriptFunctionArgument { + /** + * Serialize an argument to BCS-serialized bytes. + */ + serialize(serializer: Serializer): void; + /** + * Serialize an argument to BCS-serialized bytes as a type aware byte sequence. + * The byte sequence contains an enum variant index followed by the BCS-serialized + * bytes for a typed argument. + */ + serializeForScriptFunction(serializer: Serializer): void; +} +/** + * This class exists solely to represent a sequence of fixed bytes as a serialized entry function, because + * serializing an entry function appends a prefix that's *only* used for entry function arguments. + * + * NOTE: Attempting to use this class for a serialized script function will result in erroneous + * and unexpected behavior. + * + * If you wish to convert this class back to a TransactionArgument, you must know the type + * of the argument beforehand, and use the appropriate class to deserialize the bytes within + * an instance of this class. + */ +export class EntryFunctionBytes + extends Serializable + implements EntryFunctionArgument +{ + public readonly value: FixedBytes; + + private constructor(value: HexInput) { + super(); + this.value = new FixedBytes(value); + } + + // Note that both serialize and serializeForEntryFunction are the same. + // We need them to be equivalent so that when we re-serialize this class as an entry + // function payload's arguments, we don't re-serialize the length prefix. + serialize(serializer: Serializer): void { + serializer.serialize(this.value); + } + + // This is necessary to implement the interface, so we can use this class as an EntryFunctionArgument. + serializeForEntryFunction(serializer: Serializer): void { + serializer.serialize(this); + } + /** + * The only way to create an instance of this class is to use this static method. + * + * This function should only be used when deserializing a sequence of EntryFunctionPayload arguments. + * @param deserializer the deserializer instance with the buffered bytes + * @param length the length of the bytes to deserialize + * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument + */ + static deserialize( + deserializer: Deserializer, + length: number + ): EntryFunctionBytes { + const fixedBytes = FixedBytes.deserialize(deserializer, length); + return new EntryFunctionBytes(fixedBytes.value); + } +} diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index 0a43d25ba..c3d93a46a 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -10,9 +10,7 @@ import { ScriptTransactionArgument } from "./scriptTransactionArguments"; import { ModuleId } from "./moduleId"; import { TransactionPayloadVariants } from "../../types"; import { TypeTag } from "../typeTag/typeTag"; -import { U8 } from "../../bcs/serializable/move-primitives"; -import { MoveVector } from "../../bcs/serializable/move-structs"; -import { FixedBytes } from "../../bcs/serializable/fixed-bytes"; +import { EntryFunctionArgument, EntryFunctionBytes, ScriptFunctionArgument } from "./transactionArgument"; /** * Representation of the supported Transaction Payload @@ -119,7 +117,7 @@ export class EntryFunction { public readonly type_args: Array; - public readonly args: Array; + public readonly args: Array; /** * Contains the payload to run a function within a module. @@ -140,7 +138,7 @@ export class EntryFunction { * public entry fun transfer(from: &signer, to: address, amount: u64) * ``` */ - constructor(module_name: ModuleId, function_name: Identifier, type_args: Array, args: Array) { + constructor(module_name: ModuleId, function_name: Identifier, type_args: Array, args: Array) { this.module_name = module_name; this.function_name = function_name; this.type_args = type_args; @@ -172,7 +170,7 @@ export class EntryFunction { module_name: `${string}::${string}`, function_name: string, type_args: Array, - args: Array, + args: Array, ): EntryFunction { return new EntryFunction(ModuleId.fromStr(module_name), new Identifier(function_name), type_args, args); } @@ -181,20 +179,20 @@ export class EntryFunction { this.module_name.serialize(serializer); this.function_name.serialize(serializer); serializer.serializeVector(this.type_args); - serializer.serializeU32AsUleb128(this.args.length); - this.args.forEach((item: Serializable) => { - const bytes = item.bcsToBytes(); - serializer.serializeBytes(bytes); + this.args.forEach((item: EntryFunctionArgument) => { + item.serializeForEntryFunction(serializer); }); } /** - * Deserializes an entry function payload with the arguments represented as FixedBytes instances. - * @see FixedBytes + * Deserializes an entry function payload with the arguments represented as EntryFunctionBytes instances. + * @see EntryFunctionBytes * * NOTE: When you deserialize an EntryFunction payload with this method, the entry function - * arguments are populated as type-agnostic, raw fixed bytes in the form of the FixedBytes class. + * arguments are populated into the deserialized instance as type-agnostic, raw fixed bytes + * in the form of the EntryFunctionBytes class. + * * In order to correctly deserialize these arguments as their actual type representations, you * must know the types of the arguments beforehand and deserialize them yourself individually. * @@ -211,16 +209,14 @@ export class EntryFunction { const type_args = deserializer.deserializeVector(TypeTag); const length = deserializer.deserializeUleb128AsU32(); - const list: Array = new Array>(); + const args: Array = new Array(); for (let i = 0; i < length; i += 1) { const fixedBytesLength = deserializer.deserializeUleb128AsU32(); - const fixedBytes = FixedBytes.deserialize(deserializer, fixedBytesLength); - list.push(fixedBytes); + const fixedBytes = EntryFunctionBytes.deserialize(deserializer, fixedBytesLength); + args.push(fixedBytes); } - const args = list; - return new EntryFunction(module_name, function_name, type_args, args); } } @@ -242,7 +238,7 @@ export class Script { /** * The arguments that the bytecode function requires. */ - public readonly args: Array; + public readonly args: Array; /** * Scripts contain the Move bytecodes payload that can be submitted to Aptos chain for execution. @@ -263,7 +259,7 @@ export class Script { * public(script) fun transfer(from: &signer, to: address, amount: u64,) * ``` */ - constructor(bytecode: Uint8Array, type_args: Array, args: Array) { + constructor(bytecode: Uint8Array, type_args: Array, args: Array) { this.bytecode = bytecode; this.type_args = type_args; this.args = args; @@ -272,7 +268,10 @@ export class Script { serialize(serializer: Serializer): void { serializer.serializeBytes(this.bytecode); serializer.serializeVector(this.type_args); - serializer.serializeVector(this.args); + serializer.serializeU32AsUleb128(this.args.length); + this.args.forEach((item: ScriptFunctionArgument) => { + item.serializeForScriptFunction(serializer); + }); } static deserialize(deserializer: Deserializer): Script { diff --git a/src/transactions/typeTag/typeTag.ts b/src/transactions/typeTag/typeTag.ts index a6459b10c..d845e6c02 100644 --- a/src/transactions/typeTag/typeTag.ts +++ b/src/transactions/typeTag/typeTag.ts @@ -235,7 +235,7 @@ export class StructTag extends Serializable { } } -export const stringStructTag = new StructTag( +export const stringStructTag = () => new StructTag( AccountAddress.ONE, new Identifier("string"), new Identifier("String"), @@ -352,7 +352,7 @@ export class TypeTagParser { return new TypeTagVector(res); } if (tokenVal === "string") { - return new TypeTagStruct(stringStructTag); + return new TypeTagStruct(stringStructTag()); } if (tokenTy === "IDENT" && (tokenVal.startsWith("0x") || tokenVal.startsWith("0X"))) { const address = AccountAddress.fromHexInput({ input: tokenVal }); diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 21ce5ae6e..35ef0a5e0 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { AptosConfig } from "../api/aptos_config"; -import { Serializable } from "../bcs"; import { AccountAddress } from "../core"; import { PublicKey } from "../core/crypto/asymmetric_crypto"; import { HexInput, MoveStructType } from "../types"; @@ -10,11 +9,11 @@ import { MultiAgentRawTransaction, FeePayerRawTransaction, RawTransaction, - ScriptTransactionArgument, TransactionPayloadEntryFunction, TransactionPayloadMultisig, TransactionPayloadScript, } from "./instances"; +import { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; import { TypeTag } from "./typeTag/typeTag"; /** @@ -54,7 +53,7 @@ export type GenerateTransactionPayloadData = EntryFunctionData | ScriptData | Mu export type EntryFunctionData = { function: MoveStructType; type_arguments: Array; - arguments: Array; + arguments: Array; }; /** @@ -70,7 +69,7 @@ export type MultiSigData = { export type ScriptData = { bytecode: string; type_arguments: Array; - arguments: Array; + arguments: Array; }; /** diff --git a/tests/unit/bcs-helper.test.ts b/tests/unit/bcs-helper.test.ts index 1894e7cce..790389114 100644 --- a/tests/unit/bcs-helper.test.ts +++ b/tests/unit/bcs-helper.test.ts @@ -177,7 +177,7 @@ describe("Tests for the Serializable class", () => { MoveOption.U128(undefined), MoveOption.U256(undefined), MoveOption.Bool(undefined), - MoveOption.String(undefined), + MoveOption.MoveString(undefined), ]; const noneBytes = noneOptionValues.map((_) => new Uint8Array([0])); @@ -238,7 +238,7 @@ describe("Tests for the Serializable class", () => { testSerdeAndUnwrap(MoveOption.U128, U128); testSerdeAndUnwrap(MoveOption.U256, U256); testSerdeAndUnwrap(MoveOption.Bool, Bool); - testSerdeAndUnwrap(MoveOption.String, MoveString); + testSerdeAndUnwrap(MoveOption.MoveString, MoveString); }); it("serializes and deserializes a Vector of MoveOption types correctly", () => { @@ -345,7 +345,7 @@ describe("Tests for the Serializable class", () => { const u64VectorFrom = MoveVector.U64([1, 2, 3]); const u128VectorFrom = MoveVector.U128([1, 2, 3]); const u256VectorFrom = MoveVector.U256([1, 2, 3]); - const stringVectorFrom = MoveVector.String(["abc", "def", "ghi"]); + const stringVectorFrom = MoveVector.MoveString(["abc", "def", "ghi"]); const boolVectorBytes = new Uint8Array([3, 1, 0, 1]); const u8VectorBytes = new Uint8Array([3, 1, 2, 3]); @@ -389,7 +389,7 @@ describe("Tests for the Serializable class", () => { const u256Vector = new MoveVector([new U256(1), new U256(2), new U256(3)]); const u256VectorFrom = MoveVector.U256([1, 2, 3]); const stringVector = new MoveVector([new MoveString("abc"), new MoveString("def"), new MoveString("ghi")]); - const stringVectorFrom = MoveVector.String(["abc", "def", "ghi"]); + const stringVectorFrom = MoveVector.MoveString(["abc", "def", "ghi"]); expect(boolVector.bcsToBytes()).toEqual(boolVectorFrom.bcsToBytes()); expect(u8Vector.bcsToBytes()).toEqual(u8VectorFrom.bcsToBytes()); @@ -490,7 +490,7 @@ describe("Tests for the Serializable class", () => { MoveVector.U64([1, 2, 3]), MoveVector.U128([1, 2, 3]), MoveVector.U256([1, 2, 3]), - MoveVector.String(["abc", "def", "ghi"]), + MoveVector.MoveString(["abc", "def", "ghi"]), new MoveOption(new Bool(true)), new MoveOption(), new MoveOption(new MoveString("abc")), @@ -548,4 +548,43 @@ describe("Tests for the Serializable class", () => { const deserializedFixedBytes = FixedBytes.deserialize(deserializer, AccountAddress.LENGTH); expect(deserializedFixedBytes.value).toEqual(address.data); }); + + describe("MoveVector.U8 factory method tests", () => { + it("creates a MoveVector.U8 correctly", () => { + const vec = MoveVector.U8([1, 2, 3]); + const vecFromString = MoveVector.U8("0x010203"); + const vecFromUint8Array = MoveVector.U8(new Uint8Array([1, 2, 3])); + expect(vec.bcsToBytes()).toEqual(vecFromString.bcsToBytes()); + expect(vec.bcsToBytes()).toEqual(vecFromUint8Array.bcsToBytes()); + }); + + it("serializes and deserializes a MoveVector.U8 from various input types correctly", () => { + const vec = MoveVector.U8([1, 2, 3]); + const vecFromString = MoveVector.U8("0x010203"); + const vecFromUint8Array = MoveVector.U8(new Uint8Array([1, 2, 3])); + const deserializedVec = MoveVector.deserialize(new Deserializer(vec.bcsToBytes()), U8); + const deserializedVecFromString = MoveVector.deserialize(new Deserializer(vecFromString.bcsToBytes()), U8); + const deserializedVecFromUint8Array = MoveVector.deserialize( + new Deserializer(vecFromUint8Array.bcsToBytes()), + U8, + ); + expect(deserializedVec.values.map((v) => v.value)).toEqual(vec.values.map((v) => v.value)); + expect(deserializedVecFromString.values.map((v) => v.value)).toEqual(vec.values.map((v) => v.value)); + expect(deserializedVecFromUint8Array.values.map((v) => v.value)).toEqual(vec.values.map((v) => v.value)); + }); + + it("throws an error when trying to create a MoveVector.U8 from an invalid hex string", () => { + expect(() => MoveVector.U8("0x0102030")).toThrow(); + expect(() => MoveVector.U8("0xgg")).toThrow(); + // TODO: Add input validation to HexInput for truncating non-hex values like below + // expect(() => MoveVector.U8("asdf")).toThrow(); + expect(() => MoveVector.U8("gg")).toThrow(); + }); + + it("throws an error when trying to create a MoveVector.U8 from an invalid input type", () => { + expect(() => MoveVector.U8({} as any)).toThrow(); + expect(() => MoveVector.U8(["01", "02", "03"] as any)).toThrow(); + expect(() => MoveVector.U8([BigInt(1)] as any)).toThrow(); + }); + }); }); diff --git a/tests/unit/script_transaction_arguments.test.ts b/tests/unit/script_transaction_arguments.test.ts new file mode 100644 index 000000000..31f76e11c --- /dev/null +++ b/tests/unit/script_transaction_arguments.test.ts @@ -0,0 +1,125 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import { Serializer, Deserializer } from "../../src/bcs"; +import { AccountAddress } from "../../src/core"; +import { + ScriptTransactionArgument, + ScriptTransactionArgumentAddress, + ScriptTransactionArgumentBool, + ScriptTransactionArgumentU128, + ScriptTransactionArgumentU16, + ScriptTransactionArgumentU256, + ScriptTransactionArgumentU32, + ScriptTransactionArgumentU64, + ScriptTransactionArgumentU8, + ScriptTransactionArgumentU8Vector, +} from "../../src/transactions/instances"; +import { Bool, U128, U16, U256, U32, U64, U8 } from "../../src/bcs/serializable/move-primitives"; +import { MoveVector } from "../../src/bcs/serializable/move-structs"; + +describe("Tests for the script transaction argument class", () => { + let serializer: Serializer; + let scriptU8Bytes: Uint8Array; + let scriptU16Bytes: Uint8Array; + let scriptU32Bytes: Uint8Array; + let scriptU64Bytes: Uint8Array; + let scriptU128Bytes: Uint8Array; + let scriptU256Bytes: Uint8Array; + let scriptBoolBytes: Uint8Array; + let scriptAddressBytes: Uint8Array; + let scriptVectorU8Bytes: Uint8Array; + + beforeEach(() => { + serializer = new Serializer(); + scriptU8Bytes = new Uint8Array([0, 1]); + scriptU16Bytes = new Uint8Array([6, 2, 0]); + scriptU32Bytes = new Uint8Array([7, 3, 0, 0, 0]); + scriptU64Bytes = new Uint8Array([1, 4, 0, 0, 0, 0, 0, 0, 0]); + scriptU128Bytes = new Uint8Array([2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + scriptU256Bytes = new Uint8Array([ + 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + scriptBoolBytes = new Uint8Array([5, 0]); + scriptAddressBytes = new Uint8Array([3, ...AccountAddress.FOUR.data]); + scriptVectorU8Bytes = new Uint8Array([4, 5, 1, 2, 3, 4, 5]); + }); + + it("should serialize all types of ScriptTransactionArguments correctly", () => { + const validateBytes = (input: ScriptTransactionArgument, expectedOutput: Uint8Array) => { + const serializer = new Serializer(); + input.serialize(serializer); + const serializedBytes = serializer.toUint8Array(); + expect(serializedBytes).toEqual(expectedOutput); + }; + validateBytes(new ScriptTransactionArgumentU8(1), scriptU8Bytes); + validateBytes(new ScriptTransactionArgumentU16(2), scriptU16Bytes); + validateBytes(new ScriptTransactionArgumentU32(3), scriptU32Bytes); + validateBytes(new ScriptTransactionArgumentU64(4), scriptU64Bytes); + validateBytes(new ScriptTransactionArgumentU128(5), scriptU128Bytes); + validateBytes(new ScriptTransactionArgumentU256(6), scriptU256Bytes); + validateBytes(new ScriptTransactionArgumentBool(false), scriptBoolBytes); + validateBytes(new ScriptTransactionArgumentAddress(AccountAddress.FOUR), scriptAddressBytes); + validateBytes(new ScriptTransactionArgumentU8Vector([1, 2, 3, 4, 5]), scriptVectorU8Bytes); + }); + + it("should deserialize all types of ScriptTransactionArguments correctly", () => { + const deserializeToScriptArg = (input: ScriptTransactionArgument) => { + const deserializer = new Deserializer(input.bcsToBytes()); + return ScriptTransactionArgument.deserialize(deserializer); + }; + + const scriptArgU8 = deserializeToScriptArg(new ScriptTransactionArgumentU8(1)) as ScriptTransactionArgumentU8; + const scriptArgU16 = deserializeToScriptArg(new ScriptTransactionArgumentU16(2)) as ScriptTransactionArgumentU16; + const scriptArgU32 = deserializeToScriptArg(new ScriptTransactionArgumentU32(3)) as ScriptTransactionArgumentU32; + const scriptArgU64 = deserializeToScriptArg(new ScriptTransactionArgumentU64(4)) as ScriptTransactionArgumentU64; + const scriptArgU128 = deserializeToScriptArg(new ScriptTransactionArgumentU128(5)) as ScriptTransactionArgumentU128; + const scriptArgU256 = deserializeToScriptArg(new ScriptTransactionArgumentU256(6)) as ScriptTransactionArgumentU256; + const scriptArgBool = deserializeToScriptArg( + new ScriptTransactionArgumentBool(false), + ) as ScriptTransactionArgumentBool; + const scriptArgAddress = deserializeToScriptArg( + new ScriptTransactionArgumentAddress(AccountAddress.FOUR), + ) as ScriptTransactionArgumentAddress; + const scriptArgU8Vector = deserializeToScriptArg( + new ScriptTransactionArgumentU8Vector([1, 2, 3, 4, 5]), + ) as ScriptTransactionArgumentU8Vector; + + expect(scriptArgU8.value.value).toEqual(1); + expect(scriptArgU16.value.value).toEqual(2); + expect(scriptArgU32.value.value).toEqual(3); + expect(scriptArgU64.value.value).toEqual(4n); + expect(scriptArgU128.value.value).toEqual(5n); + expect(scriptArgU256.value.value).toEqual(6n); + expect(scriptArgBool.value.value).toEqual(false); + expect(scriptArgAddress.value.data).toEqual(AccountAddress.FOUR.data); + expect(scriptArgU8Vector.value.values.map((v) => v.value)).toEqual([1, 2, 3, 4, 5]); + }); + + it("should convert all Move primitives to script transaction arguments correctly", () => { + const deserializeToScriptArg = ( + input: U8 | U16 | U32 | U64 | U128 | U256 | Bool | MoveVector | AccountAddress, + ) => { + serializer = new Serializer(); + input.serializeForScriptFunction(serializer); + const deserializer = new Deserializer(serializer.toUint8Array()); + const asdf = ScriptTransactionArgument.deserialize(deserializer); + console.log(asdf); + return asdf; + }; + + expect(deserializeToScriptArg(new U8(1)) instanceof ScriptTransactionArgumentU8).toBe(true); + expect(deserializeToScriptArg(new U16(2)) instanceof ScriptTransactionArgumentU16).toBe(true); + expect(deserializeToScriptArg(new U32(3)) instanceof ScriptTransactionArgumentU32).toBe(true); + expect(deserializeToScriptArg(new U64(4)) instanceof ScriptTransactionArgumentU64).toBe(true); + expect(deserializeToScriptArg(new U128(5)) instanceof ScriptTransactionArgumentU128).toBe(true); + expect(deserializeToScriptArg(new U256(6)) instanceof ScriptTransactionArgumentU256).toBe(true); + expect(deserializeToScriptArg(new Bool(false)) instanceof ScriptTransactionArgumentBool).toBe(true); + expect( + deserializeToScriptArg(new AccountAddress(AccountAddress.FOUR)) instanceof ScriptTransactionArgumentAddress, + ).toBe(true); + expect(deserializeToScriptArg(MoveVector.U8([1, 2, 3, 4, 5])) instanceof ScriptTransactionArgumentU8Vector).toBe( + true, + ); + }); +}); \ No newline at end of file diff --git a/tests/unit/type_tag.test.ts b/tests/unit/type_tag.test.ts index 36f7347b2..02adbee5f 100644 --- a/tests/unit/type_tag.test.ts +++ b/tests/unit/type_tag.test.ts @@ -72,7 +72,8 @@ describe("TypeTagParser", () => { expect(error).toBeInstanceOf(ParsingError); const typeTagError = error as ParsingError; expect(typeTagError.message).toEqual( - `The given hex string ${typeTag} is a special address not in LONG form, it must be 0x0 to 0xf without padding zeroes.`, + `The given hex string ${typeTag} is a special address not in LONG form, + it must be 0x0 to 0xf without padding zeroes.` ); } From 59a2a4d3926c674077905cd96aff3b75afdee72c Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:13:36 -0700 Subject: [PATCH 02/11] Serialization/deserialization fixed --- src/api/transaction_submission.ts | 1 - src/bcs/serializer.ts | 10 ++++++++++ src/transactions/instances/transactionArgument.ts | 14 ++++++++++---- tests/unit/script_transaction_arguments.test.ts | 4 +--- tests/unit/transaction_builder.test.ts | 7 ++++++- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/api/transaction_submission.ts b/src/api/transaction_submission.ts index edcf8a7d0..c5f7b4682 100644 --- a/src/api/transaction_submission.ts +++ b/src/api/transaction_submission.ts @@ -14,7 +14,6 @@ import { GenerateSingleSignerRawTransactionInput, SingleSignerTransaction, SimulateTransactionData, - GenerateSingleSignerRawTransactionArgs, } from "../transactions/types"; import { UserTransactionResponse, PendingTransactionResponse } from "../types"; import { diff --git a/src/bcs/serializer.ts b/src/bcs/serializer.ts index 4de550bbf..9fb0e250c 100644 --- a/src/bcs/serializer.ts +++ b/src/bcs/serializer.ts @@ -11,6 +11,7 @@ import { MAX_U256_BIG_INT, } from "./consts"; import { AnyNumber, Uint16, Uint32, Uint8 } from "../types"; +import { Hex } from "../core"; // This class is intended to be used as a base class for all serializable types. // It can be used to facilitate composable serialization of a complex type and @@ -28,6 +29,15 @@ export abstract class Serializable { this.serialize(serializer); return serializer.toUint8Array(); } + + /** + * Helper function to get a value's BCS-serialized bytes as a Hex instance. + * @returns a Hex instance with the BCS-serialized bytes loaded into its underlying Uint8Array + */ + bcsToHex() { //: Hex { + // const bcsBytes = this.bcsToBytes(); + // return Hex.fromHexInput({ hexInput: bcsBytes }); + } } export class Serializer { diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index 867dad34b..1343dad3c 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -52,15 +52,21 @@ export class EntryFunctionBytes this.value = new FixedBytes(value); } - // Note that both serialize and serializeForEntryFunction are the same. - // We need them to be equivalent so that when we re-serialize this class as an entry - // function payload's arguments, we don't re-serialize the length prefix. + // Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector, + // we must not serialize the length prefix. + // + // In other words, this class is only used to represent a sequence of bytes that are already + // BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact + // representation. serialize(serializer: Serializer): void { serializer.serialize(this.value); } - // This is necessary to implement the interface, so we can use this class as an EntryFunctionArgument. + // When we serialize these bytes as an entry function argument, we need to + // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic + // byte vector to an `any` type. serializeForEntryFunction(serializer: Serializer): void { + serializer.serializeU32AsUleb128(this.value.value.length); serializer.serialize(this); } /** diff --git a/tests/unit/script_transaction_arguments.test.ts b/tests/unit/script_transaction_arguments.test.ts index 31f76e11c..89d10af03 100644 --- a/tests/unit/script_transaction_arguments.test.ts +++ b/tests/unit/script_transaction_arguments.test.ts @@ -103,9 +103,7 @@ describe("Tests for the script transaction argument class", () => { serializer = new Serializer(); input.serializeForScriptFunction(serializer); const deserializer = new Deserializer(serializer.toUint8Array()); - const asdf = ScriptTransactionArgument.deserialize(deserializer); - console.log(asdf); - return asdf; + return ScriptTransactionArgument.deserialize(deserializer); }; expect(deserializeToScriptArg(new U8(1)) instanceof ScriptTransactionArgumentU8).toBe(true); diff --git a/tests/unit/transaction_builder.test.ts b/tests/unit/transaction_builder.test.ts index 94705f831..e98693188 100644 --- a/tests/unit/transaction_builder.test.ts +++ b/tests/unit/transaction_builder.test.ts @@ -26,6 +26,7 @@ import { SigningScheme } from "../../src/types"; import { SignedTransaction } from "../../src/transactions/instances/signedTransaction"; import { U64 } from "../../src/bcs/serializable/move-primitives"; import { MoveObject } from "../../src/bcs/serializable/move-structs"; +import { Hex } from "../../src/core"; describe("transaction builder", () => { describe("generate transaction payload", () => { @@ -439,7 +440,11 @@ describe("transaction builder", () => { hexInput: "0x5aba8dab1c523be32bd4dafe2cc612f7f8050ce42a3322b60216ef67dc97768c", }), }); - const bob = Account.generate({ scheme: SigningScheme.Ed25519 }); + const bob = Account.fromPrivateKey({ + privateKey: new Ed25519PrivateKey({ + hexInput: "0x5aba8dab1c523be32bd4dafe2cc612f7f8050ce42a3322b60216ef67dc97768c", + }), + }); const payload = generateTransactionPayload({ function: "0x1::aptos_account::transfer", type_arguments: [], From 38368e4640f7c1afcc6fa8d509029e24d32a03f9 Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:17:23 -0700 Subject: [PATCH 03/11] Formatting and rebasing --- src/bcs/serializable/fixed-bytes.ts | 5 +- src/bcs/serializable/move-primitives.ts | 7 +-- src/bcs/serializable/move-structs.ts | 62 +++++-------------- src/bcs/serializer.ts | 3 +- .../instances/scriptTransactionArguments.ts | 2 +- .../instances/transactionArgument.ts | 14 +---- .../instances/transactionPayload.ts | 9 ++- src/transactions/typeTag/typeTag.ts | 8 +-- .../unit/script_transaction_arguments.test.ts | 2 +- tests/unit/type_tag.test.ts | 2 +- 10 files changed, 33 insertions(+), 81 deletions(-) diff --git a/src/bcs/serializable/fixed-bytes.ts b/src/bcs/serializable/fixed-bytes.ts index e6a927629..870839c4f 100644 --- a/src/bcs/serializable/fixed-bytes.ts +++ b/src/bcs/serializable/fixed-bytes.ts @@ -5,10 +5,7 @@ import { Serializable, Serializer } from "../serializer"; import { Deserializer } from "../deserializer"; import { HexInput } from "../../types"; import { Hex } from "../../core"; -import { - EntryFunctionArgument, - TransactionArgument, -} from "../../transactions/instances/transactionArgument"; +import { EntryFunctionArgument, TransactionArgument } from "../../transactions/instances/transactionArgument"; /** * This class exists to represent a contiguous sequence of BCS bytes that when serialized diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index 5a57fd003..4a1f5aec2 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -11,12 +11,7 @@ import { } from "../consts"; import { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; import { Deserializer } from "../deserializer"; -import { - Serializable, - Serializer, - ensureBoolean, - validateNumberInRange, -} from "../serializer"; +import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer"; import { TransactionArgument } from "../../transactions/instances/transactionArgument"; import { ScriptTransactionArgumentBool, diff --git a/src/bcs/serializable/move-structs.ts b/src/bcs/serializable/move-structs.ts index 59093abe3..da0758803 100644 --- a/src/bcs/serializable/move-structs.ts +++ b/src/bcs/serializable/move-structs.ts @@ -6,14 +6,8 @@ import { Deserializable, Deserializer } from "../deserializer"; import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { AnyNumber, HexInput } from "../../types"; import { AccountAddress, Hex } from "../../core"; -import { - EntryFunctionArgument, - TransactionArgument, -} from "../../transactions/instances/transactionArgument"; -import { - ScriptTransactionArgumentAddress, - ScriptTransactionArgumentU8Vector, -} from "../../transactions/instances"; +import { EntryFunctionArgument, TransactionArgument } from "../../transactions/instances/transactionArgument"; +import { ScriptTransactionArgumentAddress, ScriptTransactionArgumentU8Vector } from "../../transactions/instances"; /** * This class is the Aptos Typescript SDK representation of a Move `vector`, @@ -58,10 +52,7 @@ import { * values: an Array of values where T is a class that implements Serializable * @returns a `MoveVector` with the values `values` */ -export class MoveVector - extends Serializable - implements TransactionArgument -{ +export class MoveVector extends Serializable implements TransactionArgument { public values: Array; constructor(values: Array) { @@ -219,10 +210,7 @@ export class MoveVector * @returns a MoveVector of the corresponding class T * * */ - static deserialize( - deserializer: Deserializer, - cls: Deserializable - ): MoveVector { + static deserialize(deserializer: Deserializer, cls: Deserializable): MoveVector { const length = deserializer.deserializeUleb128AsU32(); const values = new Array(); for (let i = 0; i < length; i += 1) { @@ -260,10 +248,7 @@ export class MoveString extends Serializable implements TransactionArgument { } } -export class MoveOption - extends Serializable - implements EntryFunctionArgument -{ +export class MoveOption extends Serializable implements EntryFunctionArgument { private vec: MoveVector; public readonly value?: T; @@ -331,9 +316,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U8(value?: number | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U8(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U8(value) : undefined); } /** @@ -348,9 +331,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U16(value?: number | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U16(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U16(value) : undefined); } /** @@ -365,9 +346,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U32(value?: number | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U32(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U32(value) : undefined); } /** @@ -382,9 +361,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U64(value?: AnyNumber | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U64(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U64(value) : undefined); } /** @@ -399,9 +376,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U128(value?: AnyNumber | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U128(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U128(value) : undefined); } /** @@ -416,9 +391,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static U256(value?: AnyNumber | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new U256(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new U256(value) : undefined); } /** @@ -433,9 +406,7 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static Bool(value?: boolean | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new Bool(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new Bool(value) : undefined); } /** @@ -451,15 +422,10 @@ export class MoveOption * @returns a MoveOption with an inner value `value` */ static MoveString(value?: string | null): MoveOption { - return new MoveOption( - value !== null && value !== undefined ? new MoveString(value) : undefined - ); + return new MoveOption(value !== null && value !== undefined ? new MoveString(value) : undefined); } - static deserialize( - deserializer: Deserializer, - cls: Deserializable - ): MoveOption { + static deserialize(deserializer: Deserializer, cls: Deserializable): MoveOption { const vector = MoveVector.deserialize(deserializer, cls); return new MoveOption(vector.values[0]); } diff --git a/src/bcs/serializer.ts b/src/bcs/serializer.ts index 9fb0e250c..8937e8bb4 100644 --- a/src/bcs/serializer.ts +++ b/src/bcs/serializer.ts @@ -34,7 +34,8 @@ export abstract class Serializable { * Helper function to get a value's BCS-serialized bytes as a Hex instance. * @returns a Hex instance with the BCS-serialized bytes loaded into its underlying Uint8Array */ - bcsToHex() { //: Hex { + bcsToHex() { + //: Hex { // const bcsBytes = this.bcsToBytes(); // return Hex.fromHexInput({ hexInput: bcsBytes }); } diff --git a/src/transactions/instances/scriptTransactionArguments.ts b/src/transactions/instances/scriptTransactionArguments.ts index 4d1762e28..7eac21dfc 100644 --- a/src/transactions/instances/scriptTransactionArguments.ts +++ b/src/transactions/instances/scriptTransactionArguments.ts @@ -259,4 +259,4 @@ export class ScriptTransactionArgumentBool extends ScriptTransactionArgument imp const value = deserializer.deserializeBool(); return new ScriptTransactionArgumentBool(value); } -} \ No newline at end of file +} diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index 1343dad3c..59d664a3d 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -2,9 +2,7 @@ import { Serializer, Deserializer, Serializable } from "../../bcs"; import { HexInput } from "../../types"; import { FixedBytes } from "../../bcs/serializable/fixed-bytes"; -export interface TransactionArgument - extends EntryFunctionArgument, - ScriptFunctionArgument {} +export interface TransactionArgument extends EntryFunctionArgument, ScriptFunctionArgument {} export interface EntryFunctionArgument { /** @@ -41,10 +39,7 @@ export interface ScriptFunctionArgument { * of the argument beforehand, and use the appropriate class to deserialize the bytes within * an instance of this class. */ -export class EntryFunctionBytes - extends Serializable - implements EntryFunctionArgument -{ +export class EntryFunctionBytes extends Serializable implements EntryFunctionArgument { public readonly value: FixedBytes; private constructor(value: HexInput) { @@ -77,10 +72,7 @@ export class EntryFunctionBytes * @param length the length of the bytes to deserialize * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument */ - static deserialize( - deserializer: Deserializer, - length: number - ): EntryFunctionBytes { + static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes { const fixedBytes = FixedBytes.deserialize(deserializer, length); return new EntryFunctionBytes(fixedBytes.value); } diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index c3d93a46a..63f723422 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -138,7 +138,12 @@ export class EntryFunction { * public entry fun transfer(from: &signer, to: address, amount: u64) * ``` */ - constructor(module_name: ModuleId, function_name: Identifier, type_args: Array, args: Array) { + constructor( + module_name: ModuleId, + function_name: Identifier, + type_args: Array, + args: Array, + ) { this.module_name = module_name; this.function_name = function_name; this.type_args = type_args; @@ -192,7 +197,7 @@ export class EntryFunction { * NOTE: When you deserialize an EntryFunction payload with this method, the entry function * arguments are populated into the deserialized instance as type-agnostic, raw fixed bytes * in the form of the EntryFunctionBytes class. - * + * * In order to correctly deserialize these arguments as their actual type representations, you * must know the types of the arguments beforehand and deserialize them yourself individually. * diff --git a/src/transactions/typeTag/typeTag.ts b/src/transactions/typeTag/typeTag.ts index d845e6c02..a4b5dbe62 100644 --- a/src/transactions/typeTag/typeTag.ts +++ b/src/transactions/typeTag/typeTag.ts @@ -235,12 +235,8 @@ export class StructTag extends Serializable { } } -export const stringStructTag = () => new StructTag( - AccountAddress.ONE, - new Identifier("string"), - new Identifier("String"), - [], -); +export const stringStructTag = () => + new StructTag(AccountAddress.ONE, new Identifier("string"), new Identifier("String"), []); export function optionStructTag(typeArg: TypeTag): StructTag { return new StructTag(AccountAddress.ONE, new Identifier("option"), new Identifier("Option"), [typeArg]); diff --git a/tests/unit/script_transaction_arguments.test.ts b/tests/unit/script_transaction_arguments.test.ts index 89d10af03..2f808a2dd 100644 --- a/tests/unit/script_transaction_arguments.test.ts +++ b/tests/unit/script_transaction_arguments.test.ts @@ -120,4 +120,4 @@ describe("Tests for the script transaction argument class", () => { true, ); }); -}); \ No newline at end of file +}); diff --git a/tests/unit/type_tag.test.ts b/tests/unit/type_tag.test.ts index 02adbee5f..80ace2220 100644 --- a/tests/unit/type_tag.test.ts +++ b/tests/unit/type_tag.test.ts @@ -73,7 +73,7 @@ describe("TypeTagParser", () => { const typeTagError = error as ParsingError; expect(typeTagError.message).toEqual( `The given hex string ${typeTag} is a special address not in LONG form, - it must be 0x0 to 0xf without padding zeroes.` + it must be 0x0 to 0xf without padding zeroes.`, ); } From ecc25f708ac7315450ebb75f8383de0f66c84edd Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:19:31 -0700 Subject: [PATCH 04/11] Reformat typetag error message --- tests/unit/type_tag.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/type_tag.test.ts b/tests/unit/type_tag.test.ts index 80ace2220..36f7347b2 100644 --- a/tests/unit/type_tag.test.ts +++ b/tests/unit/type_tag.test.ts @@ -72,8 +72,7 @@ describe("TypeTagParser", () => { expect(error).toBeInstanceOf(ParsingError); const typeTagError = error as ParsingError; expect(typeTagError.message).toEqual( - `The given hex string ${typeTag} is a special address not in LONG form, - it must be 0x0 to 0xf without padding zeroes.`, + `The given hex string ${typeTag} is a special address not in LONG form, it must be 0x0 to 0xf without padding zeroes.`, ); } From acb9c86b96c539e58b95806ec6bcca1f2a3ca90d Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:29:51 -0700 Subject: [PATCH 05/11] The beginning of the formatting changes --- src/api/transaction_submission.ts | 3 -- src/bcs/serializable/fixed-bytes.ts | 38 +++++++++++-------- src/bcs/serializable/move-primitives.ts | 2 +- src/bcs/serializable/move-structs.ts | 8 +++- .../instances/transactionArgument.ts | 3 +- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/api/transaction_submission.ts b/src/api/transaction_submission.ts index c5f7b4682..55c08c1df 100644 --- a/src/api/transaction_submission.ts +++ b/src/api/transaction_submission.ts @@ -1,6 +1,3 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - import { AptosConfig } from "./aptos_config"; import { Account } from "../core/account"; import { AccountAuthenticator } from "../transactions/authenticator/account"; diff --git a/src/bcs/serializable/fixed-bytes.ts b/src/bcs/serializable/fixed-bytes.ts index 870839c4f..84644535e 100644 --- a/src/bcs/serializable/fixed-bytes.ts +++ b/src/bcs/serializable/fixed-bytes.ts @@ -4,15 +4,31 @@ import { Serializable, Serializer } from "../serializer"; import { Deserializer } from "../deserializer"; import { HexInput } from "../../types"; -import { Hex } from "../../core"; -import { EntryFunctionArgument, TransactionArgument } from "../../transactions/instances/transactionArgument"; +import { Hex } from "../../core/hex"; +import { TransactionArgument } from "../../transactions/instances/transactionArgument"; /** - * This class exists to represent a contiguous sequence of BCS bytes that when serialized - * do *not* prepend the length of the byte sequence at the beginning. + * This class exists to represent a contiguous sequence of already serialized BCS-bytes. * - * The main time to use this class is when you are passing around already BCS-serialized bytes - * that do not need to undergo another round of BCS serialization. + * It differs from most other Serializable classes in that its internal byte buffer is serialized to BCS + * bytes exactly as-is, without prepending the length of the bytes. + * + * If you want to write your own serialization function and pass the bytes as a transaction argument, + * you should use this class. + * + * This class is also more generally used to represent type-agnostic BCS bytes as a vector. + * + * An example of this is the bytes resulting from entry function arguments that have been serialized + * for an entry function. + * + * @example + * const yourCustomSerializedBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + * const fixedBytes = new FixedBytes(yourCustomSerializedBytes); + * const payload = generateTransactionPayload({ + * function: "0xbeefcafe::your_module::your_function_that_requires_custom_serialization", + * type_arguments: [], + * arguments: [yourCustomBytes], + * }); * * For example, if you store each of the 32 bytes for an address as a U8 in a MoveVector, when you * serialize that MoveVector, it will be serialized to 33 bytes. If you solely want to pass around @@ -21,15 +37,7 @@ import { EntryFunctionArgument, TransactionArgument } from "../../transactions/i * * @params value: HexInput representing a sequence of Uint8 bytes * @returns a Serializable FixedBytes instance, which when serialized, does not prepend the length of the bytes - * @example - * const address = AccountAddress.ONE; - * const bytes = address.bcsToBytes(); - * // bytes is the Move serialized version of an address - * // it has a fixed length, meaning it doesn't have a length at the beginning. - * const fixedBytes = new FixedBytes(bytes); - * // or, say, deserializing it from a sequence of bytes and you *do* know the length - * const fixedBytes = FixedBytes.deserialize(deserializer, 32); - * @see EntryFunction + * @see EntryFunctionBytes */ export class FixedBytes extends Serializable implements TransactionArgument { public value: Uint8Array; diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index 4a1f5aec2..960da9300 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -21,7 +21,7 @@ import { ScriptTransactionArgumentU32, ScriptTransactionArgumentU64, ScriptTransactionArgumentU8, -} from "../../transactions/instances"; +} from "../../transactions/instances/scriptTransactionArguments"; export class Bool extends Serializable implements TransactionArgument { public readonly value: boolean; diff --git a/src/bcs/serializable/move-structs.ts b/src/bcs/serializable/move-structs.ts index da0758803..2ed0b812f 100644 --- a/src/bcs/serializable/move-structs.ts +++ b/src/bcs/serializable/move-structs.ts @@ -5,9 +5,13 @@ import { Serializable, Serializer } from "../serializer"; import { Deserializable, Deserializer } from "../deserializer"; import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { AnyNumber, HexInput } from "../../types"; -import { AccountAddress, Hex } from "../../core"; +import { Hex } from "../../core/hex"; +import { AccountAddress } from "../../core/account_address"; import { EntryFunctionArgument, TransactionArgument } from "../../transactions/instances/transactionArgument"; -import { ScriptTransactionArgumentAddress, ScriptTransactionArgumentU8Vector } from "../../transactions/instances"; +import { + ScriptTransactionArgumentAddress, + ScriptTransactionArgumentU8Vector, +} from "../../transactions/instances/scriptTransactionArguments"; /** * This class is the Aptos Typescript SDK representation of a Move `vector`, diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index 59d664a3d..91a7b36bf 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -12,7 +12,6 @@ export interface EntryFunctionArgument { /** * Serialize an argument as a type-agnostic, fixed byte sequence. The byte sequence contains * the number of the following bytes followed by the BCS-serialized bytes for a typed argument. - * @example asfasdf */ serializeForEntryFunction(serializer: Serializer): void; } @@ -60,6 +59,8 @@ export class EntryFunctionBytes extends Serializable implements EntryFunctionArg // When we serialize these bytes as an entry function argument, we need to // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic // byte vector to an `any` type. + // NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this + // class and FixedBytes. serializeForEntryFunction(serializer: Serializer): void { serializer.serializeU32AsUleb128(this.value.value.length); serializer.serialize(this); From f192d6a94161c349443723cd8456c70987ddf5e3 Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:19:52 -0700 Subject: [PATCH 06/11] Trying to fix dependency cycles... --- src/api/account.ts | 4 +- src/api/aptos_config.ts | 2 +- src/api/faucet.ts | 2 +- src/bcs/deserializer.ts | 2 +- src/bcs/index.ts | 7 ++- src/bcs/serializable/entry-function-bytes.ts | 63 +++++++++++++++++++ src/bcs/serializable/fixed-bytes.ts | 2 +- src/bcs/serializable/move-primitives.ts | 2 +- src/bcs/serializable/move-structs.ts | 2 +- src/bcs/serializer.ts | 9 ++- src/core/account_address.ts | 3 +- src/internal/general.ts | 4 +- src/internal/transaction.ts | 10 +-- src/transactions/instances/chainId.ts | 6 +- src/transactions/instances/identifier.ts | 3 +- src/transactions/instances/index.ts | 1 + src/transactions/instances/moduleId.ts | 6 +- src/transactions/instances/rawTransaction.ts | 3 +- .../instances/scriptTransactionArguments.ts | 7 ++- .../instances/signedTransaction.ts | 3 +- .../instances/transactionArgument.ts | 55 +--------------- .../instances/transactionPayload.ts | 8 ++- .../transaction_builder.ts | 2 +- src/transactions/typeTag/typeTag.ts | 3 +- src/transactions/types.ts | 4 +- 25 files changed, 120 insertions(+), 93 deletions(-) create mode 100644 src/bcs/serializable/entry-function-bytes.ts diff --git a/src/api/account.ts b/src/api/account.ts index 7adba8977..bc6b963f4 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -1,8 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { AptosConfig } from "./aptos_config"; -import { +import type { AptosConfig } from "./aptos_config"; +import type { AccountData, LedgerVersion, MoveModuleBytecode, diff --git a/src/api/aptos_config.ts b/src/api/aptos_config.ts index 04e67f52a..49b48d906 100644 --- a/src/api/aptos_config.ts +++ b/src/api/aptos_config.ts @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { AptosSettings, ClientConfig } from "../types"; +import type { AptosSettings, ClientConfig } from "../types"; import { NetworkToNodeAPI, NetworkToFaucetAPI, NetworkToIndexerAPI, Network } from "../utils/apiEndpoints"; import { AptosApiType, DEFAULT_NETWORK } from "../utils/const"; diff --git a/src/api/faucet.ts b/src/api/faucet.ts index 17de12cf0..3312c3d2a 100644 --- a/src/api/faucet.ts +++ b/src/api/faucet.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { fundAccount } from "../internal/faucet"; -import { HexInput } from "../types"; +import type { HexInput } from "../types"; import { AptosConfig } from "./aptos_config"; /** diff --git a/src/bcs/deserializer.ts b/src/bcs/deserializer.ts index 1132be6f1..895748bdc 100644 --- a/src/bcs/deserializer.ts +++ b/src/bcs/deserializer.ts @@ -3,7 +3,7 @@ /* eslint-disable no-bitwise */ import { MAX_U32_NUMBER } from "./consts"; -import { Uint128, Uint16, Uint256, Uint32, Uint64, Uint8 } from "../types"; +import type { Uint128, Uint16, Uint256, Uint32, Uint64, Uint8 } from "../types"; /** * This interface exists to define Deserializable inputs for functions that diff --git a/src/bcs/index.ts b/src/bcs/index.ts index 7c43334e0..31c304292 100644 --- a/src/bcs/index.ts +++ b/src/bcs/index.ts @@ -1,5 +1,10 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -export * from "./serializer"; +export * from "./serializable/fixed-bytes"; +export * from "./serializable/move-primitives"; +export * from "./serializable/move-structs"; + +export * from "./serializable/entry-function-bytes"; export * from "./deserializer"; +export * from "./serializer"; \ No newline at end of file diff --git a/src/bcs/serializable/entry-function-bytes.ts b/src/bcs/serializable/entry-function-bytes.ts new file mode 100644 index 000000000..441189f44 --- /dev/null +++ b/src/bcs/serializable/entry-function-bytes.ts @@ -0,0 +1,63 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +import type { Serializer } from "../serializer"; +import type { Deserializer } from "../deserializer"; +import { FixedBytes } from "./fixed-bytes"; +import { Serializable } from "../serializer"; +import { EntryFunctionArgument } from "../../transactions/instances/transactionArgument"; +import type { HexInput } from "../../types"; + +/** + * This class exists solely to represent a sequence of fixed bytes as a serialized entry function, because + * serializing an entry function appends a prefix that's *only* used for entry function arguments. + * + * NOTE: Attempting to use this class for a serialized script function will result in erroneous + * and unexpected behavior. + * + * If you wish to convert this class back to a TransactionArgument, you must know the type + * of the argument beforehand, and use the appropriate class to deserialize the bytes within + * an instance of this class. + */ +export class EntryFunctionBytes extends Serializable implements EntryFunctionArgument { + public readonly value: FixedBytes; + + private constructor(value: HexInput) { + super(); + this.value = new FixedBytes(value); + } + + // Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector, + // we must not serialize the length prefix. + // + // In other words, this class is only used to represent a sequence of bytes that are already + // BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact + // representation. + serialize(serializer: Serializer): void { + serializer.serialize(this.value); + } + + // When we serialize these bytes as an entry function argument, we need to + // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic + // byte vector to an `any` type. + // NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this + // class and FixedBytes. + serializeForEntryFunction(serializer: Serializer): void { + serializer.serializeU32AsUleb128(this.value.value.length); + serializer.serialize(this); + } + + /** + * The only way to create an instance of this class is to use this static method. + * + * This function should only be used when deserializing a sequence of EntryFunctionPayload arguments. + * @param deserializer the deserializer instance with the buffered bytes + * @param length the length of the bytes to deserialize + * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument + */ + static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes { + const fixedBytes = FixedBytes.deserialize(deserializer, length); + return new EntryFunctionBytes(fixedBytes.value); + } + } + \ No newline at end of file diff --git a/src/bcs/serializable/fixed-bytes.ts b/src/bcs/serializable/fixed-bytes.ts index 84644535e..e62449e72 100644 --- a/src/bcs/serializable/fixed-bytes.ts +++ b/src/bcs/serializable/fixed-bytes.ts @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Serializable, Serializer } from "../serializer"; +import { Serializer, Serializable } from "../serializer"; import { Deserializer } from "../deserializer"; import { HexInput } from "../../types"; import { Hex } from "../../core/hex"; diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index 960da9300..ecdba38e4 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -9,7 +9,6 @@ import { MAX_U8_NUMBER, MAX_U256_BIG_INT, } from "../consts"; -import { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; import { Deserializer } from "../deserializer"; import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer"; import { TransactionArgument } from "../../transactions/instances/transactionArgument"; @@ -22,6 +21,7 @@ import { ScriptTransactionArgumentU64, ScriptTransactionArgumentU8, } from "../../transactions/instances/scriptTransactionArguments"; +import type { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; export class Bool extends Serializable implements TransactionArgument { public readonly value: boolean; diff --git a/src/bcs/serializable/move-structs.ts b/src/bcs/serializable/move-structs.ts index 2ed0b812f..47b38cfd0 100644 --- a/src/bcs/serializable/move-structs.ts +++ b/src/bcs/serializable/move-structs.ts @@ -1,9 +1,9 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { Serializable, Serializer } from "../serializer"; import { Deserializable, Deserializer } from "../deserializer"; -import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { AnyNumber, HexInput } from "../../types"; import { Hex } from "../../core/hex"; import { AccountAddress } from "../../core/account_address"; diff --git a/src/bcs/serializer.ts b/src/bcs/serializer.ts index 8937e8bb4..9ede18444 100644 --- a/src/bcs/serializer.ts +++ b/src/bcs/serializer.ts @@ -11,7 +11,7 @@ import { MAX_U256_BIG_INT, } from "./consts"; import { AnyNumber, Uint16, Uint32, Uint8 } from "../types"; -import { Hex } from "../core"; +import { Hex } from "../core/hex"; // This class is intended to be used as a base class for all serializable types. // It can be used to facilitate composable serialization of a complex type and @@ -34,10 +34,9 @@ export abstract class Serializable { * Helper function to get a value's BCS-serialized bytes as a Hex instance. * @returns a Hex instance with the BCS-serialized bytes loaded into its underlying Uint8Array */ - bcsToHex() { - //: Hex { - // const bcsBytes = this.bcsToBytes(); - // return Hex.fromHexInput({ hexInput: bcsBytes }); + bcsToHex(): Hex { + const bcsBytes = this.bcsToBytes(); + return Hex.fromHexInput({ hexInput: bcsBytes }); } } diff --git a/src/core/account_address.ts b/src/core/account_address.ts index 170fb698d..5da40f556 100644 --- a/src/core/account_address.ts +++ b/src/core/account_address.ts @@ -4,7 +4,8 @@ import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; import { HexInput } from "../types"; import { ParsingError, ParsingResult } from "./common"; -import { Deserializer, Serializable, Serializer } from "../bcs"; +import { Serializable, Serializer } from "../bcs/serializer"; +import { Deserializer } from "../bcs/deserializer"; import { TransactionArgument } from "../transactions/instances/transactionArgument"; import { ScriptTransactionArgumentAddress } from "../transactions/instances"; diff --git a/src/internal/general.ts b/src/internal/general.ts index 7c3d63f89..3de761eb0 100644 --- a/src/internal/general.ts +++ b/src/internal/general.ts @@ -8,9 +8,9 @@ * general namespace and without having a dependency cycle error. */ -import { AptosConfig } from "../api/aptos_config"; +import type { AptosConfig } from "../api/aptos_config"; import { getAptosFullNode, postAptosFullNode, postAptosIndexer } from "../client"; -import { Block, GraphqlQuery, LedgerInfo, LedgerVersion, MoveValue, TableItemRequest, ViewRequest } from "../types"; +import type { Block, GraphqlQuery, LedgerInfo, LedgerVersion, MoveValue, TableItemRequest, ViewRequest } from "../types"; export async function getLedgerInfo(args: { aptosConfig: AptosConfig }): Promise { const { aptosConfig } = args; diff --git a/src/internal/transaction.ts b/src/internal/transaction.ts index bbdf7a668..a43b2afde 100644 --- a/src/internal/transaction.ts +++ b/src/internal/transaction.ts @@ -11,12 +11,12 @@ import { AptosConfig } from "../api/aptos_config"; import { AptosApiError, getAptosFullNode, paginateWithCursor } from "../client"; import { - AnyNumber, - GasEstimation, - HexInput, - PaginationArgs, - TransactionResponse, TransactionResponseType, + type AnyNumber, + type GasEstimation, + type HexInput, + type PaginationArgs, + type TransactionResponse, } from "../types"; import { DEFAULT_TXN_TIMEOUT_SEC } from "../utils/const"; import { sleep } from "../utils/helpers"; diff --git a/src/transactions/instances/chainId.ts b/src/transactions/instances/chainId.ts index 85e580f14..035e964a5 100644 --- a/src/transactions/instances/chainId.ts +++ b/src/transactions/instances/chainId.ts @@ -1,15 +1,17 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Serializer, Deserializer } from "../../bcs"; +import { Serializer, Serializable } from "../../bcs/serializer"; +import { Deserializer } from "../../bcs/deserializer"; /** * Representation of a ChainId that can serialized and deserialized */ -export class ChainId { +export class ChainId extends Serializable { public readonly chainId: number; constructor(chainId: number) { + super(); this.chainId = chainId; } diff --git a/src/transactions/instances/identifier.ts b/src/transactions/instances/identifier.ts index 5801d0450..5d8a88321 100644 --- a/src/transactions/instances/identifier.ts +++ b/src/transactions/instances/identifier.ts @@ -1,7 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializable, Serializer, } from "../../bcs/serializer"; /** * Representation of an Identifier that can serialized and deserialized. diff --git a/src/transactions/instances/index.ts b/src/transactions/instances/index.ts index 9c0ef3984..1ef744779 100644 --- a/src/transactions/instances/index.ts +++ b/src/transactions/instances/index.ts @@ -7,3 +7,4 @@ export * from "./scriptTransactionArguments"; export * from "./transactionPayload"; export * from "./moduleId"; export * from "./identifier"; +export * from "./transactionArgument"; diff --git a/src/transactions/instances/moduleId.ts b/src/transactions/instances/moduleId.ts index 3d973db49..683f2c82a 100644 --- a/src/transactions/instances/moduleId.ts +++ b/src/transactions/instances/moduleId.ts @@ -1,7 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Serializer, Deserializer } from "../../bcs"; +import { Serializable, Serializer } from "../../bcs/serializer"; +import { Deserializer } from "../../bcs/deserializer"; import { AccountAddress } from "../../core"; import { Identifier } from "./identifier"; @@ -9,7 +10,7 @@ import { Identifier } from "./identifier"; * Representation of a ModuleId that can serialized and deserialized * ModuleId means the module address (e.g "0x1") and the module name (e.g "coin") */ -export class ModuleId { +export class ModuleId extends Serializable { public readonly address: AccountAddress; public readonly name: Identifier; @@ -20,6 +21,7 @@ export class ModuleId { * @param name The module name under the "address". e.g "coin" */ constructor(address: AccountAddress, name: Identifier) { + super(); this.address = address; this.name = name; } diff --git a/src/transactions/instances/rawTransaction.ts b/src/transactions/instances/rawTransaction.ts index ddbd95920..d6b0b27cd 100644 --- a/src/transactions/instances/rawTransaction.ts +++ b/src/transactions/instances/rawTransaction.ts @@ -3,7 +3,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Deserializer, Serializer, Serializable } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializable, Serializer, } from "../../bcs/serializer"; import { AccountAddress } from "../../core"; import { TransactionVariants } from "../../types"; import { ChainId } from "./chainId"; diff --git a/src/transactions/instances/scriptTransactionArguments.ts b/src/transactions/instances/scriptTransactionArguments.ts index 7eac21dfc..7aac2a4ee 100644 --- a/src/transactions/instances/scriptTransactionArguments.ts +++ b/src/transactions/instances/scriptTransactionArguments.ts @@ -1,12 +1,13 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializer, Serializable } from "../../bcs/serializer"; import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; import { MoveVector } from "../../bcs/serializable/move-structs"; -import { AccountAddress } from "../../core"; +import { AccountAddress } from "../../core/account_address"; import { AnyNumber, HexInput, ScriptTransactionArgumentVariants, Uint16, Uint32, Uint8 } from "../../types"; -import { ScriptFunctionArgument } from "./transactionArgument"; +import type { ScriptFunctionArgument } from "./transactionArgument"; /** * Representation of a Script Transaction Argument that can be serialized and deserialized diff --git a/src/transactions/instances/signedTransaction.ts b/src/transactions/instances/signedTransaction.ts index 150df2937..8d4bb602e 100644 --- a/src/transactions/instances/signedTransaction.ts +++ b/src/transactions/instances/signedTransaction.ts @@ -3,7 +3,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializable, Serializer } from "../../bcs/serializer"; import { TransactionAuthenticator } from "../authenticator/transaction"; import { RawTransaction } from "./rawTransaction"; diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index 91a7b36bf..97498e94b 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -1,6 +1,4 @@ -import { Serializer, Deserializer, Serializable } from "../../bcs"; -import { HexInput } from "../../types"; -import { FixedBytes } from "../../bcs/serializable/fixed-bytes"; +import type { Serializer } from "../../bcs/serializer"; export interface TransactionArgument extends EntryFunctionArgument, ScriptFunctionArgument {} @@ -27,54 +25,3 @@ export interface ScriptFunctionArgument { */ serializeForScriptFunction(serializer: Serializer): void; } -/** - * This class exists solely to represent a sequence of fixed bytes as a serialized entry function, because - * serializing an entry function appends a prefix that's *only* used for entry function arguments. - * - * NOTE: Attempting to use this class for a serialized script function will result in erroneous - * and unexpected behavior. - * - * If you wish to convert this class back to a TransactionArgument, you must know the type - * of the argument beforehand, and use the appropriate class to deserialize the bytes within - * an instance of this class. - */ -export class EntryFunctionBytes extends Serializable implements EntryFunctionArgument { - public readonly value: FixedBytes; - - private constructor(value: HexInput) { - super(); - this.value = new FixedBytes(value); - } - - // Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector, - // we must not serialize the length prefix. - // - // In other words, this class is only used to represent a sequence of bytes that are already - // BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact - // representation. - serialize(serializer: Serializer): void { - serializer.serialize(this.value); - } - - // When we serialize these bytes as an entry function argument, we need to - // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic - // byte vector to an `any` type. - // NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this - // class and FixedBytes. - serializeForEntryFunction(serializer: Serializer): void { - serializer.serializeU32AsUleb128(this.value.value.length); - serializer.serialize(this); - } - /** - * The only way to create an instance of this class is to use this static method. - * - * This function should only be used when deserializing a sequence of EntryFunctionPayload arguments. - * @param deserializer the deserializer instance with the buffered bytes - * @param length the length of the bytes to deserialize - * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument - */ - static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes { - const fixedBytes = FixedBytes.deserialize(deserializer, length); - return new EntryFunctionBytes(fixedBytes.value); - } -} diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index 63f723422..a4e94f7fc 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -3,14 +3,16 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Serializer, Deserializer, Serializable } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializable, Serializer, } from "../../bcs/serializer"; import { AccountAddress } from "../../core"; import { Identifier } from "./identifier"; -import { ScriptTransactionArgument } from "./scriptTransactionArguments"; import { ModuleId } from "./moduleId"; import { TransactionPayloadVariants } from "../../types"; import { TypeTag } from "../typeTag/typeTag"; -import { EntryFunctionArgument, EntryFunctionBytes, ScriptFunctionArgument } from "./transactionArgument"; +import type { EntryFunctionArgument, ScriptFunctionArgument } from "./transactionArgument"; +import { ScriptTransactionArgument } from "./scriptTransactionArguments"; +import { EntryFunctionBytes } from "../../bcs/serializable/entry-function-bytes"; /** * Representation of the supported Transaction Payload diff --git a/src/transactions/transaction_builder/transaction_builder.ts b/src/transactions/transaction_builder/transaction_builder.ts index 6e0d268cc..86623becd 100644 --- a/src/transactions/transaction_builder/transaction_builder.ts +++ b/src/transactions/transaction_builder/transaction_builder.ts @@ -9,7 +9,7 @@ import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; import { hexToBytes } from "@noble/hashes/utils"; import { AptosConfig } from "../../api/aptos_config"; -import { Deserializer } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; import { AccountAddress } from "../../core"; import { Account } from "../../core/account"; import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; diff --git a/src/transactions/typeTag/typeTag.ts b/src/transactions/typeTag/typeTag.ts index a4b5dbe62..a68c93ccb 100644 --- a/src/transactions/typeTag/typeTag.ts +++ b/src/transactions/typeTag/typeTag.ts @@ -5,7 +5,8 @@ /* eslint-disable class-methods-use-this */ /* eslint-disable max-classes-per-file */ import { AccountAddress } from "../../core"; -import { Deserializer, Serializable, Serializer } from "../../bcs"; +import { Deserializer } from "../../bcs/deserializer"; +import { Serializable, Serializer, } from "../../bcs/serializer"; import { Identifier } from "../instances/identifier"; import { TypeTagVariants } from "../../types"; diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 35ef0a5e0..76b5bd412 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -13,8 +13,8 @@ import { TransactionPayloadMultisig, TransactionPayloadScript, } from "./instances"; -import { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; -import { TypeTag } from "./typeTag/typeTag"; +import type { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; +import type { TypeTag } from "./typeTag/typeTag"; /** * Type that holds all raw transaction instances Aptos SDK supports From b1553e427e8919d828d55385a3e32ee88144cca9 Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Wed, 11 Oct 2023 00:45:15 -0700 Subject: [PATCH 07/11] Removing the ScriptTransactionArgument class and all the circular dependencies surrounding it --- src/bcs/index.ts | 2 +- src/bcs/serializable/entry-function-bytes.ts | 79 +++--- src/bcs/serializable/move-primitives.ts | 38 +-- src/bcs/serializable/move-structs.ts | 23 +- src/core/account_address.ts | 10 +- src/internal/general.ts | 10 +- src/transactions/instances/identifier.ts | 2 +- src/transactions/instances/index.ts | 1 - src/transactions/instances/rawTransaction.ts | 2 +- .../instances/scriptTransactionArguments.ts | 263 ------------------ .../instances/transactionArgument.ts | 7 + .../instances/transactionPayload.ts | 49 +++- src/transactions/typeTag/typeTag.ts | 2 +- .../unit/script_transaction_arguments.test.ts | 122 ++++---- tests/unit/transaction_builder.test.ts | 63 ++--- 15 files changed, 209 insertions(+), 464 deletions(-) delete mode 100644 src/transactions/instances/scriptTransactionArguments.ts diff --git a/src/bcs/index.ts b/src/bcs/index.ts index 31c304292..1e683e475 100644 --- a/src/bcs/index.ts +++ b/src/bcs/index.ts @@ -7,4 +7,4 @@ export * from "./serializable/move-structs"; export * from "./serializable/entry-function-bytes"; export * from "./deserializer"; -export * from "./serializer"; \ No newline at end of file +export * from "./serializer"; diff --git a/src/bcs/serializable/entry-function-bytes.ts b/src/bcs/serializable/entry-function-bytes.ts index 441189f44..79cc4d0af 100644 --- a/src/bcs/serializable/entry-function-bytes.ts +++ b/src/bcs/serializable/entry-function-bytes.ts @@ -20,44 +20,43 @@ import type { HexInput } from "../../types"; * an instance of this class. */ export class EntryFunctionBytes extends Serializable implements EntryFunctionArgument { - public readonly value: FixedBytes; - - private constructor(value: HexInput) { - super(); - this.value = new FixedBytes(value); - } - - // Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector, - // we must not serialize the length prefix. - // - // In other words, this class is only used to represent a sequence of bytes that are already - // BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact - // representation. - serialize(serializer: Serializer): void { - serializer.serialize(this.value); - } - - // When we serialize these bytes as an entry function argument, we need to - // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic - // byte vector to an `any` type. - // NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this - // class and FixedBytes. - serializeForEntryFunction(serializer: Serializer): void { - serializer.serializeU32AsUleb128(this.value.value.length); - serializer.serialize(this); - } - - /** - * The only way to create an instance of this class is to use this static method. - * - * This function should only be used when deserializing a sequence of EntryFunctionPayload arguments. - * @param deserializer the deserializer instance with the buffered bytes - * @param length the length of the bytes to deserialize - * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument - */ - static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes { - const fixedBytes = FixedBytes.deserialize(deserializer, length); - return new EntryFunctionBytes(fixedBytes.value); - } + public readonly value: FixedBytes; + + private constructor(value: HexInput) { + super(); + this.value = new FixedBytes(value); + } + + // Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector, + // we must not serialize the length prefix. + // + // In other words, this class is only used to represent a sequence of bytes that are already + // BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact + // representation. + serialize(serializer: Serializer): void { + serializer.serialize(this.value); + } + + // When we serialize these bytes as an entry function argument, we need to + // serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic + // byte vector to an `any` type. + // NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this + // class and FixedBytes. + serializeForEntryFunction(serializer: Serializer): void { + serializer.serializeU32AsUleb128(this.value.value.length); + serializer.serialize(this); + } + + /** + * The only way to create an instance of this class is to use this static method. + * + * This function should only be used when deserializing a sequence of EntryFunctionPayload arguments. + * @param deserializer the deserializer instance with the buffered bytes + * @param length the length of the bytes to deserialize + * @returns an instance of this class, which will now only be usable as an EntryFunctionArgument + */ + static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes { + const fixedBytes = FixedBytes.deserialize(deserializer, length); + return new EntryFunctionBytes(fixedBytes.value); } - \ No newline at end of file +} diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index ecdba38e4..cc358cd6e 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -12,16 +12,8 @@ import { import { Deserializer } from "../deserializer"; import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer"; import { TransactionArgument } from "../../transactions/instances/transactionArgument"; -import { - ScriptTransactionArgumentBool, - ScriptTransactionArgumentU128, - ScriptTransactionArgumentU16, - ScriptTransactionArgumentU256, - ScriptTransactionArgumentU32, - ScriptTransactionArgumentU64, - ScriptTransactionArgumentU8, -} from "../../transactions/instances/scriptTransactionArguments"; import type { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; +import { ScriptTransactionArgumentVariants } from "../../types"; export class Bool extends Serializable implements TransactionArgument { public readonly value: boolean; @@ -42,8 +34,8 @@ export class Bool extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentBool(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Bool); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): Bool { @@ -70,8 +62,8 @@ export class U8 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU8(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U8 { @@ -98,8 +90,8 @@ export class U16 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU16(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U16); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U16 { @@ -126,8 +118,8 @@ export class U32 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU32(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U32); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U32 { @@ -154,8 +146,8 @@ export class U64 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU64(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U64); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U64 { @@ -182,8 +174,8 @@ export class U128 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU128(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U128); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U128 { @@ -210,8 +202,8 @@ export class U256 extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentU256(this.value); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U256); + serializer.serialize(this); } static deserialize(deserializer: Deserializer): U256 { diff --git a/src/bcs/serializable/move-structs.ts b/src/bcs/serializable/move-structs.ts index 47b38cfd0..657300161 100644 --- a/src/bcs/serializable/move-structs.ts +++ b/src/bcs/serializable/move-structs.ts @@ -4,14 +4,10 @@ import { Bool, U128, U16, U256, U32, U64, U8 } from "./move-primitives"; import { Serializable, Serializer } from "../serializer"; import { Deserializable, Deserializer } from "../deserializer"; -import { AnyNumber, HexInput } from "../../types"; +import { AnyNumber, HexInput, ScriptTransactionArgumentVariants } from "../../types"; import { Hex } from "../../core/hex"; import { AccountAddress } from "../../core/account_address"; import { EntryFunctionArgument, TransactionArgument } from "../../transactions/instances/transactionArgument"; -import { - ScriptTransactionArgumentAddress, - ScriptTransactionArgumentU8Vector, -} from "../../transactions/instances/scriptTransactionArguments"; /** * This class is the Aptos Typescript SDK representation of a Move `vector`, @@ -80,9 +76,8 @@ export class MoveVector extends Serializable implements if (!isU8) { throw new Error("Script function arguments only accept u8 vectors"); } - const bcsBytes = this.bcsToBytes(); - const scriptTxArg = new ScriptTransactionArgumentU8Vector(bcsBytes); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8Vector); + serializer.serialize(this); } /** @@ -242,9 +237,9 @@ export class MoveString extends Serializable implements TransactionArgument { } serializeForScriptFunction(serializer: Serializer): void { - const bcsBytes = this.bcsToBytes(); - const scriptTxArg = new ScriptTransactionArgumentU8Vector(bcsBytes); - serializer.serialize(scriptTxArg); + // serialize the string, load it into a vector and serialize it as a script vector argument + const vectorU8 = MoveVector.U8(this.bcsToBytes()); + vectorU8.serializeForScriptFunction(serializer); } static deserialize(deserializer: Deserializer): MoveString { @@ -453,13 +448,11 @@ export class MoveObject extends Serializable implements TransactionArgument { } serializeForEntryFunction(serializer: Serializer): void { - const bcsBytes = this.bcsToBytes(); - serializer.serializeBytes(bcsBytes); + this.value.serializeForEntryFunction(serializer); } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentAddress(this.value); - serializer.serialize(scriptTxArg); + this.value.serializeForScriptFunction(serializer); } static deserialize(deserializer: Deserializer): MoveObject { diff --git a/src/core/account_address.ts b/src/core/account_address.ts index 5da40f556..d4cb60256 100644 --- a/src/core/account_address.ts +++ b/src/core/account_address.ts @@ -2,12 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; -import { HexInput } from "../types"; +import { HexInput, ScriptTransactionArgumentVariants } from "../types"; import { ParsingError, ParsingResult } from "./common"; import { Serializable, Serializer } from "../bcs/serializer"; import { Deserializer } from "../bcs/deserializer"; import { TransactionArgument } from "../transactions/instances/transactionArgument"; -import { ScriptTransactionArgumentAddress } from "../transactions/instances"; /** * This enum is used to explain why an address was invalid. @@ -185,12 +184,13 @@ export class AccountAddress extends Serializable implements TransactionArgument } serializeForEntryFunction(serializer: Serializer): void { - serializer.serialize(this); + const bcsBytes = this.bcsToBytes(); + serializer.serializeBytes(bcsBytes); } serializeForScriptFunction(serializer: Serializer): void { - const scriptTxArg = new ScriptTransactionArgumentAddress(this); - serializer.serialize(scriptTxArg); + serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Address); + serializer.serialize(this); } /** diff --git a/src/internal/general.ts b/src/internal/general.ts index 3de761eb0..b4e403d6b 100644 --- a/src/internal/general.ts +++ b/src/internal/general.ts @@ -10,7 +10,15 @@ import type { AptosConfig } from "../api/aptos_config"; import { getAptosFullNode, postAptosFullNode, postAptosIndexer } from "../client"; -import type { Block, GraphqlQuery, LedgerInfo, LedgerVersion, MoveValue, TableItemRequest, ViewRequest } from "../types"; +import type { + Block, + GraphqlQuery, + LedgerInfo, + LedgerVersion, + MoveValue, + TableItemRequest, + ViewRequest, +} from "../types"; export async function getLedgerInfo(args: { aptosConfig: AptosConfig }): Promise { const { aptosConfig } = args; diff --git a/src/transactions/instances/identifier.ts b/src/transactions/instances/identifier.ts index 5d8a88321..1346eb597 100644 --- a/src/transactions/instances/identifier.ts +++ b/src/transactions/instances/identifier.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Deserializer } from "../../bcs/deserializer"; -import { Serializable, Serializer, } from "../../bcs/serializer"; +import { Serializable, Serializer } from "../../bcs/serializer"; /** * Representation of an Identifier that can serialized and deserialized. diff --git a/src/transactions/instances/index.ts b/src/transactions/instances/index.ts index 1ef744779..a253d247a 100644 --- a/src/transactions/instances/index.ts +++ b/src/transactions/instances/index.ts @@ -3,7 +3,6 @@ export * from "./chainId"; export * from "./rawTransaction"; -export * from "./scriptTransactionArguments"; export * from "./transactionPayload"; export * from "./moduleId"; export * from "./identifier"; diff --git a/src/transactions/instances/rawTransaction.ts b/src/transactions/instances/rawTransaction.ts index d6b0b27cd..59fc7f0d6 100644 --- a/src/transactions/instances/rawTransaction.ts +++ b/src/transactions/instances/rawTransaction.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Deserializer } from "../../bcs/deserializer"; -import { Serializable, Serializer, } from "../../bcs/serializer"; +import { Serializable, Serializer } from "../../bcs/serializer"; import { AccountAddress } from "../../core"; import { TransactionVariants } from "../../types"; import { ChainId } from "./chainId"; diff --git a/src/transactions/instances/scriptTransactionArguments.ts b/src/transactions/instances/scriptTransactionArguments.ts deleted file mode 100644 index 7aac2a4ee..000000000 --- a/src/transactions/instances/scriptTransactionArguments.ts +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -import { Deserializer } from "../../bcs/deserializer"; -import { Serializer, Serializable } from "../../bcs/serializer"; -import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; -import { MoveVector } from "../../bcs/serializable/move-structs"; -import { AccountAddress } from "../../core/account_address"; -import { AnyNumber, HexInput, ScriptTransactionArgumentVariants, Uint16, Uint32, Uint8 } from "../../types"; -import type { ScriptFunctionArgument } from "./transactionArgument"; - -/** - * Representation of a Script Transaction Argument that can be serialized and deserialized - */ -export abstract class ScriptTransactionArgument extends Serializable implements ScriptFunctionArgument { - /** - * Serialize a Script Transaction Argument to its BCS byte representation. - */ - abstract serialize(serializer: Serializer): void; - - /** - * Implemented to satisfy the ScriptFunctionArgument interface. - * This is exactly the same as `serialize` for all classes that extend ScriptTransactionArgument. - */ - abstract serializeForScriptFunction(serializer: Serializer): void; - - /** - * Deserialize a Script Transaction Argument - */ - static deserialize(deserializer: Deserializer): ScriptTransactionArgument { - // index enum variant - const index = deserializer.deserializeUleb128AsU32(); - switch (index) { - case ScriptTransactionArgumentVariants.U8: - return ScriptTransactionArgumentU8.load(deserializer); - case ScriptTransactionArgumentVariants.U64: - return ScriptTransactionArgumentU64.load(deserializer); - case ScriptTransactionArgumentVariants.U128: - return ScriptTransactionArgumentU128.load(deserializer); - case ScriptTransactionArgumentVariants.Address: - return ScriptTransactionArgumentAddress.load(deserializer); - case ScriptTransactionArgumentVariants.U8Vector: - return ScriptTransactionArgumentU8Vector.load(deserializer); - case ScriptTransactionArgumentVariants.Bool: - return ScriptTransactionArgumentBool.load(deserializer); - case ScriptTransactionArgumentVariants.U16: - return ScriptTransactionArgumentU16.load(deserializer); - case ScriptTransactionArgumentVariants.U32: - return ScriptTransactionArgumentU32.load(deserializer); - case ScriptTransactionArgumentVariants.U256: - return ScriptTransactionArgumentU256.load(deserializer); - default: - throw new Error(`Unknown variant index for ScriptTransactionArgument: ${index}`); - } - } -} - -export class ScriptTransactionArgumentU8 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U8; - - constructor(value: Uint8) { - super(); - this.value = new U8(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU8 { - const value = deserializer.deserializeU8(); - return new ScriptTransactionArgumentU8(value); - } -} - -export class ScriptTransactionArgumentU16 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U16; - - constructor(value: Uint16) { - super(); - this.value = new U16(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U16); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU16 { - const value = deserializer.deserializeU16(); - return new ScriptTransactionArgumentU16(value); - } -} - -export class ScriptTransactionArgumentU32 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U32; - - constructor(value: Uint32) { - super(); - this.value = new U32(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U32); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU32 { - const value = deserializer.deserializeU32(); - return new ScriptTransactionArgumentU32(value); - } -} - -export class ScriptTransactionArgumentU64 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U64; - - constructor(value: AnyNumber) { - super(); - this.value = new U64(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U64); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU64 { - const value = deserializer.deserializeU64(); - return new ScriptTransactionArgumentU64(value); - } -} - -export class ScriptTransactionArgumentU128 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U128; - - constructor(value: AnyNumber) { - super(); - this.value = new U128(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U128); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU128 { - const value = deserializer.deserializeU128(); - return new ScriptTransactionArgumentU128(value); - } -} - -export class ScriptTransactionArgumentU256 extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: U256; - - constructor(value: AnyNumber) { - super(); - this.value = new U256(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U256); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU256 { - const value = deserializer.deserializeU256(); - return new ScriptTransactionArgumentU256(value); - } -} - -export class ScriptTransactionArgumentAddress extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: AccountAddress; - - constructor(value: AccountAddress) { - super(); - this.value = value; - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Address); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentAddress { - const value = AccountAddress.deserialize(deserializer); - return new ScriptTransactionArgumentAddress(value); - } -} - -export class ScriptTransactionArgumentU8Vector extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: MoveVector; - - constructor(values: Array | HexInput) { - super(); - this.value = MoveVector.U8(values); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8Vector); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentU8Vector { - const value = deserializer.deserializeBytes(); - return new ScriptTransactionArgumentU8Vector(value); - } -} - -export class ScriptTransactionArgumentBool extends ScriptTransactionArgument implements ScriptFunctionArgument { - public readonly value: Bool; - - constructor(value: boolean) { - super(); - this.value = new Bool(value); - } - - serialize(serializer: Serializer): void { - serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Bool); - serializer.serialize(this.value); - } - - serializeForScriptFunction(serializer: Serializer): void { - serializer.serialize(this); - } - - static load(deserializer: Deserializer): ScriptTransactionArgumentBool { - const value = deserializer.deserializeBool(); - return new ScriptTransactionArgumentBool(value); - } -} diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index 97498e94b..f47b708db 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -1,4 +1,5 @@ import type { Serializer } from "../../bcs/serializer"; +import type { Hex } from "../../core/hex"; export interface TransactionArgument extends EntryFunctionArgument, ScriptFunctionArgument {} @@ -12,6 +13,9 @@ export interface EntryFunctionArgument { * the number of the following bytes followed by the BCS-serialized bytes for a typed argument. */ serializeForEntryFunction(serializer: Serializer): void; + + bcsToBytes(): Uint8Array; + bcsToHex(): Hex; } export interface ScriptFunctionArgument { /** @@ -24,4 +28,7 @@ export interface ScriptFunctionArgument { * bytes for a typed argument. */ serializeForScriptFunction(serializer: Serializer): void; + + bcsToBytes(): Uint8Array; + bcsToHex(): Hex; } diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index a4e94f7fc..53e5a27d4 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -4,15 +4,46 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Deserializer } from "../../bcs/deserializer"; -import { Serializable, Serializer, } from "../../bcs/serializer"; +import { Serializable, Serializer } from "../../bcs/serializer"; import { AccountAddress } from "../../core"; import { Identifier } from "./identifier"; import { ModuleId } from "./moduleId"; -import { TransactionPayloadVariants } from "../../types"; +import { ScriptTransactionArgumentVariants, TransactionPayloadVariants } from "../../types"; import { TypeTag } from "../typeTag/typeTag"; -import type { EntryFunctionArgument, ScriptFunctionArgument } from "./transactionArgument"; -import { ScriptTransactionArgument } from "./scriptTransactionArguments"; +import type { EntryFunctionArgument, ScriptFunctionArgument, TransactionArgument } from "./transactionArgument"; import { EntryFunctionBytes } from "../../bcs/serializable/entry-function-bytes"; +import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; +import { MoveVector } from "../../bcs/serializable/move-structs"; + +/** + * Deserialize a Script Transaction Argument + */ +export function deserializeFromScriptArgument(deserializer: Deserializer): TransactionArgument { + // index enum variant + const index = deserializer.deserializeUleb128AsU32(); + switch (index) { + case ScriptTransactionArgumentVariants.U8: + return U8.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U64: + return U64.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U128: + return U128.deserialize(deserializer); + case ScriptTransactionArgumentVariants.Address: + return AccountAddress.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U8Vector: + return MoveVector.deserialize(deserializer, U8); + case ScriptTransactionArgumentVariants.Bool: + return Bool.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U16: + return U16.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U32: + return U32.deserialize(deserializer); + case ScriptTransactionArgumentVariants.U256: + return U256.deserialize(deserializer); + default: + throw new Error(`Unknown variant index for ScriptTransactionArgument: ${index}`); + } +} /** * Representation of the supported Transaction Payload @@ -284,7 +315,15 @@ export class Script { static deserialize(deserializer: Deserializer): Script { const bytecode = deserializer.deserializeBytes(); const type_args = deserializer.deserializeVector(TypeTag); - const args = deserializer.deserializeVector(ScriptTransactionArgument); + const length = deserializer.deserializeUleb128AsU32(); + const args = new Array(); + for (let i = 0; i < length; i += 1) { + // Note that we deserialize directly to the Move value, not its Script argument representation. + // We are abstracting away the Script argument representation because knowing about it is + // functionally useless. + const scriptArgument = deserializeFromScriptArgument(deserializer); + args.push(scriptArgument); + } return new Script(bytecode, type_args, args); } } diff --git a/src/transactions/typeTag/typeTag.ts b/src/transactions/typeTag/typeTag.ts index a68c93ccb..0f96fb37c 100644 --- a/src/transactions/typeTag/typeTag.ts +++ b/src/transactions/typeTag/typeTag.ts @@ -6,7 +6,7 @@ /* eslint-disable max-classes-per-file */ import { AccountAddress } from "../../core"; import { Deserializer } from "../../bcs/deserializer"; -import { Serializable, Serializer, } from "../../bcs/serializer"; +import { Serializable, Serializer } from "../../bcs/serializer"; import { Identifier } from "../instances/identifier"; import { TypeTagVariants } from "../../types"; diff --git a/tests/unit/script_transaction_arguments.test.ts b/tests/unit/script_transaction_arguments.test.ts index 2f808a2dd..cb1d676e8 100644 --- a/tests/unit/script_transaction_arguments.test.ts +++ b/tests/unit/script_transaction_arguments.test.ts @@ -3,20 +3,9 @@ import { Serializer, Deserializer } from "../../src/bcs"; import { AccountAddress } from "../../src/core"; -import { - ScriptTransactionArgument, - ScriptTransactionArgumentAddress, - ScriptTransactionArgumentBool, - ScriptTransactionArgumentU128, - ScriptTransactionArgumentU16, - ScriptTransactionArgumentU256, - ScriptTransactionArgumentU32, - ScriptTransactionArgumentU64, - ScriptTransactionArgumentU8, - ScriptTransactionArgumentU8Vector, -} from "../../src/transactions/instances"; import { Bool, U128, U16, U256, U32, U64, U8 } from "../../src/bcs/serializable/move-primitives"; import { MoveVector } from "../../src/bcs/serializable/move-structs"; +import { ScriptFunctionArgument, deserializeFromScriptArgument } from "../../src/transactions/instances"; describe("Tests for the script transaction argument class", () => { let serializer: Serializer; @@ -46,78 +35,63 @@ describe("Tests for the script transaction argument class", () => { }); it("should serialize all types of ScriptTransactionArguments correctly", () => { - const validateBytes = (input: ScriptTransactionArgument, expectedOutput: Uint8Array) => { + const validateBytes = (input: ScriptFunctionArgument, expectedOutput: Uint8Array) => { const serializer = new Serializer(); - input.serialize(serializer); + input.serializeForScriptFunction(serializer); const serializedBytes = serializer.toUint8Array(); expect(serializedBytes).toEqual(expectedOutput); }; - validateBytes(new ScriptTransactionArgumentU8(1), scriptU8Bytes); - validateBytes(new ScriptTransactionArgumentU16(2), scriptU16Bytes); - validateBytes(new ScriptTransactionArgumentU32(3), scriptU32Bytes); - validateBytes(new ScriptTransactionArgumentU64(4), scriptU64Bytes); - validateBytes(new ScriptTransactionArgumentU128(5), scriptU128Bytes); - validateBytes(new ScriptTransactionArgumentU256(6), scriptU256Bytes); - validateBytes(new ScriptTransactionArgumentBool(false), scriptBoolBytes); - validateBytes(new ScriptTransactionArgumentAddress(AccountAddress.FOUR), scriptAddressBytes); - validateBytes(new ScriptTransactionArgumentU8Vector([1, 2, 3, 4, 5]), scriptVectorU8Bytes); + validateBytes(new U8(1), scriptU8Bytes); + validateBytes(new U16(2), scriptU16Bytes); + validateBytes(new U32(3), scriptU32Bytes); + validateBytes(new U64(4), scriptU64Bytes); + validateBytes(new U128(5), scriptU128Bytes); + validateBytes(new U256(6), scriptU256Bytes); + validateBytes(new Bool(false), scriptBoolBytes); + validateBytes(AccountAddress.FOUR, scriptAddressBytes); + validateBytes(MoveVector.U8([1, 2, 3, 4, 5]), scriptVectorU8Bytes); }); - it("should deserialize all types of ScriptTransactionArguments correctly", () => { - const deserializeToScriptArg = (input: ScriptTransactionArgument) => { - const deserializer = new Deserializer(input.bcsToBytes()); - return ScriptTransactionArgument.deserialize(deserializer); - }; + const deserializeAsScriptArg = (input: ScriptFunctionArgument) => { + serializer = new Serializer(); + input.serializeForScriptFunction(serializer); + const deserializer = new Deserializer(serializer.toUint8Array()); + return deserializeFromScriptArgument(deserializer); + }; - const scriptArgU8 = deserializeToScriptArg(new ScriptTransactionArgumentU8(1)) as ScriptTransactionArgumentU8; - const scriptArgU16 = deserializeToScriptArg(new ScriptTransactionArgumentU16(2)) as ScriptTransactionArgumentU16; - const scriptArgU32 = deserializeToScriptArg(new ScriptTransactionArgumentU32(3)) as ScriptTransactionArgumentU32; - const scriptArgU64 = deserializeToScriptArg(new ScriptTransactionArgumentU64(4)) as ScriptTransactionArgumentU64; - const scriptArgU128 = deserializeToScriptArg(new ScriptTransactionArgumentU128(5)) as ScriptTransactionArgumentU128; - const scriptArgU256 = deserializeToScriptArg(new ScriptTransactionArgumentU256(6)) as ScriptTransactionArgumentU256; - const scriptArgBool = deserializeToScriptArg( - new ScriptTransactionArgumentBool(false), - ) as ScriptTransactionArgumentBool; - const scriptArgAddress = deserializeToScriptArg( - new ScriptTransactionArgumentAddress(AccountAddress.FOUR), - ) as ScriptTransactionArgumentAddress; - const scriptArgU8Vector = deserializeToScriptArg( - new ScriptTransactionArgumentU8Vector([1, 2, 3, 4, 5]), - ) as ScriptTransactionArgumentU8Vector; + it("should deserialize all types of ScriptTransactionArguments correctly", () => { + const scriptArgU8 = deserializeAsScriptArg(new U8(1)) as U8; + const scriptArgU16 = deserializeAsScriptArg(new U16(2)) as U16; + const scriptArgU32 = deserializeAsScriptArg(new U32(3)) as U32; + const scriptArgU64 = deserializeAsScriptArg(new U64(4)) as U64; + const scriptArgU128 = deserializeAsScriptArg(new U128(5)) as U128; + const scriptArgU256 = deserializeAsScriptArg(new U256(6)) as U256; + const scriptArgBool = deserializeAsScriptArg(new Bool(false)) as Bool; + const scriptArgAddress = deserializeAsScriptArg(AccountAddress.FOUR) as AccountAddress; + const scriptArgU8Vector = deserializeAsScriptArg(MoveVector.U8([1, 2, 3, 4, 5])) as MoveVector; - expect(scriptArgU8.value.value).toEqual(1); - expect(scriptArgU16.value.value).toEqual(2); - expect(scriptArgU32.value.value).toEqual(3); - expect(scriptArgU64.value.value).toEqual(4n); - expect(scriptArgU128.value.value).toEqual(5n); - expect(scriptArgU256.value.value).toEqual(6n); - expect(scriptArgBool.value.value).toEqual(false); - expect(scriptArgAddress.value.data).toEqual(AccountAddress.FOUR.data); - expect(scriptArgU8Vector.value.values.map((v) => v.value)).toEqual([1, 2, 3, 4, 5]); + expect(scriptArgU8.value).toEqual(1); + expect(scriptArgU16.value).toEqual(2); + expect(scriptArgU32.value).toEqual(3); + expect(scriptArgU64.value).toEqual(4n); + expect(scriptArgU128.value).toEqual(5n); + expect(scriptArgU256.value).toEqual(6n); + expect(scriptArgBool.value).toEqual(false); + expect(scriptArgAddress.data).toEqual(AccountAddress.FOUR.data); + expect(scriptArgU8Vector.values.map((v) => v.value)).toEqual([1, 2, 3, 4, 5]); }); it("should convert all Move primitives to script transaction arguments correctly", () => { - const deserializeToScriptArg = ( - input: U8 | U16 | U32 | U64 | U128 | U256 | Bool | MoveVector | AccountAddress, - ) => { - serializer = new Serializer(); - input.serializeForScriptFunction(serializer); - const deserializer = new Deserializer(serializer.toUint8Array()); - return ScriptTransactionArgument.deserialize(deserializer); - }; - - expect(deserializeToScriptArg(new U8(1)) instanceof ScriptTransactionArgumentU8).toBe(true); - expect(deserializeToScriptArg(new U16(2)) instanceof ScriptTransactionArgumentU16).toBe(true); - expect(deserializeToScriptArg(new U32(3)) instanceof ScriptTransactionArgumentU32).toBe(true); - expect(deserializeToScriptArg(new U64(4)) instanceof ScriptTransactionArgumentU64).toBe(true); - expect(deserializeToScriptArg(new U128(5)) instanceof ScriptTransactionArgumentU128).toBe(true); - expect(deserializeToScriptArg(new U256(6)) instanceof ScriptTransactionArgumentU256).toBe(true); - expect(deserializeToScriptArg(new Bool(false)) instanceof ScriptTransactionArgumentBool).toBe(true); - expect( - deserializeToScriptArg(new AccountAddress(AccountAddress.FOUR)) instanceof ScriptTransactionArgumentAddress, - ).toBe(true); - expect(deserializeToScriptArg(MoveVector.U8([1, 2, 3, 4, 5])) instanceof ScriptTransactionArgumentU8Vector).toBe( - true, - ); + expect(deserializeAsScriptArg(new U8(1)) instanceof U8).toBe(true); + expect(deserializeAsScriptArg(new U16(2)) instanceof U16).toBe(true); + expect(deserializeAsScriptArg(new U32(3)) instanceof U32).toBe(true); + expect(deserializeAsScriptArg(new U64(4)) instanceof U64).toBe(true); + expect(deserializeAsScriptArg(new U128(5)) instanceof U128).toBe(true); + expect(deserializeAsScriptArg(new U256(6)) instanceof U256).toBe(true); + expect(deserializeAsScriptArg(new Bool(false)) instanceof Bool).toBe(true); + expect(deserializeAsScriptArg(new AccountAddress(AccountAddress.FOUR)) instanceof AccountAddress).toBe(true); + expect(deserializeAsScriptArg(MoveVector.U8([1, 2, 3, 4, 5])) instanceof MoveVector).toBe(true); + const deserializedVectorU8 = deserializeAsScriptArg(MoveVector.U8([1, 2, 3, 4, 5])) as MoveVector; + expect(deserializedVectorU8.values.every((v) => v instanceof U8)).toBe(true); }); }); diff --git a/tests/unit/transaction_builder.test.ts b/tests/unit/transaction_builder.test.ts index e98693188..4446f6815 100644 --- a/tests/unit/transaction_builder.test.ts +++ b/tests/unit/transaction_builder.test.ts @@ -7,8 +7,6 @@ import { FeePayerRawTransaction, MultiAgentRawTransaction, RawTransaction, - ScriptTransactionArgumentAddress, - ScriptTransactionArgumentU64, TransactionPayloadEntryFunction, TransactionPayloadMultisig, TransactionPayloadScript, @@ -26,7 +24,6 @@ import { SigningScheme } from "../../src/types"; import { SignedTransaction } from "../../src/transactions/instances/signedTransaction"; import { U64 } from "../../src/bcs/serializable/move-primitives"; import { MoveObject } from "../../src/bcs/serializable/move-structs"; -import { Hex } from "../../src/core"; describe("transaction builder", () => { describe("generate transaction payload", () => { @@ -76,11 +73,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const rawTxn = await generateRawTransaction({ @@ -150,11 +147,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const transaction = await buildTransaction({ @@ -271,11 +268,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const transaction = await buildTransaction({ @@ -307,11 +304,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const transaction = await buildTransaction({ @@ -374,11 +371,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const rawTxn = await buildTransaction({ @@ -410,11 +407,11 @@ describe("transaction builder", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentAddress(Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(100), + new U64(200), + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + Account.generate({ scheme: SigningScheme.Ed25519 }).accountAddress, + new U64(50), ], }); const transaction = await buildTransaction({ From 8e590eb46944f746a58f262ecc8bc43214062d1d Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Wed, 11 Oct 2023 00:49:10 -0700 Subject: [PATCH 08/11] Removing all additions of import type --- src/api/account.ts | 4 ++-- src/api/aptos_config.ts | 2 +- src/api/faucet.ts | 2 +- src/bcs/deserializer.ts | 2 +- src/bcs/serializable/entry-function-bytes.ts | 7 +++---- src/bcs/serializable/move-primitives.ts | 3 +-- src/internal/general.ts | 12 ++---------- src/transactions/instances/transactionArgument.ts | 4 ++-- src/transactions/instances/transactionPayload.ts | 2 +- src/transactions/types.ts | 4 ++-- 10 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/api/account.ts b/src/api/account.ts index bc6b963f4..7adba8977 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -1,8 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import type { AptosConfig } from "./aptos_config"; -import type { +import { AptosConfig } from "./aptos_config"; +import { AccountData, LedgerVersion, MoveModuleBytecode, diff --git a/src/api/aptos_config.ts b/src/api/aptos_config.ts index 49b48d906..04e67f52a 100644 --- a/src/api/aptos_config.ts +++ b/src/api/aptos_config.ts @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import type { AptosSettings, ClientConfig } from "../types"; +import { AptosSettings, ClientConfig } from "../types"; import { NetworkToNodeAPI, NetworkToFaucetAPI, NetworkToIndexerAPI, Network } from "../utils/apiEndpoints"; import { AptosApiType, DEFAULT_NETWORK } from "../utils/const"; diff --git a/src/api/faucet.ts b/src/api/faucet.ts index 3312c3d2a..17de12cf0 100644 --- a/src/api/faucet.ts +++ b/src/api/faucet.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { fundAccount } from "../internal/faucet"; -import type { HexInput } from "../types"; +import { HexInput } from "../types"; import { AptosConfig } from "./aptos_config"; /** diff --git a/src/bcs/deserializer.ts b/src/bcs/deserializer.ts index 895748bdc..1132be6f1 100644 --- a/src/bcs/deserializer.ts +++ b/src/bcs/deserializer.ts @@ -3,7 +3,7 @@ /* eslint-disable no-bitwise */ import { MAX_U32_NUMBER } from "./consts"; -import type { Uint128, Uint16, Uint256, Uint32, Uint64, Uint8 } from "../types"; +import { Uint128, Uint16, Uint256, Uint32, Uint64, Uint8 } from "../types"; /** * This interface exists to define Deserializable inputs for functions that diff --git a/src/bcs/serializable/entry-function-bytes.ts b/src/bcs/serializable/entry-function-bytes.ts index 79cc4d0af..ef154280e 100644 --- a/src/bcs/serializable/entry-function-bytes.ts +++ b/src/bcs/serializable/entry-function-bytes.ts @@ -1,12 +1,11 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -import type { Serializer } from "../serializer"; -import type { Deserializer } from "../deserializer"; +import { Serializer, Serializable } from "../serializer"; +import { Deserializer } from "../deserializer"; import { FixedBytes } from "./fixed-bytes"; -import { Serializable } from "../serializer"; import { EntryFunctionArgument } from "../../transactions/instances/transactionArgument"; -import type { HexInput } from "../../types"; +import { HexInput } from "../../types"; /** * This class exists solely to represent a sequence of fixed bytes as a serialized entry function, because diff --git a/src/bcs/serializable/move-primitives.ts b/src/bcs/serializable/move-primitives.ts index cc358cd6e..b553be310 100644 --- a/src/bcs/serializable/move-primitives.ts +++ b/src/bcs/serializable/move-primitives.ts @@ -12,8 +12,7 @@ import { import { Deserializer } from "../deserializer"; import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer"; import { TransactionArgument } from "../../transactions/instances/transactionArgument"; -import type { AnyNumber, Uint16, Uint32, Uint8 } from "../../types"; -import { ScriptTransactionArgumentVariants } from "../../types"; +import { AnyNumber, Uint16, Uint32, Uint8, ScriptTransactionArgumentVariants } from "../../types"; export class Bool extends Serializable implements TransactionArgument { public readonly value: boolean; diff --git a/src/internal/general.ts b/src/internal/general.ts index b4e403d6b..7c3d63f89 100644 --- a/src/internal/general.ts +++ b/src/internal/general.ts @@ -8,17 +8,9 @@ * general namespace and without having a dependency cycle error. */ -import type { AptosConfig } from "../api/aptos_config"; +import { AptosConfig } from "../api/aptos_config"; import { getAptosFullNode, postAptosFullNode, postAptosIndexer } from "../client"; -import type { - Block, - GraphqlQuery, - LedgerInfo, - LedgerVersion, - MoveValue, - TableItemRequest, - ViewRequest, -} from "../types"; +import { Block, GraphqlQuery, LedgerInfo, LedgerVersion, MoveValue, TableItemRequest, ViewRequest } from "../types"; export async function getLedgerInfo(args: { aptosConfig: AptosConfig }): Promise { const { aptosConfig } = args; diff --git a/src/transactions/instances/transactionArgument.ts b/src/transactions/instances/transactionArgument.ts index f47b708db..377066625 100644 --- a/src/transactions/instances/transactionArgument.ts +++ b/src/transactions/instances/transactionArgument.ts @@ -1,5 +1,5 @@ -import type { Serializer } from "../../bcs/serializer"; -import type { Hex } from "../../core/hex"; +import { Serializer } from "../../bcs/serializer"; +import { Hex } from "../../core/hex"; export interface TransactionArgument extends EntryFunctionArgument, ScriptFunctionArgument {} diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index 53e5a27d4..1bec09d80 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -10,7 +10,7 @@ import { Identifier } from "./identifier"; import { ModuleId } from "./moduleId"; import { ScriptTransactionArgumentVariants, TransactionPayloadVariants } from "../../types"; import { TypeTag } from "../typeTag/typeTag"; -import type { EntryFunctionArgument, ScriptFunctionArgument, TransactionArgument } from "./transactionArgument"; +import { EntryFunctionArgument, ScriptFunctionArgument, TransactionArgument } from "./transactionArgument"; import { EntryFunctionBytes } from "../../bcs/serializable/entry-function-bytes"; import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; import { MoveVector } from "../../bcs/serializable/move-structs"; diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 76b5bd412..35ef0a5e0 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -13,8 +13,8 @@ import { TransactionPayloadMultisig, TransactionPayloadScript, } from "./instances"; -import type { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; -import type { TypeTag } from "./typeTag/typeTag"; +import { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; +import { TypeTag } from "./typeTag/typeTag"; /** * Type that holds all raw transaction instances Aptos SDK supports From becd4f021dd2419f1220ec7b2e6106fdfbed773f Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Wed, 11 Oct 2023 01:46:05 -0700 Subject: [PATCH 09/11] Adding EntryFunctionArgumentTypes and ScriptFunctionArgumentTypes to types.ts for clearer type errors when using incorrect types --- .../instances/transactionPayload.ts | 2 +- src/transactions/types.ts | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/transactions/instances/transactionPayload.ts b/src/transactions/instances/transactionPayload.ts index 1bec09d80..53e5a27d4 100644 --- a/src/transactions/instances/transactionPayload.ts +++ b/src/transactions/instances/transactionPayload.ts @@ -10,7 +10,7 @@ import { Identifier } from "./identifier"; import { ModuleId } from "./moduleId"; import { ScriptTransactionArgumentVariants, TransactionPayloadVariants } from "../../types"; import { TypeTag } from "../typeTag/typeTag"; -import { EntryFunctionArgument, ScriptFunctionArgument, TransactionArgument } from "./transactionArgument"; +import type { EntryFunctionArgument, ScriptFunctionArgument, TransactionArgument } from "./transactionArgument"; import { EntryFunctionBytes } from "../../bcs/serializable/entry-function-bytes"; import { Bool, U128, U16, U256, U32, U64, U8 } from "../../bcs/serializable/move-primitives"; import { MoveVector } from "../../bcs/serializable/move-structs"; diff --git a/src/transactions/types.ts b/src/transactions/types.ts index 35ef0a5e0..6638e5e30 100644 --- a/src/transactions/types.ts +++ b/src/transactions/types.ts @@ -13,8 +13,33 @@ import { TransactionPayloadMultisig, TransactionPayloadScript, } from "./instances"; -import { EntryFunctionArgument, ScriptFunctionArgument } from "./instances/transactionArgument"; import { TypeTag } from "./typeTag/typeTag"; +import { MoveObject, MoveOption, MoveVector } from "../bcs/serializable/move-structs"; +import { Bool, U128, U16, U256, U32, U64, U8 } from "../bcs/serializable/move-primitives"; + +export type EntryFunctionArgumentTypes = + | Bool + | U8 + | U16 + | U32 + | U64 + | U128 + | U256 + | AccountAddress + | MoveObject + | MoveVector + | MoveOption; +export type ScriptFunctionArgumentTypes = + | Bool + | U8 + | U16 + | U32 + | U64 + | U128 + | U256 + | AccountAddress + | MoveObject + | MoveVector; /** * Type that holds all raw transaction instances Aptos SDK supports @@ -53,7 +78,7 @@ export type GenerateTransactionPayloadData = EntryFunctionData | ScriptData | Mu export type EntryFunctionData = { function: MoveStructType; type_arguments: Array; - arguments: Array; + arguments: Array; }; /** @@ -69,7 +94,7 @@ export type MultiSigData = { export type ScriptData = { bytecode: string; type_arguments: Array; - arguments: Array; + arguments: Array; }; /** From e34c1e36501cc1fbadfef185d38906d9670f11bf Mon Sep 17 00:00:00 2001 From: xbtmatt <90358481+xbtmatt@users.noreply.github.com> Date: Wed, 11 Oct 2023 02:04:50 -0700 Subject: [PATCH 10/11] Updating test cases to remove the old script classes as arguments and use regular Move classes --- tests/e2e/api/transaction_submission.test.ts | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/e2e/api/transaction_submission.test.ts b/tests/e2e/api/transaction_submission.test.ts index 068a37a2d..7dd56268e 100644 --- a/tests/e2e/api/transaction_submission.test.ts +++ b/tests/e2e/api/transaction_submission.test.ts @@ -5,10 +5,8 @@ import { Account, AptosConfig, Ed25519PrivateKey, Network, Aptos, Deserializer } import { U64 } from "../../../src/bcs/serializable/move-primitives"; import { MoveObject } from "../../../src/bcs/serializable/move-structs"; import { AccountAuthenticator, AccountAuthenticatorEd25519 } from "../../../src/transactions/authenticator/account"; +import { RawTransaction } from "../../../src/transactions/instances"; import { - RawTransaction, - ScriptTransactionArgumentAddress, - ScriptTransactionArgumentU64, TransactionPayloadEntryFunction, TransactionPayloadMultisig, TransactionPayloadScript, @@ -111,11 +109,11 @@ describe("transaction submission", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(bob.accountAddress), - new ScriptTransactionArgumentAddress(alice.accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(BigInt(100)), + new U64(BigInt(200)), + bob.accountAddress, + alice.accountAddress, + new U64(BigInt(50)), ], }, }); @@ -232,11 +230,11 @@ describe("transaction submission", () => { "a11ceb0b060000000701000402040a030e18042608052e4307713e08af01200000000101020401000100030800010403040100010505060100010607040100010708060100000201020202030207060c060c0303050503030b000108010b000108010b0001080101080102060c03010b0001090002070b000109000b000109000002070b000109000302050b000109000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177056d657267650765787472616374076465706f73697400000000000000000000000000000000000000000000000000000000000000010000011a0b000a0238000c070b010a0338000c080d070b0838010d070b020b03160b061738020c090b040b0738030b050b09380302", type_arguments: [], arguments: [ - new ScriptTransactionArgumentU64(BigInt(100)), - new ScriptTransactionArgumentU64(BigInt(200)), - new ScriptTransactionArgumentAddress(bob.accountAddress), - new ScriptTransactionArgumentAddress(alice.accountAddress), - new ScriptTransactionArgumentU64(BigInt(50)), + new U64(BigInt(100)), + new U64(BigInt(200)), + bob.accountAddress, + alice.accountAddress, + new U64(BigInt(50)), ], }, }); From 3167861c511c1ab6eaa4f61cffcf616e095abb0d Mon Sep 17 00:00:00 2001 From: xbtmatt Date: Wed, 11 Oct 2023 10:36:18 -0700 Subject: [PATCH 11/11] Fix changes addresseed in PR --- src/api/transaction_submission.ts | 3 +++ src/bcs/index.ts | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/api/transaction_submission.ts b/src/api/transaction_submission.ts index 55c08c1df..c5f7b4682 100644 --- a/src/api/transaction_submission.ts +++ b/src/api/transaction_submission.ts @@ -1,3 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + import { AptosConfig } from "./aptos_config"; import { Account } from "../core/account"; import { AccountAuthenticator } from "../transactions/authenticator/account"; diff --git a/src/bcs/index.ts b/src/bcs/index.ts index 1e683e475..19ae156bd 100644 --- a/src/bcs/index.ts +++ b/src/bcs/index.ts @@ -1,10 +1,5 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -export * from "./serializable/fixed-bytes"; -export * from "./serializable/move-primitives"; -export * from "./serializable/move-structs"; - -export * from "./serializable/entry-function-bytes"; export * from "./deserializer"; export * from "./serializer";