diff --git a/apps/docs-snippets/src/guide/encoding/encode-and-decode.test.ts b/apps/docs-snippets/src/guide/encoding/encode-and-decode.test.ts index 68e29fc2d4b..b519e350e66 100644 --- a/apps/docs-snippets/src/guide/encoding/encode-and-decode.test.ts +++ b/apps/docs-snippets/src/guide/encoding/encode-and-decode.test.ts @@ -1,13 +1,13 @@ import { FUEL_NETWORK_URL, Provider, - AbiCoder, Script, ReceiptType, arrayify, buildFunctionResult, + Interface, } from 'fuels'; -import type { Account, JsonAbi, JsonAbiArgument, TransactionResultReturnDataReceipt } from 'fuels'; +import type { Account, JsonAbi, TransactionResultReturnDataReceipt } from 'fuels'; import { generateTestWallet } from 'fuels/test-utils'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -54,18 +54,21 @@ describe('encode and decode', () => { // #endregion encode-and-decode-3 // #region encode-and-decode-4 - // #import { JsonAbiArgument, AbiCoder}; + // #import { Interface }; // Now we can encode the argument we want to pass to the function. The argument is required - // as a function parameter for all `AbiCoder` functions and we can extract it from the ABI itself - const argument: JsonAbiArgument = abi.functions + // as a function parameter for all abi functions and we can extract it from the ABI itself + const argument = abi.functions .find((f) => f.name === 'main') - ?.inputs.find((i) => i.name === 'inputted_amount') as JsonAbiArgument; + ?.inputs.find((i) => i.name === 'inputted_amount')?.concreteTypeId as string; - // Using the `AbiCoder`'s `encode` method, we can now create the encoding required for - // a u32 which takes 4 bytes up of property space + // The `Interface` class is the entry point for encoding and decoding all things abi-related. + // We will use its `encodeType` method and create the encoding required for + // a u32 which takes 4 bytes up of property space. + + const abiInterface = new Interface(abi); const argumentToAdd = 10; - const encodedArguments = AbiCoder.encode(abi, argument, [argumentToAdd]); + const encodedArguments = abiInterface.encodeType(argument, [argumentToAdd]); // Therefore the value of 10 will be encoded to: // Uint8Array([0, 0, 0, 10] @@ -84,7 +87,7 @@ describe('encode and decode', () => { // #endregion encode-and-decode-4 // #region encode-and-decode-5 - // #import { AbiCoder, ReceiptType, TransactionResultReturnDataReceipt, arrayify, buildFunctionResult }; + // #import { ReceiptType, TransactionResultReturnDataReceipt, arrayify, buildFunctionResult }; // Get result of the transaction, including the contract call result. For this we'll need // the previously created invocation scope, the transaction response and the script @@ -111,8 +114,8 @@ describe('encode and decode', () => { // returnData = new Uint8Array([0, 0, 0, 20] // And now we can decode the returned bytes in a similar fashion to how they were - // encoded, via the `AbiCoder` - const [decodedReturnData] = AbiCoder.decode(abi, argument, returnData, 0); + // encoded, via the `Interface` + const [decodedReturnData] = abiInterface.decodeType(argument, returnData); // 20 // #endregion encode-and-decode-5 diff --git a/apps/docs-snippets/test/fixtures/abi/encode-and-decode.jsonc b/apps/docs-snippets/test/fixtures/abi/encode-and-decode.jsonc index c182c6e445a..3fc43173ada 100644 --- a/apps/docs-snippets/test/fixtures/abi/encode-and-decode.jsonc +++ b/apps/docs-snippets/test/fixtures/abi/encode-and-decode.jsonc @@ -1,30 +1,25 @@ // #region encode-and-decode-2 { - "encoding": "1", - "types": [ + "programType": "script", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ { - "typeId": 0, "type": "u32", - "components": null, "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", - "typeParameters": null, }, ], + "metadataTypes": [], "functions": [ { "inputs": [ { "name": "inputted_amount", - "type": 0, - "typeArguments": null, + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", }, ], "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null, - }, + "output": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "attributes": null, }, ], @@ -33,14 +28,9 @@ "configurables": [ { "name": "AMOUNT", - "configurableType": { - "name": "", - "type": 0, - "typeArguments": null, - }, + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "offset": 856, }, ], } - // #endregion encode-and-decode-2 diff --git a/apps/docs/spell-check-custom-words.txt b/apps/docs/spell-check-custom-words.txt index d5d6593a571..d42e17f6f79 100644 --- a/apps/docs/spell-check-custom-words.txt +++ b/apps/docs/spell-check-custom-words.txt @@ -1,5 +1,6 @@ ABI ABIs +ABI's ASM IDE IDEs diff --git a/apps/docs/src/guide/encoding/encode-and-decode.md b/apps/docs/src/guide/encoding/encode-and-decode.md index 0ed475b8ce1..8a6821829cf 100644 --- a/apps/docs/src/guide/encoding/encode-and-decode.md +++ b/apps/docs/src/guide/encoding/encode-and-decode.md @@ -1,16 +1,13 @@ # Encode and Decode -In order to interact with the FuelVM, types must be encoded and decoded as per the [argument encoding specification](https://docs.fuel.network/docs/specs/abi/argument-encoding/). The SDK provides the `AbiCoder` class to encode and decode data. +To interact with the FuelVM, types must be encoded and decoded per the [argument encoding specification](https://docs.fuel.network/docs/specs/abi/argument-encoding/). The SDK provides the `Interface` class to encode and decode data. -It has three static methods: +The relevant methods of `Interface` are: -- `encode` -- `decode` -- `getCoder` +- `encodeType` +- `decodeType` -The methods `encode` and `decode` describe the aforementioned process, while `getCoder` returns an instance of the internal coder required to serialize the passed type. This coder is then used internally by the `encode` and `decode` methods. - -All methods expect you to pass the [ABI](https://docs.fuel.network/docs/specs/abi/json-abi-format/) and ABI Argument as function parameters to deduce the specific type coders that will be required to parse the data. +The `Interface` class requires you to pass the [ABI](https://docs.fuel.network/docs/specs/abi/json-abi-format/) on initialization. Both methods accept a `concreteTypeId`, which must exist in the ABI's `concreteTypes` array. After that, a suitable coder will be assigned to encode/decode that type. Imagine we are working with the following script that returns the sum of two `u32` integers: @@ -26,7 +23,7 @@ It will produce the following ABI: <<< @/../../docs-snippets/test/fixtures/abi/encode-and-decode.jsonc#encode-and-decode-2{json:line-numbers} -Now, let's prepare some data to pass to the `main` function to retrieve the combined integer. The function expects and returns a `u32` integer. So here, we will encode the `u32` to pass it to the function and receive the same `u32` back, as bytes, that we'll use for decoding. We can do both of these with the `AbiCoder`. +Now, let's prepare some data to pass to the `main` function to retrieve the combined integer. The function expects and returns a `u32` integer. So here, we will encode the `u32` to pass it to the function and receive the same `u32` back, as bytes, that we'll use for decoding. We can do both of these with the `Interface`. First, let's prepare the transaction: diff --git a/packages/abi-coder/src/AbiCoder.ts b/packages/abi-coder/src/AbiCoder.ts index b7fd369f1de..cec57c6300e 100644 --- a/packages/abi-coder/src/AbiCoder.ts +++ b/packages/abi-coder/src/AbiCoder.ts @@ -2,11 +2,11 @@ import { ResolvedAbiType } from './ResolvedAbiType'; import type { DecodedValue, InputValue, Coder } from './encoding/coders/AbstractCoder'; import { getCoderForEncoding } from './encoding/strategies/getCoderForEncoding'; import type { EncodingOptions } from './types/EncodingOptions'; -import type { JsonAbi, JsonAbiArgument } from './types/JsonAbi'; +import type { JsonAbiOld, JsonAbiArgument } from './types/JsonAbi'; export abstract class AbiCoder { static getCoder( - abi: JsonAbi, + abi: JsonAbiOld, argument: JsonAbiArgument, options: EncodingOptions = { padToWordSize: false, @@ -17,7 +17,7 @@ export abstract class AbiCoder { } static encode( - abi: JsonAbi, + abi: JsonAbiOld, argument: JsonAbiArgument, value: InputValue, options?: EncodingOptions @@ -26,7 +26,7 @@ export abstract class AbiCoder { } static decode( - abi: JsonAbi, + abi: JsonAbiOld, argument: JsonAbiArgument, data: Uint8Array, offset: number, diff --git a/packages/abi-coder/src/FunctionFragment.ts b/packages/abi-coder/src/FunctionFragment.ts index addc6eed345..c44c9d2d4eb 100644 --- a/packages/abi-coder/src/FunctionFragment.ts +++ b/packages/abi-coder/src/FunctionFragment.ts @@ -10,40 +10,39 @@ import { ResolvedAbiType } from './ResolvedAbiType'; import type { DecodedValue, InputValue } from './encoding/coders/AbstractCoder'; import { StdStringCoder } from './encoding/coders/StdStringCoder'; import { TupleCoder } from './encoding/coders/TupleCoder'; -import type { JsonAbi, JsonAbiFunction, JsonAbiFunctionAttribute } from './types/JsonAbi'; +import type { JsonAbiOld, JsonAbiFunction } from './types/JsonAbi'; +import type { AbiFunction, AbiFunctionAttribute } from './types/JsonAbiNew'; import type { EncodingVersion } from './utils/constants'; import { getFunctionInputs } from './utils/getFunctionInputs'; -import { findFunctionByName, findNonVoidInputs, getEncodingVersion } from './utils/json-abi'; +import { findNonVoidInputs, getEncodingVersion } from './utils/json-abi'; import { padValuesWithUndefined } from './utils/padValuesWithUndefined'; -export class FunctionFragment< - TAbi extends JsonAbi = JsonAbi, - FnName extends TAbi['functions'][number]['name'] = string, -> { +export class FunctionFragment { readonly signature: string; readonly selector: string; readonly selectorBytes: Uint8Array; readonly encoding: EncodingVersion; readonly name: string; - readonly jsonFn: JsonAbiFunction; - readonly attributes: readonly JsonAbiFunctionAttribute[]; - - private readonly jsonAbi: JsonAbi; - - constructor(jsonAbi: JsonAbi, name: FnName) { - this.jsonAbi = jsonAbi; - this.jsonFn = findFunctionByName(this.jsonAbi, name); - - this.name = name; - this.signature = FunctionFragment.getSignature(this.jsonAbi, this.jsonFn); + readonly jsonFn: AbiFunction; + readonly attributes: readonly AbiFunctionAttribute[]; + + private readonly jsonAbiOld: JsonAbiOld; + private readonly jsonFnOld: JsonAbiFunction; + + constructor(jsonAbi: JsonAbiOld, fn: AbiFunction) { + this.jsonFn = fn; + this.jsonAbiOld = jsonAbi; + this.jsonFnOld = jsonAbi.functions.find((f) => f.name === fn.name) as JsonAbiFunction; + this.name = fn.name; + this.signature = FunctionFragment.getSignature(this.jsonAbiOld, this.jsonFnOld); this.selector = FunctionFragment.getFunctionSelector(this.signature); - this.selectorBytes = new StdStringCoder().encode(name); + this.selectorBytes = new StdStringCoder().encode(this.name); this.encoding = getEncodingVersion(jsonAbi.encoding); this.attributes = this.jsonFn.attributes ?? []; } - private static getSignature(abi: JsonAbi, fn: JsonAbiFunction): string { + private static getSignature(abi: JsonAbiOld, fn: JsonAbiFunction): string { const inputsSignatures = fn.inputs.map((input) => new ResolvedAbiType(abi, input).getSignature() ); @@ -57,7 +56,7 @@ export class FunctionFragment< } encodeArguments(values: InputValue[]): Uint8Array { - const inputs = getFunctionInputs({ jsonAbi: this.jsonAbi, inputs: this.jsonFn.inputs }); + const inputs = getFunctionInputs({ jsonAbi: this.jsonAbiOld, inputs: this.jsonFnOld.inputs }); const mandatoryInputLength = inputs.filter((i) => !i.isOptional).length; if (values.length < mandatoryInputLength) { throw new FuelError( @@ -66,8 +65,8 @@ export class FunctionFragment< ); } - const coders = this.jsonFn.inputs.map((t) => - AbiCoder.getCoder(this.jsonAbi, t, { + const coders = this.jsonFnOld.inputs.map((t) => + AbiCoder.getCoder(this.jsonAbiOld, t, { encoding: this.encoding, }) ); @@ -78,7 +77,7 @@ export class FunctionFragment< decodeArguments(data: BytesLike) { const bytes = arrayify(data); - const nonVoidInputs = findNonVoidInputs(this.jsonAbi, this.jsonFn.inputs); + const nonVoidInputs = findNonVoidInputs(this.jsonAbiOld, this.jsonFnOld.inputs); if (nonVoidInputs.length === 0) { // The VM is current return 0x0000000000000000, but we should treat it as undefined / void @@ -103,9 +102,9 @@ export class FunctionFragment< ); } - const result = this.jsonFn.inputs.reduce( + const result = this.jsonFnOld.inputs.reduce( (obj: { decoded: unknown[]; offset: number }, input) => { - const coder = AbiCoder.getCoder(this.jsonAbi, input, { encoding: this.encoding }); + const coder = AbiCoder.getCoder(this.jsonAbiOld, input, { encoding: this.encoding }); const [decodedValue, decodedValueByteSize] = coder.decode(bytes, obj.offset); return { @@ -121,7 +120,7 @@ export class FunctionFragment< decodeOutput(data: BytesLike): [DecodedValue | undefined, number] { const bytes = arrayify(data); - const coder = AbiCoder.getCoder(this.jsonAbi, this.jsonFn.output, { + const coder = AbiCoder.getCoder(this.jsonAbiOld, this.jsonFnOld.output, { encoding: this.encoding, }); @@ -135,6 +134,6 @@ export class FunctionFragment< */ isReadOnly(): boolean { const storageAttribute = this.attributes.find((attr) => attr.name === 'storage'); - return !storageAttribute?.arguments.includes('write'); + return !storageAttribute?.arguments?.includes('write'); } } diff --git a/packages/abi-coder/src/Interface.ts b/packages/abi-coder/src/Interface.ts index c2f9404bb69..be85aa6f7d3 100644 --- a/packages/abi-coder/src/Interface.ts +++ b/packages/abi-coder/src/Interface.ts @@ -5,23 +5,26 @@ import { arrayify } from '@fuel-ts/utils'; import { AbiCoder } from './AbiCoder'; import { FunctionFragment } from './FunctionFragment'; -import type { InputValue } from './encoding/coders/AbstractCoder'; -import type { JsonAbi, JsonAbiConfigurable } from './types/JsonAbi'; +import type { DecodedValue, InputValue } from './encoding/coders/AbstractCoder'; +import type { JsonAbiArgument, JsonAbiOld } from './types/JsonAbi'; +import type { Configurable, JsonAbi } from './types/JsonAbiNew'; import { type EncodingVersion } from './utils/constants'; -import { findTypeById, getEncodingVersion } from './utils/json-abi'; +import { getEncodingVersion } from './utils/json-abi'; +import { parseConcreteType, transpileAbi } from './utils/transpile-abi'; -export class Interface { +export class Interface { readonly functions!: Record; - readonly configurables: Record; - readonly jsonAbi: TAbi; + readonly configurables: Record; + readonly jsonAbi: JsonAbi; readonly encoding: EncodingVersion; + private readonly jsonAbiOld: JsonAbiOld; - constructor(jsonAbi: TAbi) { + constructor(jsonAbi: JsonAbi) { this.jsonAbi = jsonAbi; - this.encoding = getEncodingVersion(jsonAbi.encoding); - + this.encoding = getEncodingVersion(jsonAbi.encodingVersion); + this.jsonAbiOld = transpileAbi(jsonAbi) as JsonAbiOld; this.functions = Object.fromEntries( - this.jsonAbi.functions.map((x) => [x.name, new FunctionFragment(this.jsonAbi, x.name)]) + this.jsonAbi.functions.map((fn) => [fn.name, new FunctionFragment(this.jsonAbiOld, fn)]) ); this.configurables = Object.fromEntries(this.jsonAbi.configurables.map((x) => [x.name, x])); @@ -58,7 +61,7 @@ export class Interface { } decodeLog(data: BytesLike, logId: string): any { - const loggedType = this.jsonAbi.loggedTypes.find((type) => type.logId === logId); + const loggedType = this.jsonAbiOld.loggedTypes.find((type) => type.logId === logId); if (!loggedType) { throw new FuelError( ErrorCode.LOG_TYPE_NOT_FOUND, @@ -66,13 +69,13 @@ export class Interface { ); } - return AbiCoder.decode(this.jsonAbi, loggedType.loggedType, arrayify(data), 0, { + return AbiCoder.decode(this.jsonAbiOld, loggedType.loggedType, arrayify(data), 0, { encoding: this.encoding, }); } encodeConfigurable(name: string, value: InputValue) { - const configurable = this.jsonAbi.configurables.find((c) => c.name === name); + const configurable = this.jsonAbiOld.configurables.find((c) => c.name === name); if (!configurable) { throw new FuelError( ErrorCode.CONFIGURABLE_NOT_FOUND, @@ -80,12 +83,31 @@ export class Interface { ); } - return AbiCoder.encode(this.jsonAbi, configurable.configurableType, value, { + return AbiCoder.encode(this.jsonAbiOld, configurable.configurableType, value, { + encoding: this.encoding, + }); + } + + encodeType(concreteTypeId: string, value: InputValue): Uint8Array { + const typeArg = parseConcreteType( + this.jsonAbi, + this.jsonAbiOld.types, + concreteTypeId, + '' + ) as JsonAbiArgument; + return AbiCoder.encode(this.jsonAbiOld, typeArg, value, { encoding: this.encoding, }); } - getTypeById(typeId: number) { - return findTypeById(this.jsonAbi, typeId); + decodeType(concreteTypeId: string, data: Uint8Array): [DecodedValue | undefined, number] { + const typeArg = parseConcreteType( + this.jsonAbi, + this.jsonAbiOld.types, + concreteTypeId, + '' + ) as JsonAbiArgument; + + return AbiCoder.decode(this.jsonAbiOld, typeArg, data, 0, { encoding: this.encoding }); } } diff --git a/packages/abi-coder/src/ResolvedAbiType.ts b/packages/abi-coder/src/ResolvedAbiType.ts index c97e077a672..49e358fce4a 100644 --- a/packages/abi-coder/src/ResolvedAbiType.ts +++ b/packages/abi-coder/src/ResolvedAbiType.ts @@ -1,17 +1,17 @@ import { FuelError, ErrorCode } from '@fuel-ts/errors'; -import type { JsonAbi, JsonAbiArgument } from './types/JsonAbi'; +import type { JsonAbiOld, JsonAbiArgument } from './types/JsonAbi'; import { arrayRegEx, enumRegEx, genericRegEx, stringRegEx, structRegEx } from './utils/constants'; import { findTypeById } from './utils/json-abi'; export class ResolvedAbiType { - readonly abi: JsonAbi; + readonly abi: JsonAbiOld; name: string; readonly type: string; readonly originalTypeArguments: readonly JsonAbiArgument[] | null; readonly components: readonly ResolvedAbiType[] | null; - constructor(abi: JsonAbi, argument: JsonAbiArgument) { + constructor(abi: JsonAbiOld, argument: JsonAbiArgument) { this.abi = abi; this.name = argument.name; @@ -37,7 +37,7 @@ export class ResolvedAbiType { } private static getResolvedGenericComponents( - abi: JsonAbi, + abi: JsonAbiOld, arg: JsonAbiArgument, components: readonly JsonAbiArgument[] | null, typeParameters: readonly number[] | null @@ -70,7 +70,7 @@ export class ResolvedAbiType { } private static resolveGenericArgTypes( - abi: JsonAbi, + abi: JsonAbiOld, args: readonly JsonAbiArgument[], typeParametersAndArgsMap: Record ): readonly JsonAbiArgument[] { @@ -108,7 +108,7 @@ export class ResolvedAbiType { } private static getImplicitGenericTypeParameters( - abi: JsonAbi, + abi: JsonAbiOld, args: readonly JsonAbiArgument[] | null, implicitGenericParametersParam?: number[] ) { diff --git a/packages/abi-coder/src/index.ts b/packages/abi-coder/src/index.ts index 741fcd60b19..ffc8e62a1ba 100644 --- a/packages/abi-coder/src/index.ts +++ b/packages/abi-coder/src/index.ts @@ -2,7 +2,7 @@ export { Coder, InputValue, DecodedValue } from './encoding/coders/AbstractCoder export type { FunctionFragment } from './FunctionFragment'; export * from './encoding/coders'; export { Interface } from './Interface'; -export { JsonAbi, JsonAbiArgument } from './types/JsonAbi'; +export type { JsonAbi } from './types/JsonAbiNew'; export { SCRIPT_FIXED_SIZE, INPUT_COIN_FIXED_SIZE, @@ -14,4 +14,3 @@ export { calculateVmTxMemory, ENCODING_V1, } from './utils/constants'; -export { AbiCoder } from './AbiCoder'; diff --git a/packages/abi-coder/src/types/JsonAbi.ts b/packages/abi-coder/src/types/JsonAbi.ts index 2e481d27aaf..eed9fee4d46 100644 --- a/packages/abi-coder/src/types/JsonAbi.ts +++ b/packages/abi-coder/src/types/JsonAbi.ts @@ -1,8 +1,8 @@ /** * Types for Fuel JSON ABI Format as defined on: - * https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/json-abi-format.md + * https://github.com/FuelLabs/fuel-specs/blob/b40d87d2515727188b9ae2dd23602316c50519c0/src/abi/json-abi-format.md */ -export interface JsonAbi { +export interface JsonAbiOld { readonly types: readonly JsonAbiType[]; readonly loggedTypes: readonly JsonAbiLoggedType[]; readonly functions: readonly JsonAbiFunction[]; diff --git a/packages/abi-coder/src/types/JsonAbiNew.ts b/packages/abi-coder/src/types/JsonAbiNew.ts new file mode 100644 index 00000000000..4d9481c1128 --- /dev/null +++ b/packages/abi-coder/src/types/JsonAbiNew.ts @@ -0,0 +1,101 @@ +/** + * Types for Fuel JSON ABI Format as defined on: + * https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/json-abi-format.md + */ +export interface JsonAbi { + readonly specVersion: string; + readonly encodingVersion: string; + readonly programType: string; + readonly concreteTypes: readonly ConcreteType[]; + readonly metadataTypes: readonly MetadataType[]; + readonly functions: readonly AbiFunction[]; + readonly loggedTypes: readonly LoggedType[]; + readonly messagesTypes: readonly MessageType[]; + readonly configurables: readonly Configurable[]; +} + +export interface ConcreteType { + readonly type: string; + readonly concreteTypeId: string; + readonly metadataTypeId?: number; + readonly typeArguments?: readonly string[]; +} + +export interface MetadataType { + readonly type: string; + readonly metadataTypeId: number; + readonly components?: readonly Component[]; + readonly typeParameters?: readonly number[]; +} + +export interface Component extends TypeArgument { + readonly name: string; +} + +export interface TypeArgument { + readonly typeId: number | string; // the type metadata declaration ID or type concrete declaration hash based ID of the type of the component. + readonly typeArguments?: readonly TypeArgument[]; +} + +export interface AbiFunction { + readonly name: string; + readonly inputs: readonly AbiFunctionInput[]; + readonly output: string; + readonly attributes: readonly AbiFunctionAttribute[] | null; +} + +export interface AbiFunctionInput { + readonly name: string; + readonly concreteTypeId: string; +} + +export type AbiFunctionAttribute = { + readonly name: string; + readonly arguments?: string[]; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type AbiFunctionAttributeTodo = + | StorageAttr + | PayableAttr + | TestAttr + | InlineAttr + | DocCommentAttr + | DocAttr; + +export interface PayableAttr { + readonly name: 'payable'; +} +export interface StorageAttr { + readonly name: 'storage'; + readonly arguments: readonly ('read' | 'write')[]; +} +export interface TestAttr { + readonly name: 'test'; +} +export interface InlineAttr { + readonly name: 'inline'; + readonly arguments: 'never' | 'always'; +} +export interface DocCommentAttr { + readonly name: 'doc-comment'; + readonly arguments: string[]; +} +export interface DocAttr { + readonly name: 'doc'; +} + +export interface LoggedType { + readonly logId: string; + readonly concreteTypeId: string; // the _type concrete declaration_ hash based ID of the value being logged. +} + +export interface MessageType { + readonly message_id: string; + readonly concreteTypeId: string; +} +export interface Configurable { + readonly name: string; + readonly concreteTypeId: string; + readonly offset: number; +} diff --git a/packages/abi-coder/src/utils/getFunctionInputs.ts b/packages/abi-coder/src/utils/getFunctionInputs.ts index e487f21f8a2..44c9abcaec3 100644 --- a/packages/abi-coder/src/utils/getFunctionInputs.ts +++ b/packages/abi-coder/src/utils/getFunctionInputs.ts @@ -1,4 +1,4 @@ -import type { JsonAbi, JsonAbiArgument } from '../types/JsonAbi'; +import type { JsonAbiOld, JsonAbiArgument } from '../types/JsonAbi'; import { optionRegEx, VOID_TYPE } from './constants'; import { findTypeById } from './json-abi'; @@ -8,7 +8,7 @@ export type FunctionInput = TArg }; export const getFunctionInputs = (params: { - jsonAbi: JsonAbi; + jsonAbi: JsonAbiOld; inputs: readonly JsonAbiArgument[]; }): Array => { const { jsonAbi, inputs } = params; diff --git a/packages/abi-coder/src/utils/json-abi.test.ts b/packages/abi-coder/src/utils/json-abi.test.ts index be661e335a2..d81d28576a4 100644 --- a/packages/abi-coder/src/utils/json-abi.test.ts +++ b/packages/abi-coder/src/utils/json-abi.test.ts @@ -1,5 +1,5 @@ import type { ResolvedAbiType } from '../ResolvedAbiType'; -import type { JsonAbi, JsonAbiArgument } from '../types/JsonAbi'; +import type { JsonAbiOld, JsonAbiArgument } from '../types/JsonAbi'; import { ENCODING_V1 } from './constants'; import { @@ -10,7 +10,7 @@ import { getEncodingVersion, } from './json-abi'; -const MOCK_ABI: JsonAbi = { +const MOCK_ABI: JsonAbiOld = { types: [ { typeId: 1, type: '()', components: [], typeParameters: [] }, { typeId: 2, type: 'u256', components: [], typeParameters: [] }, diff --git a/packages/abi-coder/src/utils/json-abi.ts b/packages/abi-coder/src/utils/json-abi.ts index 1091554c8a4..0d168cd522f 100644 --- a/packages/abi-coder/src/utils/json-abi.ts +++ b/packages/abi-coder/src/utils/json-abi.ts @@ -1,7 +1,8 @@ import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { ResolvedAbiType } from '../ResolvedAbiType'; -import type { JsonAbi, JsonAbiArgument, JsonAbiFunction, JsonAbiType } from '../types/JsonAbi'; +import type { JsonAbiOld, JsonAbiArgument, JsonAbiType } from '../types/JsonAbi'; +import type { AbiFunction, JsonAbi } from '../types/JsonAbiNew'; import { ENCODING_V1, VOID_TYPE, type EncodingVersion } from './constants'; @@ -33,7 +34,7 @@ export const getEncodingVersion = (encoding?: string): EncodingVersion => { * @param name - the name of the function to find * @returns the JsonAbi function object */ -export const findFunctionByName = (abi: JsonAbi, name: string): JsonAbiFunction => { +export const findFunctionByName = (abi: JsonAbi, name: string): AbiFunction => { const fn = abi.functions.find((f) => f.name === name); if (!fn) { throw new FuelError( @@ -51,7 +52,7 @@ export const findFunctionByName = (abi: JsonAbi, name: string): JsonAbiFunction * @param typeId - the typeId of the type to find * @returns the JsonAbi type object */ -export const findTypeById = (abi: JsonAbi, typeId: number): JsonAbiType => { +export const findTypeById = (abi: JsonAbiOld, typeId: number): JsonAbiType => { const type = abi.types.find((t) => t.typeId === typeId); if (!type) { throw new FuelError( @@ -71,7 +72,7 @@ export const findTypeById = (abi: JsonAbi, typeId: number): JsonAbiType => { * @returns the list of non-void inputs */ export const findNonVoidInputs = ( - abi: JsonAbi, + abi: JsonAbiOld, inputs: readonly JsonAbiArgument[] ): JsonAbiArgument[] => inputs.filter((input) => findTypeById(abi, input.type).type !== VOID_TYPE); diff --git a/packages/abi-coder/src/utils/transpile-abi.ts b/packages/abi-coder/src/utils/transpile-abi.ts new file mode 100644 index 00000000000..5507d415dec --- /dev/null +++ b/packages/abi-coder/src/utils/transpile-abi.ts @@ -0,0 +1,154 @@ +/* eslint-disable no-restricted-globals */ +/* eslint-disable no-param-reassign */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +const findTypeByConcreteId = (types, id) => types.find((x) => x.concreteTypeId === id); + +const findConcreteTypeById = (abi, id) => abi.concreteTypes.find((x) => x.concreteTypeId === id); + +function finsertTypeIdByConcreteTypeId(abi, types, id) { + const concreteType = findConcreteTypeById(abi, id); + + if (concreteType.metadataTypeId !== undefined) { + return concreteType.metadataTypeId; + } + + const type = findTypeByConcreteId(types, id); + if (type) { + return type.typeId; + } + + types.push({ + typeId: types.length, + type: concreteType.type, + components: parseComponents(concreteType.components), + concreteTypeId: id, + typeParameters: concreteType.typeParameters ?? null, + originalConcreteTypeId: concreteType?.concreteTypeId, + }); + + return types.length - 1; +} + +function parseFunctionTypeArguments(abi, types, concreteType) { + return ( + concreteType.typeArguments?.map((cTypeId) => { + const self = findConcreteTypeById(abi, cTypeId); + const type = !isNaN(cTypeId) ? cTypeId : finsertTypeIdByConcreteTypeId(abi, types, cTypeId); + return { + name: '', + type, + // originalTypeId: cTypeId, + typeArguments: parseFunctionTypeArguments(abi, types, self), + }; + }) ?? null + ); +} + +export function parseConcreteType(abi, types, concreteTypeId, name) { + const type = finsertTypeIdByConcreteTypeId(abi, types, concreteTypeId); + const concrete = findConcreteTypeById(abi, concreteTypeId); + return { + name: name ?? '', + type, + // concreteTypeId, + typeArguments: parseFunctionTypeArguments(abi, types, concrete), + }; +} + +function parseComponents(abi, types, components) { + return ( + components?.map((component) => { + const { typeId, name, typeArguments } = component; + const type = !isNaN(typeId) ? typeId : finsertTypeIdByConcreteTypeId(abi, types, typeId); + return { + name, + type, + // originalTypeId: typeId, + typeArguments: parseComponents(abi, types, typeArguments), + }; + }) ?? null + ); +} + +/** + * This will transpile new ABIs (spec: "1") to the old format. + * + * The new format got these new props: + * - `specVersion`, + * - `concreteTypes` + * - `metadataTypes` + * + * The old format contains only: + * - `types` + */ +export function transpileAbi(abi) { + // do not transpile older versions + if (!abi.specVersion) { + return abi; + } + + // 0. define empty types array + const types = []; + + /** + * Helpers + */ + + /** + * Transpiling + */ + + // 1. root level of metadata types + abi.metadataTypes.forEach((m) => { + const t = { + typeId: m.metadataTypeId, + type: m.type, + components: m.components ?? (m.type === '()' ? [] : null), + typeParameters: m.typeParameters ?? null, + }; + types.push(t); + }); + + // 2. the metadata's components + types.forEach((t) => { + t.components = parseComponents(abi, types, t.components); + }); + + // 3. functions inputs/outputs + const functions = abi.functions.map((fn) => { + const inputs = fn.inputs.map(({ concreteTypeId, name }) => + parseConcreteType(abi, types, concreteTypeId, name) + ); + const output = parseConcreteType(abi, types, fn.output, ''); + return { ...fn, inputs, output }; + }); + + // 4. configurables + const configurables = abi.configurables.map((conf) => ({ + name: conf.name, + configurableType: parseConcreteType(abi, types, conf.concreteTypeId), + offset: conf.offset, + })); + + // 5. loggedTypes + const loggedTypes = abi.loggedTypes.map((log) => ({ + logId: log.logId, + loggedType: parseConcreteType(abi, types, log.concreteTypeId), + })); + + // transpiled ABI + const transpiled = { + encoding: abi.encodingVersion, + types, + functions, + loggedTypes, + messagesTypes: abi.messagesTypes, + configurables, + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return transpiled as any; +} diff --git a/packages/abi-coder/test/Interface.test.ts b/packages/abi-coder/test/Interface.test.ts index 702aed14747..4e28fa37628 100644 --- a/packages/abi-coder/test/Interface.test.ts +++ b/packages/abi-coder/test/Interface.test.ts @@ -4,6 +4,7 @@ import { concat } from '@fuel-ts/utils'; import { Interface } from '../src/Interface'; import type { JsonAbiConfigurable } from '../src/types/JsonAbi'; +import type { AbiFunction } from '../src/types/JsonAbiNew'; import { AbiCoderProjectsEnum, getCoderForcProject } from './fixtures/forc-projects'; import { @@ -580,16 +581,30 @@ describe('Abi interface', () => { ])( '$title: $value', ({ fn, title: _title, value, encodedValue, decodedTransformer, offset }) => { - const encoded = Array.isArray(value) - ? fn.encodeArguments(value) - : fn.encodeArguments([value]); + const fnArguments = Array.isArray(value) ? value : [value]; + const encodedArguments = fn.encodeArguments(fnArguments); const encodedVal = encodedValue instanceof Function ? encodedValue(value, offset) : encodedValue; const expectedEncoded = encodedVal instanceof Uint8Array ? encodedVal : concat(encodedVal); - expect(encoded).toEqual(expectedEncoded); + expect(encodedArguments).toEqual(expectedEncoded); + + const jsonFn = exhaustiveExamplesInterface.jsonAbi.functions.find( + (f) => f.name === fn.name + ) as AbiFunction; + + // test Interface.encodeType + const argsEncodedAsSingleTypes = jsonFn.inputs + .map((i) => i.concreteTypeId) + .map((arg, idx) => exhaustiveExamplesInterface.encodeType(arg, fnArguments[idx])); + + argsEncodedAsSingleTypes?.forEach((arg, idx, arr) => { + const argOffset = arr.slice(0, idx).reduce((result, val) => result + val.length, 0); + + expect(arg).toEqual(expectedEncoded.slice(argOffset, argOffset + arg.length)); + }); let decoded = fn.decodeOutput(expectedEncoded)[0]; @@ -600,6 +615,18 @@ describe('Abi interface', () => { const expectedDecoded = Array.isArray(value) && value.length === 1 ? value[0] : value; // the conditional is when the input is a SINGLE array/tuple - then de-nest it expect(decoded).toStrictEqual(expectedDecoded); + + // test Interface.decodeType + let decodedType = exhaustiveExamplesInterface.decodeType( + jsonFn.output, + expectedEncoded + )[0]; + + if (decodedTransformer) { + decodedType = decodedTransformer(decodedType); + } + + expect(decodedType).toEqual(expectedDecoded); } ); }); @@ -728,20 +755,4 @@ describe('Abi interface', () => { }); }); }); - - describe('abi types', () => { - it('should return the correct type when it exists', () => { - const abiType = exhaustiveExamplesInterface.getTypeById(72); - expect(abiType.type).toEqual('()'); - expect(abiType.components).toBeDefined(); - expect(abiType.typeParameters).toBeNull(); - }); - - it('should throw an error when type does not exist', () => { - const id = 999; - expect(() => exhaustiveExamplesInterface.getTypeById(id)).toThrowError( - `Type with typeId '${id}' doesn't exist in the ABI.` - ); - }); - }); }); diff --git a/packages/abi-typegen/package.json b/packages/abi-typegen/package.json index 44b8a207f4b..b569ad81f4e 100644 --- a/packages/abi-typegen/package.json +++ b/packages/abi-typegen/package.json @@ -43,10 +43,9 @@ "dist" ], "scripts": { - "pretest": "run-s build:forc:debug build:forc:release", + "pretest": "pnpm build:forc", "build": "tsup", - "build:forc:debug": "pnpm fuels-forc build -p test/fixtures/forc-projects", - "build:forc:release": "pnpm fuels-forc build -p test/fixtures/forc-projects --release", + "build:forc": "pnpm fuels-forc build -p test/fixtures/forc-projects --release", "postbuild": "tsx ../../scripts/postbuild.ts" }, "license": "Apache-2.0", diff --git a/packages/abi-typegen/src/abi/Abi.ts b/packages/abi-typegen/src/abi/Abi.ts index 849b1ec4d73..0ea8c85d7f0 100644 --- a/packages/abi-typegen/src/abi/Abi.ts +++ b/packages/abi-typegen/src/abi/Abi.ts @@ -5,9 +5,11 @@ import type { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import type { IConfigurable } from '../types/interfaces/IConfigurable'; import type { IFunction } from '../types/interfaces/IFunction'; import type { IType } from '../types/interfaces/IType'; -import type { JsonAbi } from '../types/interfaces/JsonAbi'; +import type { JsonAbiOld } from '../types/interfaces/JsonAbi'; +import type { JsonAbi } from '../types/interfaces/JsonAbiNew'; import { parseFunctions } from '../utils/parseFunctions'; import { parseTypes } from '../utils/parseTypes'; +import { transpileAbi } from '../utils/transpile-abi'; import { Configurable } from './configurable/Configurable'; @@ -81,11 +83,12 @@ export class Abi { } parse() { + const transpiled = transpileAbi(this.rawContents) as JsonAbiOld; const { types: rawAbiTypes, functions: rawAbiFunctions, configurables: rawAbiConfigurables, - } = this.rawContents; + } = transpiled; const types = parseTypes({ rawAbiTypes }); const functions = parseFunctions({ rawAbiFunctions, types }); diff --git a/packages/abi-typegen/src/abi/configurable/Configurable.test.ts b/packages/abi-typegen/src/abi/configurable/Configurable.test.ts index 37d5022084e..338bbc890a6 100644 --- a/packages/abi-typegen/src/abi/configurable/Configurable.test.ts +++ b/packages/abi-typegen/src/abi/configurable/Configurable.test.ts @@ -43,7 +43,9 @@ describe('Configurable.ts', () => { it('should get configurable declaration with type', () => { const { type, findType } = mockAllDeps(); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.PREDICATE_WITH_CONFIGURABLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.PREDICATE_WITH_CONFIGURABLE, { + transpile: true, + }); const { configurables } = project.abiContents; diff --git a/packages/abi-typegen/src/abi/functions/Function.test.ts b/packages/abi-typegen/src/abi/functions/Function.test.ts index 8270f48e981..e08696a8145 100644 --- a/packages/abi-typegen/src/abi/functions/Function.test.ts +++ b/packages/abi-typegen/src/abi/functions/Function.test.ts @@ -14,7 +14,7 @@ describe('Function.ts', () => { Method: `getDeclaration` */ test('should properly get function declaration', () => { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.MINIMAL); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.MINIMAL, { transpile: true }); const { types: rawAbiTypes, functions } = project.abiContents; @@ -35,7 +35,9 @@ describe('Function.ts', () => { Inputs / Output */ test('should compute i/o types for Vector', () => { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.VECTOR_SIMPLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.VECTOR_SIMPLE, { + transpile: true, + }); const { types: rawAbiTypes, functions } = project.abiContents; @@ -51,7 +53,9 @@ describe('Function.ts', () => { }); test('should build i/o types for Option', () => { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.OPTION_SIMPLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.OPTION_SIMPLE, { + transpile: true, + }); const { types: rawAbiTypes, functions } = project.abiContents; diff --git a/packages/abi-typegen/src/abi/types/ArrayType.test.ts b/packages/abi-typegen/src/abi/types/ArrayType.test.ts index c4100acf62e..fb136069f31 100644 --- a/packages/abi-typegen/src/abi/types/ArrayType.test.ts +++ b/packages/abi-typegen/src/abi/types/ArrayType.test.ts @@ -29,7 +29,9 @@ describe('ArrayType.ts', () => { test('should properly parse type attributes: simple', () => { const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_WITH_ARRAY); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_WITH_ARRAY, { + transpile: true, + }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); @@ -47,7 +49,9 @@ describe('ArrayType.ts', () => { test('should properly parse type attributes: nested', () => { const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.ARRAY_WITH_GENERICS); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.ARRAY_WITH_GENERICS, { + transpile: true, + }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/abi/types/EnumType.test.ts b/packages/abi-typegen/src/abi/types/EnumType.test.ts index 7e462ff8374..ee15357833b 100644 --- a/packages/abi-typegen/src/abi/types/EnumType.test.ts +++ b/packages/abi-typegen/src/abi/types/EnumType.test.ts @@ -21,7 +21,7 @@ describe('EnumType.ts', () => { function getTypesForContract(project: AbiTypegenProjectsEnum) { const { abiContents: { types: rawTypes }, - } = getTypegenForcProject(project); + } = getTypegenForcProject(project, { transpile: true }); const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts b/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts index 7b4c4ac67b1..8833b5031b9 100644 --- a/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts +++ b/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts @@ -18,7 +18,7 @@ describe('EvmAddressType.ts', () => { test('should properly parse type attributes', () => { const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.EVM_ADDRESS); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.EVM_ADDRESS, { transpile: true }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/abi/types/OptionType.test.ts b/packages/abi-typegen/src/abi/types/OptionType.test.ts index c827dea99db..db35d120f6a 100644 --- a/packages/abi-typegen/src/abi/types/OptionType.test.ts +++ b/packages/abi-typegen/src/abi/types/OptionType.test.ts @@ -17,7 +17,9 @@ describe('OptionType.ts', () => { Test helpers */ function getTypesForContract() { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.OPTION_SIMPLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.OPTION_SIMPLE, { + transpile: true, + }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/abi/types/ResultType.test.ts b/packages/abi-typegen/src/abi/types/ResultType.test.ts index 97dd44720c7..0016d834541 100644 --- a/packages/abi-typegen/src/abi/types/ResultType.test.ts +++ b/packages/abi-typegen/src/abi/types/ResultType.test.ts @@ -16,7 +16,7 @@ describe('ResultType.ts', () => { Test helpers */ function getResultType() { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.FULL); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.FULL, { transpile: true }); const rawTypes = project.abiContents.types as JsonAbiType[]; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const rawAbiTypes = [rawTypes.find((t) => t.type === 'enum std::result::Result')!]; diff --git a/packages/abi-typegen/src/abi/types/StructType.test.ts b/packages/abi-typegen/src/abi/types/StructType.test.ts index e3609138a5e..32e91191479 100644 --- a/packages/abi-typegen/src/abi/types/StructType.test.ts +++ b/packages/abi-typegen/src/abi/types/StructType.test.ts @@ -21,7 +21,9 @@ describe('StructType.ts', () => { test('should properly parse type attributes', () => { const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_SIMPLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_SIMPLE, { + transpile: true, + }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/abi/types/TupleType.test.ts b/packages/abi-typegen/src/abi/types/TupleType.test.ts index f3b8ab3e6c8..f9a8e032129 100644 --- a/packages/abi-typegen/src/abi/types/TupleType.test.ts +++ b/packages/abi-typegen/src/abi/types/TupleType.test.ts @@ -17,7 +17,7 @@ describe('TupleType.ts', () => { test('should properly parse type attributes', () => { const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); - const project = getTypegenForcProject(AbiTypegenProjectsEnum.TUPLE_SIMPLE); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.TUPLE_SIMPLE, { transpile: true }); const rawTypes = project.abiContents.types; const types = rawTypes.map((rawAbiType: JsonAbiType) => makeType({ rawAbiType })); diff --git a/packages/abi-typegen/src/runTypegen.ts b/packages/abi-typegen/src/runTypegen.ts index a8445a78753..04e1031053f 100644 --- a/packages/abi-typegen/src/runTypegen.ts +++ b/packages/abi-typegen/src/runTypegen.ts @@ -1,5 +1,4 @@ import { ErrorCode, FuelError } from '@fuel-ts/errors'; -import { transpileAbi } from '@fuel-ts/utils'; import { readFileSync, writeFileSync } from 'fs'; import { globSync } from 'glob'; import mkdirp from 'mkdirp'; @@ -53,11 +52,10 @@ export function runTypegen(params: IGenerateFilesParams) { Assembling file paths x contents */ const abiFiles = filepaths.map((filepath) => { - const raw = readFileSync(filepath, 'utf-8'); - const transpiled = JSON.stringify(transpileAbi(JSON.parse(raw)), null, 2); + const contents = readFileSync(filepath, 'utf-8'); const abi: IFile = { path: filepath, - contents: transpiled, + contents, }; return abi; diff --git a/packages/abi-typegen/src/templates/contract/dts.test.ts b/packages/abi-typegen/src/templates/contract/dts.test.ts index 10814b41565..15e6221b6ab 100644 --- a/packages/abi-typegen/src/templates/contract/dts.test.ts +++ b/packages/abi-typegen/src/templates/contract/dts.test.ts @@ -14,15 +14,12 @@ import { renderDtsTemplate } from './dts'; * @group node */ describe('templates/dts', () => { - test.each(['debug', 'release'])('should render dts template [%s]', (build) => { + test('should render dts template', () => { // mocking const { restore } = mockVersions(); // executing - const project = getTypegenForcProject( - AbiTypegenProjectsEnum.FULL, - build as 'release' | 'debug' - ); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.FULL); const { abiContents: rawContents } = project; const abi = new Abi({ diff --git a/packages/abi-typegen/src/types/interfaces/JsonAbi.ts b/packages/abi-typegen/src/types/interfaces/JsonAbi.ts index 2e481d27aaf..eed9fee4d46 100644 --- a/packages/abi-typegen/src/types/interfaces/JsonAbi.ts +++ b/packages/abi-typegen/src/types/interfaces/JsonAbi.ts @@ -1,8 +1,8 @@ /** * Types for Fuel JSON ABI Format as defined on: - * https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/json-abi-format.md + * https://github.com/FuelLabs/fuel-specs/blob/b40d87d2515727188b9ae2dd23602316c50519c0/src/abi/json-abi-format.md */ -export interface JsonAbi { +export interface JsonAbiOld { readonly types: readonly JsonAbiType[]; readonly loggedTypes: readonly JsonAbiLoggedType[]; readonly functions: readonly JsonAbiFunction[]; diff --git a/packages/abi-typegen/src/types/interfaces/JsonAbiNew.ts b/packages/abi-typegen/src/types/interfaces/JsonAbiNew.ts new file mode 100644 index 00000000000..4d9481c1128 --- /dev/null +++ b/packages/abi-typegen/src/types/interfaces/JsonAbiNew.ts @@ -0,0 +1,101 @@ +/** + * Types for Fuel JSON ABI Format as defined on: + * https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/json-abi-format.md + */ +export interface JsonAbi { + readonly specVersion: string; + readonly encodingVersion: string; + readonly programType: string; + readonly concreteTypes: readonly ConcreteType[]; + readonly metadataTypes: readonly MetadataType[]; + readonly functions: readonly AbiFunction[]; + readonly loggedTypes: readonly LoggedType[]; + readonly messagesTypes: readonly MessageType[]; + readonly configurables: readonly Configurable[]; +} + +export interface ConcreteType { + readonly type: string; + readonly concreteTypeId: string; + readonly metadataTypeId?: number; + readonly typeArguments?: readonly string[]; +} + +export interface MetadataType { + readonly type: string; + readonly metadataTypeId: number; + readonly components?: readonly Component[]; + readonly typeParameters?: readonly number[]; +} + +export interface Component extends TypeArgument { + readonly name: string; +} + +export interface TypeArgument { + readonly typeId: number | string; // the type metadata declaration ID or type concrete declaration hash based ID of the type of the component. + readonly typeArguments?: readonly TypeArgument[]; +} + +export interface AbiFunction { + readonly name: string; + readonly inputs: readonly AbiFunctionInput[]; + readonly output: string; + readonly attributes: readonly AbiFunctionAttribute[] | null; +} + +export interface AbiFunctionInput { + readonly name: string; + readonly concreteTypeId: string; +} + +export type AbiFunctionAttribute = { + readonly name: string; + readonly arguments?: string[]; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type AbiFunctionAttributeTodo = + | StorageAttr + | PayableAttr + | TestAttr + | InlineAttr + | DocCommentAttr + | DocAttr; + +export interface PayableAttr { + readonly name: 'payable'; +} +export interface StorageAttr { + readonly name: 'storage'; + readonly arguments: readonly ('read' | 'write')[]; +} +export interface TestAttr { + readonly name: 'test'; +} +export interface InlineAttr { + readonly name: 'inline'; + readonly arguments: 'never' | 'always'; +} +export interface DocCommentAttr { + readonly name: 'doc-comment'; + readonly arguments: string[]; +} +export interface DocAttr { + readonly name: 'doc'; +} + +export interface LoggedType { + readonly logId: string; + readonly concreteTypeId: string; // the _type concrete declaration_ hash based ID of the value being logged. +} + +export interface MessageType { + readonly message_id: string; + readonly concreteTypeId: string; +} +export interface Configurable { + readonly name: string; + readonly concreteTypeId: string; + readonly offset: number; +} diff --git a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts index dbb7b115f82..8595704badb 100644 --- a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts +++ b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts @@ -12,7 +12,7 @@ import { parseTypeArguments } from './parseTypeArguments'; Sample ABI with components in both fashions: — WITH and WITHOUT `typeArguments` */ -const defautRawTypes: JsonAbiType[] = [ +const defautRawTypes: readonly JsonAbiType[] = [ { typeId: 0, type: 'bool', @@ -106,7 +106,7 @@ describe('parseTypeArguments.ts', () => { }); test('should fallback to void for null outputs', () => { - const project = getTypegenForcProject(AbiTypegenProjectsEnum.FN_VOID); + const project = getTypegenForcProject(AbiTypegenProjectsEnum.FN_VOID, { transpile: true }); const types = bundleTypes(project.abiContents.types); const typeArguments = [project.abiContents.functions[0].output]; diff --git a/packages/abi-typegen/src/utils/transpile-abi.ts b/packages/abi-typegen/src/utils/transpile-abi.ts new file mode 100644 index 00000000000..5507d415dec --- /dev/null +++ b/packages/abi-typegen/src/utils/transpile-abi.ts @@ -0,0 +1,154 @@ +/* eslint-disable no-restricted-globals */ +/* eslint-disable no-param-reassign */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +const findTypeByConcreteId = (types, id) => types.find((x) => x.concreteTypeId === id); + +const findConcreteTypeById = (abi, id) => abi.concreteTypes.find((x) => x.concreteTypeId === id); + +function finsertTypeIdByConcreteTypeId(abi, types, id) { + const concreteType = findConcreteTypeById(abi, id); + + if (concreteType.metadataTypeId !== undefined) { + return concreteType.metadataTypeId; + } + + const type = findTypeByConcreteId(types, id); + if (type) { + return type.typeId; + } + + types.push({ + typeId: types.length, + type: concreteType.type, + components: parseComponents(concreteType.components), + concreteTypeId: id, + typeParameters: concreteType.typeParameters ?? null, + originalConcreteTypeId: concreteType?.concreteTypeId, + }); + + return types.length - 1; +} + +function parseFunctionTypeArguments(abi, types, concreteType) { + return ( + concreteType.typeArguments?.map((cTypeId) => { + const self = findConcreteTypeById(abi, cTypeId); + const type = !isNaN(cTypeId) ? cTypeId : finsertTypeIdByConcreteTypeId(abi, types, cTypeId); + return { + name: '', + type, + // originalTypeId: cTypeId, + typeArguments: parseFunctionTypeArguments(abi, types, self), + }; + }) ?? null + ); +} + +export function parseConcreteType(abi, types, concreteTypeId, name) { + const type = finsertTypeIdByConcreteTypeId(abi, types, concreteTypeId); + const concrete = findConcreteTypeById(abi, concreteTypeId); + return { + name: name ?? '', + type, + // concreteTypeId, + typeArguments: parseFunctionTypeArguments(abi, types, concrete), + }; +} + +function parseComponents(abi, types, components) { + return ( + components?.map((component) => { + const { typeId, name, typeArguments } = component; + const type = !isNaN(typeId) ? typeId : finsertTypeIdByConcreteTypeId(abi, types, typeId); + return { + name, + type, + // originalTypeId: typeId, + typeArguments: parseComponents(abi, types, typeArguments), + }; + }) ?? null + ); +} + +/** + * This will transpile new ABIs (spec: "1") to the old format. + * + * The new format got these new props: + * - `specVersion`, + * - `concreteTypes` + * - `metadataTypes` + * + * The old format contains only: + * - `types` + */ +export function transpileAbi(abi) { + // do not transpile older versions + if (!abi.specVersion) { + return abi; + } + + // 0. define empty types array + const types = []; + + /** + * Helpers + */ + + /** + * Transpiling + */ + + // 1. root level of metadata types + abi.metadataTypes.forEach((m) => { + const t = { + typeId: m.metadataTypeId, + type: m.type, + components: m.components ?? (m.type === '()' ? [] : null), + typeParameters: m.typeParameters ?? null, + }; + types.push(t); + }); + + // 2. the metadata's components + types.forEach((t) => { + t.components = parseComponents(abi, types, t.components); + }); + + // 3. functions inputs/outputs + const functions = abi.functions.map((fn) => { + const inputs = fn.inputs.map(({ concreteTypeId, name }) => + parseConcreteType(abi, types, concreteTypeId, name) + ); + const output = parseConcreteType(abi, types, fn.output, ''); + return { ...fn, inputs, output }; + }); + + // 4. configurables + const configurables = abi.configurables.map((conf) => ({ + name: conf.name, + configurableType: parseConcreteType(abi, types, conf.concreteTypeId), + offset: conf.offset, + })); + + // 5. loggedTypes + const loggedTypes = abi.loggedTypes.map((log) => ({ + logId: log.logId, + loggedType: parseConcreteType(abi, types, log.concreteTypeId), + })); + + // transpiled ABI + const transpiled = { + encoding: abi.encodingVersion, + types, + functions, + loggedTypes, + messagesTypes: abi.messagesTypes, + configurables, + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return transpiled as any; +} diff --git a/packages/abi-typegen/test/fixtures/forc-projects/index.ts b/packages/abi-typegen/test/fixtures/forc-projects/index.ts index 062629811a8..05fd05d1c3e 100644 --- a/packages/abi-typegen/test/fixtures/forc-projects/index.ts +++ b/packages/abi-typegen/test/fixtures/forc-projects/index.ts @@ -1,7 +1,9 @@ import { getForcProject } from '@fuel-ts/utils/test-utils'; import { join } from 'path'; -import type { JsonAbi } from '../../../src/index'; +import type { JsonAbiOld } from '../../../src'; +import type { JsonAbi } from '../../../src/types/interfaces/JsonAbiNew'; +import { transpileAbi } from '../../../src/utils/transpile-abi'; export enum AbiTypegenProjectsEnum { ARRAY_OF_ENUMS = 'array-of-enums', @@ -30,12 +32,19 @@ export enum AbiTypegenProjectsEnum { VECTOR_SIMPLE = 'vector-simple', } -export const getTypegenForcProject = ( +export const getTypegenForcProject = ( project: AbiTypegenProjectsEnum, - build: 'release' | 'debug' = 'release' -) => - getForcProject({ + { transpile }: { transpile: T } = { transpile: false as T } +) => { + const result = getForcProject({ projectDir: join(__dirname, project), projectName: project, - build, + build: 'release', }); + + if (transpile) { + result.abiContents = transpileAbi(result.abiContents); + } + + return result; +}; diff --git a/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs b/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs index cc3cf72a04b..3f6df446506 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs @@ -14,43 +14,34 @@ import type { Provider, Account, AbstractAddress, BytesLike, DeployContractOptio import type { MyContractAbi, MyContractAbiInterface } from "../MyContractAbi"; const _abi = { - "encoding": "1", - "types": [ + "programType": "contract", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ { - "typeId": 0, - "type": "str[10]", - "components": null, - "concreteTypeId": "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50", - "typeParameters": null + "type": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" }, { - "typeId": 1, - "type": "bool", - "components": null, - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "typeParameters": null + "type": "str[10]", + "concreteTypeId": "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50" } ], + "metadataTypes": [], "functions": [ { "inputs": [ { "name": "x", - "type": 0, - "typeArguments": null + "concreteTypeId": "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50" }, { "name": "y", - "type": 0, - "typeArguments": null + "concreteTypeId": "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50" } ], "name": "main", - "output": { - "name": "", - "type": 1, - "typeArguments": null - }, + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "attributes": null } ], diff --git a/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/factory.hbs b/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/factory.hbs index c65954f30a6..53a1956b40a 100644 --- a/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/factory.hbs @@ -24,50 +24,38 @@ export type MyPredicateAbiConfigurables = Partial<{ export type MyPredicateAbiInputs = [fee: BigNumberish, address: string]; const _abi = { - "encoding": "1", - "types": [ + "programType": "predicate", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ { - "typeId": 0, - "type": "u8", - "components": null, - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "typeParameters": null - }, - { - "typeId": 1, "type": "b256", - "components": null, - "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", - "typeParameters": null + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" }, { - "typeId": 2, "type": "bool", - "components": null, - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "typeParameters": null + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" } ], + "metadataTypes": [], "functions": [ { "inputs": [ { "name": "fee", - "type": 0, - "typeArguments": null + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" }, { "name": "address", - "type": 1, - "typeArguments": null + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" } ], "name": "main", - "output": { - "name": "", - "type": 2, - "typeArguments": null - }, + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "attributes": null } ], @@ -76,20 +64,12 @@ const _abi = { "configurables": [ { "name": "FEE", - "configurableType": { - "name": "", - "type": 0, - "typeArguments": null - }, + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "offset": 904 }, { "name": "ADDRESS", - "configurableType": { - "name": "", - "type": 1, - "typeArguments": null - }, + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "offset": 872 } ] diff --git a/packages/abi-typegen/test/fixtures/templates/predicate/factory.hbs b/packages/abi-typegen/test/fixtures/templates/predicate/factory.hbs index 128f4a8af9b..d0afaf537cd 100644 --- a/packages/abi-typegen/test/fixtures/templates/predicate/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/predicate/factory.hbs @@ -33,22 +33,89 @@ export type MyPredicateAbiConfigurables = Partial<{ export type MyPredicateAbiInputs = [vec: Vec, enm: MyGenericEnumInput, opt: Option, res: Result, BigNumberish>]; const _abi = { - "encoding": "1", - "types": [ + "programType": "predicate", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "type": "enum MyGenericEnum", + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0", + "metadataTypeId": 1, + "typeArguments": [ + "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + ] + }, + { + "type": "enum std::option::Option", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", + "metadataTypeId": 2, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "enum std::result::Result,u64>", + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da", + "metadataTypeId": 3, + "typeArguments": [ + "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + ] + }, + { + "type": "str[4]", + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + }, + { + "type": "struct MyGenericStruct", + "concreteTypeId": "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "metadataTypeId": 7, + "typeArguments": [ + "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + ] + }, + { + "type": "struct Validation", + "concreteTypeId": "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa", + "metadataTypeId": 8 + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b", + "metadataTypeId": 10, + "typeArguments": [ + "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa" + ] + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ { - "typeId": 0, "type": "()", - "components": [], - "typeParameters": null + "metadataTypeId": 0 }, { - "typeId": 1, "type": "enum MyGenericEnum", + "metadataTypeId": 1, "components": [ { "name": "a", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -56,18 +123,16 @@ const _abi = { ] }, { - "typeId": 2, "type": "enum std::option::Option", + "metadataTypeId": 2, "components": [ { "name": "None", - "type": 0, - "typeArguments": null + "typeId": 0 }, { "name": "Some", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -75,18 +140,16 @@ const _abi = { ] }, { - "typeId": 3, "type": "enum std::result::Result", + "metadataTypeId": 3, "components": [ { "name": "Ok", - "type": 5, - "typeArguments": null + "typeId": 5 }, { "name": "Err", - "type": 4, - "typeArguments": null + "typeId": 4 } ], "typeParameters": [ @@ -95,31 +158,24 @@ const _abi = { ] }, { - "typeId": 4, "type": "generic E", - "components": null, - "typeParameters": null + "metadataTypeId": 4 }, { - "typeId": 5, "type": "generic T", - "components": null, - "typeParameters": null + "metadataTypeId": 5 }, { - "typeId": 6, "type": "raw untyped ptr", - "components": null, - "typeParameters": null + "metadataTypeId": 6 }, { - "typeId": 7, "type": "struct MyGenericStruct", + "metadataTypeId": 7, "components": [ { "name": "a", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -127,35 +183,30 @@ const _abi = { ] }, { - "typeId": 8, "type": "struct Validation", + "metadataTypeId": 8, "components": [ { "name": "has_account", - "type": 11, - "typeArguments": null + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" }, { "name": "total_complete", - "type": 12, - "typeArguments": null + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } - ], - "typeParameters": null + ] }, { - "typeId": 9, "type": "struct std::vec::RawVec", + "metadataTypeId": 9, "components": [ { "name": "ptr", - "type": 6, - "typeArguments": null + "typeId": 6 }, { "name": "cap", - "type": 12, - "typeArguments": null + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } ], "typeParameters": [ @@ -163,64 +214,27 @@ const _abi = { ] }, { - "typeId": 10, "type": "struct std::vec::Vec", + "metadataTypeId": 10, "components": [ { "name": "buf", - "type": 9, + "typeId": 9, "typeArguments": [ { "name": "", - "type": 5, - "typeArguments": null + "typeId": 5 } ] }, { "name": "len", - "type": 12, - "typeArguments": null + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } ], "typeParameters": [ 5 ] - }, - { - "typeId": 11, - "type": "bool", - "components": null, - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "typeParameters": null - }, - { - "typeId": 12, - "type": "u64", - "components": null, - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", - "typeParameters": null - }, - { - "typeId": 13, - "type": "u16", - "components": null, - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "typeParameters": null - }, - { - "typeId": 14, - "type": "u8", - "components": null, - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "typeParameters": null - }, - { - "typeId": 15, - "type": "str[4]", - "components": null, - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", - "typeParameters": null } ], "functions": [ @@ -228,66 +242,23 @@ const _abi = { "inputs": [ { "name": "vec", - "type": 10, - "typeArguments": [ - { - "name": "", - "type": 8, - "typeArguments": null - } - ] + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b" }, { "name": "enm", - "type": 1, - "typeArguments": [ - { - "name": "", - "type": 13, - "typeArguments": null - } - ] + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0" }, { "name": "opt", - "type": 2, - "typeArguments": [ - { - "name": "", - "type": 14, - "typeArguments": null - } - ] + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" }, { "name": "res", - "type": 3, - "typeArguments": [ - { - "name": "", - "type": 7, - "typeArguments": [ - { - "name": "", - "type": 15, - "typeArguments": null - } - ] - }, - { - "name": "", - "type": 12, - "typeArguments": null - } - ] + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da" } ], "name": "main", - "output": { - "name": "", - "type": 11, - "typeArguments": null - }, + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "attributes": null } ], diff --git a/packages/abi-typegen/test/fixtures/templates/script-with-configurable/factory.hbs b/packages/abi-typegen/test/fixtures/templates/script-with-configurable/factory.hbs index 630356065ba..694e74677df 100644 --- a/packages/abi-typegen/test/fixtures/templates/script-with-configurable/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/script-with-configurable/factory.hbs @@ -26,37 +26,38 @@ export type MyScriptAbiConfigurables = Partial<{ }>; const _abi = { - "encoding": "1", - "types": [ + "programType": "script", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "type": "struct Score", + "concreteTypeId": "bd5dbfd643b808e6ef22f73778cc8277cc5f01a4f974359b3169caa06e661d7e", + "metadataTypeId": 0 + } + ], + "metadataTypes": [ { - "typeId": 0, "type": "struct Score", + "metadataTypeId": 0, "components": [ { "name": "user", - "type": 1, - "typeArguments": null + "typeId": 1 }, { "name": "points", - "type": 1, - "typeArguments": null + "typeId": 1 } - ], - "typeParameters": null + ] }, { - "typeId": 1, "type": "u8", - "components": null, - "typeParameters": null - }, - { - "typeId": 2, - "type": "bool", - "components": null, - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "typeParameters": null + "metadataTypeId": 1 } ], "functions": [ @@ -64,16 +65,11 @@ const _abi = { "inputs": [ { "name": "score", - "type": 0, - "typeArguments": null + "concreteTypeId": "bd5dbfd643b808e6ef22f73778cc8277cc5f01a4f974359b3169caa06e661d7e" } ], "name": "main", - "output": { - "name": "", - "type": 2, - "typeArguments": null - }, + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "attributes": null } ], @@ -82,11 +78,7 @@ const _abi = { "configurables": [ { "name": "SHOULD_RETURN", - "configurableType": { - "name": "", - "type": 2, - "typeArguments": null - }, + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "offset": 640 } ] diff --git a/packages/abi-typegen/test/fixtures/templates/script/factory.hbs b/packages/abi-typegen/test/fixtures/templates/script/factory.hbs index 8546c0a56c2..81990815ac4 100644 --- a/packages/abi-typegen/test/fixtures/templates/script/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/script/factory.hbs @@ -30,22 +30,89 @@ type MyScriptAbiInputs = [vec: Vec, enm: MyGenericEnumInput", + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0", + "metadataTypeId": 1, + "typeArguments": [ + "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + ] + }, + { + "type": "enum std::option::Option", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", + "metadataTypeId": 2, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "enum std::result::Result,u64>", + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da", + "metadataTypeId": 3, + "typeArguments": [ + "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + ] + }, + { + "type": "str[4]", + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + }, + { + "type": "struct MyGenericStruct", + "concreteTypeId": "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "metadataTypeId": 7, + "typeArguments": [ + "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + ] + }, + { + "type": "struct Score", + "concreteTypeId": "bd5dbfd643b808e6ef22f73778cc8277cc5f01a4f974359b3169caa06e661d7e", + "metadataTypeId": 8 + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "e84db1295c0b56a640e39ac72b9d47c1c149e52e6d6a910576e488de934cdc97", + "metadataTypeId": 10, + "typeArguments": [ + "bd5dbfd643b808e6ef22f73778cc8277cc5f01a4f974359b3169caa06e661d7e" + ] + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ { - "typeId": 0, "type": "()", - "components": [], - "typeParameters": null + "metadataTypeId": 0 }, { - "typeId": 1, "type": "enum MyGenericEnum", + "metadataTypeId": 1, "components": [ { "name": "a", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -53,18 +120,16 @@ const _abi = { ] }, { - "typeId": 2, "type": "enum std::option::Option", + "metadataTypeId": 2, "components": [ { "name": "None", - "type": 0, - "typeArguments": null + "typeId": 0 }, { "name": "Some", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -72,18 +137,16 @@ const _abi = { ] }, { - "typeId": 3, "type": "enum std::result::Result", + "metadataTypeId": 3, "components": [ { "name": "Ok", - "type": 5, - "typeArguments": null + "typeId": 5 }, { "name": "Err", - "type": 4, - "typeArguments": null + "typeId": 4 } ], "typeParameters": [ @@ -92,31 +155,24 @@ const _abi = { ] }, { - "typeId": 4, "type": "generic E", - "components": null, - "typeParameters": null + "metadataTypeId": 4 }, { - "typeId": 5, "type": "generic T", - "components": null, - "typeParameters": null + "metadataTypeId": 5 }, { - "typeId": 6, "type": "raw untyped ptr", - "components": null, - "typeParameters": null + "metadataTypeId": 6 }, { - "typeId": 7, "type": "struct MyGenericStruct", + "metadataTypeId": 7, "components": [ { "name": "a", - "type": 5, - "typeArguments": null + "typeId": 5 } ], "typeParameters": [ @@ -124,35 +180,30 @@ const _abi = { ] }, { - "typeId": 8, "type": "struct Score", + "metadataTypeId": 8, "components": [ { "name": "user", - "type": 11, - "typeArguments": null + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" }, { "name": "points", - "type": 11, - "typeArguments": null + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" } - ], - "typeParameters": null + ] }, { - "typeId": 9, "type": "struct std::vec::RawVec", + "metadataTypeId": 9, "components": [ { "name": "ptr", - "type": 6, - "typeArguments": null + "typeId": 6 }, { "name": "cap", - "type": 12, - "typeArguments": null + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } ], "typeParameters": [ @@ -160,64 +211,27 @@ const _abi = { ] }, { - "typeId": 10, "type": "struct std::vec::Vec", + "metadataTypeId": 10, "components": [ { "name": "buf", - "type": 9, + "typeId": 9, "typeArguments": [ { "name": "", - "type": 5, - "typeArguments": null + "typeId": 5 } ] }, { "name": "len", - "type": 12, - "typeArguments": null + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } ], "typeParameters": [ 5 ] - }, - { - "typeId": 11, - "type": "u8", - "components": null, - "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", - "typeParameters": null - }, - { - "typeId": 12, - "type": "u64", - "components": null, - "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", - "typeParameters": null - }, - { - "typeId": 13, - "type": "u16", - "components": null, - "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", - "typeParameters": null - }, - { - "typeId": 14, - "type": "str[4]", - "components": null, - "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", - "typeParameters": null - }, - { - "typeId": 15, - "type": "bool", - "components": null, - "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "typeParameters": null } ], "functions": [ @@ -225,66 +239,23 @@ const _abi = { "inputs": [ { "name": "vec", - "type": 10, - "typeArguments": [ - { - "name": "", - "type": 8, - "typeArguments": null - } - ] + "concreteTypeId": "e84db1295c0b56a640e39ac72b9d47c1c149e52e6d6a910576e488de934cdc97" }, { "name": "enm", - "type": 1, - "typeArguments": [ - { - "name": "", - "type": 13, - "typeArguments": null - } - ] + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0" }, { "name": "opt", - "type": 2, - "typeArguments": [ - { - "name": "", - "type": 11, - "typeArguments": null - } - ] + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" }, { "name": "res", - "type": 3, - "typeArguments": [ - { - "name": "", - "type": 7, - "typeArguments": [ - { - "name": "", - "type": 14, - "typeArguments": null - } - ] - }, - { - "name": "", - "type": 12, - "typeArguments": null - } - ] + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da" } ], "name": "main", - "output": { - "name": "", - "type": 15, - "typeArguments": null - }, + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "attributes": null } ], diff --git a/packages/account/test/fixtures/predicate-abi.ts b/packages/account/test/fixtures/predicate-abi.ts index c1f6d092238..91bf0b52c06 100644 --- a/packages/account/test/fixtures/predicate-abi.ts +++ b/packages/account/test/fixtures/predicate-abi.ts @@ -1,35 +1,30 @@ import type { JsonAbi } from '@fuel-ts/abi-coder'; export const predicateAbi: JsonAbi = { - types: [ + programType: 'predicate', + specVersion: '1', + encodingVersion: '1', + concreteTypes: [ { - typeId: 0, type: 'bool', - components: null, - typeParameters: null, + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, { - typeId: 1, type: 'b256', - components: null, - typeParameters: null, + concreteTypeId: 'a760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, ], + metadataTypes: [], functions: [ { inputs: [ { name: 'data', - type: 1, - typeArguments: null, + concreteTypeId: 'a760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, ], name: 'main', - output: { - name: '', - type: 0, - typeArguments: null, - }, + output: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', attributes: null, }, ], diff --git a/packages/account/test/fixtures/transaction-summary.ts b/packages/account/test/fixtures/transaction-summary.ts index 1b0b3e8c2a1..04660b3b57e 100644 --- a/packages/account/test/fixtures/transaction-summary.ts +++ b/packages/account/test/fixtures/transaction-summary.ts @@ -1,3 +1,4 @@ +import type { JsonAbi } from '@fuel-ts/abi-coder'; import { bn } from '@fuel-ts/math'; import type { InputCoin, @@ -258,7 +259,10 @@ export const MOCK_ABI_MAP: AbiMap = { loggedTypes: [], messagesTypes: [], configurables: [], - }, + } as unknown as JsonAbi, // used in skipped test + // packages/account/src/providers/transaction-summary/operations.test.ts + // "should ensure getContractCallOperations return contract call operations with calls details" + // when the test is unskipped, it'll fail and this mock can be adjusted or deleted }; export const CONTRACT_CALL_ABI = diff --git a/packages/account/test/fuel-wallet-connector.test.ts b/packages/account/test/fuel-wallet-connector.test.ts index 6411461fc9d..720e83190c2 100644 --- a/packages/account/test/fuel-wallet-connector.test.ts +++ b/packages/account/test/fuel-wallet-connector.test.ts @@ -283,7 +283,11 @@ describe('Fuel Connector', () => { connectors: [new MockConnector()], }); const isAdded = await fuel.addABI('0x001123', { - types: [], + concreteTypes: [], + metadataTypes: [], + encodingVersion: '1', + programType: 'contract', + specVersion: '1', loggedTypes: [], functions: [], messagesTypes: [], diff --git a/packages/program/src/contract.test.ts b/packages/program/src/contract.test.ts index d861b27d4d0..44c4d23e043 100644 --- a/packages/program/src/contract.test.ts +++ b/packages/program/src/contract.test.ts @@ -6,12 +6,10 @@ import Contract from './contract'; const CONTRACT_ID = '0x0101010101010101010101010101010101010101010101010101010101010101'; const ABI: JsonAbi = { - types: [ + concreteTypes: [ { - typeId: 0, + concreteTypeId: 'asdf', type: 'u64', - typeParameters: null, - components: null, }, ], functions: [ @@ -19,22 +17,21 @@ const ABI: JsonAbi = { inputs: [ { name: 'input', - type: 0, - typeArguments: null, + concreteTypeId: 'asdf', }, ], name: 'foo', - output: { - type: 0, - typeArguments: null, - name: '', - }, + output: 'asdf', attributes: null, }, ], loggedTypes: [], messagesTypes: [], configurables: [], + encodingVersion: '1', + metadataTypes: [], + programType: 'contract', + specVersion: '1', }; /** diff --git a/packages/script/src/script.test.ts b/packages/script/src/script.test.ts index ef107a70911..634f6effb5f 100644 --- a/packages/script/src/script.test.ts +++ b/packages/script/src/script.test.ts @@ -13,7 +13,7 @@ import { ReceiptType } from '@fuel-ts/transactions'; import { arrayify } from '@fuel-ts/utils'; import { getScriptForcProject, ScriptProjectsEnum } from '../test/fixtures'; -import { jsonAbiMock, jsonAbiFragmentMock } from '../test/mocks'; +import { jsonAbiMock } from '../test/mocks'; import { Script } from './index'; @@ -127,7 +127,7 @@ describe('Script', () => { it('should throw if script has no configurable to be set', async () => { const wallet = await setup(); - const newScript = new Script(scriptBin, jsonAbiFragmentMock, wallet); + const newScript = new Script(scriptBin, jsonAbiMock, wallet); const { error } = await safeExec(() => newScript.setConfigurableConstants({ FEE: 8 })); @@ -144,11 +144,7 @@ describe('Script', () => { configurables: [ { name: 'FEE', - configurableType: { - name: '', - type: 1, - typeArguments: null, - }, + concreteTypeId: 'a760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', offset: 44, }, ], diff --git a/packages/script/test/mocks.ts b/packages/script/test/mocks.ts index 6c7b8a9516f..61df118fbb5 100644 --- a/packages/script/test/mocks.ts +++ b/packages/script/test/mocks.ts @@ -1,90 +1,30 @@ import type { JsonAbi } from '@fuel-ts/abi-coder'; -export const jsonAbiFragmentMock: JsonAbi = { - configurables: [], - loggedTypes: [], - messagesTypes: [], - types: [ - { - typeId: 0, - type: 'bool', - components: null, - typeParameters: null, - }, - { - typeId: 1, - type: 'u64', - components: null, - typeParameters: null, - }, - { - typeId: 2, - type: 'struct MyStruct', - components: [ - { - type: 0, - name: 'arg_one', - typeArguments: null, - }, - { - type: 1, - name: 'arg_two', - typeArguments: null, - }, - ], - typeParameters: null, - }, - ], - functions: [ - { - name: 'main', - inputs: [ - { - name: 'my_struct', - type: 2, - typeArguments: null, - }, - ], - output: { - name: 'my_struct', - type: 2, - typeArguments: null, - }, - attributes: [], - }, - ], -}; - export const jsonAbiMock: JsonAbi = { - types: [ + programType: 'script', + specVersion: '1', + encodingVersion: '1', + concreteTypes: [ { - typeId: 0, type: 'bool', - components: null, - typeParameters: null, + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, { - typeId: 1, type: 'u8', - components: null, - typeParameters: null, + concreteTypeId: 'a760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, ], + metadataTypes: [], functions: [ { inputs: [ { name: 'inputed_fee', - type: 1, - typeArguments: null, + concreteTypeId: 'a760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', }, ], name: 'main', - output: { - name: '', - type: 0, - typeArguments: null, - }, + output: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', attributes: null, }, ], diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 7fe6f7574c0..ba483c283e7 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -13,4 +13,3 @@ export * from './utils/base58'; export * from './utils/dataSlice'; export * from './utils/toUtf8Bytes'; export * from './utils/toUtf8String'; -export * from './utils/transpileAbi'; diff --git a/packages/utils/src/test-utils/getForcProject.ts b/packages/utils/src/test-utils/getForcProject.ts index c02a79094bd..b8334b8b779 100644 --- a/packages/utils/src/test-utils/getForcProject.ts +++ b/packages/utils/src/test-utils/getForcProject.ts @@ -3,7 +3,6 @@ import { join } from 'path'; import { hexlify } from '../index'; import { normalizeString } from '../utils/normalizeString'; -import { transpileAbi } from '../utils/transpileAbi'; interface IGetForcProjectParams { projectDir: string; @@ -34,9 +33,8 @@ export const getProjectNormalizedName = (params: IGetForcProjectParams) => export const getProjectAbi = (params: IGetForcProjectParams) => { const projectPath = getProjectAbiPath(params); - const raw = JSON.parse(readFileSync(projectPath, 'utf-8')); - const transpiled = transpileAbi(raw); - return transpiled; + const abiContents = JSON.parse(readFileSync(projectPath, 'utf-8')); + return abiContents; }; export const getProjectStorageSlots = (params: IGetForcProjectParams) => { diff --git a/packages/utils/src/utils/transpileAbi.ts b/packages/utils/src/utils/transpileAbi.ts deleted file mode 100644 index 3c95570e3a8..00000000000 --- a/packages/utils/src/utils/transpileAbi.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* eslint-disable no-restricted-globals */ -/* eslint-disable no-param-reassign */ -/* eslint-disable @typescript-eslint/no-use-before-define */ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck - -/** - * This will transpile new ABIs to the old format. - * - * The new format got these new props: - * - `specVersion`, - * - `concreteTypes` - * - `metadataTypes` - * - * The old format contains only: - * - `types` - */ -export function transpileAbi(abi) { - // do not transpile older versions - if (!abi.specVersion) { - return abi; - } - - // 0. define empty types array - const types = []; - - /** - * Helpers - */ - const findTypeByConcreteId = (id) => types.find((x) => x.concreteTypeId === id); - - const findConcreteTypeById = (id) => abi.concreteTypes.find((x) => x.concreteTypeId === id); - - function finsertTypeIdByConcreteTypeId(id) { - const concreteType = findConcreteTypeById(id); - - if (concreteType.metadataTypeId !== undefined) { - return concreteType.metadataTypeId; - } - - const type = findTypeByConcreteId(id); - if (type) { - return type.typeId; - } - - types.push({ - typeId: types.length, - type: concreteType.type, - components: concreteType.type === '()' ? [] : parseComponents(concreteType.components), - concreteTypeId: id, - typeParameters: concreteType.typeParameters ?? null, - }); - - return types.length - 1; - } - - function parseFunctionTypeArguments(concreteType) { - return ( - concreteType.typeArguments?.map((cTypeId) => { - const self = findConcreteTypeById(cTypeId); - const type = !isNaN(cTypeId) ? cTypeId : finsertTypeIdByConcreteTypeId(cTypeId); - return { - name: '', - type, - // originalTypeId: cTypeId, - typeArguments: parseFunctionTypeArguments(self), - }; - }) ?? null - ); - } - - function parseFunctioIO(concreteTypeId, name) { - const type = finsertTypeIdByConcreteTypeId(concreteTypeId); - const concrete = findConcreteTypeById(concreteTypeId); - return { - name: name ?? '', - type, - // concreteTypeId, - typeArguments: parseFunctionTypeArguments(concrete), - }; - } - - function parseComponents(components) { - return ( - components?.map((component) => { - const { typeId, name, typeArguments } = component; - const type = !isNaN(typeId) ? typeId : finsertTypeIdByConcreteTypeId(typeId); - return { - name, - type, - // originalTypeId: typeId, - typeArguments: parseComponents(typeArguments), - }; - }) ?? null - ); - } - - /** - * Transpiling - */ - - // 1. root level of metadata types - abi.metadataTypes.forEach((m) => { - const t = { - typeId: m.metadataTypeId, - type: m.type, - components: m.components ?? (m.type === '()' ? [] : null), - typeParameters: m.typeParameters ?? null, - }; - types.push(t); - }); - - // 2. the metadata's components - types.forEach((t) => { - t.components = parseComponents(t.components); - }); - - // 3. functions inputs/outputs - const functions = abi.functions.map((fn) => { - const inputs = fn.inputs.map(({ concreteTypeId, name }) => - parseFunctioIO(concreteTypeId, name) - ); - const output = parseFunctioIO(fn.output, ''); - return { ...fn, inputs, output }; - }); - - // 4. configurables - const configurables = abi.configurables.map((conf) => ({ - name: conf.name, - configurableType: parseFunctioIO(conf.concreteTypeId), - offset: conf.offset, - })); - - // 5. loggedTypes - const loggedTypes = abi.loggedTypes.map((log) => ({ - logId: log.logId, - loggedType: parseFunctioIO(log.concreteTypeId), - })); - - // transpiled ABI - const transpiled = { - encoding: abi.encodingVersion, - types, - functions, - loggedTypes, - messagesTypes: abi.messagesTypes, - configurables, - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return transpiled as any; -}