diff --git a/.changeset/flat-roses-lie.md b/.changeset/flat-roses-lie.md new file mode 100644 index 00000000000..632f2c9f283 --- /dev/null +++ b/.changeset/flat-roses-lie.md @@ -0,0 +1,4 @@ +--- +--- + +feat: ABI refactor diff --git a/.changeset/nice-books-tease.md b/.changeset/nice-books-tease.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/nice-books-tease.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/poor-years-hang.md b/.changeset/poor-years-hang.md new file mode 100644 index 00000000000..e1335f22087 --- /dev/null +++ b/.changeset/poor-years-hang.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/abi": minor +"fuels": minor +--- + +feat!: ABI Gen diff --git a/.changeset/tender-tigers-fry.md b/.changeset/tender-tigers-fry.md new file mode 100644 index 00000000000..2e270bb05e9 --- /dev/null +++ b/.changeset/tender-tigers-fry.md @@ -0,0 +1,7 @@ +--- +"@fuel-ts/abi": patch +"fuels": patch +"@fuel-ts/errors": patch +--- + +feat: ABI parser diff --git a/.github/actions/test-setup/action.yaml b/.github/actions/test-setup/action.yaml index 8d7c3d6cf0c..0c649356d13 100644 --- a/.github/actions/test-setup/action.yaml +++ b/.github/actions/test-setup/action.yaml @@ -39,6 +39,12 @@ runs: with: bun-version: ${{ inputs.bun-version }} + - name: Set forc and fuel-core paths + shell: bash + run: | + echo "$GITHUB_WORKSPACE/internal/forc/forc-binaries" >> $GITHUB_PATH + echo "$GITHUB_WORKSPACE/internal/fuel-core/fuel-core-binaries" >> $GITHUB_PATH + - name: Build run: pnpm build shell: bash diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b4eebdd9f42..d35a042e492 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -92,6 +92,7 @@ jobs: PUBLISHED_NPM_TAG: next e2e: + if: false runs-on: ubuntu-latest timeout-minutes: 25 needs: [environments] diff --git a/.knip.json b/.knip.json index 5f0eb9e6e4f..46d7d8fbde3 100644 --- a/.knip.json +++ b/.knip.json @@ -8,7 +8,7 @@ "fuels", "bun", "@types/rimraf", - "@fuel-ts/abi-typegen", + "@fuel-ts/abi", "@internal/fuel-core", "get-graphql-schema", "events", diff --git a/apps/demo-typegen/package.json b/apps/demo-typegen/package.json index 520890ac0a3..9d6d61ce1b4 100644 --- a/apps/demo-typegen/package.json +++ b/apps/demo-typegen/package.json @@ -11,8 +11,8 @@ "forc:predicate": "pnpm fuels-forc build -p demo-predicate --release", "build:types": "run-p types:*", "types:contract": "pnpm fuels typegen -i demo-contract/out/release/demo-contract-abi.json -o src/contract-types", - "types:script": "pnpm fuels typegen -i demo-script/out/release/demo-script-abi.json -o src/script-types --script", - "types:predicate": "pnpm fuels typegen -i demo-predicate/out/release/demo-predicate-abi.json -o src/predicate-types --predicate" + "types:script": "pnpm fuels typegen -i demo-script/out/release/demo-script-abi.json -o src/script-types", + "types:predicate": "pnpm fuels typegen -i demo-predicate/out/release/demo-predicate-abi.json -o src/predicate-types" }, "license": "Apache-2.0", "dependencies": { diff --git a/apps/demo-typegen/src/demo.test.ts b/apps/demo-typegen/src/demo.test.ts index d52c7bf2383..9134931b910 100644 --- a/apps/demo-typegen/src/demo.test.ts +++ b/apps/demo-typegen/src/demo.test.ts @@ -2,11 +2,9 @@ import { toHex, Address, Wallet, FuelError, ErrorCode } from 'fuels'; import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils'; -import storageSlots from '../demo-contract/out/release/demo-contract-storage_slots.json'; - import { DemoContract, DemoContractFactory } from './contract-types'; import { DemoPredicate } from './predicate-types'; -import type { DemoPredicateInputs } from './predicate-types/DemoPredicate'; +import type { DemoPredicateInputs } from './predicate-types/predicates/DemoPredicate'; import { DemoScript } from './script-types'; /** @@ -25,7 +23,7 @@ describe('ExampleContract', () => { // #context import { DemoContractFactory } from './sway-programs-api'; const { waitForResult } = await DemoContractFactory.deploy(wallet, { - storageSlots, + storageSlots: DemoContractFactory.storageSlots, }); const { contract } = await waitForResult(); diff --git a/apps/docs-api/index.md b/apps/docs-api/index.md index 9e9f1c5e6bc..31a32022812 100644 --- a/apps/docs-api/index.md +++ b/apps/docs-api/index.md @@ -12,6 +12,9 @@ # Modules + + + - [abi-coder](https://fuels-ts-docs-api.vercel.app/modules/_fuel_ts_abi_coder.html) - [abi-typegen](https://fuels-ts-docs-api.vercel.app/modules/_fuel_ts_abi_typegen.html) - [account](https://fuels-ts-docs-api.vercel.app/modules/_fuel_ts_account.html) diff --git a/apps/docs-api/typedoc.json b/apps/docs-api/typedoc.json index 80cfb47a49a..f04aba511f4 100644 --- a/apps/docs-api/typedoc.json +++ b/apps/docs-api/typedoc.json @@ -2,6 +2,7 @@ "$schema": "https://typedoc.org/schema.json", "entryPointStrategy": "packages", "entryPoints": [ + "../../packages/abi", "../../packages/abi-coder", "../../packages/abi-typegen", "../../packages/address", diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index 288308581b4..229841ebda5 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -441,6 +441,10 @@ export default defineConfig({ text: 'Optimized React Example', link: '/guide/cookbook/optimized-react-example', }, + { + text: 'Working with the ABI', + link: '/guide/cookbook/working-with-the-abi', + }, ], }, { diff --git a/apps/docs/spell-check-custom-words.txt b/apps/docs/spell-check-custom-words.txt index 97456d9655b..b83448ca4cf 100644 --- a/apps/docs/spell-check-custom-words.txt +++ b/apps/docs/spell-check-custom-words.txt @@ -343,4 +343,5 @@ Workspaces WSL XOR XORs -YAML \ No newline at end of file +YAML +matcher \ No newline at end of file diff --git a/apps/docs/src/guide/contracts/snippets/proxy-contracts.ts b/apps/docs/src/guide/contracts/snippets/proxy-contracts.ts index 50db63824a0..6e1ee1faa37 100644 --- a/apps/docs/src/guide/contracts/snippets/proxy-contracts.ts +++ b/apps/docs/src/guide/contracts/snippets/proxy-contracts.ts @@ -1,10 +1,5 @@ // #region proxy-2 -import { - Provider, - Wallet, - Src14OwnedProxy, - Src14OwnedProxyFactory, -} from 'fuels'; +import { Provider, Wallet, Src14OwnedProxyFactory } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../env'; import { @@ -28,7 +23,7 @@ const { contract: counterContract } = await deploy.waitForResult(); * initialize the storage slots. */ const storageSlots = counterContractFactory.storageSlots.concat( - Src14OwnedProxy.storageSlots + Src14OwnedProxyFactory.storageSlots ); /** * These configurables are specific to our recommended SRC14 compliant diff --git a/apps/docs/src/guide/contracts/snippets/storage-slots/override-storage-slots.ts b/apps/docs/src/guide/contracts/snippets/storage-slots/override-storage-slots.ts index a4b4c289f1f..53f82cf028e 100644 --- a/apps/docs/src/guide/contracts/snippets/storage-slots/override-storage-slots.ts +++ b/apps/docs/src/guide/contracts/snippets/storage-slots/override-storage-slots.ts @@ -2,16 +2,13 @@ import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env'; -import { - StorageTestContract, - StorageTestContractFactory, -} from '../../../../typegend'; +import { StorageTestContractFactory } from '../../../../typegend'; const provider = new Provider(LOCAL_NETWORK_URL); const deployer = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const deploymentTx = await StorageTestContractFactory.deploy(deployer, { - storageSlots: StorageTestContract.storageSlots, + storageSlots: StorageTestContractFactory.storageSlots, }); await deploymentTx.waitForResult(); diff --git a/apps/docs/src/guide/cookbook/snippets/parsing-the-abi.ts b/apps/docs/src/guide/cookbook/snippets/parsing-the-abi.ts new file mode 100644 index 00000000000..8b54490de56 --- /dev/null +++ b/apps/docs/src/guide/cookbook/snippets/parsing-the-abi.ts @@ -0,0 +1,9 @@ +// #region full +import { AbiParser } from 'fuels'; +import type { Abi, AbiSpecificationV1 } from 'fuels'; + +import { Counter } from '../../../typegend'; + +const parsedAbi: Abi = AbiParser.parse(Counter.abi as AbiSpecificationV1); +// #endregion full +console.log('Parsed ABI:', parsedAbi); diff --git a/apps/docs/src/guide/cookbook/working-with-the-abi.md b/apps/docs/src/guide/cookbook/working-with-the-abi.md new file mode 100644 index 00000000000..3824f20c576 --- /dev/null +++ b/apps/docs/src/guide/cookbook/working-with-the-abi.md @@ -0,0 +1,11 @@ +# Working with the ABI + +Building a Sway program with `forc build` outputs multiple files, one of which is a JSON representation of the program's ABI. Because ABI specifications can change from one `forc` version to another, working directly with the ABI is cumbersome due to having to manage all ABI specification versions in order to ensure proper functionality. + + + + + +To mitigate this, The SDK provides [`AbiParser`](#working-with-the-abi) which can parse all ABI specification versions and output an object that conforms to the [`Abi`](#working-with-the-abi) interface. The SDK also internally uses this `Abi` interface for implementing its encoding/decoding and TS type generation. + +<<< @./snippets/parsing-the-abi.ts#full{ts:line-numbers} diff --git a/apps/docs/src/guide/encoding/encode-and-decode.md b/apps/docs/src/guide/encoding/encode-and-decode.md index 6ed76fad5ad..561e7feeaa1 100644 --- a/apps/docs/src/guide/encoding/encode-and-decode.md +++ b/apps/docs/src/guide/encoding/encode-and-decode.md @@ -1,13 +1,13 @@ # Encode and Decode -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. +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 `AbiCoder` class to encode and decode data. -The relevant methods of `Interface` are: +To encode and decode types, the `AbiCoder` class provides the `getType` method which returns a `AbiCoderType` instance that provides the following methods: -- `encodeType` -- `decodeType` +- `encode` +- `decode` -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. +The `AbiCoder` 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: @@ -23,7 +23,7 @@ It will produce the following ABI: <<< @./snippets/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 `Interface`. +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`. First, let's prepare the transaction: diff --git a/apps/docs/src/guide/encoding/snippets/encode-and-decode.ts b/apps/docs/src/guide/encoding/snippets/encode-and-decode.ts index 0565c7b16b8..3640ae93e8a 100644 --- a/apps/docs/src/guide/encoding/snippets/encode-and-decode.ts +++ b/apps/docs/src/guide/encoding/snippets/encode-and-decode.ts @@ -1,12 +1,15 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ // #region full -import type { JsonAbi, TransactionResultReturnDataReceipt } from 'fuels'; +import type { + AbiSpecification, + TransactionResultReturnDataReceipt, +} from 'fuels'; import { buildFunctionResult, ReceiptType, arrayify, Script, - Interface, + AbiCoder, Provider, Wallet, } from 'fuels'; @@ -20,7 +23,7 @@ const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); // First we need to build out the transaction via the script that we want to encode. // For that we'll need the ABI and the bytecode of the script -const abi: JsonAbi = ScriptSum.abi; +const abi: AbiSpecification = ScriptSum.abi; const bytecode = ScriptSum.bytecode; // Create the invocation scope for the script call, passing the initial @@ -43,13 +46,13 @@ const argument = abi.functions .find((f) => f.name === 'main') ?.inputs.find((i) => i.name === 'inputted_amount')?.concreteTypeId as string; -// The `Interface` class (imported from `fuels`) 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. +// The `AbiCoder` class (imported from `fuels`) is the entry point for encoding and decoding all things abi-related. +// We will use its `getType` method to get the coder for the argument and then call its `encode` method to +// create the encoding required for a u32 which takes 4 bytes up of property space. -const abiInterface = new Interface(abi); +const abiCoder = AbiCoder.fromAbi(abi); const argumentToAdd = 10; -const encodedArguments = abiInterface.encodeType(argument, [argumentToAdd]); +const encodedArguments = abiCoder.getType(argument).encode([argumentToAdd]); // Therefore the value of 10 will be encoded to: // Uint8Array([0, 0, 0, 10] @@ -91,10 +94,13 @@ const returnData = arrayify(returnDataReceipt.data); // 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 `Interface` -const [decodedReturnData] = abiInterface.decodeType(argument, returnData); +// encoded, via the `AbiCoder` +const decodedReturnData = abiCoder.getType(argument).decode(returnData); // 20 const totalValue = argumentToAdd + initialValue; // #endregion encode-and-decode-5 // #endregion full + +console.log('decodedReturnData should be 20', decodedReturnData === 20); +console.log('totalValue should be 20', totalValue === 20); diff --git a/apps/docs/src/guide/encoding/snippets/working-with-bytes.ts b/apps/docs/src/guide/encoding/snippets/working-with-bytes.ts index 1dc89c369dd..814b7645cec 100644 --- a/apps/docs/src/guide/encoding/snippets/working-with-bytes.ts +++ b/apps/docs/src/guide/encoding/snippets/working-with-bytes.ts @@ -1,88 +1,115 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ // #region full import { randomBytes } from 'crypto'; -import { - ArrayCoder, - B256Coder, - B512Coder, - BigNumberCoder, - BooleanCoder, - EnumCoder, - NumberCoder, - RawSliceCoder, - StdStringCoder, - StringCoder, - StructCoder, - TupleCoder, - VecCoder, - hexlify, -} from 'fuels'; +import { encoding, hexlify } from 'fuels'; // #region working-with-bytes-1 -const u8Coder = new NumberCoder('u8'); -const encodedU8 = u8Coder.encode(255); +const encodedU8 = encoding.u8.encode(255); -const u16Coder = new NumberCoder('u16'); -const encodedU16 = u16Coder.encode(255); +const encodedU16 = encoding.u16.encode(255); -const u32Coder = new NumberCoder('u32'); -const encodedU32 = u32Coder.encode(255); +const encodedU32 = encoding.u32.encode(255); -const u64Coder = new BigNumberCoder('u64'); -const encodedU64 = u64Coder.encode(255); +const encodedU64 = encoding.u64.encode(255); -const u256Coder = new BigNumberCoder('u256'); -const encodedU256 = u256Coder.encode(255); +const encodedU256 = encoding.u256.encode(255); // #endregion working-with-bytes-1 // #region working-with-bytes-2 -const booleanCoder = new BooleanCoder(); -const encodedTrue = booleanCoder.encode(true); - -const encodedFalse = booleanCoder.encode(false); +const encodedTrue = encoding.bool.encode(true); +const encodedFalse = encoding.bool.encode(false); // #endregion working-with-bytes-2 // #region working-with-bytes-3 -const stringCoder = new StringCoder(5); -const encoded = stringCoder.encode('hello'); +const stringCoder = encoding.string(5); +const encodedString = stringCoder.encode('hello'); // #endregion working-with-bytes-3 // #region working-with-bytes-4 -const b256Coder = new B256Coder(); -const encodedB256 = b256Coder.encode(hexlify(randomBytes(32))); -const b512Coder = new B512Coder(); -const encodedB512 = b512Coder.encode(hexlify(randomBytes(64))); +const encodedB256 = encoding.b256.encode(hexlify(randomBytes(32))); + +const encodedB512 = encoding.b512.encode(hexlify(randomBytes(64))); // #endregion working-with-bytes-4 // #region working-with-bytes-5 -const tupleCoder = new TupleCoder([ - new NumberCoder('u8'), - new NumberCoder('u16'), -]); +const tupleCoder = encoding.tuple([encoding.u8, encoding.u16]); const encodedTuple = tupleCoder.encode([255, 255]); -const structCoder = new StructCoder('struct', { - a: new NumberCoder('u8'), - b: new NumberCoder('u16'), +const structCoder = encoding.struct({ + a: encoding.u8, + b: encoding.u16, }); const encodedStruct = structCoder.encode({ a: 255, b: 255 }); -const arrayCoder = new ArrayCoder(new NumberCoder('u8'), 4); +const arrayCoder = encoding.array(encoding.u8, 4); const encodedArray = arrayCoder.encode([255, 0, 255, 0]); -const enumCoder = new EnumCoder('enum', { a: new NumberCoder('u32') }); +const enumCoder = encoding.enum({ a: encoding.u32 }); const encodedEnum = enumCoder.encode({ a: 255 }); // #endregion working-with-bytes-5 // #region working-with-bytes-6 -const vecCoder = new VecCoder(new NumberCoder('u8')); +const vecCoder = encoding.vector(encoding.u8); const encodedVec = vecCoder.encode([255, 0, 255]); -const stdStringCoder = new StdStringCoder(); -const encodedStdString = stdStringCoder.encode('hello'); +const encodedStdString = encoding.stdString.encode('hello'); -const rawSliceCoder = new RawSliceCoder(); -const encodedRawSlice = rawSliceCoder.encode([1, 2, 3, 4]); +const encodedRawSlice = encoding.rawSlice.encode([1, 2, 3, 4]); // #endregion working-with-bytes-6 // #endregion full + +console.log('encodedU8 should be [255]', encodedU8.toString() === '255'); +console.log('encodedU16 should be [0, 255]', encodedU16.toString() === '0,255'); +console.log( + 'encodedU32 should be [0, 0, 0, 255]', + encodedU32.toString() === '0,0,0,255' +); +console.log( + 'encodedU64 should be [0, 0, 0, 0, 0, 0, 0, 255]', + encodedU64.toString() === '0,0,0,0,0,0,0,255' +); +console.log( + 'encodedU256 should be [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,255]', + encodedU256.toString() === + '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,255' +); + +console.log('encodedTrue should be [1]', encodedTrue.toString() === '1'); +console.log('encodedFalse should be [0]', encodedFalse.toString() === '0'); + +console.log( + 'encodedString should be [104, 101, 108, 108, 111]', + encodedString.toString() === '104,101,108,108,111' +); + +console.log('encodedB256 should be 32', encodedB256.length === 32); +console.log('encodedB512 should be 64', encodedB512.length === 64); + +console.log( + 'encodedTuple should be [255, 0, 255]', + encodedTuple.toString() === '255,0,255' +); +console.log( + 'encodedStruct should be [255, 0, 255]', + encodedStruct.toString() === '255,0,255' +); +console.log( + 'encodedArray should be [255, 0, 255, 0]', + encodedArray.toString() === '255,0,255,0' +); +console.log( + 'encodedEnum should be [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255]', + encodedEnum.toString() === '0,0,0,0,0,0,0,0,0,0,0,255' +); +console.log( + 'encodedVec should be [0, 0, 0, 0, 0, 0, 0, 3, 255, 0, 255]', + encodedVec.toString() === '0,0,0,0,0,0,0,3,255,0,255' +); +console.log( + 'encodedStdString should be [0, 0, 0, 0, 0, 0, 0, 5, 104, 101, 108, 108, 111]', + encodedStdString.toString() === '0,0,0,0,0,0,0,5,104,101,108,108,111' +); +console.log( + 'encodedRawSlice should be [0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 3, 4]', + encodedRawSlice.toString() === '0,0,0,0,0,0,0,4,1,2,3,4' +); diff --git a/apps/docs/src/guide/encoding/working-with-bytes.md b/apps/docs/src/guide/encoding/working-with-bytes.md index 9d1f8149cbb..e82bb7e9cea 100644 --- a/apps/docs/src/guide/encoding/working-with-bytes.md +++ b/apps/docs/src/guide/encoding/working-with-bytes.md @@ -6,7 +6,7 @@ This guide aims to give a high-level overview of how to work with bytes in the S We know the sizes of all core types at compile time. They are the building blocks of the more complex types and are the most common types you will encounter. -### Unsigned Integer (`u8` / `u16` / `u32` / `u64` / `u128` / `u256`) +### Unsigned Integer (`u8` / `u16` / `u32` / `u64` / `u256`) Each type will only contain the number of bits specified in the name. For example, a `u8` will contain 8 bits, and a `u256` will contain 256 bits and take up the exact property space with no additional padding. diff --git a/apps/docs/src/guide/errors/index.md b/apps/docs/src/guide/errors/index.md index f28a91ef8b4..136faaa0b66 100644 --- a/apps/docs/src/guide/errors/index.md +++ b/apps/docs/src/guide/errors/index.md @@ -18,6 +18,12 @@ When the arguments supplied to the function do not match the minimum required in Check that the arguments supplied to the function match the required type. +### `ABI_SPECIFICATION_INVALID` + +When the ABI specification provided is invalid. + +Check that the ABI specification is valid. + ### `ACCOUNT_REQUIRED` When an [`Account`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_account.Account.html) is required for an operation. This will usually be in the form of a [`Wallet`](../wallets/index.md). @@ -324,6 +330,12 @@ In cases where the error hasn't been mapped yet, this code will be used. If you believe you found a bug, please report the [issue](https://github.com/FuelLabs/fuels-ts/issues/new/choose) to the team. +### `MATCHER_NOT_FOUND` + +When a matcher is not found for a given Sway type. + +Check that the Sway type is correct and exists in the ABI. + ### `MAX_INPUTS_EXCEEDED` When the number of transaction inputs exceeds the maximum limit allowed by the blockchain. diff --git a/apps/docs/src/guide/fuels-cli/commands.md b/apps/docs/src/guide/fuels-cli/commands.md index 97e936c676f..43bbf92925e 100644 --- a/apps/docs/src/guide/fuels-cli/commands.md +++ b/apps/docs/src/guide/fuels-cli/commands.md @@ -79,7 +79,7 @@ npx fuels@{{fuels}} build ``` 1. Build all Sway programs under your `workspace` using `forc` [1](https://docs.fuel.network/docs/forc/commands/forc_build/) -1. Generate types for them using `fuels-typegen` [2](#fuels-typegen) +1. Generate types for them using `fuels typegen` [2](#fuels-typegen) ```console-vue npx fuels@{{fuels}} build --deploy @@ -167,9 +167,6 @@ npx fuels@{{fuels}} help typegen Options: -i, --inputs Input paths/globals to your Abi JSON files -o, --output Directory path for generated files - -c, --contract Generate types for Contracts [default] - -s, --script Generate types for Scripts - -p, --predicate Generate types for Predicates -S, --silent Omit output messages ``` diff --git a/apps/docs/src/guide/fuels-cli/generating-types.md b/apps/docs/src/guide/fuels-cli/generating-types.md index 6a42b1b530d..57523586e26 100644 --- a/apps/docs/src/guide/fuels-cli/generating-types.md +++ b/apps/docs/src/guide/fuels-cli/generating-types.md @@ -27,18 +27,15 @@ Generate Typescript from Sway ABI JSON files Options: -i, --inputs Input paths/globals to your ABI JSON files -o, --output Directory path for generated files - -c, --contract Generate types for Contracts [default] - -s, --script Generate types for Scripts - -p, --predicate Generate types for Predicates -S, --silent Omit output messages -h, --help Display help ``` -## Generating Types for Contracts +## Generating Types -You can generate types for a Sway contract using the command below: +You can generate types for a Sway program using the command below: - + ```console @@ -50,30 +47,15 @@ pnpm fuels typegen -i ./abis/*-abi.json -o ./types -The path after the input flag `-i` should point to the file ending in `-abi.json` produced when the contract was built. +The path after the input flag `-i` should point to the file ending in `-abi.json` produced when the Sway program was built. -The path after the output flag `-o` will be the output directory for the generated types. +- For scripts and predicates, you'll need the bytecode of the program to be in the same folder for the command to work. +- For contracts, the command will work without the bytecode but the corresponding `ContractFactory` file won't be generated as factories need the bytecode to operate. -You can omit the `--contract` option here since it's the default. +The path after the output flag `-o` will be the output directory for the generated types. -## Generating Types for Scripts - -To generate types for a Sway script, use the `--script` flag: - -```console -pnpm fuels typegen -i ./abis/*-abi.json -o ./types --script -``` - -## Generating Types for Predicates - -To generate types for a Sway predicate, use the `--predicate` flag: - -```console -pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate -``` - --- See also: diff --git a/apps/docs/src/guide/fuels-cli/using-generated-types.md b/apps/docs/src/guide/fuels-cli/using-generated-types.md index 4702c2e0394..7e38ecf61c3 100644 --- a/apps/docs/src/guide/fuels-cli/using-generated-types.md +++ b/apps/docs/src/guide/fuels-cli/using-generated-types.md @@ -29,7 +29,7 @@ Typegen tries to resolve, auto-load, and embed the [Storage Slots](../contracts/ After generating types via: ```console -pnpm fuels typegen -i ./abis/*-abi.json -o ./types --script +pnpm fuels typegen -i ./abis/*-abi.json -o ./types ``` We can use these files like so: @@ -41,7 +41,7 @@ We can use these files like so: After generating types via: ```console -pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate +pnpm fuels typegen -i ./abis/*-abi.json -o ./types ``` We can use these files like so: diff --git a/apps/docs/src/guide/scripts/snippets/initialising-scripts.ts b/apps/docs/src/guide/scripts/snippets/initialising-scripts.ts index 6516ba10849..bae140341c4 100644 --- a/apps/docs/src/guide/scripts/snippets/initialising-scripts.ts +++ b/apps/docs/src/guide/scripts/snippets/initialising-scripts.ts @@ -29,10 +29,9 @@ const scriptRequest = new ScriptRequest( throw new Error('fail'); } - const [decodedResult] = script.interface.functions.main.decodeOutput( + return script.interface.functions.main.decodeOutput( scriptResult.returnReceipt.data ); - return decodedResult; } ); // #endregion script-init diff --git a/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-1.ts b/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-1.ts index d3bc38d837d..2194c4fdc16 100644 --- a/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-1.ts +++ b/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-1.ts @@ -2,7 +2,7 @@ import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env'; import { EchoEnumFactory } from '../../../../typegend'; -import { UserErrorInput } from '../../../../typegend/contracts/EchoEnum'; +import { UserError } from '../../../../typegend/contracts/EchoEnum'; const provider = new Provider(LOCAL_NETWORK_URL); const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); @@ -10,7 +10,7 @@ const deploy = await EchoEnumFactory.deploy(wallet); const { contract } = await deploy.waitForResult(); // #region snippet-1 -const enumParam = { UserError: UserErrorInput.InsufficientPermissions }; +const enumParam = { UserError: UserError.InsufficientPermissions }; const { value } = await contract.functions.echo_error_enum(enumParam).get(); diff --git a/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-2.ts b/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-2.ts index d38469bd533..2c6318b0696 100644 --- a/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-2.ts +++ b/apps/docs/src/guide/types/snippets/enums/using-enums-of-enums-2.ts @@ -2,7 +2,7 @@ import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env'; import { EchoEnumFactory } from '../../../../typegend'; -import { StateErrorInput } from '../../../../typegend/contracts/EchoEnum'; +import { StateError } from '../../../../typegend/contracts/EchoEnum'; const provider = new Provider(LOCAL_NETWORK_URL); const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); @@ -10,7 +10,7 @@ const deploy = await EchoEnumFactory.deploy(wallet); const { contract } = await deploy.waitForResult(); // #region snippet-1 -const enumParam = { StateError: StateErrorInput.Completed }; +const enumParam = { StateError: StateError.Completed }; const { value } = await contract.functions.echo_error_enum(enumParam).get(); diff --git a/apps/docs/src/guide/types/snippets/enums/using-sway-enums.ts b/apps/docs/src/guide/types/snippets/enums/using-sway-enums.ts index 4c3b0233ace..93c7fc3d3c7 100644 --- a/apps/docs/src/guide/types/snippets/enums/using-sway-enums.ts +++ b/apps/docs/src/guide/types/snippets/enums/using-sway-enums.ts @@ -3,14 +3,14 @@ import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env'; import { EchoEnumFactory } from '../../../../typegend'; -import { StateErrorInput } from '../../../../typegend/contracts/EchoEnum'; +import { StateError } from '../../../../typegend/contracts/EchoEnum'; const provider = new Provider(LOCAL_NETWORK_URL); const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const deploy = await EchoEnumFactory.deploy(wallet); const { contract } = await deploy.waitForResult(); -const enumParam = StateErrorInput.Completed; +const enumParam = StateError.Completed; const { value } = await contract.functions .echo_state_error_enum(enumParam) diff --git a/internal/check-imports/package.json b/internal/check-imports/package.json index 1b8d66bf044..a34d3cf4852 100644 --- a/internal/check-imports/package.json +++ b/internal/check-imports/package.json @@ -9,6 +9,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@fuel-ts/abi": "workspace:*", "@fuel-ts/abi-coder": "workspace:*", "@fuel-ts/abi-typegen": "workspace:*", "@fuel-ts/address": "workspace:*", diff --git a/internal/check-imports/src/imports.ts b/internal/check-imports/src/imports.ts index 570ede6e6a6..3cd0aa73f9f 100644 --- a/internal/check-imports/src/imports.ts +++ b/internal/check-imports/src/imports.ts @@ -1,3 +1,4 @@ +import * as abi from '@fuel-ts/abi'; import * as abiCoder from '@fuel-ts/abi-coder'; import * as abiTypegen from '@fuel-ts/abi-typegen'; import * as account from '@fuel-ts/account'; @@ -20,6 +21,7 @@ import * as fuels from 'fuels'; const { log } = console; log([ + abi, abiCoder, abiTypegen, address, diff --git a/internal/check-imports/src/references.ts b/internal/check-imports/src/references.ts index aa4fbbe098a..508c8f40655 100644 --- a/internal/check-imports/src/references.ts +++ b/internal/check-imports/src/references.ts @@ -1,3 +1,4 @@ +import { AbiCoder, encoding } from '@fuel-ts/abi'; import { Interface, StringCoder } from '@fuel-ts/abi-coder'; import { AbiTypeGen } from '@fuel-ts/abi-typegen'; import { runCliAction } from '@fuel-ts/abi-typegen/cli'; @@ -37,10 +38,20 @@ import { arrayify, hexlify, createConfig, + AbiParser, } from 'fuels'; const { log } = console; +/** + * abi + */ +log(AbiCoder); +log(encoding); +log(encoding.v1.string); +log(encoding.v1.string(8)); +log(AbiParser); + /** * abi-coder */ diff --git a/packages/abi-coder/src/FunctionFragment.ts b/packages/abi-coder/src/FunctionFragment.ts index 32087c21d19..9aabd92f021 100644 --- a/packages/abi-coder/src/FunctionFragment.ts +++ b/packages/abi-coder/src/FunctionFragment.ts @@ -75,7 +75,7 @@ export class FunctionFragment { return new TupleCoder(coders).encode(argumentValues); } - decodeArguments(data: BytesLike) { + decodeArguments(data: BytesLike): unknown[] | undefined { const bytes = arrayify(data); const nonVoidInputs = findNonVoidInputs(this.jsonAbiOld, this.jsonFnOld.inputs); diff --git a/packages/abi-coder/src/encoding/coders/B256Coder.test.ts b/packages/abi-coder/src/encoding/coders/B256Coder.test.ts index b2c19ff78df..64df40ac176 100644 --- a/packages/abi-coder/src/encoding/coders/B256Coder.test.ts +++ b/packages/abi-coder/src/encoding/coders/B256Coder.test.ts @@ -18,7 +18,7 @@ describe('B256Coder', () => { const coder = new B256Coder(); - it('should encode zero as a 256 bit hash string', () => { + it('should encode [zero] as a 256 bit hash string', () => { const expected = B256_ZERO_ENCODED; const actual = coder.encode(B256_ZERO_DECODED); diff --git a/packages/abi-coder/src/encoding/coders/OptionCoder.test.ts b/packages/abi-coder/src/encoding/coders/OptionCoder.test.ts index e43ca579211..e930ef911fe 100644 --- a/packages/abi-coder/src/encoding/coders/OptionCoder.test.ts +++ b/packages/abi-coder/src/encoding/coders/OptionCoder.test.ts @@ -8,36 +8,36 @@ import { VoidCoder } from './VoidCoder'; */ describe('OptionCoder', () => { const coder = new OptionCoder('std::option::Option', { - Some: new NumberCoder('u8'), None: new VoidCoder(), + Some: new NumberCoder('u8'), }); describe('encode', () => { it('should encode a Some value', () => { const encoded = coder.encode(100); - const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 100]); + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1, 100]); expect(encoded).toEqual(expected); }); it('should encode a None value', () => { const encoded = coder.encode(undefined); - const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]); + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); expect(encoded).toEqual(expected); }); it('should encode a None value [optional]', () => { const encoded = coder.encode(); - const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]); + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); expect(encoded).toEqual(expected); }); }); describe('decode', () => { it('should decode a Some value', () => { - const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 100]); + const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1, 100]); const expected = [100, 9]; const decoded = coder.decode(input, 0); @@ -46,7 +46,7 @@ describe('OptionCoder', () => { }); it('should decode a None value', () => { - const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1]); + const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); const expected = [undefined, 8]; const decoded = coder.decode(input, 0); diff --git a/packages/abi-typegen/package.json b/packages/abi-typegen/package.json index a0c7a220487..d4695347303 100644 --- a/packages/abi-typegen/package.json +++ b/packages/abi-typegen/package.json @@ -3,9 +3,6 @@ "version": "0.98.0", "description": "Generates Typescript definitions from Sway ABI Json files", "author": "Fuel Labs (https://fuel.network/)", - "bin": { - "fuels-typegen": "typegen.js" - }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", diff --git a/packages/abi-typegen/src/templates/contract/main.hbs b/packages/abi-typegen/src/templates/contract/main.hbs index eef7efd30a2..ba606c1c975 100644 --- a/packages/abi-typegen/src/templates/contract/main.hbs +++ b/packages/abi-typegen/src/templates/contract/main.hbs @@ -1,12 +1,13 @@ {{header}} -import { Contract, Interface } from "fuels"; +import { Contract, AbiCoder } from "fuels"; {{#if imports}} import type { Provider, Account, StorageSlot, Address, + AbiSpecification, {{#each imports}} {{this}}, {{/each}} @@ -53,18 +54,14 @@ export type {{capitalizedName}}Configurables = Partial<{ }>; {{/if}} -const abi = {{abiJsonString}}; +const abi: AbiSpecification = {{abiJsonString}}; const storageSlots: StorageSlot[] = {{storageSlotsJsonString}}; -export class {{capitalizedName}}Interface extends Interface { - constructor() { - super(abi); - } - +export class {{capitalizedName}}AbiCoder extends AbiCoder { declare functions: { {{#each functionsFragments}} - {{this}}: FunctionFragment; + {{this}}: AbiCoderFunction; {{/each}} }; } @@ -73,7 +70,7 @@ export class {{capitalizedName}} extends Contract { static readonly abi = abi; static readonly storageSlots = storageSlots; - declare interface: {{capitalizedName}}Interface; + declare interface: {{capitalizedName}}AbiCoder; declare functions: { {{#each functionsTypedefs}} {{this}}; diff --git a/packages/abi-typegen/src/templates/contract/main.ts b/packages/abi-typegen/src/templates/contract/main.ts index 759f3624f78..258296f8061 100644 --- a/packages/abi-typegen/src/templates/contract/main.ts +++ b/packages/abi-typegen/src/templates/contract/main.ts @@ -32,7 +32,7 @@ export function renderMainTemplate(params: { abi: Abi; versions: BinaryVersions const { structs } = formatStructs({ types }); const { imports } = formatImports({ types, - baseMembers: ['FunctionFragment', 'InvokeFunction'], + baseMembers: ['AbiCoderFunction', 'InvokeFunction'], }); const { rawContents, storageSlotsContents } = params.abi; diff --git a/packages/abi-typegen/src/templates/predicate/main.hbs b/packages/abi-typegen/src/templates/predicate/main.hbs index 03cbaf76dd6..56f68ed8a79 100644 --- a/packages/abi-typegen/src/templates/predicate/main.hbs +++ b/packages/abi-typegen/src/templates/predicate/main.hbs @@ -57,7 +57,7 @@ export type {{capitalizedName}}Parameters = Omit< 'abi' | 'bytecode' >; -const abi = {{abiJsonString}}; +const abi: AbiSpecification = {{abiJsonString}}; const bytecode = decompressBytecode('{{compressedBytecode}}'); diff --git a/packages/abi-typegen/src/templates/predicate/main.ts b/packages/abi-typegen/src/templates/predicate/main.ts index a2c928ecf94..01e0fee0134 100644 --- a/packages/abi-typegen/src/templates/predicate/main.ts +++ b/packages/abi-typegen/src/templates/predicate/main.ts @@ -34,7 +34,14 @@ export function renderMainTemplate(params: { abi: Abi; versions: BinaryVersions const { structs } = formatStructs({ types }); const { imports } = formatImports({ types, - baseMembers: ['Predicate', 'Provider', 'InputValue', 'PredicateParams', 'decompressBytecode'], + baseMembers: [ + 'Predicate', + 'Provider', + 'InputValue', + 'PredicateParams', + 'decompressBytecode', + 'AbiSpecification', + ], }); const { prefixedInputs: inputs, output } = func.attributes; diff --git a/packages/abi-typegen/src/templates/script/main.hbs b/packages/abi-typegen/src/templates/script/main.hbs index 225d540e9ca..403aec80ba3 100644 --- a/packages/abi-typegen/src/templates/script/main.hbs +++ b/packages/abi-typegen/src/templates/script/main.hbs @@ -51,7 +51,7 @@ export type {{capitalizedName}}Configurables = Partial<{ }>; {{/if}} -const abi = {{abiJsonString}}; +const abi: AbiSpecification = {{abiJsonString}}; const bytecode = decompressBytecode('{{compressedBytecode}}'); diff --git a/packages/abi-typegen/src/templates/script/main.ts b/packages/abi-typegen/src/templates/script/main.ts index 36b76d2ce74..3ce3e15615a 100644 --- a/packages/abi-typegen/src/templates/script/main.ts +++ b/packages/abi-typegen/src/templates/script/main.ts @@ -34,7 +34,7 @@ export function renderMainTemplate(params: { abi: Abi; versions: BinaryVersions const { structs } = formatStructs({ types }); const { imports } = formatImports({ types, - baseMembers: ['Script', 'Account', 'decompressBytecode'], + baseMembers: ['Script', 'Account', 'decompressBytecode', 'AbiSpecification'], }); const { prefixedInputs: inputs, output } = func.attributes; diff --git a/packages/abi-typegen/test/fixtures/forc-projects/full/src/main.sw b/packages/abi-typegen/test/fixtures/forc-projects/full/src/main.sw index 500f8639108..74af47c4e97 100644 --- a/packages/abi-typegen/test/fixtures/forc-projects/full/src/main.sw +++ b/packages/abi-typegen/test/fixtures/forc-projects/full/src/main.sw @@ -81,15 +81,15 @@ abi MyContract { fn types_vector_geo(x: Vec) -> Vec; fn types_vector_option(x: Vec) -> Vec; fn types_option(x: Option) -> Option; - fn types_option_geo(x: Option) -> Option; + fn types_option_struct(x: Option) -> Option; fn types_evm_address(x: EvmAddress) -> EvmAddress; fn types_bytes(x: Bytes) -> Bytes; fn types_raw_slice(x: raw_slice) -> raw_slice; fn types_str_slice(x: str) -> str; fn types_std_string(x: String) -> String; fn types_result(x: Result) -> Result; - fn type_address(x: Address) -> Address; - fn type_contract_id(x: ContractId) -> ContractId; + fn types_address(x: Address) -> Address; + fn types_contract_id(x: ContractId) -> ContractId; fn type_identity(x: Identity) -> Identity; fn type_external_struct(x: ExternalStruct) -> ExternalStruct; fn type_external_enum(x: ExternalEnum) -> ExternalEnum; @@ -182,7 +182,7 @@ impl MyContract for Contract { fn types_option(x: Option) -> Option { x } - fn types_option_geo(x: Option) -> Option { + fn types_option_struct(x: Option) -> Option { x } fn types_evm_address(x: EvmAddress) -> EvmAddress { @@ -211,10 +211,10 @@ impl MyContract for Contract { Err(MyContractError::DivisionByZero) => Err(__to_str_array("DivisError")), } } - fn type_address(x: Address) -> Address { + fn types_address(x: Address) -> Address { x } - fn type_contract_id(x: ContractId) -> ContractId { + fn types_contract_id(x: ContractId) -> ContractId { x } fn type_identity(x: Identity) -> Identity { diff --git a/packages/abi-typegen/test/fixtures/templates/contract-with-configurable/main.hbs b/packages/abi-typegen/test/fixtures/templates/contract-with-configurable/main.hbs index 992052611ff..9e76887d9fe 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract-with-configurable/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract-with-configurable/main.hbs @@ -10,14 +10,15 @@ Fuel-Core version: 33.33.33 */ -import { Contract, Interface } from "fuels"; +import { Contract, AbiCoder } from "fuels"; import type { Provider, Account, StorageSlot, Address, + AbiSpecification, + AbiCoderFunction, BigNumberish, - FunctionFragment, InvokeFunction, } from 'fuels'; @@ -32,7 +33,7 @@ export type MyContractConfigurables = Partial<{ A_GENERIC_STRUCT: GenericStructInput, BigNumberish>; }>; -const abi = { +const abi: AbiSpecification = { "programType": "contract", "specVersion": "1", "encodingVersion": "1", @@ -177,13 +178,9 @@ const abi = { const storageSlots: StorageSlot[] = []; -export class MyContractInterface extends Interface { - constructor() { - super(abi); - } - +export class MyContractAbiCoder extends AbiCoder { declare functions: { - main: FunctionFragment; + main: AbiCoderFunction; }; } @@ -191,7 +188,7 @@ export class MyContract extends Contract { static readonly abi = abi; static readonly storageSlots = storageSlots; - declare interface: MyContractInterface; + declare interface: MyContractAbiCoder; declare functions: { main: InvokeFunction<[x: string, y: string], boolean>; }; diff --git a/packages/abi-typegen/test/fixtures/templates/contract/main.hbs b/packages/abi-typegen/test/fixtures/templates/contract/main.hbs index a0350d966f7..cacdab706d7 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/main.hbs @@ -10,17 +10,18 @@ Fuel-Core version: 33.33.33 */ -import { Contract, Interface } from "fuels"; +import { Contract, AbiCoder } from "fuels"; import type { Provider, Account, StorageSlot, Address, + AbiSpecification, + AbiCoderFunction, BigNumberish, BN, Bytes, EvmAddress, - FunctionFragment, InvokeFunction, RawSlice, StdString, @@ -55,7 +56,7 @@ export type MyStructOutput = { x: number, y: number, state: MyEnumOutput }; export type StructWithMultiOptionInput = { multiple: [Option, Option, Option, Option, Option] }; export type StructWithMultiOptionOutput = { multiple: [Option, Option, Option, Option, Option] }; -const abi = { +const abi: AbiSpecification = { "programType": "contract", "specVersion": "1", "encodingVersion": "1", @@ -685,28 +686,6 @@ const abi = { "output": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6", "attributes": null }, - { - "inputs": [ - { - "name": "x", - "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308" - } - ], - "name": "type_address", - "output": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308", - "attributes": null - }, - { - "inputs": [ - { - "name": "x", - "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" - } - ], - "name": "type_contract_id", - "output": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", - "attributes": null - }, { "inputs": [ { @@ -740,6 +719,17 @@ const abi = { "output": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335", "attributes": null }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308" + } + ], + "name": "types_address", + "output": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308", + "attributes": null + }, { "inputs": [ { @@ -806,6 +796,17 @@ const abi = { "output": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", "attributes": null }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" + } + ], + "name": "types_contract_id", + "output": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", + "attributes": null + }, { "inputs": [ { @@ -905,7 +906,7 @@ const abi = { "concreteTypeId": "3597e0782bd4dbaf5c8025b40ff3a325845ee34caa713a6d664bda034a31d02a" } ], - "name": "types_option_geo", + "name": "types_option_struct", "output": "3597e0782bd4dbaf5c8025b40ff3a325845ee34caa713a6d664bda034a31d02a", "attributes": null }, @@ -1150,52 +1151,48 @@ const abi = { const storageSlots: StorageSlot[] = []; -export class MyContractInterface extends Interface { - constructor() { - super(abi); - } - +export class MyContractAbiCoder extends AbiCoder { declare functions: { - alias_types_tuple_with_native_types: FunctionFragment; - type_address: FunctionFragment; - type_contract_id: FunctionFragment; - type_external_enum: FunctionFragment; - type_external_struct: FunctionFragment; - type_identity: FunctionFragment; - types_array: FunctionFragment; - types_asset_id: FunctionFragment; - types_b256: FunctionFragment; - types_b512: FunctionFragment; - types_bool: FunctionFragment; - types_bytes: FunctionFragment; - types_empty: FunctionFragment; - types_empty_then_value: FunctionFragment; - types_enum: FunctionFragment; - types_enum_with_vector: FunctionFragment; - types_evm_address: FunctionFragment; - types_generic_enum: FunctionFragment; - types_generic_struct: FunctionFragment; - types_option: FunctionFragment; - types_option_geo: FunctionFragment; - types_raw_slice: FunctionFragment; - types_result: FunctionFragment; - types_std_string: FunctionFragment; - types_str: FunctionFragment; - types_str_slice: FunctionFragment; - types_struct: FunctionFragment; - types_tuple: FunctionFragment; - types_tuple_with_native_types: FunctionFragment; - types_u16: FunctionFragment; - types_u256: FunctionFragment; - types_u32: FunctionFragment; - types_u64: FunctionFragment; - types_u8: FunctionFragment; - types_value_then_empty: FunctionFragment; - types_value_then_empty_then_value: FunctionFragment; - types_value_then_value_then_empty_then_empty: FunctionFragment; - types_vector_geo: FunctionFragment; - types_vector_option: FunctionFragment; - types_vector_u8: FunctionFragment; + alias_types_tuple_with_native_types: AbiCoderFunction; + type_external_enum: AbiCoderFunction; + type_external_struct: AbiCoderFunction; + type_identity: AbiCoderFunction; + types_address: AbiCoderFunction; + types_array: AbiCoderFunction; + types_asset_id: AbiCoderFunction; + types_b256: AbiCoderFunction; + types_b512: AbiCoderFunction; + types_bool: AbiCoderFunction; + types_bytes: AbiCoderFunction; + types_contract_id: AbiCoderFunction; + types_empty: AbiCoderFunction; + types_empty_then_value: AbiCoderFunction; + types_enum: AbiCoderFunction; + types_enum_with_vector: AbiCoderFunction; + types_evm_address: AbiCoderFunction; + types_generic_enum: AbiCoderFunction; + types_generic_struct: AbiCoderFunction; + types_option: AbiCoderFunction; + types_option_struct: AbiCoderFunction; + types_raw_slice: AbiCoderFunction; + types_result: AbiCoderFunction; + types_std_string: AbiCoderFunction; + types_str: AbiCoderFunction; + types_str_slice: AbiCoderFunction; + types_struct: AbiCoderFunction; + types_tuple: AbiCoderFunction; + types_tuple_with_native_types: AbiCoderFunction; + types_u16: AbiCoderFunction; + types_u256: AbiCoderFunction; + types_u32: AbiCoderFunction; + types_u64: AbiCoderFunction; + types_u8: AbiCoderFunction; + types_value_then_empty: AbiCoderFunction; + types_value_then_empty_then_value: AbiCoderFunction; + types_value_then_value_then_empty_then_empty: AbiCoderFunction; + types_vector_geo: AbiCoderFunction; + types_vector_option: AbiCoderFunction; + types_vector_u8: AbiCoderFunction; }; } @@ -1203,20 +1200,20 @@ export class MyContract extends Contract { static readonly abi = abi; static readonly storageSlots = storageSlots; - declare interface: MyContractInterface; + declare interface: MyContractAbiCoder; declare functions: { alias_types_tuple_with_native_types: InvokeFunction<[x: [AssetIdInput, AssetIdInput, boolean]], [AssetIdOutput, AssetIdOutput, boolean]>; - type_address: InvokeFunction<[x: AddressInput], AddressOutput>; - type_contract_id: InvokeFunction<[x: ContractIdInput], ContractIdOutput>; type_external_enum: InvokeFunction<[x: ExternalEnumInput], ExternalEnumOutput>; type_external_struct: InvokeFunction<[x: ExternalStructInput], ExternalStructOutput>; type_identity: InvokeFunction<[x: IdentityInput], IdentityOutput>; + types_address: InvokeFunction<[x: AddressInput], AddressOutput>; types_array: InvokeFunction<[x: [BigNumberish, BigNumberish, BigNumberish]], [number, number, number]>; types_asset_id: InvokeFunction<[x: AssetIdInput], AssetIdOutput>; types_b256: InvokeFunction<[x: string], string>; types_b512: InvokeFunction<[x: string], string>; types_bool: InvokeFunction<[x: boolean], boolean>; types_bytes: InvokeFunction<[x: Bytes], Bytes>; + types_contract_id: InvokeFunction<[x: ContractIdInput], ContractIdOutput>; types_empty: InvokeFunction<[x?: undefined], void>; types_empty_then_value: InvokeFunction<[x: undefined, y: BigNumberish], void>; types_enum: InvokeFunction<[x: MyEnumInput], MyEnumOutput>; @@ -1225,7 +1222,7 @@ export class MyContract extends Contract { types_generic_enum: InvokeFunction<[x: GenericEnumInput], GenericEnumOutput>; types_generic_struct: InvokeFunction<[x: GenericStructWithEnumInput], GenericStructWithEnumOutput>; types_option: InvokeFunction<[x?: Option], Option>; - types_option_geo: InvokeFunction<[x?: Option], Option>; + types_option_struct: InvokeFunction<[x?: Option], Option>; types_raw_slice: InvokeFunction<[x: RawSlice], RawSlice>; types_result: InvokeFunction<[x: Result], Result>; types_std_string: InvokeFunction<[x: StdString], StdString>; diff --git a/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/main.hbs b/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/main.hbs index c40afd5a99d..4fb4f6f1738 100644 --- a/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/predicate-with-configurable/main.hbs @@ -11,6 +11,7 @@ */ import { + AbiSpecification, BigNumberish, decompressBytecode, InputValue, @@ -31,7 +32,7 @@ export type MyPredicateParameters = Omit< 'abi' | 'bytecode' >; -const abi = { +const abi: AbiSpecification = { "programType": "predicate", "specVersion": "1", "encodingVersion": "1", diff --git a/packages/abi-typegen/test/fixtures/templates/predicate/main.hbs b/packages/abi-typegen/test/fixtures/templates/predicate/main.hbs index 8eaed3fbf41..fe83ed8aba1 100644 --- a/packages/abi-typegen/test/fixtures/templates/predicate/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/predicate/main.hbs @@ -11,6 +11,7 @@ */ import { + AbiSpecification, BigNumberish, BN, decompressBytecode, @@ -39,7 +40,7 @@ export type MyPredicateParameters = Omit< 'abi' | 'bytecode' >; -const abi = { +const abi: AbiSpecification = { "programType": "predicate", "specVersion": "1", "encodingVersion": "1", diff --git a/packages/abi-typegen/test/fixtures/templates/script-with-configurable/main.hbs b/packages/abi-typegen/test/fixtures/templates/script-with-configurable/main.hbs index 34c9a4f1722..4dd791da2b7 100644 --- a/packages/abi-typegen/test/fixtures/templates/script-with-configurable/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/script-with-configurable/main.hbs @@ -11,6 +11,7 @@ */ import { + AbiSpecification, Account, BigNumberish, decompressBytecode, @@ -27,7 +28,7 @@ export type MyScriptConfigurables = Partial<{ SHOULD_RETURN: boolean; }>; -const abi = { +const abi: AbiSpecification = { "programType": "script", "specVersion": "1", "encodingVersion": "1", diff --git a/packages/abi-typegen/test/fixtures/templates/script/main.hbs b/packages/abi-typegen/test/fixtures/templates/script/main.hbs index aa93e951140..0d08fdbfabd 100644 --- a/packages/abi-typegen/test/fixtures/templates/script/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/script/main.hbs @@ -11,6 +11,7 @@ */ import { + AbiSpecification, Account, BigNumberish, BN, @@ -31,7 +32,7 @@ export type ScoreOutput = { user: number, points: number }; export type MyScriptInputs = [vec: Vec, enm: MyGenericEnumInput, opt: Option, res: Result, BigNumberish>]; export type MyScriptOutput = boolean; -const abi = { +const abi: AbiSpecification = { "programType": "script", "specVersion": "1", "encodingVersion": "1", diff --git a/packages/abi/LICENSE b/packages/abi/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/abi/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/abi/README.md b/packages/abi/README.md new file mode 100644 index 00000000000..ced0388cf00 --- /dev/null +++ b/packages/abi/README.md @@ -0,0 +1,3 @@ +## @fuel-ts/abi + +TODO write up README diff --git a/packages/abi/package.json b/packages/abi/package.json new file mode 100644 index 00000000000..077818f53cd --- /dev/null +++ b/packages/abi/package.json @@ -0,0 +1,56 @@ +{ + "name": "@fuel-ts/abi", + "private": true, + "version": "0.0.0", + "description": "", + "author": "Fuel Labs (https://fuel.network/)", + "license": "Apache-2.0", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "engines": { + "node": "^18.20.3 || ^20.0.0 || ^22.0.0" + }, + "bin": { + "fuels-typegen": "typegen.js" + }, + "exports": { + ".": { + "require": "./dist/index.js", + "import": "./dist/index.mjs", + "types": "./dist/index.d.ts" + }, + "./cli": { + "types": "./dist/cli.d.ts", + "require": "./dist/cli.js", + "import": "./dist/cli.mjs" + } + }, + "typesVersions": { + "*": { + "cli": [ + "./dist/cli.d.ts" + ] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "postbuild": "tsx ../../scripts/postbuild.ts" + }, + "dependencies": { + "@fuel-ts/crypto": "workspace:*", + "@fuel-ts/errors": "workspace:*", + "@fuel-ts/hasher": "workspace:*", + "@fuel-ts/math": "workspace:*", + "@fuel-ts/utils": "workspace:*", + "type-fest": "4.26.1", + "@fuel-ts/versions": "workspace:*", + "handlebars": "4.7.8", + "commander": "12.1.0", + "glob": "10.4.5" + }, + "devDependencies": {} +} diff --git a/packages/abi/src/bin.ts b/packages/abi/src/bin.ts new file mode 100755 index 00000000000..4d4c3bed880 --- /dev/null +++ b/packages/abi/src/bin.ts @@ -0,0 +1,15 @@ +import { getBinaryVersions } from '@fuel-ts/versions/cli'; +import { Command } from 'commander'; + +import { configureTypegenCliOptions } from './cli'; + +const program = new Command(); + +program.name('fuels-typegen'); +program.version(getBinaryVersions().FUELS); +program.usage(`-i ../out/*-abi.json -o ./generated/`); +program.option('-S, --silent', 'Omit output messages', false); + +configureTypegenCliOptions(program); + +program.parse(process.argv); diff --git a/packages/abi/src/cli.ts b/packages/abi/src/cli.ts new file mode 100644 index 00000000000..c37108bd746 --- /dev/null +++ b/packages/abi/src/cli.ts @@ -0,0 +1 @@ +export * from './gen/cli'; diff --git a/packages/abi/src/coder/AbiCoder.ts b/packages/abi/src/coder/AbiCoder.ts new file mode 100644 index 00000000000..5073916bd4a --- /dev/null +++ b/packages/abi/src/coder/AbiCoder.ts @@ -0,0 +1,148 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { Abi, AbiSpecification } from '../parser'; +import { AbiParser } from '../parser'; + +import type { + AbiCoderConfigurable, + AbiCoderFunction, + AbiCoderLog, + AbiCoderType, +} from './abi-coder-types'; +import type { AbiEncoding } from './encoding'; +import { encoding } from './encoding'; +import { createConfigurable } from './utils/createConfigurable'; +import { createFunction } from './utils/createFunction'; +import { createLog } from './utils/createLog'; +import { createType } from './utils/createType'; + +export class AbiCoder { + public readonly abi: Abi; + public readonly specification: AbiSpecification; + + private readonly encoding: AbiEncoding; + private readonly functionLookup: Map; + public readonly functions: Record; + public readonly configurables: Record; + public readonly logs: Record; + + /** + * Create an instance of `AbiCoder` from an ABI specification + * + * @param specification - The ABI specification + */ + public constructor(specification: AbiSpecification) { + this.abi = AbiParser.parse(specification); + this.specification = specification; + this.encoding = encoding.fromVersion(this.abi.encodingVersion); + + const { functions, configurables, loggedTypes } = this.abi; + this.functionLookup = new Map(); + this.functions = Object.fromEntries( + functions.map((fn) => { + const func = createFunction(fn, this.encoding); + this.functionLookup.set(fn.name, func); + this.functionLookup.set(func.signature, func); + this.functionLookup.set(func.selector, func); + return [fn.name, func]; + }) + ); + this.configurables = Object.fromEntries( + configurables.map((conf) => [conf.name, createConfigurable(conf, this.encoding)]) + ); + this.logs = Object.fromEntries( + loggedTypes.map((log) => [log.logId, createLog(log, this.encoding)]) + ); + } + + /** + * Create an instance of `AbiCoder` from an ABI specification + * + * @param abi - The ABI specification + * @returns The `AbiCoder` instance + */ + static fromAbi(abi: AbiSpecification): AbiCoder { + return new AbiCoder(abi); + } + + /** + * Get a function by its name, signature or selector + * + * @param nameOrSignatureOrSelector - The function name, signature or selector + * @returns The function + * + * @throws FuelError - when the function is not found + */ + public getFunction(nameOrSignatureOrSelector: string): AbiCoderFunction { + const fn = this.functionLookup.get(nameOrSignatureOrSelector); + + if (fn === undefined) { + throw new FuelError( + FuelError.CODES.FUNCTION_NOT_FOUND, + `Unable to find function with the name or signature or selector of "${nameOrSignatureOrSelector}".` + ); + } + + return fn; + } + + /** + * Get a configurable by its name + * + * @param name - The configurable name + * @returns The configurable + * + * @throws FuelError - when the configurable is not found + */ + public getConfigurable(name: string): AbiCoderConfigurable { + const configurable = this.configurables[name]; + if (configurable === undefined) { + throw new FuelError( + FuelError.CODES.CONFIGURABLE_NOT_FOUND, + `Configurable with name '${name}' doesn't exist in the ABI.` + ); + } + + return configurable; + } + + /** + * Get a log by its log ID + * + * @param logId - The log ID + * @returns The log + * + * @throws FuelError - when the log is not found + */ + public getLog(logId: string): AbiCoderLog { + const log = this.logs[logId]; + if (log === undefined) { + throw new FuelError( + FuelError.CODES.LOG_TYPE_NOT_FOUND, + `Log type with logId '${logId}' doesn't exist in the ABI.` + ); + } + + return log; + } + + /** + * Get a type by its concrete type ID + * + * @param concreteTypeId - The concrete type ID + * @returns The type + * + * @throws FuelError - when the type is not found + */ + public getType(concreteTypeId: string): AbiCoderType { + const type = this.abi.concreteTypes.find((t) => t.concreteTypeId === concreteTypeId); + if (type === undefined) { + throw new FuelError( + FuelError.CODES.TYPE_NOT_FOUND, + `Type with concreteTypeId '${concreteTypeId}' doesn't exist in the ABI.` + ); + } + + return createType(type, this.encoding); + } +} diff --git a/packages/abi/src/coder/abi-coder-types.ts b/packages/abi/src/coder/abi-coder-types.ts new file mode 100644 index 00000000000..9c5bb1e24e6 --- /dev/null +++ b/packages/abi/src/coder/abi-coder-types.ts @@ -0,0 +1,37 @@ +import type { BytesLike } from '@fuel-ts/utils'; + +import type { AbiConfigurable, AbiFunction, AbiLoggedType } from '../parser'; + +import type { DecodedValue, InputValue } from './encoding/encoding-types'; + +export interface AbiCoderFunction { + name: AbiFunction['name']; + inputs: AbiFunction['inputs']; + signature: string; + selector: string; + selectorBytes: Uint8Array; + attributes: AbiFunction['attributes']; + isReadOnly: () => boolean; + encodeArguments: (values: InputValue[]) => Uint8Array; + decodeArguments: (data: BytesLike) => DecodedValue[]; + encodeOutput: (value: InputValue) => Uint8Array; + decodeOutput: (data: BytesLike) => DecodedValue; +} + +export interface AbiCoderConfigurable { + name: AbiConfigurable['name']; + offset: AbiConfigurable['offset']; + encode: (values: InputValue) => Uint8Array; + decode: (data: BytesLike) => DecodedValue; +} + +export interface AbiCoderLog { + logId: AbiLoggedType['logId']; + encode: (values: InputValue) => Uint8Array; + decode: (data: BytesLike) => DecodedValue; +} + +export interface AbiCoderType { + encode: (value: InputValue) => Uint8Array; + decode: (data: BytesLike) => DecodedValue; +} diff --git a/packages/abi/src/coder/constants.ts b/packages/abi/src/coder/constants.ts new file mode 100644 index 00000000000..96eb8e36ac6 --- /dev/null +++ b/packages/abi/src/coder/constants.ts @@ -0,0 +1,28 @@ +/** + * Property space and config constants + */ +export const WORD_SIZE = 8; +export const BYTES_32 = 32; +export const UTXO_ID_LEN = BYTES_32 + 2; +export const ASSET_ID_LEN = BYTES_32; +export const CONTRACT_ID_LEN = BYTES_32; +export const MAX_BYTES = 2 ** 32 - 1; // Max u32 + +export const calculateVmTxMemory = ({ maxInputs }: { maxInputs: number }) => + BYTES_32 + // Tx ID + ASSET_ID_LEN + // Base asset ID + // Asset ID/Balance coin input pairs + maxInputs * (ASSET_ID_LEN + WORD_SIZE) + + WORD_SIZE; // Tx size + +// SCRIPT_FIXED_SIZE = 104 +export const SCRIPT_FIXED_SIZE = + WORD_SIZE + // Identifier + WORD_SIZE + // Gas limit + WORD_SIZE + // Script size + WORD_SIZE + // Script data size + WORD_SIZE + // Policies + WORD_SIZE + // Inputs size + WORD_SIZE + // Outputs size + WORD_SIZE + // Witnesses size + BYTES_32; // Receipts root diff --git a/packages/abi/src/coder/encoding/encoding-constants.ts b/packages/abi/src/coder/encoding/encoding-constants.ts new file mode 100644 index 00000000000..2fa4621b5fe --- /dev/null +++ b/packages/abi/src/coder/encoding/encoding-constants.ts @@ -0,0 +1,43 @@ +export const BOOL_TYPE = 'bool'; +export const U8_TYPE = 'u8'; +export const U16_TYPE = 'u16'; +export const U32_TYPE = 'u32'; +export const U64_TYPE = 'u64'; +export const U256_TYPE = 'u256'; +export const B256_TYPE = 'b256'; +export const B512_TYPE = 'b512'; +export const VOID_TYPE = 'void'; +export const BYTES_TYPE = 'bytes'; +export const RAW_SLICE_TYPE = 'raw slice'; +export const STD_STRING_TYPE = 'std string'; +export const STR_SLICE_TYPE = 'str'; +export const VECTOR_TYPE = 'vector'; +export const STRING_TYPE = 'string'; +export const STRUCT_TYPE = 'struct'; +export const ARRAY_TYPE = 'array'; +export const ENUM_TYPE = 'enum'; +export const OPTION_TYPE = 'option'; +export const TUPLE_TYPE = 'tuple'; + +export const ENCODING_TYPES = [ + U8_TYPE, + U16_TYPE, + U32_TYPE, + U64_TYPE, + U256_TYPE, + BOOL_TYPE, + B256_TYPE, + B512_TYPE, + VOID_TYPE, + BYTES_TYPE, + RAW_SLICE_TYPE, + STD_STRING_TYPE, + STR_SLICE_TYPE, + VECTOR_TYPE, + STRING_TYPE, + STRUCT_TYPE, + ARRAY_TYPE, + ENUM_TYPE, + OPTION_TYPE, + TUPLE_TYPE, +] as const; diff --git a/packages/abi/src/coder/encoding/encoding-types.ts b/packages/abi/src/coder/encoding/encoding-types.ts new file mode 100644 index 00000000000..5a9e6a1512a --- /dev/null +++ b/packages/abi/src/coder/encoding/encoding-types.ts @@ -0,0 +1,112 @@ +import type { BN } from '@fuel-ts/math'; +import type { BytesLike } from '@fuel-ts/utils'; + +import type { AbiConcreteType, AbiTypeComponent } from '../../parser'; + +import type { coders as v1 } from './v1'; + +export type SupportedCodersV1 = typeof v1; + +/** + * All the supported coders, across all versions. + */ +export type SupportedCoders = SupportedCodersV1; +export type SupportedCoder = SupportedCoders[keyof SupportedCoders]; + +/** + * A primitive type value + */ +export type Primitive = string | number | boolean; + +/** + * An option type value + */ +export type Option = T | undefined; + +export type Bytes = Uint8Array | number[]; +export type RawSlice = Uint8Array | number[]; +export type StdString = string; +export type StrSlice = string; + +/** + * The type of value you can provide to `Coder.encode` + */ +export type InputValue = + | Primitive + | BN + | Option + | BytesLike + | InputValue[] + | { [key: string]: InputValue } + | Record; + +/** + * The type of value you can get from `Coder.decode` + */ +export type DecodedValue = + | Primitive + | DecodedValue[] + | { [key: string]: DecodedValue } + | Record; + +/** + * Used to infer the encoded and decoded values of a coder. + */ +export type TypesOfCoder = + TCoder extends Coder ? { Input: TInput; Decoded: TDecoded } : never; + +export abstract class Coder { + /** + * The type of the coder + */ + abstract type: string; + + /** + * Encode a value. + * + * @param value - The value to encode. + * @returns The encoded value. + * + * @throws FuelError - with a ENCODE_ERROR code + */ + abstract encode(value: TEncoded): Uint8Array; + + /** + * Decode a value. + * + * @param value - The value to decode. + * @param offset - The offset to start decoding from. + * @returns A tuple with the [decoded value, offset of the final decoded] + * + * @throws FuelError - with a DECODE_ERROR code + */ + abstract decode(value: Uint8Array, offset?: number): [TDecoded, number]; +} + +export type CoderFactoryParameters = { + name?: string; + type: AbiConcreteType | AbiTypeComponent['type']; +}; +export type CoderFactory = ( + params: CoderFactoryParameters, + factory: CoderFactory +) => TCoder; + +type CurrentEncoding = typeof v1; + +export type Encoding = CurrentEncoding & { + v1: typeof v1; + fromVersion: (version: string) => AbiEncoding; +}; + +export type AbiEncoding = { + coders: SupportedCoders; + + /** + * Get a coder for a given type and name + * + * @param opts - The options object + * @returns A coder + */ + getCoder: (params: CoderFactoryParameters) => Coder; +}; diff --git a/packages/abi/src/coder/encoding/encoding.ts b/packages/abi/src/coder/encoding/encoding.ts new file mode 100644 index 00000000000..de0090c25f5 --- /dev/null +++ b/packages/abi/src/coder/encoding/encoding.ts @@ -0,0 +1,74 @@ +import { FuelError } from '@fuel-ts/errors'; +import { assertUnreachable } from '@fuel-ts/utils'; + +import type { Matcher } from '../../matchers/sway-type-matchers'; + +import type { + AbiEncoding, + Coder, + CoderFactoryParameters, + Encoding, + SupportedCoder, + SupportedCoders, +} from './encoding-types'; +import * as v1 from './v1'; + +interface SupportedEncoding { + coders: SupportedCoders; + matcher: Matcher; +} + +const currentEncoding = v1.coders; +const supportedEncodings: Record = { + '1': { coders: v1.coders, matcher: v1.matcher }, +}; + +export const encoding: Encoding = { + ...currentEncoding, + v1: v1.coders, + fromVersion: (version: string): AbiEncoding => { + const supportedEncoding = supportedEncodings[version as keyof typeof supportedEncodings]; + if (!supportedEncoding) { + throw new FuelError( + FuelError.CODES.UNSUPPORTED_ENCODING_VERSION, + `Unsupported encoding version "${version}"` + ); + } + + const getCoder = (opts: CoderFactoryParameters) => { + const { type, name } = opts; + let coder: SupportedCoder | undefined; + try { + coder = supportedEncoding.matcher(type); + } catch (error) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `Unsupported coder type "${type.swayType}" for element "${name}"` + ); + } + + if (!coder) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `Unsupported coder type "${type.swayType}" for element "${name}"` + ); + } + + if (typeof coder === 'object') { + return coder as Coder; + } + + if (typeof coder === 'function') { + return coder.factory(opts, getCoder); + } + + // Coders should always be either an object or function + return assertUnreachable(coder as never); + }; + + return { + coders: supportedEncoding.coders, + getCoder, + }; + }, +}; diff --git a/packages/abi/src/coder/encoding/index.ts b/packages/abi/src/coder/encoding/index.ts new file mode 100644 index 00000000000..9fc5c4794dd --- /dev/null +++ b/packages/abi/src/coder/encoding/index.ts @@ -0,0 +1,2 @@ +export { encoding } from './encoding'; +export * from './encoding-types'; diff --git a/packages/abi/src/coder/encoding/v1/array.ts b/packages/abi/src/coder/encoding/v1/array.ts new file mode 100644 index 00000000000..f90d53ced99 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/array.ts @@ -0,0 +1,46 @@ +import { concat } from '@fuel-ts/utils'; + +import { ARRAY_TYPE } from '../encoding-constants'; +import type { Coder, TypesOfCoder } from '../encoding-types'; +import { + assertValueIsArray, + assertDataLengthLessThanMax, + assertValueLengthEqualsExpected, +} from '../validation'; + +/** + * `array` coder + */ +export type ArrayEncodeValue = Array['Input']>; +export type ArrayDecodeData = Array['Decoded']>; +export type ArrayCoder = Coder< + ArrayEncodeValue, + ArrayDecodeData +>; + +export const arrayCoder = ( + coder: TCoder, + size: number +): ArrayCoder => ({ + type: ARRAY_TYPE, + encode: (value: ArrayEncodeValue): Uint8Array => { + assertValueIsArray(value, ARRAY_TYPE); + assertValueLengthEqualsExpected(value, size, ARRAY_TYPE); + const encodedValues = value.map((elementValue) => coder.encode(elementValue)); + return concat(encodedValues); + }, + decode: (data: Uint8Array, initialOffset: number = 0): [ArrayDecodeData, number] => { + assertDataLengthLessThanMax(data, ARRAY_TYPE); + + let decoded; + let offset = initialOffset; + + const decodedValue = Array(size) + .fill(0) + .map(() => { + [decoded, offset] = coder.decode(data, offset); + return decoded; + }); + return [decodedValue as ArrayDecodeData, offset]; + }, +}); diff --git a/packages/abi/src/coder/encoding/v1/enum.ts b/packages/abi/src/coder/encoding/v1/enum.ts new file mode 100644 index 00000000000..01171d1d88c --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/enum.ts @@ -0,0 +1,90 @@ +import { concat } from '@fuel-ts/utils'; +import type { RequireExactlyOne } from 'type-fest'; + +import { ENUM_TYPE, VOID_TYPE } from '../encoding-constants'; +import type { Coder, TypesOfCoder } from '../encoding-types'; +import { errors } from '../validation'; + +import { u64 } from './fixed'; + +export type EnumEncodeValue> = RequireExactlyOne<{ + [P in keyof TCoders]: TypesOfCoder['Input']; +}>; +export type EnumDecodeValue> = RequireExactlyOne<{ + [P in keyof TCoders]: TypesOfCoder['Decoded']; +}>; +export type EnumCoder = Record> = Coder< + EnumEncodeValue, + EnumDecodeValue +>; + +const isNativeCoder = (coder: Coder) => coder.type === VOID_TYPE; +const extractCaseKeysFromValue = >( + value: EnumEncodeValue +): string[] => (typeof value === 'string' ? [value] : Object.keys(value)); +const extractCaseValueFromValue = >( + value: EnumEncodeValue +) => (typeof value === 'string' ? undefined : Object.values(value)[0]); +const extractCaseIndexFromData = (data: Uint8Array, initialOffset: number) => { + const [decoded, offset] = u64.decode(data, initialOffset); + return [decoded.toNumber(), offset]; +}; + +const createCaseValueCoder = (key: string, index: number, coder: TCoder) => { + const isNative = isNativeCoder(coder); + return { + type: ENUM_TYPE, + encode: (value: TypesOfCoder['Input']): Uint8Array => { + const encodedIndex = u64.encode(index); + const encodedValue = coder.encode(value); + return concat([encodedIndex, encodedValue]); + }, + decode: (data: Uint8Array, initialOffset = 0): [TypesOfCoder['Decoded'], number] => { + const [decoded, offset] = coder.decode(data, initialOffset); + return [isNative ? key : { [key]: decoded }, offset]; + }, + }; +}; + +export const enumCoder = >( + initialCoders: TCoders, + type: string = ENUM_TYPE +) => { + const coders = Object.fromEntries( + Object.entries(initialCoders).map(([key, coder], index) => [ + key, + createCaseValueCoder(key, index, coder), + ]) + ); + const validKeys = Object.keys(coders); + + return { + type, + encode: (value: EnumEncodeValue) => { + const [caseKey, ...shouldBeEmpty] = extractCaseKeysFromValue(value); + if (!caseKey) { + throw errors.invalidEnumValueCaseKeyMissing(type, value, validKeys); + } + if (!validKeys.includes(caseKey)) { + throw errors.invalidEnumValueCaseKey(type, value, caseKey, validKeys); + } + if (shouldBeEmpty.length > 0) { + throw errors.invalidEnumValueMultipleCaseKeys(type, value, validKeys); + } + const caseValue = extractCaseValueFromValue(value); + return coders[caseKey].encode(caseValue); + }, + decode: (data: Uint8Array, initialOffset = 0): [EnumDecodeValue, number] => { + if (data.length < initialOffset + 8) { + throw errors.invalidEnumDataCaseKey(type, validKeys); + } + const [caseIndex, indexOffset] = extractCaseIndexFromData(data, initialOffset); + if (caseIndex < 0 || caseIndex >= validKeys.length) { + throw errors.invalidEnumDataCaseKey(type, validKeys); + } + const caseKey = validKeys[caseIndex]; + const [decoded, offset] = coders[caseKey].decode(data, indexOffset); + return [decoded as EnumDecodeValue, offset]; + }, + }; +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/array.ts b/packages/abi/src/coder/encoding/v1/factories/array.ts new file mode 100644 index 00000000000..2a3df18d745 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/array.ts @@ -0,0 +1,34 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { AbiTypeComponent } from '../../../..'; +import { ARRAY_REGEX } from '../../../../matchers/sway-type-matchers'; +import { ARRAY_TYPE } from '../../encoding-constants'; +import type { CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import type { ArrayCoder } from '../array'; +import { arrayCoder } from '../array'; + +export const arrayCoderFactory: CoderFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + const arrayMatch = ARRAY_REGEX.exec(swayType)?.groups; + if (!arrayMatch) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${ARRAY_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const arraySize = parseInt(arrayMatch.length, 10); + const arrayElement: AbiTypeComponent | undefined = components?.[0]; + if (!arrayElement) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${ARRAY_TYPE} type is missing a ABI component.` + ); + } + + const arrayElementCoder = factory(arrayElement, factory); + return arrayCoder(arrayElementCoder, arraySize); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/enum.ts b/packages/abi/src/coder/encoding/v1/factories/enum.ts new file mode 100644 index 00000000000..795a4e948ff --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/enum.ts @@ -0,0 +1,29 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { AbiTypeComponent } from '../../../../parser'; +import { ENUM_TYPE } from '../../encoding-constants'; +import type { Coder, CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import { enumCoder } from '../enum'; +import type { EnumCoder } from '../enum'; + +export const enumCoderFactory: CoderFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + if (!components) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${ENUM_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const coders = components.reduce((obj, component: AbiTypeComponent) => { + const o: Record = obj; + + o[component.name] = factory(component, factory); + return o; + }, {}); + + return enumCoder(coders); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/index.ts b/packages/abi/src/coder/encoding/v1/factories/index.ts new file mode 100644 index 00000000000..4bb7663a80c --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/index.ts @@ -0,0 +1,17 @@ +import { arrayCoderFactory } from './array'; +import { enumCoderFactory } from './enum'; +import { optionFactory } from './option'; +import { stringFactory } from './string'; +import { structCoderFactory } from './struct'; +import { tupleCoderFactory } from './tuple'; +import { vectorFactory } from './vector'; + +export const factories = { + array: arrayCoderFactory, + enum: enumCoderFactory, + tuple: tupleCoderFactory, + struct: structCoderFactory, + vector: vectorFactory, + string: stringFactory, + option: optionFactory, +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/option.ts b/packages/abi/src/coder/encoding/v1/factories/option.ts new file mode 100644 index 00000000000..d1da469eeae --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/option.ts @@ -0,0 +1,28 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { AbiTypeComponent } from '../../../../parser'; +import { OPTION_TYPE } from '../../encoding-constants'; +import type { CoderFactory, CoderFactoryParameters, Coder } from '../../encoding-types'; +import { option } from '../option'; + +export const optionFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + if (!components) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${OPTION_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const coders = components.reduce((obj, component: AbiTypeComponent) => { + const o: Record = obj; + + o[component.name] = factory(component, factory); + return o; + }, {}); + + return option(coders); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/string.ts b/packages/abi/src/coder/encoding/v1/factories/string.ts new file mode 100644 index 00000000000..4cf47dd60e7 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/string.ts @@ -0,0 +1,22 @@ +import { FuelError } from '@fuel-ts/errors'; + +import { STRING_REGEX } from '../../../../matchers/sway-type-matchers'; +import { STRING_TYPE } from '../../encoding-constants'; +import type { CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import { string } from '../string'; + +export const stringFactory = ( + { type: { swayType } }: CoderFactoryParameters, + _factory: CoderFactory +) => { + const match = STRING_REGEX.exec(swayType)?.groups; + if (!match) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `Unable to find ${STRING_TYPE} coder for the provided type "${swayType}".`, + { swayType } + ); + } + const encodedLength = parseInt(match.length, 10); + return string(encodedLength); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/struct.ts b/packages/abi/src/coder/encoding/v1/factories/struct.ts new file mode 100644 index 00000000000..87964cacc5e --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/struct.ts @@ -0,0 +1,27 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { AbiTypeComponent } from '../../../../parser'; +import { STRUCT_TYPE } from '../../encoding-constants'; +import type { Coder, CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import { struct } from '../struct'; + +export const structCoderFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + if (!components) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${STRUCT_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const coders = components.reduce((obj, component: AbiTypeComponent) => { + const o: Record = obj; + + o[component.name] = factory(component, factory); + return o; + }, {}); + return struct(coders); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/tuple.ts b/packages/abi/src/coder/encoding/v1/factories/tuple.ts new file mode 100644 index 00000000000..ca4e0495cd7 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/tuple.ts @@ -0,0 +1,23 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { AbiTypeComponent } from '../../../../parser'; +import { TUPLE_TYPE } from '../../encoding-constants'; +import type { CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import { tuple } from '../tuple'; +import type { TupleCoder } from '../tuple'; + +export const tupleCoderFactory: CoderFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + if (!components) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${TUPLE_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const coders = components.map((component: AbiTypeComponent) => factory(component, factory)); + return tuple(coders); +}; diff --git a/packages/abi/src/coder/encoding/v1/factories/vector.ts b/packages/abi/src/coder/encoding/v1/factories/vector.ts new file mode 100644 index 00000000000..8310b7e2af4 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/factories/vector.ts @@ -0,0 +1,21 @@ +import { FuelError } from '@fuel-ts/errors'; + +import { VECTOR_TYPE } from '../../encoding-constants'; +import type { CoderFactory, CoderFactoryParameters } from '../../encoding-types'; +import { vector } from '../heap'; + +export const vectorFactory = ( + { type: { swayType, components } }: CoderFactoryParameters, + factory: CoderFactory +) => { + if (!components) { + throw new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + `The provided ${VECTOR_TYPE} type is missing ABI components.`, + { swayType, components } + ); + } + + const vecElementCoder = factory(components[0], factory); + return vector(vecElementCoder); +}; diff --git a/packages/abi/src/coder/encoding/v1/fixed.ts b/packages/abi/src/coder/encoding/v1/fixed.ts new file mode 100644 index 00000000000..73d3fe33b0c --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/fixed.ts @@ -0,0 +1,194 @@ +import type { BN, BNInput } from '@fuel-ts/math'; +import { toNumber, toBytes, bn, toHex } from '@fuel-ts/math'; +import { arrayify } from '@fuel-ts/utils'; + +import { + B256_TYPE, + B512_TYPE, + BOOL_TYPE, + U16_TYPE, + U256_TYPE, + U32_TYPE, + U64_TYPE, + U8_TYPE, + VOID_TYPE, +} from '../encoding-constants'; +import type { Coder } from '../encoding-types'; +import { + assertBnValueByteLengthLessThan, + assertBnValueNonNegative, + assertBooleanValue, + assertDataLengthMoreThanExpected, + assertEncode, + assertEncodedLengthEquals, + assertSafeNumberValue, + errors, +} from '../validation'; + +/** + * A number coder (u8, u16, u32) + * + * @param encodedLength - The number of bytes to encode the value. + * @param type - The type of the coder. + * @returns A number based coder. + */ +const createNumberCoder = (encodedLength: number, type: string): Coder => ({ + type, + /** + * Encode a number value. + * + * @param value - The number value to encode. + * @returns The encoded number value. + */ + encode: (value: number): Uint8Array => { + const bnValue = bn(value); + assertBnValueNonNegative(bnValue, type); + assertBnValueByteLengthLessThan(bnValue, encodedLength, type); + return toBytes(bnValue, encodedLength); + }, + decode: (data: Uint8Array, offset: number = 0): [number, number] => { + const elementData = data.slice(offset, offset + encodedLength); + assertEncodedLengthEquals(elementData, encodedLength, type); + return [toNumber(elementData), offset + encodedLength]; + }, +}); + +/** + * A big number coder (u64, u256) + * + * @param encodedLength - The number of bytes to encode the value. + * @param type - The type of the coder. + * @returns A big number based coder. + */ +const createBigNumberCoder = (encodedLength: number, type: string): Coder => ({ + type, + /** + * Encode a big number value. + * + * @param value - The big number value to encode. + * @returns The encoded big number value. + */ + encode: (value: BN | BNInput): Uint8Array => { + assertSafeNumberValue(value, type); + const bnValue = assertEncode(bn, value, errors.invalidBnValue(type, value)); + assertBnValueNonNegative(bnValue, type); + assertBnValueByteLengthLessThan(bnValue, encodedLength, type); + return toBytes(bnValue, encodedLength); + }, + /** + * Decode a big number value. + * + * @param data - The encoded data to decode. + * @param offset - The offset to start decoding from. + * @returns A tuple with the [decoded value, offset of the final decoded] + */ + decode: (data: Uint8Array, offset: number = 0): [BN, number] => { + const elementData = data.slice(offset, offset + encodedLength); + assertEncodedLengthEquals(elementData, encodedLength, type); + return [bn(elementData), offset + encodedLength]; + }, +}); + +/** + * A hex coder (b256, b512) + * + * @param encodedLength - The number of bytes to encode the value. + * @param type - The type of the coder. + * @returns A hex based coder. + */ +const createHexCoder = (encodedLength: number, type: string): Coder => ({ + type, + /** + * Encode a hex value. + * + * @param value - The hex value to encode. + * @returns The encoded hex value. + * + * @throws {@link FuelError} - when a malformed hex value is provided + * @throws {@link FuelError} - when the encoded value's byte length does not match the expected length + */ + encode: (value: string): Uint8Array => { + let encodedValue; + try { + encodedValue = arrayify(value); + } catch (error) { + throw errors.malformedHexValue(type, value, encodedLength); + } + + assertEncodedLengthEquals(encodedValue, encodedLength, type); + return encodedValue; + }, + /** + * Decode a hex value. + * + * @param data - The encoded data to decode. + * @param offset - The offset to start decoding from. + * @returns A tuple with the [decoded value, offset of the final decoded] + * + * @throws {@link FuelError} - when the encoded data length does not match the expected length + */ + decode: (data: Uint8Array, offset: number = 0): [string, number] => { + let bytes = data.slice(offset, offset + encodedLength); + assertEncodedLengthEquals(bytes, encodedLength, type); + + if (bn(bytes).isZero()) { + bytes = new Uint8Array(encodedLength); + } + return [toHex(bytes, encodedLength), offset + encodedLength]; + }, +}); + +export const u8: Coder = createNumberCoder(1, U8_TYPE); +export const u16: Coder = createNumberCoder(2, U16_TYPE); +export const u32: Coder = createNumberCoder(4, U32_TYPE); +export const u64: Coder = createBigNumberCoder(8, U64_TYPE); +export const u256: Coder = createBigNumberCoder(32, U256_TYPE); +export const b256: Coder = createHexCoder(32, B256_TYPE); +export const b512: Coder = createHexCoder(64, B512_TYPE); +export const voidCoder: Coder = { + type: VOID_TYPE, + /** + * Encode a void value. + * + * @returns The encoded void value. + */ + encode: (): Uint8Array => new Uint8Array(), + /** + * Decode a void value. + * + * @param data - The encoded data to decode. + * @param offset - The offset to start decoding from. + * @returns A tuple with the [decoded value, offset of the final decoded] + */ + decode: (_data: Uint8Array, offset: number = 0): [undefined, number] => [undefined, offset], +}; + +export const bool: Coder = { + type: BOOL_TYPE, + /** + * Encode a boolean value. + * + * @param value - The boolean value to encode. + * @returns The encoded boolean value. + */ + encode: (value: boolean): Uint8Array => { + assertBooleanValue(value); + return toBytes(value ? 1 : 0); + }, + /** + * Decode a boolean value. + * + * @param data - The encoded data to decode. + * @param offset - The offset to start decoding from. + * @returns A tuple with the [decoded value, offset of the final decoded] + */ + decode: (data: Uint8Array, initialOffset: number = 0): [boolean, number] => { + assertDataLengthMoreThanExpected(data, 1, BOOL_TYPE); + + const [value, offset] = u8.decode(data, initialOffset); + if (value !== 0 && value !== 1) { + throw errors.invalidBooleanData(BOOL_TYPE, data, value); + } + return [Boolean(value), offset]; + }, +}; diff --git a/packages/abi/src/coder/encoding/v1/heap.ts b/packages/abi/src/coder/encoding/v1/heap.ts new file mode 100644 index 00000000000..37b2ccce751 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/heap.ts @@ -0,0 +1,138 @@ +import { FuelError } from '@fuel-ts/errors'; +import { concat, toUtf8Bytes, toUtf8String } from '@fuel-ts/utils'; + +import { + BYTES_TYPE, + RAW_SLICE_TYPE, + STD_STRING_TYPE, + STR_SLICE_TYPE, + VECTOR_TYPE, +} from '../encoding-constants'; +import type { Coder } from '../encoding-types'; +import { assertDataLengthLessThanMax, assertEncodedLengthEquals, errors } from '../validation'; + +import { u64 } from './fixed'; + +const createHeapType = ({ + type, + encode, + decode, +}: Coder): Coder => ({ + type, + encode: (value: TEncoded) => { + try { + // Encode the length of the bytes + const dataLengthBytes = u64.encode(value.length); + + // Encode the value + const valueBytes = encode(value); + + // Concatenate the encoded length with the bytes + return concat([dataLengthBytes, valueBytes]); + } catch (error) { + throw errors.invalidValue(type, value); + } + }, + decode: (data: Uint8Array, initialOffset = 0): [TDecoded, number] => { + try { + // Obtain the length of the bytes + const [dataLengthBn, dataLowerOffset] = u64.decode(data, initialOffset); + const dataLength = dataLengthBn.toNumber(); + + // Obtain the data bytes + const dataUpperOffset = dataLowerOffset + dataLength; + const dataBytes = data.slice(dataLowerOffset, dataUpperOffset); + assertEncodedLengthEquals(dataBytes, dataLength, type); + + return decode(dataBytes, dataUpperOffset); + } catch (error) { + throw errors.malformedBytes(type, data); + } + }, +}); + +const bytesTransformer: Coder = { + type: BYTES_TYPE, + encode: (value: Uint8Array | number[]) => (Array.isArray(value) ? new Uint8Array(value) : value), + decode: (data: Uint8Array, offset: number): [Uint8Array, number] => [data, offset], +}; +export const bytes: Coder = createHeapType(bytesTransformer); + +const rawSliceTransformer: Coder = { + type: RAW_SLICE_TYPE, + encode: (value: number[]) => new Uint8Array(value), + decode: (data: Uint8Array, offset: number): [number[], number] => [Array.from(data), offset], +}; +export const rawSlice: Coder = createHeapType(rawSliceTransformer); + +const createStringCoder = (type: string): Coder => + createHeapType({ + type, + encode: (value: string) => toUtf8Bytes(value), + decode: (data: Uint8Array, offset: number): [string, number] => [toUtf8String(data), offset], + }); + +export const stdString = createStringCoder(STD_STRING_TYPE); +export const str = createStringCoder(STR_SLICE_TYPE); + +export type VecEncodedValue = + | Array[0]> + | Uint8Array; +export type VecDecodedValue = + | Array[0]> + | Uint8Array; + +const isUint8Array = (value: unknown): value is Uint8Array => value instanceof Uint8Array; + +export const vector = (coder: TCoder): Coder> => ({ + type: VECTOR_TYPE, + encode: (value: VecEncodedValue): Uint8Array => { + if (!Array.isArray(value) && !isUint8Array(value)) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${VECTOR_TYPE} value - expected array value, or a Uint8Array.`, + { value } + ); + } + + const valueArray = Array.isArray(value) ? value : Array.from(value); + + // Encode the length of the bytes + const dataLengthBytes = u64.encode(valueArray.length); + + // Encode the value + const valueBytes = valueArray.map((elementValue) => coder.encode(elementValue)); + + // Concatenate the encoded length with the bytes + return concat([dataLengthBytes, ...valueBytes]); + }, + decode: (data: Uint8Array, initialOffset = 0): [VecDecodedValue, number] => { + assertDataLengthLessThanMax(data, VECTOR_TYPE); + + let dataLengthBn; + let dataLowerOffset; + + try { + // Obtain the length of the bytes + [dataLengthBn, dataLowerOffset] = u64.decode(data, initialOffset); + } catch (error) { + throw new FuelError( + FuelError.CODES.DECODE_ERROR, + `Invalid ${VECTOR_TYPE} data - malformed bytes.`, + { data } + ); + } + + // Obtain the data bytes + const elements: unknown[] = []; + const dataLength = dataLengthBn.toNumber(); + let offset = dataLowerOffset; + for (let i = 0; i < dataLength; i++) { + const [decodedElement, elementOffset] = coder.decode(data, offset); + offset = elementOffset; + elements.push(decodedElement); + } + + return [elements as VecDecodedValue, offset]; + }, +}); diff --git a/packages/abi/src/coder/encoding/v1/index.ts b/packages/abi/src/coder/encoding/v1/index.ts new file mode 100644 index 00000000000..53c0cf4c78c --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/index.ts @@ -0,0 +1,66 @@ +import { createMatcher } from '../../../matchers/sway-type-matchers'; +import type { SupportedCoder } from '../encoding-types'; + +import { arrayCoder } from './array'; +import { enumCoder } from './enum'; +import { factories } from './factories'; +import { voidCoder, u16, u32, u8, u64, u256, b256, b512, bool } from './fixed'; +import { bytes, rawSlice, stdString, str, vector } from './heap'; +import { option } from './option'; +import { string } from './string'; +import { struct } from './struct'; +import { tuple } from './tuple'; + +export const coders = { + u8, + u16, + u32, + u64, + u256, + b256, + b512, + bool, + void: voidCoder, + bytes, + rawSlice, + str, + stdString, + array: Object.assign(arrayCoder, { factory: factories.array }), + tuple: Object.assign(tuple, { factory: factories.tuple }), + struct: Object.assign(struct, { factory: factories.struct }), + enum: Object.assign(enumCoder, { factory: factories.enum }), + vector: Object.assign(vector, { factory: factories.vector }), + string: Object.assign(string, { factory: factories.string }), + option: Object.assign(option, { factory: factories.option }), +} as const; + +export const matcher = createMatcher({ + u8: coders.u8, + u16: coders.u16, + u32: coders.u32, + u64: coders.u64, + u256: coders.u256, + rawUntypedSlice: coders.rawSlice, + b256: coders.b256, + b512: coders.b512, + bool: coders.bool, + void: coders.void, + + string: coders.string, + array: coders.array, + tuple: coders.tuple, + vector: coders.vector, + struct: coders.struct, + bytes: coders.bytes, + stdString: coders.stdString, + str: coders.str, + enum: coders.enum, + option: coders.option, + + assetId: coders.struct, + evmAddress: coders.struct, + result: coders.enum, + + // Unmatchable + generic: undefined, +}); diff --git a/packages/abi/src/coder/encoding/v1/option.ts b/packages/abi/src/coder/encoding/v1/option.ts new file mode 100644 index 00000000000..643df9f7d12 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/option.ts @@ -0,0 +1,45 @@ +import { OPTION_TYPE } from '../encoding-constants'; +import type { Coder, Option } from '../encoding-types'; +import { assertDecode, assertEncode, errors } from '../validation'; + +import { enumCoder } from './enum'; +import type { EnumDecodeValue, EnumEncodeValue } from './enum'; + +type SwayOption = 'None' | { Some: T }; + +export type OptionEncodeValue> = + | EnumEncodeValue['Some'] + | undefined; +export type OptionDecodeData> = + EnumDecodeValue[keyof TCoders]; +export type OptionCoder> = Coder< + OptionEncodeValue, + OptionDecodeData +>; + +export const option = >( + coders: TCoders +): OptionCoder => { + const valueCoder = enumCoder(coders, OPTION_TYPE); + return { + type: OPTION_TYPE, + encode: (value: Option): Uint8Array => { + const input: SwayOption = value === undefined ? 'None' : { Some: value }; + return assertEncode( + valueCoder.encode, + input as unknown as EnumEncodeValue, + errors.invalidValue(OPTION_TYPE, input) + ); + }, + decode: (data: Uint8Array, initialOffset = 0): [OptionDecodeData, number] => { + const [decoded, offset] = assertDecode( + valueCoder.decode, + data, + initialOffset, + errors.malformedBytes(OPTION_TYPE, data) + ); + const optionValue = typeof decoded === 'string' ? undefined : decoded.Some; + return [optionValue as OptionDecodeData, offset]; + }, + }; +}; diff --git a/packages/abi/src/coder/encoding/v1/string.ts b/packages/abi/src/coder/encoding/v1/string.ts new file mode 100644 index 00000000000..9211c0393b0 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/string.ts @@ -0,0 +1,25 @@ +import { toUtf8Bytes, toUtf8String } from '@fuel-ts/utils'; + +import { STRING_TYPE } from '../encoding-constants'; +import type { Coder } from '../encoding-types'; +import { assertEncodedLengthEquals, assertValueLengthEqualsExpected } from '../validation'; + +/** + * `string` coder + * + * @param encodedLength - The length of the encoded array. + * @returns + */ +export const string = (encodedLength: number): Coder => ({ + type: 'string', + encode: (value: string): Uint8Array => { + assertValueLengthEqualsExpected(value, encodedLength, STRING_TYPE); + return toUtf8Bytes(value); + }, + decode: (data: Uint8Array, offset: number = 0): [string, number] => { + const upperOffset = offset + encodedLength; + const bytes = data.slice(offset, upperOffset); + assertEncodedLengthEquals(bytes, encodedLength, STRING_TYPE); + return [toUtf8String(bytes), upperOffset]; + }, +}); diff --git a/packages/abi/src/coder/encoding/v1/struct.ts b/packages/abi/src/coder/encoding/v1/struct.ts new file mode 100644 index 00000000000..42eec900507 --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/struct.ts @@ -0,0 +1,44 @@ +import { concatBytes } from '@fuel-ts/utils'; + +import { STRUCT_TYPE } from '../encoding-constants'; +import type { Coder, TypesOfCoder } from '../encoding-types'; +import { assertObjectKeysAllPresent } from '../validation'; + +/** + * `struct` coder + */ +export type StructEncodeValue> = { + [P in keyof TCoders]: TypesOfCoder['Input']; +}; +export type StructDecodeData> = { + [P in keyof TCoders]: TypesOfCoder['Decoded']; +}; +export type StructCoder = Record> = Coder< + StructEncodeValue, + StructDecodeData +>; + +export const struct = >( + coders: TCoders +): StructCoder => ({ + type: STRUCT_TYPE, + encode: (value: StructEncodeValue): Uint8Array => { + assertObjectKeysAllPresent(value, coders, STRUCT_TYPE); + + // Encode each value + const encodedValues = Object.entries(coders).map(([key, coder]) => coder.encode(value[key])); + + return concatBytes(encodedValues); + }, + decode: (data: Uint8Array, initialOffset = 0): [StructDecodeData, number] => { + let offset = initialOffset; + let decoded; + + const decodedValue = Object.entries(coders).reduce((acc, [caseKey, fieldCoder]) => { + [decoded, offset] = fieldCoder.decode(data, offset); + acc[caseKey as keyof StructDecodeData] = decoded; + return acc; + }, {} as StructDecodeData); + return [decodedValue, offset]; + }, +}); diff --git a/packages/abi/src/coder/encoding/v1/tuple.ts b/packages/abi/src/coder/encoding/v1/tuple.ts new file mode 100644 index 00000000000..430dbe79fcb --- /dev/null +++ b/packages/abi/src/coder/encoding/v1/tuple.ts @@ -0,0 +1,45 @@ +import { concat } from '@fuel-ts/utils'; + +import { TUPLE_TYPE } from '../encoding-constants'; +import type { Coder, TypesOfCoder } from '../encoding-types'; +import { assertValueLengthEqualsExpected, errors } from '../validation'; + +/** + * Tuple coder + */ +export type TupleEncodeValue = { + [P in keyof TCoders]: TypesOfCoder['Input']; +}; +export type TupleDecodeValue = { + [P in keyof TCoders]: TypesOfCoder['Decoded']; +}; +export type TupleCoder = Coder< + TupleEncodeValue, + TupleDecodeValue +>; + +export const tuple = (coders: TCoders): TupleCoder => ({ + type: TUPLE_TYPE, + encode: (value: TupleEncodeValue): Uint8Array => { + assertValueLengthEqualsExpected(value, coders.length, TUPLE_TYPE); + const encodedValues = coders.map((elementCoder, elementIndex) => { + const elementValue = value[elementIndex]; + return elementCoder.encode(elementValue); + }); + return concat(encodedValues); + }, + decode: (data: Uint8Array, initialOffset = 0): [TupleDecodeValue, number] => { + try { + let offset = initialOffset; + let decoded; + + const decodedValue = coders.map((coder) => { + [decoded, offset] = coder.decode(data, offset); + return decoded; + }); + return [decodedValue as TupleDecodeValue, offset]; + } catch (error) { + throw errors.malformedData(TUPLE_TYPE, data, initialOffset); + } + }, +}); diff --git a/packages/abi/src/coder/encoding/validation.ts b/packages/abi/src/coder/encoding/validation.ts new file mode 100644 index 00000000000..1cbb55d1ab7 --- /dev/null +++ b/packages/abi/src/coder/encoding/validation.ts @@ -0,0 +1,240 @@ +import { FuelError } from '@fuel-ts/errors'; +import type { BN, BNInput } from '@fuel-ts/math'; + +import type { Coder } from '../..'; +import { MAX_BYTES } from '../constants'; + +export const errors = { + /** + * Encode errors + */ + invalidValue: (type: string, value: unknown) => + new FuelError(FuelError.CODES.ENCODE_ERROR, `Invalid ${type} value - malformed value.`, { + value, + }), + invalidBnValue: (type: string, value: BNInput) => + new FuelError(FuelError.CODES.ENCODE_ERROR, `Invalid ${type} value - expected a BNInput.`, { + value, + }), + malformedHexValue: (type: string, value: string, expectedLength: number) => + new FuelError(FuelError.CODES.ENCODE_ERROR, `Invalid ${type} value - malformed hex value.`, { + value, + expectedLength, + }), + invalidEnumValueCaseKeyMissing: (type: string, value: unknown, validKeys: string[]) => + new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - a valid case key must be provided.`, + { + value, + validKeys, + } + ), + invalidEnumValueCaseKey: (type: string, value: unknown, caseKey: string, validKeys: string[]) => + new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - invalid case key "${caseKey}".`, + { value, validKeys } + ), + invalidEnumValueMultipleCaseKeys: (type: string, value: unknown, validKeys: string[]) => + new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - only one field must be provided.`, + { value, validKeys } + ), + /** + * Decode errors + */ + malformedData: (type: string, data: Uint8Array, expectedLength: number) => + new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} data - malformed data.`, { + data, + expectedLength, + }), + malformedBytes: (type: string, data: Uint8Array) => + new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} data - malformed bytes.`, { + data, + }), + invalidEnumDataCaseKey: (type: string, validKeys: string[]) => + new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} data - invalid case key.`, { + validKeys, + }), + invalidBooleanData: (type: string, data: Uint8Array, decoded: unknown) => + new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} value - invalid boolean value.`, { + data, + type, + value: decoded, + }), +}; + +export const assertEncode = ( + encode: (value: TValue) => TOutput, + value: TValue, + thrownError: FuelError +) => { + try { + return encode(value); + } catch (error) { + throw thrownError; + } +}; + +/** + * Asserts that a value is a boolean. + * + * @throws {@link FuelError} - with a ENCODE_ERROR code if the value is not a boolean + */ +export const assertBooleanValue = (value: unknown) => { + if (typeof value !== 'boolean') { + throw new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid boolean value.', { value }); + } +}; + +export const assertValueIsArray = (value: unknown, type: string) => { + if (!Array.isArray(value)) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - expected array value.`, + { + value, + type, + } + ); + } +}; + +/** + * Asserts that a number value is less than the max safe integer. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#description + * + * @throws {@link FuelError} - with a ENCODE_ERROR code if the value is more than the max safe integer + */ +export const assertSafeNumberValue = (value: unknown, type: string) => { + if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} type - number value is too large. Number can only safely handle up to 53 bits.` + ); + } +}; + +/** + * Asserts that a BN value is non-negative. + * + * @throws {@link FuelError} - with a ENCODE_ERROR code if the value is less than zero + */ +export const assertBnValueNonNegative = (value: BN, type: string) => { + if (value.isNeg()) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - value is less than zero.`, + { value: value.toString(), type } + ); + } +}; + +/** + * Asserts that a BN value's byte length is less than a maximum. + * + * @throws {@link FuelError} - with a ENCODE_ERROR code if the value's byte length exceeds the maximum + */ +export const assertBnValueByteLengthLessThan = (value: BN, max: number, type: string) => { + if (value.byteLength() > max) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - value exceeds maximum.`, + { value: value.toString(), type } + ); + } +}; + +export function assertValueLengthEqualsExpected( + value: ArrayLike, + expectedLength: number, + type: string +) { + if (value.length !== expectedLength) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid ${type} value - unexpected length.`, + { value, expectedLength } + ); + } +} + +export function assertObjectKeysAllPresent>( + value: Record, + coders: TCoders, + type: string +) { + const actualKeys = Object.keys(value); + const expectedKeys = Object.keys(coders); + + // Check if there are any missing keys or extra keys + const missingKeys = expectedKeys.filter((key) => !actualKeys.includes(key)); + if (missingKeys.length > 0) { + const paths = missingKeys.map((key) => ({ path: key, error: 'Field not present.' })); + throw new FuelError(FuelError.CODES.ENCODE_ERROR, `Invalid ${type} value - malformed object.`, { + value, + paths, + }); + } +} + +export const assertDecode = ( + decode: (data: Uint8Array, offset: number) => [T, number], + data: Uint8Array, + offset: number, + thrownError: FuelError +) => { + try { + return decode(data, offset); + } catch (error) { + throw thrownError; + } +}; + +/** + * Asserts that the encoded data length matches the expected length + * + * @throws {@link FuelError} - with a DECODE_ERROR code if the encoded data length does not match the expected length + */ +export const assertEncodedLengthEquals = ( + data: Uint8Array, + expectedLength: number, + type: string +) => { + if (data.length !== expectedLength) { + throw new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} data - unexpected length.`, { + data, + type, + expectedLength, + }); + } +}; + +export function assertDataLengthLessThanMax(data: Uint8Array, type: string) { + if (data.length > MAX_BYTES) { + throw new FuelError( + FuelError.CODES.DECODE_ERROR, + `Invalid ${type} data - exceeds maximum bytes.`, + { + data, + length: data.length, + maxLength: MAX_BYTES, + } + ); + } +} + +export function assertDataLengthMoreThanExpected( + data: Uint8Array, + expectedLength: number, + type: string +) { + if (data.length < expectedLength) { + throw new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid ${type} data - not enough data.`, { + data, + expectedLength, + }); + } +} diff --git a/packages/abi/src/coder/index.ts b/packages/abi/src/coder/index.ts new file mode 100644 index 00000000000..1c4e5254600 --- /dev/null +++ b/packages/abi/src/coder/index.ts @@ -0,0 +1,4 @@ +export { AbiCoder } from './AbiCoder'; +export * from './abi-coder-types'; +export * from './constants'; +export * from './encoding'; diff --git a/packages/abi/src/coder/utils/createConfigurable.ts b/packages/abi/src/coder/utils/createConfigurable.ts new file mode 100644 index 00000000000..49129e7c065 --- /dev/null +++ b/packages/abi/src/coder/utils/createConfigurable.ts @@ -0,0 +1,23 @@ +import type { BytesLike } from '@fuel-ts/utils'; +import { arrayify } from '@fuel-ts/utils'; + +import type { AbiConfigurable } from '../../parser'; +import type { AbiCoderConfigurable } from '../abi-coder-types'; +import type { AbiEncoding, DecodedValue } from '../encoding'; + +export const createConfigurable = ( + configurable: AbiConfigurable, + encoding: AbiEncoding +): AbiCoderConfigurable => { + const configurableCoder = encoding.getCoder(configurable); + return { + name: configurable.name, + offset: configurable.offset, + encode: configurableCoder.encode, + decode: (data: BytesLike): DecodedValue => { + const bytes = arrayify(data); + const [decodedValue] = configurableCoder.decode(bytes); + return decodedValue as DecodedValue; + }, + }; +}; diff --git a/packages/abi/src/coder/utils/createFunction.ts b/packages/abi/src/coder/utils/createFunction.ts new file mode 100644 index 00000000000..00cd02c8ce8 --- /dev/null +++ b/packages/abi/src/coder/utils/createFunction.ts @@ -0,0 +1,59 @@ +import { FuelError } from '@fuel-ts/errors'; +import type { BytesLike } from '@fuel-ts/utils'; +import { arrayify } from '@fuel-ts/utils'; + +import type { AbiFunction } from '../../parser'; +import type { AbiCoderFunction } from '../abi-coder-types'; +import type { AbiEncoding, DecodedValue, InputValue } from '../encoding'; + +import { createFunctionSelector } from './createFunctionSelector'; +import { createFunctionSignature } from './createFunctionSignature'; +import { getFunctionInputs } from './getFunctionInputs'; +import { padValuesWithUndefined } from './padValuesWithUndefined'; + +export const createFunction = (fn: AbiFunction, encoding: AbiEncoding): AbiCoderFunction => { + const signature = createFunctionSignature(fn); + const argumentCoder = encoding.coders.tuple(fn.inputs.map((input) => encoding.getCoder(input))); + const outputCoder = encoding.getCoder({ type: fn.output }); + + const storageAttribute = fn.attributes?.find((attr) => attr.name === 'storage'); + const isReadOnly = !storageAttribute?.arguments?.includes('write'); + + const functionInputs = getFunctionInputs({ inputs: fn.inputs }); + const mandatoryInputLength = functionInputs.filter((i) => !i.isOptional).length; + + return { + // Function fields + name: fn.name, + inputs: fn.inputs, + signature, + selector: createFunctionSelector(signature), + selectorBytes: encoding.coders.stdString.encode(fn.name), + attributes: fn.attributes ?? [], + isReadOnly: () => isReadOnly, + + // Coders + encodeArguments: (values: InputValue[]): Uint8Array => { + if (values.length < mandatoryInputLength) { + throw new FuelError( + FuelError.CODES.ENCODE_ERROR, + `Invalid number of arguments. Expected a minimum of ${mandatoryInputLength} arguments, received ${values.length}` + ); + } + + const paddedValues = padValuesWithUndefined(values, functionInputs); + return argumentCoder.encode(paddedValues); + }, + decodeArguments: (data: BytesLike): DecodedValue[] => { + const bytes = arrayify(data); + const [decoded] = argumentCoder.decode(bytes); + return decoded as DecodedValue[]; + }, + encodeOutput: (value: InputValue): Uint8Array => outputCoder.encode(value), + decodeOutput: (data: BytesLike): DecodedValue => { + const bytes = arrayify(data); + const [decoded] = outputCoder.decode(bytes); + return decoded as DecodedValue; + }, + }; +}; diff --git a/packages/abi/src/coder/utils/createFunctionSelector.ts b/packages/abi/src/coder/utils/createFunctionSelector.ts new file mode 100644 index 00000000000..febcdbf73cf --- /dev/null +++ b/packages/abi/src/coder/utils/createFunctionSelector.ts @@ -0,0 +1,18 @@ +import { bufferFromString } from '@fuel-ts/crypto'; +import { sha256 } from '@fuel-ts/hasher'; +import { bn } from '@fuel-ts/math'; + +/** + * Creates a function selector from a function signature. This acts as a unique identifier for the function + * that can be used to identify the function in the contract. Previously, this was used in contract calls adhering + * to the v0 specification. This has since been superseded to be a StdString representation of a function name. + * However this selector still exists as a unique identifier for the function. + * + * @param functionSignature - The function signature to create a selector from. + * @returns The function selector. + */ +export const createFunctionSelector = (functionSignature: string): string => { + const hashedFunctionSignature = sha256(bufferFromString(functionSignature, 'utf-8')); + // get first 4 bytes of signature + 0x prefix. then left-pad it to 8 bytes using toHex(8) + return bn(hashedFunctionSignature.slice(0, 10)).toHex(8); +}; diff --git a/packages/abi/src/coder/utils/createFunctionSignature.ts b/packages/abi/src/coder/utils/createFunctionSignature.ts new file mode 100644 index 00000000000..6a492692b00 --- /dev/null +++ b/packages/abi/src/coder/utils/createFunctionSignature.ts @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { + ARRAY_REGEX, + ENUM_REGEX, + STRING_REGEX, + STRUCT_REGEX, + swayTypeMatchers, +} from '../../matchers/sway-type-matchers'; +import type { AbiFunction, AbiConcreteType, AbiTypeComponent } from '../../parser'; + +const HEAP_TYPE_SIGNATURE = '(rawptr,u64),u64'; + +const createSignaturePrefix = ({ + type, +}: { + type: AbiConcreteType | AbiTypeComponent['type']; +}): string => { + switch (true) { + case STRUCT_REGEX.test(type.swayType): + return 's'; + + case ARRAY_REGEX.test(type.swayType): + return 'a'; + + case ENUM_REGEX.test(type.swayType): + return 'e'; + + default: + return ''; + } +}; + +const createSignatureContents = ({ + type, +}: { + type: AbiConcreteType | AbiTypeComponent['type']; +}): string => { + const { swayType, components, metadata } = type; + + if (swayTypeMatchers.rawUntypedSlice(type)) { + return 'rawslice'; + } + + const strMatch = STRING_REGEX.exec(swayType)?.groups; + if (strMatch) { + return `str[${strMatch.length}]`; + } + + if (swayTypeMatchers.bytes(type)) { + return `(s${HEAP_TYPE_SIGNATURE})`; + } + + if (components === undefined) { + return swayType; + } + + const arrayMatch = ARRAY_REGEX.exec(swayType)?.groups; + if (arrayMatch) { + const arrayElementSignature = createSignatureForType(components[0]); + return `[${arrayElementSignature};${arrayMatch.length}]`; + } + + const typeArgumentsSignature = metadata?.typeArguments + ? `<${metadata.typeArguments + ?.map((typeArgument) => createSignatureForType({ type: typeArgument })) + .join(',')}>` + : ''; + + const componentsSignature = swayTypeMatchers.vector(type) + ? `s${typeArgumentsSignature}${HEAP_TYPE_SIGNATURE}` + : components.map(createSignatureForType).join(','); + + return `${typeArgumentsSignature}(${componentsSignature})`; +}; + +const createSignatureForType = (input: { + type: AbiConcreteType | AbiTypeComponent['type']; +}): string => { + const prefix = createSignaturePrefix(input); + const contents = createSignatureContents(input); + + return `${prefix}${contents}`; +}; + +export const createFunctionSignature = (fn: AbiFunction): string => { + const functionInputsSignature = fn.inputs.map(createSignatureForType).join(','); + return `${fn.name}(${functionInputsSignature})`; +}; diff --git a/packages/abi/src/coder/utils/createLog.ts b/packages/abi/src/coder/utils/createLog.ts new file mode 100644 index 00000000000..371825c766c --- /dev/null +++ b/packages/abi/src/coder/utils/createLog.ts @@ -0,0 +1,20 @@ +import type { BytesLike } from '@fuel-ts/utils'; +import { arrayify } from '@fuel-ts/utils'; + +import type { AbiLoggedType } from '../../parser'; +import type { AbiCoderLog } from '../abi-coder-types'; +import type { AbiEncoding } from '../encoding'; +import type { DecodedValue } from '../encoding/encoding-types'; + +export const createLog = (loggedType: AbiLoggedType, encoding: AbiEncoding): AbiCoderLog => { + const loggedTypeCoder = encoding.getCoder(loggedType); + return { + logId: loggedType.logId, + encode: loggedTypeCoder.encode, + decode: (data: BytesLike): DecodedValue => { + const bytes = arrayify(data); + const [decoded] = loggedTypeCoder.decode(bytes); + return decoded as DecodedValue; + }, + }; +}; diff --git a/packages/abi/src/coder/utils/createType.ts b/packages/abi/src/coder/utils/createType.ts new file mode 100644 index 00000000000..fa395031489 --- /dev/null +++ b/packages/abi/src/coder/utils/createType.ts @@ -0,0 +1,22 @@ +import type { BytesLike } from '@fuel-ts/utils'; +import { arrayify } from '@fuel-ts/utils'; + +import type { AbiConcreteType, AbiTypeComponent } from '../../parser'; +import type { AbiCoderType } from '../abi-coder-types'; +import type { AbiEncoding } from '../encoding'; +import type { DecodedValue } from '../encoding/encoding-types'; + +export function createType( + type: AbiConcreteType | AbiTypeComponent['type'], + encoding: AbiEncoding +): AbiCoderType { + const coder = encoding.getCoder({ type }); + return { + encode: coder.encode, + decode: (data: BytesLike): DecodedValue => { + const bytes = arrayify(data); + const [decoded] = coder.decode(bytes); + return decoded as DecodedValue; + }, + }; +} diff --git a/packages/abi/src/coder/utils/getFunctionInputs.ts b/packages/abi/src/coder/utils/getFunctionInputs.ts new file mode 100644 index 00000000000..a71c8d96296 --- /dev/null +++ b/packages/abi/src/coder/utils/getFunctionInputs.ts @@ -0,0 +1,33 @@ +import { swayTypeMatchers } from '../../matchers/sway-type-matchers'; +import type { AbiFunctionInput } from '../../parser'; + +export type FunctionInput = TArg & { + isOptional: boolean; +}; + +const isMandatoryInput = (input: AbiFunctionInput) => + !swayTypeMatchers.void(input.type) && !swayTypeMatchers.option(input.type); + +/** + * Get the function inputs with the `isOptional` flag set to true for optional inputs. + * + * Optional parameters are ones which don't require inputs (void or Option) + * Given a function input of (u8, Option), the second input will be marked as optional as it could take a value or not. + * + * @param params - The function inputs. + * @returns an array of function inputs with the `isOptional` flag set to true for optional inputs. + */ +export const getFunctionInputs = (params: { + inputs: readonly AbiFunctionInput[]; +}): Array => { + const { inputs } = params; + let isMandatory = false; + + /** + * We iterate over the inputs in reverse order to ensure that the first optional input is marked as such. + */ + return inputs.reduceRight((result, input) => { + isMandatory = isMandatory || isMandatoryInput(input); + return [{ ...input, isOptional: !isMandatory }, ...result]; + }, [] as FunctionInput[]); +}; diff --git a/packages/abi/src/coder/utils/padValuesWithUndefined.ts b/packages/abi/src/coder/utils/padValuesWithUndefined.ts new file mode 100644 index 00000000000..179c265037a --- /dev/null +++ b/packages/abi/src/coder/utils/padValuesWithUndefined.ts @@ -0,0 +1,9 @@ +import type { InputValue } from '../encoding/encoding-types'; + +export const padValuesWithUndefined = (values: InputValue[], inputs: ArrayLike) => { + if (values.length >= inputs.length) { + return values; + } + + return [...values, ...Array(inputs.length - values.length).fill(undefined)]; +}; diff --git a/packages/abi/src/gen/abi-gen-types.ts b/packages/abi/src/gen/abi-gen-types.ts new file mode 100644 index 00000000000..032c27a243f --- /dev/null +++ b/packages/abi/src/gen/abi-gen-types.ts @@ -0,0 +1,55 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; + +import type { Abi } from '../parser'; + +export interface AbiGenInput { + /** + * The details of the program to generate the files for. + */ + programDetails: ProgramDetails[]; + /** + * The versions of the binaries used to generate the files. + */ + versions: BinaryVersions; + /** + * The mode to generate the files in. + * Defaults to 'ts' which generates typescript files. + */ + mode?: 'ts'; +} + +export interface AbiGenResult { + /** + * The filename of the generated file. + */ + filename: string; + /** + * The content of the generated file. + */ + content: string; +} + +export interface ProgramDetails { + /** + * The name of the program to generate files for. + * This will be used to name the generated files, + * as well as throughout the generated code. + */ + name: string; + /** + * The compressed bytecode of the program in base64 format. + */ + binCompressed?: string; + /** + * The abi of the program in the format returned via `AbiParser`. + */ + abi: Abi; + /** + * The original abi contents in string format. + */ + abiContents: string; + /** + * The storage slots, if working with a contract. + */ + storageSlots?: string; +} diff --git a/packages/abi/src/gen/abi-gen.ts b/packages/abi/src/gen/abi-gen.ts new file mode 100644 index 00000000000..db6b2d9f46a --- /dev/null +++ b/packages/abi/src/gen/abi-gen.ts @@ -0,0 +1,19 @@ +import type { AbiGenInput, AbiGenResult } from './abi-gen-types'; +import { getRenderer } from './renderers/getRenderer'; + +/** + * The main class to generate files for given sway programs. + * These contents of these generated files make it easier to interact + * with the sway programs, because type definitions are added, + * as well as some automatic loading is done for the user. + */ +export class AbiGen { + /** + * @returns an array of generated files for the given program details. + * They can be saved to disk as-is or further processed. + */ + public static generate({ programDetails, mode, versions }: AbiGenInput): AbiGenResult[] { + const render = getRenderer(mode); + return render(programDetails, versions); + } +} diff --git a/packages/abi/src/gen/cli/index.ts b/packages/abi/src/gen/cli/index.ts new file mode 100644 index 00000000000..939508c0f1b --- /dev/null +++ b/packages/abi/src/gen/cli/index.ts @@ -0,0 +1 @@ +export { configureTypegenCliOptions, runTypegen } from './run'; diff --git a/packages/abi/src/gen/cli/run.ts b/packages/abi/src/gen/cli/run.ts new file mode 100644 index 00000000000..ab87e9c61d4 --- /dev/null +++ b/packages/abi/src/gen/cli/run.ts @@ -0,0 +1,52 @@ +import { getBinaryVersions } from '@fuel-ts/versions/cli'; +import type { Command } from 'commander'; +import { mkdirSync, writeFileSync } from 'fs'; +import { join } from 'path'; + +import { AbiGen } from '../abi-gen'; + +import { getProgramDetails, loggingConfig } from './utils'; + +export interface RunTypegenOptions { + inputs: string[]; + output: string; + silent?: boolean; +} + +export function runTypegen(options: RunTypegenOptions) { + const { inputs, output, silent } = options; + + loggingConfig.silent = !!silent; + + const programDetails = getProgramDetails(inputs); + + const results = AbiGen.generate({ programDetails, versions: getBinaryVersions() }); + + mkdirSync(output, { recursive: true }); + + const subDirectories = new Set(); + + results.forEach((r) => { + const dir = r.filename.split('/').slice(0, -1).join('/'); + if (dir !== '') { + subDirectories.add(dir); + } + }); + + subDirectories.forEach((dir) => { + mkdirSync(join(output, dir), { recursive: true }); + }); + + results.forEach((r) => { + const outputPath = join(output, r.filename); + writeFileSync(outputPath, r.content); + }); +} + +export function configureTypegenCliOptions(program: Command) { + return program + .description(`Generate Typescript from forc build outputs`) + .requiredOption('-i, --inputs ', 'Input paths/globals to your ABI JSON files') + .requiredOption('-o, --output ', 'Directory path for generated files') + .action(runTypegen); +} diff --git a/packages/abi/src/gen/cli/utils.test.ts b/packages/abi/src/gen/cli/utils.test.ts new file mode 100644 index 00000000000..394b52180f9 --- /dev/null +++ b/packages/abi/src/gen/cli/utils.test.ts @@ -0,0 +1,38 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { normalizeProjectName } from './utils'; + +/** + * @group node + */ +describe('normalizeProjectName', () => { + test('should normalize project name', () => { + expect(normalizeProjectName('DsToken')).toEqual('DsToken'); + expect(normalizeProjectName('test')).toEqual('Test'); + expect(normalizeProjectName('ds-token')).toEqual('DsToken'); + expect(normalizeProjectName('ds_token')).toEqual('DsToken'); + expect(normalizeProjectName('Aaa_bbb_CCDD-EEE')).toEqual('AaaBbbCCDDEEE'); + expect(normalizeProjectName('ds token')).toEqual('DsToken'); + expect(normalizeProjectName('name.abi')).toEqual('NameAbi'); + expect(normalizeProjectName('1234name.abi')).toEqual('NameAbi'); + expect(normalizeProjectName('ERC20.abi')).toEqual('ERC20Abi'); + expect(normalizeProjectName('my-contract')).toEqual('MyContract'); + expect(normalizeProjectName('my contract')).toEqual('MyContract'); + expect(normalizeProjectName('my.contract')).toEqual('MyContract'); + expect(normalizeProjectName('still-my.contract')).toEqual('StillMyContract'); + expect(normalizeProjectName('also my.contract')).toEqual('AlsoMyContract'); + }); + + test('throws if name cannot be normalized', async () => { + await expectToThrowFuelError( + () => normalizeProjectName(''), + new FuelError( + ErrorCode.PARSE_FAILED, + `The provided string '' results in an empty output after`.concat( + ` normalization, therefore, it can't normalize string.` + ) + ) + ); + }); +}); diff --git a/packages/abi/src/gen/cli/utils.ts b/packages/abi/src/gen/cli/utils.ts new file mode 100644 index 00000000000..dd2f987b3cc --- /dev/null +++ b/packages/abi/src/gen/cli/utils.ts @@ -0,0 +1,109 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { assertUnreachable, compressBytecode, hexlify } from '@fuel-ts/utils'; +import { readFileSync } from 'fs'; +import { globSync } from 'glob'; + +import type { AbiSpecification } from '../../parser'; +import { AbiParser } from '../../parser'; +import type { ProgramDetails } from '../abi-gen-types'; + +export const loggingConfig = { + silent: false, +}; + +export function log(...args: Parameters) { + if (!loggingConfig.silent) { + // eslint-disable-next-line no-console + console.log(...args); + } +} + +/** + * Converts `some.string-value` into `SomeStringValue`. + * + * Examples: + * my-simple.test —— MySimpleTest + * myFile.ts —— MyFileTs + * my-abi.json —— MyAbiJson + */ +export function normalizeProjectName(str: string): string { + const transformations: ((s: string) => string)[] = [ + (s) => s.replace(/\s+/g, '-'), // spaces to - + (s) => s.replace(/\./g, '-'), // dots to - + (s) => s.replace(/_/g, '-'), // underscore to - + (s) => s.replace(/-[a-z]/g, (match) => match.slice(-1).toUpperCase()), // delete '-' and capitalize the letter after them + (s) => s.replace(/-/g, ''), // delete any '-' left + (s) => s.replace(/^\d+/, ''), // removes leading digits + ]; + + const output = transformations.reduce((s, t) => t(s), str); + + if (output === '') { + const errMsg = `The provided string '${str}' results in an empty output after`.concat( + ` normalization, therefore, it can't normalize string.` + ); + throw new FuelError(ErrorCode.PARSE_FAILED, errMsg); + } + + return output[0].toUpperCase() + output.slice(1); // capitalize first letter +} + +function handleMissingBinary(path: string, abi: AbiSpecification) { + const programType = abi.programType as 'predicate' | 'script' | 'contract' | 'library'; + switch (programType) { + case 'predicate': + throw new FuelError( + ErrorCode.BIN_FILE_NOT_FOUND, + `For predicates, the bytecode is required. No bytecode found for predicate at ${path}.` + ); + case 'script': + throw new FuelError( + ErrorCode.BIN_FILE_NOT_FOUND, + `For scripts, the bytecode is required. No bytecode found for script at ${path}.` + ); + case 'contract': + log(`No bytecode found for contract at ${path}, will not generate ContractFactory for it.`); + break; + case 'library': + // ignore; + break; + default: + assertUnreachable(programType); + } +} + +export function getProgramDetails(paths: string[]) { + const details: ProgramDetails[] = []; + paths.forEach((path) => { + const abiPath = path.match(/.+-abi\.json/) ? path : globSync(`${path}/*-abi.json`)[0]; + if (abiPath === undefined) { + log(`No abi file found in ${path}, skipping this path.`); + return; + } + + const dir = abiPath.match(/.*\//)?.[0] as string; + const projectName = abiPath.match(/([^/])+(?=-abi\.json)/)?.[0] as string; + const abiContentsStr = readFileSync(abiPath).toString(); + const abi = JSON.parse(abiContentsStr) as AbiSpecification; + + const [storageSlotsPath] = globSync(`${dir}/*-storage_slots.json`); + const storageSlots = storageSlotsPath ? readFileSync(storageSlotsPath).toString() : undefined; + + const [binPath] = globSync(`${dir}/*.bin`); + if (binPath === undefined) { + handleMissingBinary(path, abi); + } + + const binCompressed = binPath && compressBytecode(hexlify(readFileSync(binPath))); + + details.push({ + name: normalizeProjectName(projectName), + abi: AbiParser.parse(JSON.parse(abiContentsStr) as AbiSpecification), + binCompressed, + abiContents: abiContentsStr, + storageSlots, + }); + }); + + return details; +} diff --git a/packages/abi/src/gen/hbs.d.ts b/packages/abi/src/gen/hbs.d.ts new file mode 100644 index 00000000000..cefe640967a --- /dev/null +++ b/packages/abi/src/gen/hbs.d.ts @@ -0,0 +1,5 @@ +// informs TS about Handlebar `.hbs` templates extension +declare module '*.hbs' { + const value: string; + export default value; +} diff --git a/packages/abi/src/gen/index.ts b/packages/abi/src/gen/index.ts new file mode 100644 index 00000000000..29b42ee4313 --- /dev/null +++ b/packages/abi/src/gen/index.ts @@ -0,0 +1,2 @@ +export { AbiGen } from './abi-gen'; +export * from './abi-gen-types'; diff --git a/packages/abi/src/gen/renderers/getRenderer.ts b/packages/abi/src/gen/renderers/getRenderer.ts new file mode 100644 index 00000000000..bf43bb5d832 --- /dev/null +++ b/packages/abi/src/gen/renderers/getRenderer.ts @@ -0,0 +1,16 @@ +import { assertUnreachable } from '@fuel-ts/utils'; + +import type { AbiGenInput } from '../abi-gen-types'; + +import { renderTs } from './ts/render-ts'; +import type { Renderer } from './types'; + +export function getRenderer(mode: AbiGenInput['mode']): Renderer { + switch (mode) { + case 'ts': + case undefined: + return renderTs; + default: + return assertUnreachable(mode); + } +} diff --git a/packages/abi/src/gen/renderers/ts/render-ts.ts b/packages/abi/src/gen/renderers/ts/render-ts.ts new file mode 100644 index 00000000000..6654b4c6902 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/render-ts.ts @@ -0,0 +1,17 @@ +import type { AbiGenResult, ProgramDetails } from '../../abi-gen-types'; +import type { Renderer } from '../types'; + +import { renderPrograms } from './renderers/render-programs'; +import { templateRenderer } from './renderers/template-renderer'; +import commonTemplate from './templates/common.hbs'; + +export const renderTs: Renderer = (details: ProgramDetails[], versions): AbiGenResult[] => { + const results = renderPrograms(details, versions); + + results.push({ + filename: 'common.ts', + content: templateRenderer({ template: commonTemplate, versions }), + }); + + return results; +}; diff --git a/packages/abi/src/gen/renderers/ts/renderers/get-parent-dir-wrapper.ts b/packages/abi/src/gen/renderers/ts/renderers/get-parent-dir-wrapper.ts new file mode 100644 index 00000000000..a8239ef814e --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/get-parent-dir-wrapper.ts @@ -0,0 +1,32 @@ +import { assertUnreachable } from '@fuel-ts/utils'; + +import type { Abi } from '../../../../parser'; + +export function getParentDirWrapper(programType: Abi['programType']): { + parentDir: string; + withParentDir: (file: string) => string; + removeParentDir: (file: string) => string; +} { + let parentDir: string = ''; + switch (programType) { + case 'contract': + parentDir = 'contracts'; + break; + case 'predicate': + parentDir = 'predicates'; + break; + case 'script': + parentDir = 'scripts'; + break; + case 'library': + break; + default: + assertUnreachable(programType); + } + + return { + parentDir, + withParentDir: (file) => `${parentDir}/${file}`, + removeParentDir: (file) => file.split(`${parentDir}/`)[1], + }; +} diff --git a/packages/abi/src/gen/renderers/ts/renderers/render-index-files.ts b/packages/abi/src/gen/renderers/ts/renderers/render-index-files.ts new file mode 100644 index 00000000000..12c9c479871 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/render-index-files.ts @@ -0,0 +1,73 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; + +import type { Abi } from '../../../../parser'; +import type { AbiGenResult } from '../../../abi-gen-types'; +import indexTemplate from '../templates/index.hbs'; + +import { getParentDirWrapper } from './get-parent-dir-wrapper'; +import { templateRenderer } from './template-renderer'; + +export type IndexContents = Map< + Abi['programType'], + { filename: string; exportedContent: string[] }[] +>; +/** + * @returns an array of index files + * that includes the root index.ts and the index.ts for each provided program type. + */ +export function renderIndexFiles( + indexContents: IndexContents, + versions: BinaryVersions +): AbiGenResult[] { + const results: AbiGenResult[] = []; + + indexContents.forEach((files, programType) => { + const { withParentDir, removeParentDir } = getParentDirWrapper(programType); + + // from index.ts to e.g. contracts/index.ts + const indexFilename = withParentDir('index.ts'); + + const exports = files.map(({ filename, exportedContent }) => { + // from e.g. contracts/AbiContract.ts to AbiContract.ts + const relativePathToFile = removeParentDir(filename); + // remove .ts extension + return { + path: relativePathToFile.split('.')[0], + exportedContent: `{ ${exportedContent.join(', ')} }`, + }; + }); + + const content = templateRenderer({ + versions, + template: indexTemplate, + data: { + exports, + }, + }); + + results.push({ + filename: indexFilename, + content, + }); + }); + + const mainIndexFileExports = [...indexContents.keys()] + .sort() + .map((programType) => getParentDirWrapper(programType).parentDir) + .map((path) => ({ path, exportedContent: '*' })); + + const mainIndexFile: AbiGenResult = { + filename: 'index.ts', + content: templateRenderer({ + versions, + template: indexTemplate, + data: { + exports: mainIndexFileExports, + }, + }), + }; + + results.push(mainIndexFile); + + return results; +} diff --git a/packages/abi/src/gen/renderers/ts/renderers/render-program.ts b/packages/abi/src/gen/renderers/ts/renderers/render-program.ts new file mode 100644 index 00000000000..7bcb0c6a017 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/render-program.ts @@ -0,0 +1,113 @@ +import { assertUnreachable } from '@fuel-ts/utils'; +import type { BinaryVersions } from '@fuel-ts/versions'; + +import type { ProgramDetails } from '../../../abi-gen-types'; +import type { TsAbiGenResult } from '../../types'; +import abiTemplate from '../templates/abi.hbs'; +import bytecodeTemplate from '../templates/bytecode.hbs'; +import contractFactoryTemplate from '../templates/contract-factory.hbs'; +import contractTemplate from '../templates/contract.hbs'; +import predicateTemplate from '../templates/predicate.hbs'; +import scriptTemplate from '../templates/script.hbs'; +import storageSlotsTemplate from '../templates/storage-slots.hbs'; + +import { getParentDirWrapper } from './get-parent-dir-wrapper'; +import { renderTypes } from './render-types'; +import { templateRenderer } from './template-renderer'; + +/** + * Renders program-related files based on the program type. + * @returns An array of results containing filenames and their corresponding content. + * The files returned are all related to the program except the types. + * This includes the abi, bytecode, and the program-related classes. + */ +export function renderProgram(details: ProgramDetails, versions: BinaryVersions): TsAbiGenResult[] { + const { abi, binCompressed, name, abiContents, storageSlots } = details; + + const results: TsAbiGenResult[] = [ + { + filename: `${name}Types.ts`, + content: renderTypes(details, versions), + }, + { + filename: `${name}-abi.ts`, + content: templateRenderer({ template: abiTemplate, versions, data: { abiContents } }), + }, + ]; + + if (binCompressed) { + results.push({ + filename: `${name}-bytecode.ts`, + content: templateRenderer({ template: bytecodeTemplate, versions, data: { binCompressed } }), + }); + } + + switch (abi.programType) { + case 'contract': + results.push( + { + filename: `${name}.ts`, + content: templateRenderer({ + template: contractTemplate, + versions, + data: { name }, + }), + exportInIndexFile: [name], + }, + + { + filename: `${name}-storage-slots.ts`, + content: templateRenderer({ + template: storageSlotsTemplate, + versions, + data: { storageSlots }, + }), + } + ); + + if (binCompressed) { + results.push({ + filename: `${name}Factory.ts`, + content: templateRenderer({ + template: contractFactoryTemplate, + versions, + data: { name }, + }), + exportInIndexFile: [`${name}Factory`], + }); + } + break; + case 'predicate': + results.push({ + filename: `${name}.ts`, + content: templateRenderer({ + template: predicateTemplate, + versions, + data: { name }, + }), + exportInIndexFile: [name], + }); + break; + case 'script': + results.push({ + filename: `${name}.ts`, + content: templateRenderer({ + template: scriptTemplate, + versions, + data: { name }, + }), + exportInIndexFile: [name], + }); + break; + case 'library': + // we do nothing for library + break; + default: + assertUnreachable(abi.programType); + break; + } + + const { withParentDir } = getParentDirWrapper(abi.programType); + + return results.map((r) => ({ ...r, filename: withParentDir(r.filename) })); +} diff --git a/packages/abi/src/gen/renderers/ts/renderers/render-programs.ts b/packages/abi/src/gen/renderers/ts/renderers/render-programs.ts new file mode 100644 index 00000000000..269e90f8676 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/render-programs.ts @@ -0,0 +1,37 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; + +import type { AbiGenResult, ProgramDetails } from '../../../abi-gen-types'; + +import type { IndexContents } from './render-index-files'; +import { renderIndexFiles } from './render-index-files'; +import { renderProgram } from './render-program'; + +/** + * For the given program details, render all program-related files. + * That includes the abi, bytecode, program-related classes, + * type files and the index files. + */ +export function renderPrograms(details: ProgramDetails[], versions: BinaryVersions) { + const results: AbiGenResult[] = []; + const indexContents: IndexContents = new Map(); + + for (const d of details) { + const files = renderProgram(d, versions); + + results.push(...files); + + files.forEach((file) => { + if (!file.exportInIndexFile?.length) { + return; + } + + const contents = indexContents.get(d.abi.programType) ?? []; + contents.push({ filename: file.filename, exportedContent: file.exportInIndexFile }); + indexContents.set(d.abi.programType, contents); + }); + } + + results.push(...renderIndexFiles(indexContents, versions)); + + return results; +} diff --git a/packages/abi/src/gen/renderers/ts/renderers/render-types.ts b/packages/abi/src/gen/renderers/ts/renderers/render-types.ts new file mode 100644 index 00000000000..d8a88e2a362 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/render-types.ts @@ -0,0 +1,120 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; + +import { createMatcher } from '../../../../matchers/sway-type-matchers'; +import type { Abi } from '../../../../parser'; +import { evaluateFunctionInputsOptionality } from '../../../../utils/evaluate-function-inputs-optionality'; +import type { ProgramDetails } from '../../../abi-gen-types'; +import typesTemplate from '../templates/types.hbs'; +import { generateTsType } from '../typers/generate-ts-type'; +import { flattenImports } from '../typers/helpers'; +import type { TyperReturn } from '../typers/types'; + +import { templateRenderer } from './template-renderer'; + +const metadataTypeFilter = createMatcher({ + enum: true, + struct: true, + assetId: true, + string: false, + void: false, + bool: false, + u8: false, + u16: false, + u32: false, + u64: false, + u256: false, + b256: false, + generic: false, + stdString: false, + option: false, + result: false, + str: false, + b512: false, + bytes: false, + vector: false, + tuple: false, + array: false, + evmAddress: false, + rawUntypedSlice: false, +}); + +export function sortAlphabetically(a: TyperReturn, b: TyperReturn) { + if (a.input < b.input) { + return -1; + } + if (a.input > b.input) { + return 1; + } + return 0; +} + +function mergeTypeImports(mTypes: TyperReturn[], cTypesMap: Record) { + const cTypes = Object.values(cTypesMap); + + const imports = flattenImports(mTypes.concat(cTypes)); + + const fuelsTypeImports = [...new Set(imports.fuelsTypeImports)].sort().join(', '); + + const commonTypeImports = [...new Set(imports.commonTypeImports)].sort().join(', '); + + return { fuelsTypeImports, commonTypeImports }; +} + +function mapFunctions(abi: Abi, cTypes: Record) { + return abi.functions.map((fn) => { + const inputs = evaluateFunctionInputsOptionality(fn); + + return { + name: fn.name, + inputs: `[${inputs.map((i) => `${i.name}${i.isOptional ? '?' : ''}: ${cTypes[i.type.concreteTypeId].input}`).join(', ')}]`, + output: cTypes[fn.output.concreteTypeId].output, + }; + }); +} + +function mapConfigurables(abi: Abi, cTypes: Record) { + return abi.configurables.length > 0 + ? abi.configurables.map(({ name, type }) => ({ + name, + input: cTypes[type.concreteTypeId].input, + })) + : undefined; +} + +/** + * Renders the types file for a program. + * @returns An object containing the filename and the content of the types file. + * The type rendering logic is the same for all program types. + */ +export function renderTypes( + { name: programName, abi }: ProgramDetails, + versions: BinaryVersions +): string { + const mTypes = abi.metadataTypes + .filter(metadataTypeFilter) + .map((abiType) => generateTsType({ abiType })); + + const cTypes = abi.concreteTypes.reduce>((res, abiType) => { + res[abiType.concreteTypeId] = generateTsType({ abiType, asReference: true }); + return res; + }, {}); + + const { fuelsTypeImports, commonTypeImports } = mergeTypeImports(mTypes, cTypes); + + const content = templateRenderer({ + template: typesTemplate, + versions, + data: { + isContract: abi.programType === 'contract', + name: programName, + fuelsTypeImports, + commonTypeImports, + enums: mTypes.filter(({ tsType }) => tsType === 'enum').sort(sortAlphabetically), + types: mTypes.filter(({ tsType }) => tsType === 'type').sort(sortAlphabetically), + functions: mapFunctions(abi, cTypes), + configurables: mapConfigurables(abi, cTypes), + }, + }); + + return content; +} diff --git a/packages/abi/src/gen/renderers/ts/renderers/template-renderer.ts b/packages/abi/src/gen/renderers/ts/renderers/template-renderer.ts new file mode 100644 index 00000000000..f1a4ceaf7ea --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/renderers/template-renderer.ts @@ -0,0 +1,29 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; +import Handlebars from 'handlebars'; + +import headerTemplate from '../templates/header.hbs'; + +/* + Renders the given template w/ the given data, while injecting common + header for disabling lint rules and annotating Fuel component's versions. +*/ +export function templateRenderer(params: { + template: string; + data?: Record; + versions: BinaryVersions; +}) { + const { data, template, versions } = params; + + const options = { + strict: true, + noEscape: true, + }; + + const renderHeaderTemplate = Handlebars.compile(headerTemplate, options); + const renderTemplate = Handlebars.compile(template, options); + + return renderTemplate({ + header: renderHeaderTemplate(versions), + ...(data ?? {}), + }); +} diff --git a/packages/abi/src/gen/renderers/ts/templates/abi.hbs b/packages/abi/src/gen/renderers/ts/templates/abi.hbs new file mode 100644 index 00000000000..0f1e0f35b16 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/abi.hbs @@ -0,0 +1,5 @@ +{{header}} + +import { AbiSpecification } from 'fuels'; + +export const abi: AbiSpecification = {{abiContents}}; diff --git a/packages/abi/src/gen/renderers/ts/templates/bytecode.hbs b/packages/abi/src/gen/renderers/ts/templates/bytecode.hbs new file mode 100644 index 00000000000..5d859be0dd2 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/bytecode.hbs @@ -0,0 +1,5 @@ +{{header}} + +import { decompressBytecode } from "fuels"; + +export const bytecode = decompressBytecode("{{binCompressed}}"); \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/common.hbs b/packages/abi/src/gen/renderers/ts/templates/common.hbs new file mode 100644 index 00000000000..bf8285b0218 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/common.hbs @@ -0,0 +1,44 @@ +{{header}} + +import type { AbiCoderFunction, InvokeFunction } from 'fuels'; + +/** + * Mimics Sway Enum. + * Requires one and only one Key-Value pair and raises error if more are provided. + */ +export type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +/** + * Mimics Sway Option type. + */ +export type Option = T | undefined; + +/** + * Mimics Sway Result enum type. + * Ok represents the success case, while Err represents the error case. + */ +export type Result = Enum<{ Ok: T; Err: E }>; + +/** + * Mimics Sway array type. For example, [u64; 10] is converted to ArrayOfLength. + */ +export type ArrayOfLength< + T, + Length extends number, + Arr extends unknown[] = [], +> = Arr['length'] extends Length ? Arr : ArrayOfLength; + +interface Types { + functions: Record; + configurables: Partial>; +} + +export type ProgramFunctionMapper = { + [K in keyof T]: InvokeFunction; +}; + +export type InterfaceFunctionMapper = { + [K in keyof T]: AbiCoderFunction; +}; \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/contract-factory.hbs b/packages/abi/src/gen/renderers/ts/templates/contract-factory.hbs new file mode 100644 index 00000000000..b57f0ea8e33 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/contract-factory.hbs @@ -0,0 +1,26 @@ +{{header}} + +import { ContractFactory } from 'fuels'; +import type { Account, Provider, DeployContractOptions } from 'fuels'; +import { {{name}} } from './{{name}}'; +import { bytecode } from './{{name}}-bytecode'; +import { abi } from './{{name}}-abi'; +import { storageSlots } from './{{name}}-storage-slots'; + +export class {{name}}Factory extends ContractFactory<{{name}}> { + + static readonly bytecode = bytecode; + static readonly storageSlots = storageSlots; + + constructor(accountOrProvider: Account | Provider) { + super(bytecode, abi, accountOrProvider, {{name}}Factory.storageSlots); + } + + static deploy ( + wallet: Account, + options: DeployContractOptions = {} + ) { + const factory = new {{name}}Factory(wallet); + return factory.deploy(options); + } +} diff --git a/packages/abi/src/gen/renderers/ts/templates/contract.hbs b/packages/abi/src/gen/renderers/ts/templates/contract.hbs new file mode 100644 index 00000000000..16de85e0f4c --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/contract.hbs @@ -0,0 +1,33 @@ +{{header}} + +import { Contract, AbiCoder } from "fuels"; +import type { Address, Account, Provider } from 'fuels'; +import type { {{name}}Types as Types } from './{{name}}Types'; +import type { InterfaceFunctionMapper, ProgramFunctionMapper } from '../common'; +import { abi } from './{{name}}-abi'; + +export * from './{{name}}Types'; + +export type {{name}}Configurables = Types['configurables']; + +export class {{name}}AbiCoder extends AbiCoder { + declare functions: InterfaceFunctionMapper; + + constructor() { + super(abi); + } +} + +export class {{name}} extends Contract { + declare interface: {{name}}AbiCoder; + declare functions: ProgramFunctionMapper; + + public static readonly abi = abi; + + constructor( + id: string | Address, + accountOrProvider: Account | Provider, + ) { + super(id, abi, accountOrProvider); + } +} \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/header.hbs b/packages/abi/src/gen/renderers/ts/templates/header.hbs new file mode 100644 index 00000000000..a8ec5578f8b --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/header.hbs @@ -0,0 +1,9 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: {{FUELS}} + Forc version: {{FORC}} +*/ diff --git a/packages/abi/src/gen/renderers/ts/templates/index.hbs b/packages/abi/src/gen/renderers/ts/templates/index.hbs new file mode 100644 index 00000000000..f323c7973da --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/index.hbs @@ -0,0 +1,5 @@ +{{header}} + +{{#each exports}} +export {{exportedContent}} from './{{path}}'; +{{/each}} \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/predicate.hbs b/packages/abi/src/gen/renderers/ts/templates/predicate.hbs new file mode 100644 index 00000000000..2925628d4e5 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/predicate.hbs @@ -0,0 +1,33 @@ +{{header}} + +import { Predicate } from 'fuels'; +import type { PredicateParams } from 'fuels'; +import { abi } from './{{name}}-abi'; +import { bytecode } from './{{name}}-bytecode'; +import type { {{name}}Types as Types } from './{{name}}Types'; + +export * from './{{name}}Types'; + +export type {{name}}Inputs = Types['inputs']; +export type {{name}}Output = Types['output']; +export type {{name}}Configurables = Types['configurables']; + +export type {{name}}Parameters = Omit< + PredicateParams< + Types['inputs'], + Types['configurables'] + >, + 'abi' | 'bytecode' +>; + +export class {{name}} extends Predicate< + Types['inputs'], + Types['configurables'] +> { + public static readonly abi = abi; + public static readonly bytecode = bytecode; + + constructor(params: {{name}}Parameters) { + super({ abi, bytecode, ...params }); + } +} \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/script.hbs b/packages/abi/src/gen/renderers/ts/templates/script.hbs new file mode 100644 index 00000000000..b304d851616 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/script.hbs @@ -0,0 +1,25 @@ +{{header}} + +import { Script } from 'fuels'; +import type { Account } from 'fuels'; +import { abi } from './{{name}}-abi'; +import { bytecode } from './{{name}}-bytecode'; +import type { {{name}}Types as Types } from './{{name}}Types'; + +export * from './{{name}}Types'; + +export type {{name}}Inputs = Types['inputs']; +export type {{name}}Output = Types['output']; +export type {{name}}Configurables = Types['configurables']; + +export class {{name}} extends Script< + Types['inputs'], + Types['output'] +> { + public static readonly abi = abi; + public static readonly bytecode = bytecode; + + constructor(wallet: Account) { + super(bytecode, abi, wallet); + } +} \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/storage-slots.hbs b/packages/abi/src/gen/renderers/ts/templates/storage-slots.hbs new file mode 100644 index 00000000000..a877acccda5 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/storage-slots.hbs @@ -0,0 +1,5 @@ +{{header}} + +import type { StorageSlot } from 'fuels'; + +export const storageSlots: StorageSlot[] = {{storageSlots}}; \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/templates/types.hbs b/packages/abi/src/gen/renderers/ts/templates/types.hbs new file mode 100644 index 00000000000..8624ec21820 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/templates/types.hbs @@ -0,0 +1,36 @@ +{{header}} + +import type { {{fuelsTypeImports}} } from 'fuels'; +import type { {{commonTypeImports}} } from '../common'; + +{{#each enums}} +export enum {{input}}; +{{/each}} + +{{#each types}} +export type {{input}}; +export type {{output}}; +{{/each}} + +export interface {{name}}Types { + {{#if isContract}} + functions: { + {{#each functions}} + {{name}}: { + inputs: {{inputs}}; + output: {{output}}; + }; + {{/each}} + }; + {{else}} + {{#each functions}} + inputs: {{inputs}}; + output: {{output}}; + {{/each}} + {{/if}} + configurables: {{#if configurables}}Partial<{ + {{#each configurables}} + {{name}}: {{input}}; + {{/each}} + }>{{else}}undefined{{/if}}; +} \ No newline at end of file diff --git a/packages/abi/src/gen/renderers/ts/typers/enums.ts b/packages/abi/src/gen/renderers/ts/typers/enums.ts new file mode 100644 index 00000000000..4e583219d8b --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/enums.ts @@ -0,0 +1,68 @@ +import { swayTypeMatchers, ENUM_REGEX } from '../../../../matchers/sway-type-matchers'; + +import { structTyper } from './struct'; +import type { Typer, TyperAbiType } from './types'; + +function isNativeEnum(abiType: TyperAbiType) { + return abiType.components?.every((t) => swayTypeMatchers.void(t.type)) === true; +} + +export const enumTyper: Typer = (params, typer) => { + const { abiType } = params; + if (isNativeEnum(abiType)) { + const typeName = ENUM_REGEX.exec(abiType.swayType)?.[2] as string; + + if (params.asReference) { + return { input: typeName, output: typeName }; + } + + const enumFields = abiType.components?.map((c) => `${c.name} = '${c.name}'`).join(', '); + const input = `${typeName} { ${enumFields} }`; + return { + input, + output: input, + tsType: 'enum', + }; + } + + return structTyper(params, typer); +}; + +export const optionTyper: Typer = ({ abiType }, typer) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { type } = abiType.components![1]!; + const some = typer({ abiType: type, asReference: true }); + const input = `Option<${some.input}>`; + const output = `Option<${some.output}>`; + return { + input, + output, + commonTypeImports: ['Option'], + }; +}; + +export const resultTyper: Typer = ({ abiType }, typer) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const [{ type: ok }, { type: err }] = abiType.components!; + const mappedOk = typer({ abiType: ok, asReference: true }); + const mappedErr = typer({ abiType: err, asReference: true }); + + const input = `Result<${mappedOk.input}, ${mappedErr.input}>`; + const output = `Result<${mappedOk.output}, ${mappedErr.output}>`; + + const fuelsTypeImports = [ + mappedOk.fuelsTypeImports ?? [], + mappedErr.fuelsTypeImports ?? [], + ].flat(); + const commonTypeImports = [ + mappedOk.commonTypeImports ?? [], + mappedErr.commonTypeImports ?? [], + ['Result'], + ].flat(); + return { + input, + output, + fuelsTypeImports, + commonTypeImports, + }; +}; diff --git a/packages/abi/src/gen/renderers/ts/typers/generate-ts-type.ts b/packages/abi/src/gen/renderers/ts/typers/generate-ts-type.ts new file mode 100644 index 00000000000..09ce1a1aa55 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/generate-ts-type.ts @@ -0,0 +1,6 @@ +import { typerMatcher } from './typer-matcher'; +import type { TyperParams, TyperReturn } from './types'; + +export function generateTsType(params: TyperParams): TyperReturn { + return typerMatcher(params.abiType)(params, generateTsType); +} diff --git a/packages/abi/src/gen/renderers/ts/typers/helpers.ts b/packages/abi/src/gen/renderers/ts/typers/helpers.ts new file mode 100644 index 00000000000..892c6e1b62f --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/helpers.ts @@ -0,0 +1,10 @@ +import type { TyperReturn } from './types'; + +export function flattenImports(mapped: TyperReturn[]) { + const fuelsTypeImports = mapped.flatMap((m) => m.fuelsTypeImports).filter((x) => x !== undefined); + const commonTypeImports = mapped + .flatMap((m) => m.commonTypeImports) + .filter((x) => x !== undefined); + + return { fuelsTypeImports, commonTypeImports }; +} diff --git a/packages/abi/src/gen/renderers/ts/typers/iterators.ts b/packages/abi/src/gen/renderers/ts/typers/iterators.ts new file mode 100644 index 00000000000..3ed4f99743d --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/iterators.ts @@ -0,0 +1,37 @@ +import { ARRAY_REGEX } from '../../../../matchers/sway-type-matchers'; +import type { AbiTypeComponent } from '../../../../parser'; + +import type { Typer } from './types'; +import { mapComponents } from './utils'; + +export const tupleTyper: Typer = ({ abiType }, typer) => + mapComponents({ parent: abiType, includeComponentNames: false, typer }); + +export const arrayTyper: Typer = ({ abiType }, typer) => { + const length = ARRAY_REGEX.exec(abiType.swayType)?.[2]; + + const { type } = abiType.components?.[0] as AbiTypeComponent; + const mapped = typer({ abiType: type, asReference: true }); + + const input = `ArrayOfLength<${mapped.input}, ${length}>`; + const output = `ArrayOfLength<${mapped.output}, ${length}>`; + + return { + input, + output, + fuelsTypeImports: mapped.fuelsTypeImports, + commonTypeImports: ['ArrayOfLength', ...(mapped.commonTypeImports ?? [])], + }; +}; + +export const vectorTyper: Typer = ({ abiType }, typer) => { + const { type } = abiType.components?.[0] as AbiTypeComponent; + const mapped = typer({ abiType: type, asReference: true }); + const input = `${mapped.input}[]`; + const output = `${mapped.output}[]`; + return { + ...mapped, + input, + output, + }; +}; diff --git a/packages/abi/src/gen/renderers/ts/typers/simple.ts b/packages/abi/src/gen/renderers/ts/typers/simple.ts new file mode 100644 index 00000000000..9440f5aec51 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/simple.ts @@ -0,0 +1,82 @@ +import { GENERIC_REGEX } from '../../../../matchers/sway-type-matchers'; + +import type { TyperReturn, Typer } from './types'; + +const numberTyperReturn: TyperReturn = { + input: 'BigNumberish', + output: 'number', + fuelsTypeImports: ['BigNumberish'], +}; + +export const u8Typer: Typer = () => numberTyperReturn; +export const u16Typer = u8Typer; +export const u32Typer = u8Typer; + +const u64TyperReturn: TyperReturn = { + input: 'BigNumberish', + output: 'BN', + fuelsTypeImports: ['BigNumberish', 'BN'], +}; +export const u64Typer: Typer = () => u64TyperReturn; +export const u256Typer: Typer = u64Typer; + +const boolTyperReturn = { + input: 'boolean', + output: 'boolean', +}; +export const boolTyper: Typer = () => boolTyperReturn; + +const stringTyperReturn: TyperReturn = { + input: 'string', + output: 'string', +}; +export const stringTyper: Typer = () => stringTyperReturn; +export const b256Typer: Typer = stringTyper; +export const b512Typer: Typer = stringTyper; + +const evmAddressTyperReturn: TyperReturn = { + input: 'EvmAddress', + output: 'EvmAddress', + fuelsTypeImports: ['EvmAddress'], +}; +export const evmAddressTyper: Typer = () => evmAddressTyperReturn; + +const bytesTyperReturn: TyperReturn = { + input: 'Bytes', + output: 'Bytes', + fuelsTypeImports: ['Bytes'], +}; +export const bytesTyper: Typer = () => bytesTyperReturn; + +const strTyperReturn: TyperReturn = { + input: 'StrSlice', + output: 'StrSlice', + fuelsTypeImports: ['StrSlice'], +}; +export const strTyper: Typer = () => strTyperReturn; + +const rawSliceTyperReturn: TyperReturn = { + input: 'RawSlice', + output: 'RawSlice', + fuelsTypeImports: ['RawSlice'], +}; +export const rawSliceTyper = () => rawSliceTyperReturn; + +const stdStringTyperReturn: TyperReturn = { + input: 'StdString', + output: 'StdString', + fuelsTypeImports: ['StdString'], +}; +export const stdStringTyper: Typer = () => stdStringTyperReturn; + +const voidTyperReturn: TyperReturn = { input: 'undefined', output: 'void' }; +export const voidTyper: Typer = () => voidTyperReturn; + +export const genericTyper: Typer = ({ abiType }) => { + // extracts the `T` part from `generic T` + const typeName = GENERIC_REGEX.exec(abiType.swayType)?.[1] as string; + return { + input: typeName, + output: typeName, + }; +}; diff --git a/packages/abi/src/gen/renderers/ts/typers/struct.ts b/packages/abi/src/gen/renderers/ts/typers/struct.ts new file mode 100644 index 00000000000..fbbcc5942fa --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/struct.ts @@ -0,0 +1,88 @@ +import { ENUM_REGEX, STRUCT_REGEX } from '../../../../matchers/sway-type-matchers'; +import type { AbiMetadataType, AbiTypeArgument } from '../../../../parser'; + +import { flattenImports } from './helpers'; +import type { TyperReturn, Typer, GlobalTyper, TyperAbiType } from './types'; +import { mapComponents } from './utils'; + +function mapGenericTypeParameters( + typeArgs: AbiTypeArgument[] | AbiMetadataType['typeParameters'], + typer: GlobalTyper +): TyperReturn { + if (!typeArgs) { + return { + input: '', + output: '', + }; + } + const mapped = typeArgs.map((ta) => typer({ abiType: ta, asReference: true })); + const { fuelsTypeImports, commonTypeImports } = flattenImports(mapped); + + const input = mapped.map((r) => r.input).join(', '); + const output = mapped.map((r) => r.output).join(', '); + return { + fuelsTypeImports, + commonTypeImports, + input: `<${input}>`, + output: `<${output}>`, + }; +} + +function getTypeNames(abiType: TyperAbiType) { + const typeName = + STRUCT_REGEX.exec(abiType.swayType)?.[2] ?? ENUM_REGEX.exec(abiType.swayType)?.[2]; + return { + inputName: `${typeName}Input`, + outputName: `${typeName}Output`, + }; +} + +function mapStructAsReference(abiType: TyperAbiType, typer: GlobalTyper): TyperReturn { + const { inputName, outputName } = getTypeNames(abiType); + + const typeArgs = mapGenericTypeParameters( + 'metadata' in abiType + ? abiType.metadata?.typeArguments + : (abiType as AbiMetadataType).typeParameters, + typer + ); + + return { + ...typeArgs, + input: `${inputName}${typeArgs.input}`, + output: `${outputName}${typeArgs.output}`, + }; +} + +export const structTyper: Typer = ({ abiType, asReference }, typer) => { + if ('metadata' in abiType || asReference) { + return mapStructAsReference(abiType, typer); + } + + const { inputName, outputName } = getTypeNames(abiType); + + const typeParameters = mapGenericTypeParameters( + (abiType as AbiMetadataType).typeParameters, + typer + ); + const content = mapComponents({ parent: abiType, includeComponentNames: true, typer }); + + const inputType = `${inputName}${typeParameters.input}`; + const outputType = `${outputName}${typeParameters.output}`; + + const input = `${inputType} = ${content.input}`; + let output = ''; + if (content.input === content.output) { + output = `${outputType} = ${inputType}`; + } else { + output = `${outputType} = ${content.output}`; + } + + return { + input, + output, + commonTypeImports: content.commonTypeImports, + fuelsTypeImports: content.fuelsTypeImports, + tsType: 'type', + }; +}; diff --git a/packages/abi/src/gen/renderers/ts/typers/typer-matcher.ts b/packages/abi/src/gen/renderers/ts/typers/typer-matcher.ts new file mode 100644 index 00000000000..03da529b250 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/typer-matcher.ts @@ -0,0 +1,51 @@ +import { createMatcher } from '../../../../matchers/sway-type-matchers'; + +import { optionTyper, enumTyper, resultTyper } from './enums'; +import { tupleTyper, arrayTyper, vectorTyper } from './iterators'; +import { + boolTyper, + u8Typer, + u16Typer, + u32Typer, + u64Typer, + u256Typer, + b256Typer, + stringTyper, + evmAddressTyper, + genericTyper, + b512Typer, + bytesTyper, + rawSliceTyper, + stdStringTyper, + strTyper, + voidTyper, +} from './simple'; +import { structTyper } from './struct'; +import type { Typer } from './types'; + +export const typerMatcher = createMatcher({ + bool: boolTyper, + u8: u8Typer, + u16: u16Typer, + u32: u32Typer, + u64: u64Typer, + u256: u256Typer, + b256: b256Typer, + b512: b512Typer, + tuple: tupleTyper, + array: arrayTyper, + struct: structTyper, + generic: genericTyper, + string: stringTyper, + vector: vectorTyper, + option: optionTyper, + bytes: bytesTyper, + str: strTyper, + rawUntypedSlice: rawSliceTyper, + stdString: stdStringTyper, + enum: enumTyper, + result: resultTyper, + void: voidTyper, + assetId: structTyper, + evmAddress: evmAddressTyper, +}); diff --git a/packages/abi/src/gen/renderers/ts/typers/types.ts b/packages/abi/src/gen/renderers/ts/typers/types.ts new file mode 100644 index 00000000000..e31a4c8f5e9 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/types.ts @@ -0,0 +1,20 @@ +import type { AbiConcreteType, AbiTypeComponent, AbiMetadataType } from '../../../../parser'; + +export interface TyperReturn { + input: string; + output: string; + fuelsTypeImports?: string[]; + commonTypeImports?: string[]; + tsType?: 'enum' | 'type'; +} + +export type TyperAbiType = AbiConcreteType | AbiMetadataType | AbiTypeComponent['type']; + +export type TyperParams = { + abiType: TyperAbiType; + asReference?: boolean; +}; + +export type GlobalTyper = (p: TyperParams) => TyperReturn; + +export type Typer = (params: TyperParams, typer: GlobalTyper) => TyperReturn; diff --git a/packages/abi/src/gen/renderers/ts/typers/utils.ts b/packages/abi/src/gen/renderers/ts/typers/utils.ts new file mode 100644 index 00000000000..9305850eb43 --- /dev/null +++ b/packages/abi/src/gen/renderers/ts/typers/utils.ts @@ -0,0 +1,81 @@ +import { assertUnreachable } from '@fuel-ts/utils'; + +import { ENUM_REGEX, TUPLE_REGEX } from '../../../../matchers/sway-type-matchers'; +import type { AbiTypeComponent } from '../../../../parser'; + +import { flattenImports } from './helpers'; +import type { TyperAbiType, GlobalTyper, TyperReturn } from './types'; + +function componentMapper( + c: AbiTypeComponent, + includeName: boolean, + generateTsType: GlobalTyper +): TyperReturn { + const mapped = generateTsType({ abiType: c.type, asReference: true }); + + if (!includeName) { + return mapped; + } + + return { + ...mapped, + input: `${c.name}: ${mapped.input}`, + output: `${c.name}: ${mapped.output}`, + }; +} + +function wrapStructContent(text: string, wrap: '{}' | '[]' | 'Enum'): string { + switch (wrap) { + case '{}': + return `{ ${text} }`; + case '[]': + return `[${text}]`; + case 'Enum': { + const wrappedAsObj = wrapStructContent(text, '{}'); + return `Enum<${wrappedAsObj}>`; + } + default: + return assertUnreachable(wrap); + } +} + +/** + * This function maps components for a given parent type + * which can be a tuple or struct (and enum). + */ +export function mapComponents(params: { + parent: TyperAbiType; + /** + * Component names are included for structs and enums, + * but they're not included for tuples (we ignore the `__tuple_element` name). + */ + includeComponentNames: boolean; + typer: GlobalTyper; +}) { + const { parent, includeComponentNames, typer } = params; + const components = parent.components as AbiTypeComponent[]; + const mapped = components.map((c) => componentMapper(c, includeComponentNames, typer)); + + // eslint-disable-next-line no-nested-ternary + const wrap = ENUM_REGEX.test(parent.swayType) + ? 'Enum' + : TUPLE_REGEX.test(parent.swayType) + ? '[]' + : '{}'; + + const input = wrapStructContent(mapped.map((m) => m.input).join(', '), wrap); + const output = wrapStructContent(mapped.map((m) => m.output).join(', '), wrap); + + const { fuelsTypeImports, commonTypeImports } = flattenImports(mapped); + + if (wrap === 'Enum') { + commonTypeImports.push('Enum'); + } + + return { + input, + output, + fuelsTypeImports, + commonTypeImports, + }; +} diff --git a/packages/abi/src/gen/renderers/types.ts b/packages/abi/src/gen/renderers/types.ts new file mode 100644 index 00000000000..5d18ccaa6e9 --- /dev/null +++ b/packages/abi/src/gen/renderers/types.ts @@ -0,0 +1,9 @@ +import type { BinaryVersions } from '@fuel-ts/versions'; + +import type { AbiGenResult, ProgramDetails } from '../abi-gen-types'; + +export type Renderer = (details: ProgramDetails[], versions: BinaryVersions) => AbiGenResult[]; + +export type TsAbiGenResult = AbiGenResult & { + exportInIndexFile?: string[]; +}; diff --git a/packages/abi/src/index.ts b/packages/abi/src/index.ts new file mode 100644 index 00000000000..c589c25a433 --- /dev/null +++ b/packages/abi/src/index.ts @@ -0,0 +1,3 @@ +export * from './parser'; +export * from './coder'; +export * from './gen'; diff --git a/packages/abi/src/matchers/sway-type-matchers.test.ts b/packages/abi/src/matchers/sway-type-matchers.test.ts new file mode 100644 index 00000000000..ea6f8bfa77a --- /dev/null +++ b/packages/abi/src/matchers/sway-type-matchers.test.ts @@ -0,0 +1,271 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { SwayType, swayTypeMatchers } from './sway-type-matchers'; +import { createMatcher } from './sway-type-matchers'; + +const testMappings: Record = { + string: 'string-matched', + void: 'void-matched', + bool: 'bool-matched', + u8: 'u8-matched', + u16: 'u16-matched', + u32: 'u32-matched', + u64: 'u64-matched', + u256: 'u256-matched', + b256: 'b256-matched', + generic: 'generic-matched', + stdString: 'stdString-matched', + option: 'option-matched', + result: 'result-matched', + enum: 'enum-matched', + struct: 'struct-matched', + b512: 'b512-matched', + bytes: 'bytes-matched', + vector: 'vector-matched', + tuple: 'tuple-matched', + array: 'array-matched', + assetId: 'assetId-matched', + evmAddress: 'evmAddress-matched', + rawUntypedSlice: 'rawUntypedSlice-matched', + str: 'str-matched', +}; + +const matcher = createMatcher(testMappings); + +async function verifyOtherMatchersDontMatch(key: keyof typeof testMappings, swayType: string) { + const testMappingsWithoutKey = Object.fromEntries( + Object.entries(testMappings).filter(([k]) => k !== key) + ); + + const verifier = createMatcher(testMappingsWithoutKey as Record); + + await expectToThrowFuelError( + () => verifier({ swayType }), + new FuelError( + FuelError.CODES.MATCHER_NOT_FOUND, + `Matcher not found for Sway type "${swayType}".` + ) + ); +} + +/** + * @group node + * @group browser + */ +describe('sway type matchers', () => { + test('void', async () => { + const key = 'void'; + const swayType = '()'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('bool', async () => { + const key = 'bool'; + const swayType = 'bool'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('u8', async () => { + const key = 'u8'; + const swayType = 'u8'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('u16', async () => { + const key = 'u16'; + const swayType = 'u16'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('u32', async () => { + const key = 'u32'; + const swayType = 'u32'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('u64', async () => { + const key = 'u64'; + const swayType = 'u64'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('u256', async () => { + const key = 'u256'; + const swayType = 'u256'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('b256', async () => { + const key = 'b256'; + const swayType = 'b256'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('string', async () => { + const key = 'string'; + const swayType = 'str[5]'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('array', async () => { + const key = 'array'; + const swayType = '[_; 3]'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('tuple', async () => { + const key = 'tuple'; + const swayType = '(_, _, _)'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('struct', async () => { + const key = 'struct'; + const swayType = 'struct MyStruct'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('assetId', async () => { + const key = 'assetId'; + const swayType = 'struct std::asset_id::AssetId'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('b512', async () => { + const key = 'b512'; + const swayType = 'struct std::b512::B512'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('assetId', async () => { + const key = 'assetId'; + const swayType = 'struct std::asset_id::AssetId'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('bytes', async () => { + const key = 'bytes'; + const swayType = 'struct std::bytes::Bytes'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('stdString', async () => { + const key = 'stdString'; + const swayType = 'struct std::string::String'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('evmAddress', async () => { + const key = 'evmAddress'; + const swayType = 'struct std::vm::evm::evm_address::EvmAddress'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('vector', async () => { + const key = 'vector'; + const swayType = 'struct std::vec::Vec'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('enum', async () => { + const key = 'enum'; + const swayType = 'enum MyEnum'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('option', async () => { + const key = 'option'; + const swayType = 'enum std::option::Option'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('result', async () => { + const key = 'result'; + const swayType = 'enum std::result::Result'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('rawUntypedSlice', async () => { + const key = 'rawUntypedSlice'; + const swayType = 'raw untyped slice'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('generic', async () => { + const key = 'generic'; + const swayType = 'generic T'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('str', async () => { + const key = 'str'; + const swayType = 'str'; + + expect(matcher({ swayType })).toEqual(`${key}-matched`); + await verifyOtherMatchersDontMatch(key, swayType); + }); + + test('matcher without mapping for valid sway type throws', async () => { + const swayType = 'str'; + + // @ts-expect-error intentionally missing key for valid swayType + const matcherWithoutMappings = createMatcher({}); + + await expectToThrowFuelError( + () => matcherWithoutMappings({ swayType }), + new FuelError( + FuelError.CODES.MATCHER_NOT_FOUND, + `Matcher not found for Sway type "${swayType}".` + ) + ); + }); +}); diff --git a/packages/abi/src/matchers/sway-type-matchers.ts b/packages/abi/src/matchers/sway-type-matchers.ts new file mode 100644 index 00000000000..ab47080192b --- /dev/null +++ b/packages/abi/src/matchers/sway-type-matchers.ts @@ -0,0 +1,126 @@ +import { FuelError } from '@fuel-ts/errors'; + +export type SwayType = + | 'void' + | 'bool' + | 'u8' + | 'u16' + | 'u32' + | 'u64' + | 'u256' + | 'b256' + | 'generic' + | 'string' + | 'str' + | 'stdString' + | 'option' + | 'result' + | 'enum' + | 'struct' + | 'b512' + | 'bytes' + | 'vector' + | 'tuple' + | 'array' + | 'assetId' + | 'evmAddress' + | 'rawUntypedSlice'; + +export type Matcher = (opts: { swayType: string }) => T; + +const voidMatcher: Matcher = ({ swayType }) => swayType === '()'; +const bool: Matcher = ({ swayType }) => swayType === 'bool'; +const u8: Matcher = ({ swayType }) => swayType === 'u8'; +const u16: Matcher = ({ swayType }) => swayType === 'u16'; +const u32: Matcher = ({ swayType }) => swayType === 'u32'; +const u64: Matcher = ({ swayType }) => swayType === 'u64'; +const u256: Matcher = ({ swayType }) => swayType === 'u256'; +const b256: Matcher = ({ swayType }) => swayType === 'b256'; + +export const GENERIC_REGEX = /^generic ([^\s]+)$/m; +const generic: Matcher = ({ swayType }) => GENERIC_REGEX.test(swayType); + +export const STRING_REGEX = /^str\[(?[0-9]+)\]/; +const string: Matcher = ({ swayType }) => STRING_REGEX.test(swayType); +const str: Matcher = ({ swayType }) => swayType === 'str'; + +export const TUPLE_REGEX = /^\((?.+)\)$/m; +const tuple: Matcher = ({ swayType }) => TUPLE_REGEX.test(swayType); + +export const ARRAY_REGEX = /^\[(?[\w\s\\[\]]+);\s*(?[0-9]+)\]/; +const array: Matcher = ({ swayType }) => ARRAY_REGEX.test(swayType); + +export const STRUCT_REGEX = /^struct (.+::)?(?.+)$/m; +const STRUCT_STD_REGEX = + /^struct std::.*(AssetId|B512|Vec|RawVec|EvmAddress|Bytes|String|RawBytes)$/m; +const struct: Matcher = ({ swayType }) => + STRUCT_REGEX.test(swayType) && !STRUCT_STD_REGEX.test(swayType); +const assetId: Matcher = ({ swayType }) => swayType === 'struct std::asset_id::AssetId'; +const b512: Matcher = ({ swayType }) => swayType === 'struct std::b512::B512'; +const bytes: Matcher = ({ swayType }) => swayType === 'struct std::bytes::Bytes'; +const evmAddress: Matcher = ({ swayType }) => + swayType === 'struct std::vm::evm::evm_address::EvmAddress'; +const stdString: Matcher = ({ swayType }) => swayType === 'struct std::string::String'; +const vector: Matcher = ({ swayType }) => swayType === 'struct std::vec::Vec'; + +const option: Matcher = ({ swayType }) => swayType === 'enum std::option::Option'; +const result: Matcher = ({ swayType }) => swayType === 'enum std::result::Result'; + +export const ENUM_REGEX = /^enum (.+::)?(?.+)$/m; +const enumMatcher: Matcher = (opts) => + !option(opts) && !result(opts) && ENUM_REGEX.test(opts.swayType); + +const rawUntypedSlice: Matcher = ({ swayType }) => swayType === 'raw untyped slice'; + +export const swayTypeMatchers: Record = { + void: voidMatcher, + generic, + bool, + u8, + u16, + u32, + u64, + u256, + b256, + + string, + str, + tuple, + array, + + struct, + assetId, + b512, + bytes, + evmAddress, + stdString, + vector, + + enum: enumMatcher, + option, + result, + + rawUntypedSlice, +}; + +const swayTypeMatcherEntries = Object.entries(swayTypeMatchers); + +export function createMatcher(mappings: Record): Matcher { + return (opts: { swayType: string }): T => { + const { swayType } = opts; + + for (const [key, matcher] of swayTypeMatcherEntries) { + if (matcher({ swayType })) { + if (key in mappings) { + return mappings[key as SwayType]; + } + break; + } + } + + throw new FuelError( + FuelError.CODES.MATCHER_NOT_FOUND, + `Matcher not found for Sway type "${swayType}".` + ); + }; +} diff --git a/packages/abi/src/parser/abi-parser.ts b/packages/abi/src/parser/abi-parser.ts new file mode 100644 index 00000000000..31d230ce3cb --- /dev/null +++ b/packages/abi/src/parser/abi-parser.ts @@ -0,0 +1,44 @@ +import { FuelError } from '@fuel-ts/errors'; + +import type { Abi } from './abi'; +import type { AbiSpecificationV1 } from './specifications'; +import { AbiParserV1 } from './specifications'; + +/** + * A typed ABI object or a stringified json of a Sway program's ABI + */ +export type AbiSpecification = AbiSpecificationV1; + +export class AbiParser { + /** + * ABI specifications transpilers + */ + private static specifications = { + '1': AbiParserV1.parse, + } as const; + + /** + * Parses an ABI in JSON format. + * + * @param abi - a JSON ABI of a Sway program + * @returns an public interface for the Abi + */ + static parse(abi: AbiSpecification): Abi { + if (typeof abi.specVersion !== 'string') { + throw new FuelError( + FuelError.CODES.ABI_SPECIFICATION_INVALID, + 'Invalid ABI: the specification version is not a string.' + ); + } + + const parse = AbiParser.specifications[abi.specVersion]; + if (!parse) { + throw new FuelError( + FuelError.CODES.ABI_SPECIFICATION_INVALID, + `Invalid ABI: Unsupported ABI specification version ("${abi.specVersion}").` + ); + } + + return parse(abi); + } +} diff --git a/packages/abi/src/parser/abi.ts b/packages/abi/src/parser/abi.ts new file mode 100644 index 00000000000..345851ec354 --- /dev/null +++ b/packages/abi/src/parser/abi.ts @@ -0,0 +1,142 @@ +/** + * This interface serves as a representation of the ABI format outputted by `forc build` + * that won't be changing with the introduction of new abi specifications in Sway. + * Its purpose is to provide a stable interface for users to work with, + * which won't be affected by changing ABI specification versions. + */ +export interface Abi { + encodingVersion: string; + programType: 'contract' | 'predicate' | 'script' | 'library'; + /** + * Metadata types describe the structure of the types used in the `concreteTypes` field. + * One metadata type can be referenced multiple times if it is used in multiple concrete types. + */ + metadataTypes: AbiMetadataType[]; + /** + * Concrete types are types that are used in: + * function inputs/outputs, configurables, logged types, or message types. + * + * Their structure is fully known and they do not contain any unresolved generic parameters. + */ + concreteTypes: AbiConcreteType[]; + functions: AbiFunction[]; + loggedTypes: AbiLoggedType[]; + messageTypes: AbiMessageType[]; + configurables: AbiConfigurable[]; +} + +export interface AbiConcreteType { + swayType: string; + concreteTypeId: string; + /** + * The components field is populated when the type is any non-primitive type. + * That includes structs, enums, arrays, and tuples. + */ + components?: AbiTypeComponent[]; + /** + * A concrete type can be an implementation of a metadata type, + * in which case the `metadata` field is populated. + * If the underlying metadata type has type parameters (is generic), + * the `typeArguments` field corresponds to those type parameters. + */ + metadata?: { + metadataTypeId: number; + /** + * Type arguments used to resolve the type parameters in the metadata type. + * They are ordered in the same way as the type parameters in the metadata type. + */ + typeArguments?: AbiConcreteType[]; + }; +} + +export interface AbiMetadataType { + swayType: string; + metadataTypeId: number; + /** + * The components field is populated when the type is any non-primitive type. + * That includes structs, enums, arrays, and tuples. + */ + components?: AbiTypeComponent[]; + /** + * The existence of type parameters indicates that the metadata type is generic. + */ + typeParameters?: AbiMetadataType[]; +} + +export interface AbiTypeComponent { + name: string; + type: AbiConcreteType | AbiAppliedMetadataType; +} + +/** + * AbiAppliedMetadataType point to a metadata type but aren't the same as metadata types, + * as the metadata type describes the structure of the type, + * whereas the component is an actual implementation of that type. + */ +export interface AbiAppliedMetadataType { + swayType: string; + components?: AbiTypeComponent[]; + metadata: { + metadataTypeId: number; + typeArguments?: AbiTypeArgument[]; + }; +} + +export type AbiTypeArgument = AbiConcreteType | AbiAppliedMetadataType; + +export interface AbiFunctionInput { + name: string; + type: AbiConcreteType; +} + +export interface AbiFunction { + name: string; + inputs: AbiFunctionInput[]; + output: AbiConcreteType; + attributes?: readonly AbiFunctionAttribute[]; +} + +export interface AbiLoggedType { + logId: string; + type: AbiConcreteType; +} + +export interface AbiMessageType { + messageId: string; + type: AbiConcreteType; +} + +export interface AbiConfigurable { + name: string; + offset: number; + type: AbiConcreteType; +} + +export type AbiFunctionAttribute = + | StorageAttr + | PayableAttr + | TestAttr + | InlineAttr + | DocCommentAttr; + +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: readonly string[]; +} diff --git a/packages/abi/src/parser/index.ts b/packages/abi/src/parser/index.ts new file mode 100644 index 00000000000..053e0c0a003 --- /dev/null +++ b/packages/abi/src/parser/index.ts @@ -0,0 +1,3 @@ +export { AbiParser, type AbiSpecification } from './abi-parser'; +export * from './abi'; +export * from './specifications/v1/specification'; diff --git a/packages/abi/src/parser/specifications/index.ts b/packages/abi/src/parser/specifications/index.ts new file mode 100644 index 00000000000..788ae99a778 --- /dev/null +++ b/packages/abi/src/parser/specifications/index.ts @@ -0,0 +1,2 @@ +export { AbiParserV1 } from './v1/parser'; +export * from './v1/specification'; diff --git a/packages/abi/src/parser/specifications/v1/abi-type-mappers.ts b/packages/abi/src/parser/specifications/v1/abi-type-mappers.ts new file mode 100644 index 00000000000..5dad5545b11 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/abi-type-mappers.ts @@ -0,0 +1,94 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import type { + AbiConcreteType, + AbiMetadataType, + AbiTypeArgument, + AbiTypeComponent, +} from '../../abi'; + +import type { ResolvableComponent, ResolvableType } from './resolvable-type'; +import type { ResolvedType } from './resolved-type'; + +function mapMetadata(type: ResolvableType | ResolvedType) { + const result: AbiTypeComponent['type']['metadata'] = { + metadataTypeId: type.metadataType?.metadataTypeId as number, + }; + + if (type.typeParamsArgsMap && type.metadataType?.typeParameters?.length) { + result.typeArguments = [...type.typeParamsArgsMap.values()].map((rt) => toTypeArgument(rt)); + } + + return result; +} + +function isResolvedConcreteType( + type: ResolvableType | ResolvedType +): type is ResolvedType & { typeId: string } { + const isResolvedType = 'typeId' in type; + + return isResolvedType && typeof type.typeId === 'string'; +} + +function mapComponentType(component: ResolvableComponent): AbiTypeComponent { + const { name, type } = component; + + let result: AbiTypeComponent['type']; + + if (isResolvedConcreteType(type)) { + result = { + swayType: type.swayType, + concreteTypeId: type.typeId, + }; + if (type.metadataType) { + result.metadata = mapMetadata(type) as AbiConcreteType['metadata']; + } + } else { + result = { + swayType: type.swayType, + metadata: mapMetadata(type), + }; + } + + if (type.components) { + result.components = type.components.map(mapComponentType); + } + + return { name, type: result }; +} + +function toTypeArgument(type: ResolvableType | ResolvedType): AbiTypeArgument { + // type args and components follow the same mapping logic + return mapComponentType({ name: '', type }).type; +} + +export function toAbiType(t: ResolvableType | ResolvedType): AbiConcreteType | AbiMetadataType { + let result: AbiConcreteType | AbiMetadataType; + + if (isResolvedConcreteType(t)) { + result = { + concreteTypeId: t.typeId, + swayType: t.swayType, + }; + + if (t.metadataType) { + result.metadata = mapMetadata(t) as AbiConcreteType['metadata']; + } + } else { + result = { + swayType: t.swayType, + metadataTypeId: t.metadataType?.metadataTypeId as number, + }; + + if (t.typeParamsArgsMap && t.metadataType?.typeParameters?.length) { + result.typeParameters = [...t.typeParamsArgsMap.values()].map( + (rt) => toAbiType(rt) as AbiMetadataType + ); + } + } + + if (t.components) { + result.components = t.components.map(mapComponentType); + } + + return result; +} diff --git a/packages/abi/src/parser/specifications/v1/cleanup-abi.ts b/packages/abi/src/parser/specifications/v1/cleanup-abi.ts new file mode 100644 index 00000000000..1d517f43707 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/cleanup-abi.ts @@ -0,0 +1,52 @@ +import type { AbiSpecificationV1 } from './specification'; + +/** + * Both RawVec and RawBytes are private sway std library types + * that can never be used directly in sway, + * and the only reason they're in the abi is because they're used internally by Vec and Bytes + * and not ignored when forc builds the outputs. + * We can safely ignore them and simplify the `Vec` and `Bytes` types. + * This makes it simpler for us to consume these types in typegen and coder, + * as well as for others consuming the parsed abi, + * who now don't have to worry about this unnecessary complexity. + * `raw untyped ptr` is also in the abi only because of RawVec and RawBytes, + * so we ignore that as well. + */ +const IGNORED_TYPES = ['struct std::vec::RawVec', 'struct std::bytes::RawBytes', 'raw untyped ptr']; + +export function cleanupAbi(abi: AbiSpecificationV1): AbiSpecificationV1 { + return { + ...abi, + metadataTypes: abi.metadataTypes + .filter((metadataType) => !IGNORED_TYPES.includes(metadataType.type)) + .map((metadataType) => { + switch (metadataType.type) { + /** + * Vectors consist of multiple components, + * but we only care about the `buf`'s first type argument + * which defines the type of the vector data. + * Everything else is being ignored, + * as it's then easier to reason about the vector. + */ + case 'struct std::vec::Vec': + return { + ...metadataType, + components: metadataType.components?.[0].typeArguments, + }; + + /** + * We treat Bytes as a special type + * that is handled only based on its type name ('struct std::bytes::Bytes') + * and not its components. + */ + case 'struct std::bytes::Bytes': + return { + type: metadataType.type, + metadataTypeId: metadataType.metadataTypeId, + }; + default: + return metadataType; + } + }), + }; +} diff --git a/packages/abi/src/parser/specifications/v1/map-attribute.ts b/packages/abi/src/parser/specifications/v1/map-attribute.ts new file mode 100644 index 00000000000..b14ea2e83c0 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/map-attribute.ts @@ -0,0 +1,23 @@ +import { assertUnreachable } from '@fuel-ts/utils'; + +import type { AbiFunctionAttribute } from '../../abi'; + +import type { AbiFunctionAttributeV1 } from './specification'; + +export const mapAttribute = (attribute: AbiFunctionAttributeV1): AbiFunctionAttribute => { + const { name, arguments: args } = attribute; + + switch (name) { + case 'inline': + return { name, arguments: args[0] }; + case 'storage': + return { name: 'storage', arguments: args }; + case 'doc-comment': + return { name, arguments: args }; + case 'payable': + case 'test': + return { name }; + default: + return assertUnreachable(attribute); + } +}; diff --git a/packages/abi/src/parser/specifications/v1/parser.ts b/packages/abi/src/parser/specifications/v1/parser.ts new file mode 100644 index 00000000000..0048ea802f3 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/parser.ts @@ -0,0 +1,75 @@ +import type { Abi, AbiConcreteType, AbiMetadataType } from '../../abi'; + +import { toAbiType } from './abi-type-mappers'; +import { cleanupAbi } from './cleanup-abi'; +import { mapAttribute } from './map-attribute'; +import { ResolvableType } from './resolvable-type'; +import { ResolvedType } from './resolved-type'; +import type { + AbiConfigurableV1, + AbiFunctionInputV1, + AbiFunctionV1, + AbiLoggedTypeV1, + AbiMessageTypeV1, + AbiSpecificationV1, +} from './specification'; + +export class AbiParserV1 { + static parse(abi: AbiSpecificationV1): Abi { + const cleanAbi = cleanupAbi(abi); + + const abiTypeMaps = { + metadataTypes: new Map(cleanAbi.metadataTypes.map((type) => [type.metadataTypeId, type])), + concreteTypes: new Map(cleanAbi.concreteTypes.map((type) => [type.concreteTypeId, type])), + }; + + const resolvableTypes = cleanAbi.metadataTypes.map( + (metadataType) => new ResolvableType(abiTypeMaps, metadataType.metadataTypeId, undefined) + ); + + const concreteTypes = cleanAbi.concreteTypes.map((concreteType) => { + const resolvableType = resolvableTypes.find( + (resolvable) => resolvable.metadataTypeId === concreteType.metadataTypeId + ); + + const resolvedType = resolvableType + ? resolvableType.resolve(concreteType) + : new ResolvedType({ swayType: concreteType.type, typeId: concreteType.concreteTypeId }); + + return toAbiType(resolvedType) as AbiConcreteType; + }); + + const getType = (concreteTypeId: string) => + // this will always be defined because it's in the context of the same ABI + concreteTypes.find((abiType) => abiType.concreteTypeId === concreteTypeId) as AbiConcreteType; + + return { + metadataTypes: resolvableTypes.map((rt) => toAbiType(rt) as AbiMetadataType), + concreteTypes, + encodingVersion: cleanAbi.encodingVersion, + programType: cleanAbi.programType as Abi['programType'], + functions: cleanAbi.functions.map((fn: AbiFunctionV1) => ({ + attributes: fn.attributes?.map(mapAttribute) ?? undefined, + name: fn.name, + output: getType(fn.output), + inputs: fn.inputs.map((input: AbiFunctionInputV1) => ({ + name: input.name, + type: getType(input.concreteTypeId), + })), + })), + loggedTypes: cleanAbi.loggedTypes.map((loggedType: AbiLoggedTypeV1) => ({ + logId: loggedType.logId, + type: getType(loggedType.concreteTypeId), + })), + messageTypes: cleanAbi.messagesTypes.map((messageType: AbiMessageTypeV1) => ({ + messageId: messageType.messageId, + type: getType(messageType.concreteTypeId), + })), + configurables: cleanAbi.configurables.map((configurable: AbiConfigurableV1) => ({ + name: configurable.name, + offset: configurable.offset, + type: getType(configurable.concreteTypeId), + })), + }; + } +} diff --git a/packages/abi/src/parser/specifications/v1/resolvable-type.ts b/packages/abi/src/parser/specifications/v1/resolvable-type.ts new file mode 100644 index 00000000000..94aa367dc19 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/resolvable-type.ts @@ -0,0 +1,373 @@ +import { FuelError } from '@fuel-ts/errors'; + +import { swayTypeMatchers } from '../../../matchers/sway-type-matchers'; + +import type { ResolvedComponent } from './resolved-type'; +import { ResolvedType } from './resolved-type'; +import type { + AbiComponentV1, + AbiConcreteTypeV1, + AbiMetadataTypeV1, + AbiTypeArgumentV1, +} from './specification'; + +export interface ResolvableComponent { + name: string; + type: ResolvableType | ResolvedType; +} + +export class ResolvableType { + metadataType: AbiMetadataTypeV1; + swayType: string; + components: ResolvableComponent[] | undefined; + + constructor( + private abiTypeMaps: { + metadataTypes: Map; + concreteTypes: Map; + }, + public metadataTypeId: number, + public typeParamsArgsMap: Map | undefined + ) { + this.metadataType = this.findMetadataType(metadataTypeId); + this.swayType = this.metadataType.type; + this.typeParamsArgsMap ??= + this.metadataType.typeParameters && + new Map( + this.metadataType.typeParameters.map((typeParameter) => [ + typeParameter, + new ResolvableType(this.abiTypeMaps, typeParameter, undefined), + ]) + ); + + this.components = this.metadataType.components?.map((c) => + this.createResolvableComponent(this, c) + ); + } + + /** + * Find a metadata type by its ID. + * @param metadataTypeId - The ID of the metadata type to find. + * @returns The metadata type. + * + * @throws If the metadata type can not be found in the ABI. + */ + private findMetadataType(metadataTypeId: number): AbiMetadataTypeV1 { + const metadataType = this.abiTypeMaps.metadataTypes.get(metadataTypeId); + + if (!metadataType) { + throw new FuelError( + FuelError.CODES.TYPE_NOT_FOUND, + `Metadata type with id ${metadataTypeId} not found` + ); + } + return metadataType; + } + + /** + * Find a concrete type by its ID. + * @param concreteTypeId - The ID of the concrete type to find. + * @returns The concrete type. + * + * @throws If the concrete type can not be found in the ABI. + */ + private findConcreteType(concreteTypeId: string): AbiConcreteTypeV1 { + const concreteType = this.abiTypeMaps.concreteTypes.get(concreteTypeId); + + if (!concreteType) { + throw new FuelError( + FuelError.CODES.TYPE_NOT_FOUND, + `Concrete type with id ${concreteTypeId} not found` + ); + } + return concreteType; + } + + private static mapTypeParametersAndArgs( + metadataType: AbiMetadataTypeV1, + args: (ResolvableType | ResolvedType)[] + ): Map | undefined { + return ( + metadataType.typeParameters && + new Map( + metadataType.typeParameters.map((typeParameter, idx) => [typeParameter, args[idx]]) + ) + ); + } + + private createResolvableComponent( + parent: ResolvableType, + { typeId, typeArguments, name }: AbiComponentV1 | AbiTypeArgumentV1 + ): ResolvableComponent { + const isConcreteType = typeof typeId === 'string'; + + if (isConcreteType) { + const concreteType = this.findConcreteType(typeId); + return { + name, + type: this.resolveConcreteType(concreteType), + }; + } + + const metadataType = this.findMetadataType(typeId); + return { + name, + type: this.handleMetadataType(parent, metadataType, typeArguments), + }; + } + + /** + * Concrete types are *resolved* because everything is known about them. + */ + private resolveConcreteType(type: AbiConcreteTypeV1): ResolvedType { + /** + * If the concrete type doesn't have a linked metadata type, we can resolve it immediately. + * This is the case for e.g. u8, u16, ... + */ + if (type.metadataTypeId === undefined) { + return new ResolvedType({ + swayType: type.type, + typeId: type.concreteTypeId, + }); + } + /** + * The concrete type has an associated metadata type. + * If it's not generic (no type arguments), + * we'll create a ResolvableType with that metadata type, and then resolve it immediately. + * This would be the case for e.g. non-generic structs and enums. + */ + if (!type.typeArguments) { + return new ResolvableType(this.abiTypeMaps, type.metadataTypeId, undefined).resolveInternal( + type.concreteTypeId, + undefined + ); + } + + /** + * The concrete type's underlying metadata type is generic. + * We must resolve all its type parameters with the provided type arguments of the concrete type, + * and then resolve the metadata type itself. + */ + const metadataType = this.findMetadataType(type.metadataTypeId); + + const concreteTypeArgs = type.typeArguments.map((typeArgument) => { + const concreteTypeArg = this.findConcreteType(typeArgument); + return this.resolveConcreteType(concreteTypeArg); + }); + + return new ResolvableType( + this.abiTypeMaps, + type.metadataTypeId, + ResolvableType.mapTypeParametersAndArgs(metadataType, concreteTypeArgs) + ).resolveInternal(type.concreteTypeId, undefined); + } + + /** + * Metadata types are *handled* and not *resolved* because they might be generic, + * in which case they cannot be resolved. + * If they're not generic, they can be immediately resolved. + */ + private handleMetadataType( + parent: ResolvableType, + metadataType: AbiMetadataTypeV1, + typeArguments: AbiComponentV1['typeArguments'] + ): ResolvableType | ResolvedType { + /** + * If the type is generic, we can't resolve it and thus we create a `ResolvableType` from it. + * This propagates to the parent type, forcing it to be a `ResolvableType` as well, + * as it can't be resolved until this generic type is substituted with a type argument. + */ + if (swayTypeMatchers.generic({ swayType: metadataType.type })) { + /** + * This search solves the case where an e.g. `generic T` is being substituted by `generic E`. + * This can happen when a generic type is nested in another generic type and they have differently-named type parameters. + * e.g. `GenericStruct` is nested in `Vec`: `struct MyStruct { a: Vec }` + * We check in the parent's typeParamsArgsMap if the metadata type we're solving for + * has been substituted with a different generic type, and then we use that generic type. + */ + const resolvableTypeParameter = parent.typeParamsArgsMap?.get(metadataType.metadataTypeId); + + return ( + resolvableTypeParameter ?? + new ResolvableType(this.abiTypeMaps, metadataType.metadataTypeId, undefined) + ); + } + + if (!metadataType.components) { + /** + * types like u8, u16 can make their way into metadata types + * if they aren't used _directly_ in a function-input/function-output/log/configurable/messageType + * These types are characterized by not having components and we can resolve them as-is + */ + return new ResolvableType( + this.abiTypeMaps, + metadataType.metadataTypeId, + undefined + ).resolveInternal(metadataType.metadataTypeId, undefined); + } + + const typeArgs = typeArguments?.map( + (typeArgument) => this.createResolvableComponent(parent, typeArgument).type + ); + + const resolvable = new ResolvableType( + this.abiTypeMaps, + metadataType.metadataTypeId, + !typeArgs?.length + ? undefined + : ResolvableType.mapTypeParametersAndArgs(metadataType, typeArgs) + ); + + /** + * If any component is unresolved, this means that the metadata type is generic. + * We can't resolve it yet, so we return the resolvable type. + * If all components are resolved, we can resolve the metadata type immediately. + */ + const isGeneric = resolvable.components?.some( + (component) => component.type instanceof ResolvableType + ); + + return isGeneric + ? resolvable + : resolvable.resolveInternal(metadataType.metadataTypeId, undefined); + } + + private resolveInternal( + typeId: string | number, + typeParamsArgsMap: Map | undefined + ): ResolvedType { + const resolvedType = new ResolvedType({ + swayType: this.swayType, + typeId, + metadataType: this.metadataType, + }); + + /** + * A type without components can be immediately resolved. + */ + if (!this.components) { + return resolvedType; + } + + /** + * Before resolving the components, + * we need to substitute the type parameters of the underlying metadata type + * with the type arguments of the concrete type, + * so that we can substitute the generic components with them later. + */ + const typeArgs = this.resolveTypeArgs(typeParamsArgsMap); + + const components: ResolvedComponent[] = this.components.map((component) => { + const { name, type } = component; + + if (type instanceof ResolvedType) { + return component as ResolvedComponent; + } + + /** + * Here the component's type is a `ResolvableType`. + * If the component is a generic type parameter itself, + * its corresponding type argument will be found in the typeArgs, + * which will be used to substitute the component with. + */ + const resolvedGenericType = typeArgs?.get(type.metadataTypeId); + + if (resolvedGenericType) { + return { + name, + type: resolvedGenericType, + }; + } + + return { + name, + /** + * The component is a `ResolvableType`, but it's not a generic type parameter itself. + * This means that one of its components (or component's components) + * is a generic type. + * We need to resolve that first before resolving the component. + * + * Note that we are passing in the original `typeParamsArgsMap` by default, + * which will be used to substitute the component's generic type parameters + * with the appropriate type arguments. + * + * The non-default case of passing `typeArgs` happens only for tuples/arrays + * which contain structs with implicit generics, + * e.g. `(bool, StructWithImplicitGenerics)` + */ + type: type.resolveInternal(type.metadataTypeId, typeParamsArgsMap ?? typeArgs), + }; + }); + + resolvedType.components = components; + resolvedType.typeParamsArgsMap = typeArgs; + + return resolvedType; + } + + private resolveTypeArgs( + typeParamsArgsMap: Map | undefined + ): Map | undefined { + /** + * This case only happens when the metadata type is *implicitly* generic. + * The type itself doesn't have any type parameters that should be resolved, + * but its components are still generic types. + * This happens in the following type: + * `struct StructWithImplicitGenerics { a: [E; 3], b: (E, F)}`. + */ + if (this.typeParamsArgsMap === undefined) { + return typeParamsArgsMap; + } + + const newMap = new Map(); + + /** + * We resolve the type parameters of the underlying metadata type + * with the type arguments of the concrete type. + */ + this.typeParamsArgsMap.forEach((arg, typeParameter) => { + /** + * Some type parameters can already be resolved + * e.g. `struct MyStruct { a: DoubleGeneric }` + * where the second type parameter of DoubleGeneric is already known. + */ + if (arg instanceof ResolvedType) { + newMap.set(typeParameter, arg); + return; + } + + /** + * The type parameter is either directly substituted with a type argument, + * or it's a metadata type which accepts the type argument, + * so that metadata type will be resolved and subsitute the type parameter. + */ + const resolved = + typeParamsArgsMap?.get(arg.metadataTypeId) ?? + arg.resolveInternal(arg.metadataTypeId, typeParamsArgsMap); + + newMap.set(arg.metadataTypeId, resolved); + }); + + return newMap; + } + + /** + * Resolves the instance of `ResolvableType` with the specific concrete type's data. + * @returns a `ResolvedType` in which all its components are resolved. + */ + public resolve(concreteType: AbiConcreteTypeV1): ResolvedType { + const concreteTypeArgs = concreteType.typeArguments?.map((typeArgument) => { + const concreteTypeArg = this.findConcreteType(typeArgument); + return this.resolveConcreteType(concreteTypeArg); + }); + + const typeParamsArgsMap = concreteTypeArgs + ? (ResolvableType.mapTypeParametersAndArgs(this.metadataType, concreteTypeArgs) as Map< + number, + ResolvedType + >) + : undefined; + + return this.resolveInternal(concreteType.concreteTypeId, typeParamsArgsMap); + } +} diff --git a/packages/abi/src/parser/specifications/v1/resolved-type.ts b/packages/abi/src/parser/specifications/v1/resolved-type.ts new file mode 100644 index 00000000000..6465d10f6d5 --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/resolved-type.ts @@ -0,0 +1,28 @@ +import type { AbiMetadataTypeV1 } from './specification'; + +export interface ResolvedComponent { + name: string; + type: ResolvedType; +} + +export class ResolvedType { + public swayType: string; + public typeId: string | number; + public components: ResolvedComponent[] | undefined; + public typeParamsArgsMap: Map | undefined; + public metadataType: AbiMetadataTypeV1 | undefined; + + constructor(params: { + swayType: string; + typeId: string | number; + components?: ResolvedComponent[]; + typeParamsArgsMap?: Map; + metadataType?: AbiMetadataTypeV1; + }) { + this.swayType = params.swayType; + this.typeId = params.typeId; + this.components = params.components; + this.typeParamsArgsMap = params.typeParamsArgsMap; + this.metadataType = params.metadataType; + } +} diff --git a/packages/abi/src/parser/specifications/v1/specification.ts b/packages/abi/src/parser/specifications/v1/specification.ts new file mode 100644 index 00000000000..f360fbba8bb --- /dev/null +++ b/packages/abi/src/parser/specifications/v1/specification.ts @@ -0,0 +1,98 @@ +/** + * Types for Fuel JSON ABI Format specification v1, as defined on: + * https://github.com/FuelLabs/fuel-specs/blob/master/src/abi/json-abi-format.md + */ +export interface AbiSpecificationV1 { + readonly specVersion: '1'; + readonly encodingVersion: string; + readonly programType: string; + readonly concreteTypes: readonly AbiConcreteTypeV1[]; + readonly metadataTypes: readonly AbiMetadataTypeV1[]; + readonly functions: readonly AbiFunctionV1[]; + readonly loggedTypes: readonly AbiLoggedTypeV1[]; + readonly messagesTypes: readonly AbiMessageTypeV1[]; + readonly configurables: readonly AbiConfigurableV1[]; +} + +export interface AbiConcreteTypeV1 { + readonly type: string; + readonly concreteTypeId: string; + readonly metadataTypeId?: number; + readonly typeArguments?: readonly string[]; +} + +export interface AbiMetadataTypeV1 { + readonly type: string; + readonly metadataTypeId: number; + readonly components?: readonly AbiComponentV1[]; + readonly typeParameters?: readonly number[]; +} + +export interface AbiComponentV1 extends AbiTypeArgumentV1 {} + +export interface AbiTypeArgumentV1 { + readonly name: string; + 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 AbiTypeArgumentV1[]; +} + +export interface AbiFunctionV1 { + readonly name: string; + readonly inputs: readonly AbiFunctionInputV1[]; + readonly output: string; + readonly attributes: readonly AbiFunctionAttributeV1[] | null; +} + +export interface AbiFunctionInputV1 { + readonly name: string; + readonly concreteTypeId: string; +} + +export interface AbiLoggedTypeV1 { + readonly logId: string; + // the _type concrete declaration_ hash based ID of the value being logged. + readonly concreteTypeId: string; +} + +export interface AbiMessageTypeV1 { + readonly messageId: string; + readonly concreteTypeId: string; +} + +export interface AbiConfigurableV1 { + readonly name: string; + readonly concreteTypeId: string; + readonly offset: number; +} + +export type AbiFunctionAttributeV1 = + | StorageAttrV1 + | PayableAttrV1 + | TestAttrV1 + | InlineAttrV1 + | DocCommentAttrV1; + +export interface PayableAttrV1 { + readonly name: 'payable'; + readonly arguments: readonly []; +} + +export interface StorageAttrV1 { + readonly name: 'storage'; + readonly arguments: readonly ('read' | 'write')[]; +} + +export interface TestAttrV1 { + readonly name: 'test'; + readonly arguments: readonly []; +} + +export interface InlineAttrV1 { + readonly name: 'inline'; + readonly arguments: readonly ['never'] | readonly ['always']; +} + +export interface DocCommentAttrV1 { + readonly name: 'doc-comment'; + readonly arguments: string[]; +} diff --git a/packages/abi/src/utils/evaluate-function-inputs-optionality.ts b/packages/abi/src/utils/evaluate-function-inputs-optionality.ts new file mode 100644 index 00000000000..0664e920df2 --- /dev/null +++ b/packages/abi/src/utils/evaluate-function-inputs-optionality.ts @@ -0,0 +1,15 @@ +import { swayTypeMatchers } from '../matchers/sway-type-matchers'; +import type { AbiFunction, AbiFunctionInput } from '../parser'; + +export function evaluateFunctionInputsOptionality( + fn: AbiFunction +): (AbiFunctionInput & { isOptional: boolean })[] { + let isMandatory = false; + return fn.inputs.reduceRight<(AbiFunctionInput & { isOptional: boolean })[]>((result, input) => { + const isTypeMandatory = + !swayTypeMatchers.void(input.type) && !swayTypeMatchers.option(input.type); + + isMandatory = isMandatory || isTypeMandatory; + return [{ ...input, isOptional: !isMandatory }, ...result]; + }, []); +} diff --git a/packages/abi/test/encoding/AbiCoder.test.ts b/packages/abi/test/encoding/AbiCoder.test.ts new file mode 100644 index 00000000000..db86b96c5e1 --- /dev/null +++ b/packages/abi/test/encoding/AbiCoder.test.ts @@ -0,0 +1,361 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { AbiCoder } from '../../src'; +import { v1 as specificationV1 } from '../fixtures/v1'; + +/** + * @group node + * @group browser + */ +describe('AbiCoder', () => { + describe('intialisation', () => { + it('should create an ABI coder', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + expect(coder).toBeDefined(); + expect(coder.abi).toBeInstanceOf(Object); + expect(coder.specification).toEqual(specificationV1); + + expect(coder.functions).toBeInstanceOf(Object); + expect(coder.logs).toBeInstanceOf(Object); + expect(coder.configurables).toBeInstanceOf(Object); + + expect(coder.getFunction).toBeInstanceOf(Function); + expect(coder.getLog).toBeInstanceOf(Function); + expect(coder.getType).toBeInstanceOf(Function); + expect(coder.getConfigurable).toBeInstanceOf(Function); + }); + }); + + describe('getFunction', () => { + describe('properties', () => { + it('should get a function and return the correct values', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const fn = coder.getFunction('types_u8'); + + expect(fn).toBeDefined(); + expect(fn.name).toBe('types_u8'); + expect(fn.inputs).toStrictEqual([ + { + name: 'x', + type: { + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + swayType: 'u8', + }, + }, + ]); + expect(fn.signature).toStrictEqual('types_u8(u8)'); + expect(fn.selector).toStrictEqual('0x00000000469feadd'); + expect(fn.selectorBytes).toStrictEqual( + new Uint8Array([0, 0, 0, 0, 0, 0, 0, 8, 116, 121, 112, 101, 115, 95, 117, 56]) + ); + expect(fn.attributes).toStrictEqual([]); + expect(fn.isReadOnly()).toBe(true); + }); + + it('should get a function by name', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const fn = coder.getFunction('configurables'); + + expect(fn).toBeDefined(); + expect(fn.name).toStrictEqual('configurables'); + expect(fn.signature).toStrictEqual('configurables()'); + expect(fn.selector).toStrictEqual('0x00000000fdaf4480'); + }); + + it('should get a function by signature', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const fn = coder.getFunction('configurables()'); + + expect(fn).toBeDefined(); + expect(fn.name).toStrictEqual('configurables'); + expect(fn.signature).toStrictEqual('configurables()'); + expect(fn.selector).toStrictEqual('0x00000000fdaf4480'); + }); + + it('should get a function by selector', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const fn = coder.getFunction('0x00000000fdaf4480'); + + expect(fn).toBeDefined(); + expect(fn.name).toStrictEqual('configurables'); + expect(fn.signature).toStrictEqual('configurables()'); + expect(fn.selector).toStrictEqual('0x00000000fdaf4480'); + }); + + it('should throw an error if the function is not found', async () => { + const coder = AbiCoder.fromAbi(specificationV1); + + await expectToThrowFuelError( + () => coder.getFunction('unknown'), + new FuelError( + FuelError.CODES.FUNCTION_NOT_FOUND, + 'Unable to find function with the name or signature or selector of "unknown".' + ) + ); + }); + }); + + describe('getFunction::encodeArguments', () => { + it('should encode the arguments correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const encoded = fn.encodeArguments([1]); + + expect(encoded).toStrictEqual(new Uint8Array([1])); + }); + + it('should pad the arguments with undefined if the number of arguments is less than the expected number', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_void'); + + const encoded = fn.encodeArguments([]); + + expect(encoded).toStrictEqual(new Uint8Array([])); + }); + + it('should throw an error if the number of arguments is less than the expected number', async () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + await expectToThrowFuelError( + () => fn.encodeArguments([]), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid number of arguments. Expected a minimum of 1 arguments, received 0' + ) + ); + }); + }); + + describe('decodeArguments', () => { + it('should decode the arguments correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const decoded = fn.decodeArguments(new Uint8Array([1])); + + expect(decoded).toStrictEqual([1]); + }); + + it('should arrayify the arguments', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const decoded = fn.decodeArguments('0x01'); + + expect(decoded).toStrictEqual([1]); + }); + }); + + describe('encodeOutput', () => { + it('should encode the output correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const encoded = fn.encodeOutput(1); + + expect(encoded).toStrictEqual(new Uint8Array([1])); + }); + }); + + describe('decodeOutput', () => { + it('should decode the output correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const decoded = fn.decodeOutput(new Uint8Array([1])); + + expect(decoded).toStrictEqual(1); + }); + + it('should arrayify the output', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const fn = coder.getFunction('types_u8'); + + const decoded = fn.decodeOutput('0x01'); + + expect(decoded).toStrictEqual(1); + }); + }); + }); + + describe('getConfigurable', () => { + describe('properties', () => { + it('should get a configurable by name', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const configurable = coder.getConfigurable('U8_VALUE'); + + expect(configurable).toBeDefined(); + expect(configurable.name).toBe('U8_VALUE'); + expect(configurable.offset).toBe(113392); + expect(configurable.encode).toBeDefined(); + expect(configurable.decode).toBeDefined(); + }); + + it('should throw an error if the configurable is not found', async () => { + const coder = AbiCoder.fromAbi(specificationV1); + + await expectToThrowFuelError( + () => coder.getConfigurable('unknown'), + new FuelError( + FuelError.CODES.CONFIGURABLE_NOT_FOUND, + "Configurable with name 'unknown' doesn't exist in the ABI." + ) + ); + }); + }); + + describe('encode', () => { + it('should encode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const configurable = coder.getConfigurable('U8_VALUE'); + + const encoded = configurable.encode(1); + + expect(encoded).toStrictEqual(new Uint8Array([1])); + }); + }); + + describe('decode', () => { + it('should decode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const configurable = coder.getConfigurable('U8_VALUE'); + + const decoded = configurable.decode(new Uint8Array([1])); + + expect(decoded).toStrictEqual(1); + }); + + it('should arrayify the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const configurable = coder.getConfigurable('U8_VALUE'); + + const decoded = configurable.decode('0x01'); + + expect(decoded).toStrictEqual(1); + }); + }); + }); + + describe('getLog', () => { + it('should get a log by id', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const log = coder.getLog('8961848586872524460'); + + expect(log).toBeDefined(); + }); + + it('should throw an error if the log is not found', async () => { + const coder = AbiCoder.fromAbi(specificationV1); + + await expectToThrowFuelError( + () => coder.getLog('unknown'), + new FuelError( + FuelError.CODES.LOG_TYPE_NOT_FOUND, + `Log type with logId 'unknown' doesn't exist in the ABI.` + ) + ); + }); + + describe('encode', () => { + it('should encode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const log = coder.getLog('8961848586872524460'); + const expected = new Uint8Array([ + -1, 58, 156, 113, 34, 38, 148, 86, 145, 26, 238, 58, 41, 126, 101, 222, 26, 221, 83, 216, + 61, 217, 86, 108, 149, 69, 160, 219, 244, 168, 219, 162, + ]); + const value = '0xff3a9c7122269456911aee3a297e65de1add53d83dd9566c9545a0dbf4a8dba2'; + + const encoded = log.encode(value); + + expect(encoded).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const log = coder.getLog('8961848586872524460'); + const expected = '0xff3a9c7122269456911aee3a297e65de1add53d83dd9566c9545a0dbf4a8dba2'; + const data = new Uint8Array([ + -1, 58, 156, 113, 34, 38, 148, 86, 145, 26, 238, 58, 41, 126, 101, 222, 26, 221, 83, 216, + 61, 217, 86, 108, 149, 69, 160, 219, 244, 168, 219, 162, 209, + ]); + + const decoded = log.decode(data); + + expect(decoded).toStrictEqual(expected); + }); + }); + }); + + describe('getType', () => { + it('should get a type by concreteTypeId', () => { + const coder = AbiCoder.fromAbi(specificationV1); + + const type = coder.getType( + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b' + ); + + expect(type).toBeDefined(); + }); + + it('should throw an error if the type is not found', async () => { + const coder = AbiCoder.fromAbi(specificationV1); + + await expectToThrowFuelError( + () => coder.getType('unknown'), + new FuelError( + FuelError.CODES.TYPE_NOT_FOUND, + `Type with concreteTypeId 'unknown' doesn't exist in the ABI.` + ) + ); + }); + + describe('encode', () => { + it('should encode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const type = coder.getType( + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b' + ); + const expected = new Uint8Array([ + -1, 58, 156, 113, 34, 38, 148, 86, 145, 26, 238, 58, 41, 126, 101, 222, 26, 221, 83, 216, + 61, 217, 86, 108, 149, 69, 160, 219, 244, 168, 219, 162, + ]); + const value = '0xff3a9c7122269456911aee3a297e65de1add53d83dd9566c9545a0dbf4a8dba2'; + + const encoded = type.encode(value); + + expect(encoded).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode the value correctly', () => { + const coder = AbiCoder.fromAbi(specificationV1); + const type = coder.getType( + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b' + ); + const expected = '0xff3a9c7122269456911aee3a297e65de1add53d83dd9566c9545a0dbf4a8dba2'; + const data = new Uint8Array([ + -1, 58, 156, 113, 34, 38, 148, 86, 145, 26, 238, 58, 41, 126, 101, 222, 26, 221, 83, 216, + 61, 217, 86, 108, 149, 69, 160, 219, 244, 168, 219, 162, 209, + ]); + + const decoded = type.decode(data); + + expect(decoded).toStrictEqual(expected); + }); + }); + }); +}); diff --git a/packages/abi/test/encoding/AbiEncoding.test.ts b/packages/abi/test/encoding/AbiEncoding.test.ts new file mode 100644 index 00000000000..b0830ed9dab --- /dev/null +++ b/packages/abi/test/encoding/AbiEncoding.test.ts @@ -0,0 +1,136 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { CoderFactoryParameters } from '../../src/coder/encoding'; +import { encoding } from '../../src/coder/encoding'; +import { coders as v1 } from '../../src/coder/encoding/v1'; + +/** + * @group node + * @group browser + */ +describe('encoding', () => { + describe('from', () => { + it('should create a encoding instance just fine', () => { + expect(encoding).toBeDefined(); + + // Current version (v1) + expect(encoding.array).toEqual(v1.array); + expect(encoding.enum).toEqual(v1.enum); + expect(encoding.option).toEqual(v1.option); + expect(encoding.vector).toEqual(v1.vector); + expect(encoding.u8).toEqual(v1.u8); + expect(encoding.u16).toEqual(v1.u16); + expect(encoding.u32).toEqual(v1.u32); + expect(encoding.u64).toEqual(v1.u64); + expect(encoding.u256).toEqual(v1.u256); + expect(encoding.b256).toEqual(v1.b256); + expect(encoding.b512).toEqual(v1.b512); + expect(encoding.bool).toEqual(v1.bool); + expect(encoding.void).toEqual(v1.void); + expect(encoding.bytes).toEqual(v1.bytes); + expect(encoding.rawSlice).toEqual(v1.rawSlice); + expect(encoding.str).toEqual(v1.str); + expect(encoding.stdString).toEqual(v1.stdString); + expect(encoding.string).toEqual(v1.string); + expect(encoding.vector).toEqual(v1.vector); + expect(encoding.array).toEqual(v1.array); + expect(encoding.tuple).toEqual(v1.tuple); + expect(encoding.struct).toEqual(v1.struct); + expect(encoding.enum).toEqual(v1.enum); + expect(encoding.option).toEqual(v1.option); + + // V1 + expect(encoding.v1.array).toEqual(v1.array); + expect(encoding.v1.enum).toEqual(v1.enum); + expect(encoding.v1.option).toEqual(v1.option); + expect(encoding.v1.vector).toEqual(v1.vector); + expect(encoding.v1.u8).toEqual(v1.u8); + expect(encoding.v1.u16).toEqual(v1.u16); + expect(encoding.v1.u32).toEqual(v1.u32); + expect(encoding.v1.u64).toEqual(v1.u64); + expect(encoding.v1.u256).toEqual(v1.u256); + expect(encoding.v1.b256).toEqual(v1.b256); + expect(encoding.v1.b512).toEqual(v1.b512); + expect(encoding.v1.bool).toEqual(v1.bool); + expect(encoding.v1.void).toEqual(v1.void); + expect(encoding.v1.bytes).toEqual(v1.bytes); + expect(encoding.v1.rawSlice).toEqual(v1.rawSlice); + expect(encoding.v1.str).toEqual(v1.str); + expect(encoding.v1.stdString).toEqual(v1.stdString); + expect(encoding.v1.string).toEqual(v1.string); + expect(encoding.v1.vector).toEqual(v1.vector); + expect(encoding.v1.array).toEqual(v1.array); + expect(encoding.v1.tuple).toEqual(v1.tuple); + expect(encoding.v1.struct).toEqual(v1.struct); + expect(encoding.v1.enum).toEqual(v1.enum); + expect(encoding.v1.option).toEqual(v1.option); + }); + + it('should throw an error if the version is not supported', async () => { + await expectToThrowFuelError( + () => encoding.fromVersion('0'), + new FuelError( + FuelError.CODES.UNSUPPORTED_ENCODING_VERSION, + 'Unsupported encoding version "0"' + ) + ); + }); + }); + + describe('getCoder', () => { + const encodingV1 = encoding.fromVersion('1'); + + it('should get a coder for a given type and name [u8]', () => { + const params: CoderFactoryParameters = { + name: 'test', + type: { swayType: 'u8', concreteTypeId: '0x1' }, + }; + + const coder = encodingV1.getCoder(params); + + expect(coder).toBeDefined(); + }); + + it('should get a coder for a given type and name [string]', () => { + const params: CoderFactoryParameters = { + name: 'test', + type: { swayType: 'str[4]', concreteTypeId: '0x1' }, + }; + + const coder = encodingV1.getCoder(params); + + expect(coder).toBeDefined(); + }); + + it('should throw an error if the coder is not supported', async () => { + const params: CoderFactoryParameters = { + name: 'test', + type: { swayType: 'unknown', concreteTypeId: '0x1' }, + }; + + await expectToThrowFuelError( + () => encodingV1.getCoder(params), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'Unsupported coder type "unknown" for element "test"' + ) + ); + }); + + it('should throw an error if a generic coder is used [should not happen]', async () => { + const params: CoderFactoryParameters = { + name: 'test', + type: { swayType: 'generic', concreteTypeId: '0x1' }, + }; + + await expectToThrowFuelError( + () => encodingV1.getCoder(params), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'Unsupported coder type "generic" for element "test"' + ) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/array.test.ts b/packages/abi/test/encoding/v1/array.test.ts new file mode 100644 index 00000000000..c28d6802ea9 --- /dev/null +++ b/packages/abi/test/encoding/v1/array.test.ts @@ -0,0 +1,229 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding, MAX_BYTES } from '../../../src'; +import { U8_MAX } from '../../utils/constants'; + +const isBrowser = typeof window !== 'undefined'; + +/** + * @group node + * @group browser + */ +describe('v1/array', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const swayType = 'enum MyEnum'; + const components: AbiTypeComponent[] = []; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.array.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided array type is missing ABI components.', + { swayType } + ) + ); + }); + + it('should throw when a component is not provided', async () => { + const swayType = '[u8; 4]'; + const components: AbiTypeComponent[] = []; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.array.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided array type is missing a ABI component.' + ) + ); + }); + + it('should get the coder for a valid array type', () => { + const swayType = '[u8; 4]'; + const components: AbiTypeComponent[] = [{} as AbiTypeComponent]; + const factory = vi.fn(); + + const coder = encoding.v1.array.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ); + + expect(factory).toHaveBeenCalledWith(components[0], factory); + expect(factory).toHaveBeenCalledTimes(1); + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode an array [u8, length = 0]', () => { + const coder = encoding.v1.array(encoding.v1.u8, 0); + const input: number[] = []; + const expected = new Uint8Array([]); + + const actual = coder.encode(input); + + expect(actual).toEqual(expected); + }); + + it('should encode an array [u8, length = 4]', () => { + const coder = encoding.v1.array(encoding.v1.u8, 4); + const input = [0, 13, 37, 255]; + const expected = new Uint8Array(input); + + const actual = coder.encode(input); + + expect(actual).toEqual(expected); + }); + + it('should encode an array of enums [enum, length = 4]', () => { + const coder = encoding.v1.array( + encoding.v1.enum({ a: encoding.v1.u8, b: encoding.v1.bool }), + 4 + ); + const input = [{ a: 0 }, { b: false }, { b: true }, { a: U8_MAX }]; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 255, + ]); + + const actual = coder.encode(input); + + expect(actual).toEqual(expected); + }); + + it('should throw when value to encode is not an array [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const input = { object: true }; + + await expectToThrowFuelError( + // @ts-expect-error - testing a non-array value + () => coder.encode(input), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid array value - expected array value.', { + value: input, + }) + ); + }); + + it('should throw when value to encode is not an array of the expected length [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const input = [1, 2, 3]; + + await expectToThrowFuelError( + () => coder.encode(input), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid array value - unexpected length.', { + value: input, + expectedLength: 1, + }) + ); + }); + + it('should throw when value to encode is invalid [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const input = [256]; + + await expectToThrowFuelError( + () => coder.encode(input), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value exceeds maximum.', { + type: 'u8', + value: '256', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode an array [u8, length = 0]', () => { + const coder = encoding.v1.array(encoding.v1.u8, 0); + const input = new Uint8Array([]); + const expected: number[] = []; + + const [actual, offset] = coder.decode(input); + + expect(actual).toEqual(expected); + expect(offset).toEqual(0); + }); + + it('should decode an array [u8, length = 4]', () => { + const coder = encoding.v1.array(encoding.v1.u8, 4); + const input = new Uint8Array([0, 13, 37, 255]); + const expected = [0, 13, 37, 255]; + + const [actual, offset] = coder.decode(input); + + expect(actual).toEqual(expected); + expect(offset).toEqual(4); + }); + + it('should decode an array of enums [enum, length = 4]', () => { + const coder = encoding.v1.array( + encoding.v1.enum({ a: encoding.v1.u8, b: encoding.v1.bool }), + 4 + ); + const expected = [{ a: 0 }, { b: false }, { b: true }, { a: U8_MAX }]; + const data = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 255, + ]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toEqual(expected); + expect(offset).toEqual(36); + }); + + /** + * TODO: this test is failing on browser, need to investigate why. + * + * RangeError: Array buffer allocation failed + */ + it.skipIf(isBrowser)('should throw when decoding too many bytes [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const data = new Uint8Array(MAX_BYTES + 1); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid array data - exceeds maximum bytes.', { + data, + length: data.length, + maxLength: MAX_BYTES, + }) + ); + }); + + it('should throw when decoding empty bytes [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u8 data - unexpected length.', { + data, + }) + ); + }); + + it('should throw when decoding too few bytes [u8, length = 1]', async () => { + const coder = encoding.v1.array(encoding.v1.u8, 1); + const data = new Uint8Array([]); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u8 data - unexpected length.', { + data, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/b256.test.ts b/packages/abi/test/encoding/v1/b256.test.ts new file mode 100644 index 00000000000..82ebec9c69c --- /dev/null +++ b/packages/abi/test/encoding/v1/b256.test.ts @@ -0,0 +1,138 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('b256', () => { + describe('encode', () => { + it('should encode a b256 [zero]', () => { + const coder = encoding.v1.b256; + const expected = new Uint8Array(32).fill(0); + const value = '0x0000000000000000000000000000000000000000000000000000000000000000'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a b256 [address]`, () => { + const coder = encoding.v1.b256; + const expected = new Uint8Array([ + 213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34, 152, + 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, + ]); + const value = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw an error encoding a b256 [too short]', async () => { + const coder = encoding.v1.b256; + const value = '0xTooShort'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value, + expectedLength: 32, + }) + ); + }); + + it('should throw an error encoding a b256 [too long]', async () => { + const coder = encoding.v1.b256; + const value = `0x${'a'.repeat(33)}`; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value, + expectedLength: 32, + }) + ); + }); + + it('should throw an error encoding a b256 [not a hex string]', async () => { + const coder = encoding.v1.b256; + const value = 'not a hex string'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.') + ); + }); + }); + + describe('decode', () => { + it('should decode a b256 [zero]', () => { + const coder = encoding.v1.b256; + const expected = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const data = new Uint8Array(32).fill(0); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(32); + }); + + it(`should decode a b256 [address]`, () => { + const coder = encoding.v1.b256; + const expected = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; + const data = new Uint8Array([ + 213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34, 152, + 244, 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, + ]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(32); + }); + + it('should throw an error decoding a b256 [too short]', async () => { + const coder = encoding.v1.b256; + const data = new Uint8Array(31); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 data - unexpected length.', { + data, + expectedLength: 32, + }) + ); + }); + + it('should throw an error decoding a b256 [empty]', async () => { + const coder = encoding.v1.b256; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 data - unexpected length.', { + data, + expectedLength: 32, + }) + ); + }); + + it('should throw an error decoding a b256 [with offset]', async () => { + const coder = encoding.v1.b256; + const offset = 10; + const data = new Uint8Array(32); + + await expectToThrowFuelError( + () => coder.decode(data, offset), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 data - unexpected length.', { + data: data.slice(offset), + expectedLength: 32, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/b512.test.ts b/packages/abi/test/encoding/v1/b512.test.ts new file mode 100644 index 00000000000..7709f250338 --- /dev/null +++ b/packages/abi/test/encoding/v1/b512.test.ts @@ -0,0 +1,146 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('b512', () => { + describe('encode', () => { + it('should encode a b512 [zero]', () => { + const coder = encoding.v1.b512; + const expected = new Uint8Array(64).fill(0); + const value = + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a b512 [address]`, () => { + const coder = encoding.v1.b512; + const expected = new Uint8Array([ + 142, 157, 218, 111, 119, 147, 116, 90, 197, 170, 207, 158, 144, 124, 174, 48, 178, 160, 31, + 223, 13, 35, 183, 117, 10, 133, 198, 164, 79, 202, 12, 41, 240, 144, 111, 157, 31, 30, 146, + 230, 161, 251, 60, 61, 206, 243, 204, 59, 60, 219, 170, 226, 126, 71, 185, 217, 164, 198, + 164, 252, 228, 207, 22, 178, + ]); + const value = + '0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw an error encoding a b512 [too short]', async () => { + const coder = encoding.v1.b512; + const value = '0xTooShort'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.', { + value, + expectedLength: 64, + }) + ); + }); + + it('should throw an error encoding a b512 [too long]', async () => { + const coder = encoding.v1.b512; + const value = `0x${'a'.repeat(33)}`; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.', { + value, + expectedLength: 64, + }) + ); + }); + + it('should throw an error encoding a b512 [not a hex string]', async () => { + const coder = encoding.v1.b512; + const value = 'not a hex string'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.') + ); + }); + }); + + describe('decode', () => { + it('should decode a b512 [zero]', () => { + const coder = encoding.v1.b512; + const expected = + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + const data = new Uint8Array(64).fill(0); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(64); + }); + + it(`should decode a b512 [address]`, () => { + const coder = encoding.v1.b512; + const expected = + '0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2'; + const data = new Uint8Array([ + 142, 157, 218, 111, 119, 147, 116, 90, 197, 170, 207, 158, 144, 124, 174, 48, 178, 160, 31, + 223, 13, 35, 183, 117, 10, 133, 198, 164, 79, 202, 12, 41, 240, 144, 111, 157, 31, 30, 146, + 230, 161, 251, 60, 61, 206, 243, 204, 59, 60, 219, 170, 226, 126, 71, 185, 217, 164, 198, + 164, 252, 228, 207, 22, 178, + ]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(64); + }); + + it('should throw an error decoding a b512 [too short]', async () => { + const coder = encoding.v1.b512; + const data = new Uint8Array(31); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 data - unexpected length.', { + data, + expectedLength: 64, + }) + ); + }); + + it('should throw an error decoding a b512 [empty]', async () => { + const coder = encoding.v1.b512; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 data - unexpected length.', { + data, + expectedLength: 64, + }) + ); + }); + + it('should throw an error decoding a b512 [with offset]', async () => { + const coder = encoding.v1.b512; + const offset = 10; + const data = new Uint8Array(64); + + await expectToThrowFuelError( + () => coder.decode(data, offset), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 data - unexpected length.', { + data: data.slice(offset), + expectedLength: 64, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/boolean.test.ts b/packages/abi/test/encoding/v1/boolean.test.ts new file mode 100644 index 00000000000..d64e5b63e9f --- /dev/null +++ b/packages/abi/test/encoding/v1/boolean.test.ts @@ -0,0 +1,107 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('boolean', () => { + describe('encode', () => { + it('should encode a boolean [true]', () => { + const coder = encoding.v1.bool; + const value = true; + const expected = new Uint8Array([1]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a boolean [false]', () => { + const coder = encoding.v1.bool; + const value = false; + const expected = new Uint8Array([0]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw when encoding an invalid boolean value [undefined]', async () => { + const coder = encoding.v1.bool; + const value = undefined; + + await expectToThrowFuelError( + // @ts-expect-error or expects a boolean value + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid boolean value.', { + value: undefined, + }) + ); + }); + + it('should throw when encoding an invalid boolean value [string value]', async () => { + const coder = encoding.v1.bool; + const value = 'true'; + + await expectToThrowFuelError( + // @ts-expect-error or expects a boolean value + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid boolean value.', { value: 'true' }) + ); + }); + }); + + describe('decode', () => { + it('should decode a boolean [1 = true]', () => { + const coder = encoding.v1.bool; + const value = new Uint8Array([1]); + const expected = true; + + const [actual, offset] = coder.decode(value); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(value.length); + }); + + it('should decode a boolean [0 = false]', () => { + const coder = encoding.v1.bool; + const value = new Uint8Array([0]); + const expected = false; + + const [actual, offset] = coder.decode(value); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(value.length); + }); + + it('should throw when decoding an invalid boolean value [empty]', async () => { + const coder = encoding.v1.bool; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid bool data - not enough data.', { + data, + expectedLength: 1, + }) + ); + }); + + it('should throw when decoding an invalid boolean value [2]', async () => { + const coder = encoding.v1.bool; + const data = new Uint8Array([2]); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid bool value - invalid boolean value.', { + data, + type: 'bool', + value: 2, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/byte.test.ts b/packages/abi/test/encoding/v1/byte.test.ts new file mode 100644 index 00000000000..c7ceaadb780 --- /dev/null +++ b/packages/abi/test/encoding/v1/byte.test.ts @@ -0,0 +1,84 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('byte', () => { + describe('encode', () => { + it('should encode an array of numbers [1, 2, 3]', () => { + const coder = encoding.v1.bytes; + const value = [1, 2, 3]; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a Uint8Array [1, 2, 3]', () => { + const coder = encoding.v1.bytes; + const value = new Uint8Array([1, 2, 3]); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a full word size [1, 2, 3, 4, 5, 6, 7, 8]', () => { + const coder = encoding.v1.bytes; + const value = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 8, 1, 2, 3, 4, 5, 6, 7, 8]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode a byte array [1, 2, 3]', () => { + const coder = encoding.v1.bytes; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3]); + const expected = new Uint8Array([1, 2, 3]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toEqual(11); + }); + + it('should throw when decoding an empty byte array', async () => { + const coder = encoding.v1.bytes; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid bytes data - malformed bytes.', { + data, + }) + ); + }); + + it('should throw when decoding a byte array with not enough data', async () => { + const coder = encoding.v1.bytes; + const data = new Uint8Array([ + // Length = 0 + 0, 0, 0, 0, 0, 0, 0, 3, + // Data = [1, 2] + 1, 2, + ]); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid bytes data - malformed bytes.', { + data, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/enum.test.ts b/packages/abi/test/encoding/v1/enum.test.ts new file mode 100644 index 00000000000..00fbbbfa796 --- /dev/null +++ b/packages/abi/test/encoding/v1/enum.test.ts @@ -0,0 +1,272 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; +import { bn } from '@fuel-ts/math'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding } from '../../../src'; +import { U64_MAX } from '../../utils/constants'; + +/** + * @group node + * @group browser + */ +describe('enum', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const factory = vi.fn(); + const swayType = 'enum MyEnum'; + const components: AbiTypeComponent[] | undefined = undefined; + + await expectToThrowFuelError( + () => + encoding.v1.enum.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided enum type is missing ABI components.', + { swayType, components } + ) + ); + }); + + it('should get the coder for a valid enum type', () => { + const components: AbiTypeComponent[] = [ + { name: 'a', type: 'bool' } as unknown as AbiTypeComponent, + { name: 'b', type: 'u64' } as unknown as AbiTypeComponent, + ]; + const factory = vi.fn(); + factory.mockReturnValue(encoding.v1.bool); + factory.mockReturnValue(encoding.v1.u64); + + const coder = encoding.v1.enum.factory( + { type: { components } } as CoderFactoryParameters, + factory + ); + + expect(factory).toHaveBeenCalledWith(components[0], factory); + expect(factory).toHaveBeenCalledWith(components[1], factory); + expect(factory).toHaveBeenCalledTimes(2); + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode an enum [boolean]', () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1]); + const value = { a: true }; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an enum [u64]', () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, + ]); + const value = { b: bn(U64_MAX) }; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an enum [non-native]', () => { + const coder = encoding.v1.enum({ + nonNative: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1]); + const value = { nonNative: true }; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an enum [native]', () => { + const coder = encoding.v1.enum({ + nonNative: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 2]); + const value = 'native'; + + // @ts-expect-error native + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw when encoding [no enum key]', async () => { + const coder = encoding.v1.enum({ + a: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const value = {}; + await expectToThrowFuelError( + // @ts-expect-error no enum key + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid enum value - a valid case key must be provided.', + { value, validKeys: ['a', 'b', 'native'] } + ) + ); + }); + + it('should throw when encoding [non-existent enum key]', async () => { + const coder = encoding.v1.enum({ + a: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const value = { nonExistentKey: true }; + + await expectToThrowFuelError( + // @ts-expect-error non-existent enum key + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid enum value - invalid case key "nonExistentKey".', + { value, validKeys: ['a', 'b', 'native'] } + ) + ); + }); + + it('should throw when encoding [multiple fields]', async () => { + const coder = encoding.v1.enum({ + a: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const value = { a: true, b: bn(1) }; + + await expectToThrowFuelError( + // @ts-expect-error multiple fields + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid enum value - only one field must be provided.', + { value, validKeys: ['a', 'b', 'native'] } + ) + ); + }); + + it('should throw when encoding [invalid case value]', async () => { + const coder = encoding.v1.enum({ + a: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const value = { b: true }; + + await expectToThrowFuelError( + // @ts-expect-error invalid case value + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - expected a BNInput.', { + value: true, + }) + ); + }); + }); + + describe('decode', () => { + it('should decode an enum [boolean]', () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1]); + const expected = { a: true }; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toBe(9); + }); + + it('should decode an enum [u64]', () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255]); + const expected = { b: bn(U64_MAX) }; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toBe(16); + }); + + it('should decode an enum [non-native]', () => { + const coder = encoding.v1.enum({ + nonNative: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1]); + const expected = { nonNative: true }; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toBe(9); + }); + + it('should decode an enum [native]', () => { + const coder = encoding.v1.enum({ + nonNative: encoding.v1.bool, + b: encoding.v1.u64, + native: encoding.v1.void, + }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 2]); + const expected = 'native'; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toBe(8); + }); + + it('should throw an error [invalid case key]', async () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 255, 255]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid enum data - invalid case key.', { + validKeys: ['a', 'b'], + }) + ); + }); + + it('should throw an error [empty bytes]', async () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid enum data - invalid case key.', { + validKeys: ['a', 'b'], + }) + ); + }); + + it('should throw an error [empty case element]', async () => { + const coder = encoding.v1.enum({ a: encoding.v1.bool, b: encoding.v1.u64 }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u64 data - unexpected length.', { + data: new Uint8Array([]), + expectedLength: 8, + type: 'u64', + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/option.test.ts b/packages/abi/test/encoding/v1/option.test.ts new file mode 100644 index 00000000000..0a91b6fde85 --- /dev/null +++ b/packages/abi/test/encoding/v1/option.test.ts @@ -0,0 +1,133 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('option', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const swayType = 'enum std::option::Option'; + const components: AbiTypeComponent[] | undefined = undefined; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.option.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided option type is missing ABI components.', + { swayType, components } + ) + ); + }); + + it('should get the coder for a valid option type', () => { + const components: AbiTypeComponent[] = [ + { name: 'None', type: '()' } as unknown as AbiTypeComponent, + { name: 'Some', type: 'u64' } as unknown as AbiTypeComponent, + ]; + const factory = vi.fn(); + factory.mockReturnValue(encoding.v1.void); + factory.mockReturnValue(encoding.v1.u64); + + const coder = encoding.v1.option.factory( + { type: { components } } as CoderFactoryParameters, + factory + ); + + expect(factory).toHaveBeenCalledWith(components[0], factory); + expect(factory).toHaveBeenCalledWith(components[1], factory); + expect(factory).toHaveBeenCalledTimes(2); + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode a value [Some]', () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const value = 100; + + const actual = coder.encode(value); + + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1, 100]); + expect(actual).toEqual(expected); + }); + + it('should encode a value [None]', () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const value = undefined; + + const actual = coder.encode(value); + + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); + expect(actual).toEqual(expected); + }); + + it('should encode a value [None - optional]', () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + + // @ts-expect-error optional value + const actual = coder.encode(); + + const expected = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); + expect(actual).toEqual(expected); + }); + + it('should throw when encoding [malformed value]', async () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const value = 'malformed value'; + + await expectToThrowFuelError( + // @ts-expect-error invalid option value + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, `Invalid option value - malformed value.`, { + value: { Some: value }, + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a value [Some]', () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 1, 100]); + const expected = 100; + + const [decoded, length] = coder.decode(input, 0); + + expect(decoded).toEqual(expected); + expect(length).toEqual(9); + }); + + it('should decode a value [None]', () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const input = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]); + const expected = undefined; + + const [decoded, length] = coder.decode(input, 0); + + expect(decoded).toEqual(expected); + expect(length).toEqual(8); + }); + + it('should throw when decoding [malformed value]', async () => { + const coder = encoding.v1.option({ None: encoding.v1.void, Some: encoding.v1.u8 }); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, `Invalid option data - malformed bytes.`, { + data: new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]), + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/raw-slice.test.ts b/packages/abi/test/encoding/v1/raw-slice.test.ts new file mode 100644 index 00000000000..f8abaa69065 --- /dev/null +++ b/packages/abi/test/encoding/v1/raw-slice.test.ts @@ -0,0 +1,74 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('raw_slice', () => { + describe('encode', () => { + it('should encode an array of numbers [1, 2, 3]', () => { + const coder = encoding.v1.rawSlice; + const value = [1, 2, 3]; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a full word size [1, 2, 3, 4, 5, 6, 7, 8]', () => { + const coder = encoding.v1.rawSlice; + const value = [1, 2, 3, 4, 5, 6, 7, 8]; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 8, 1, 2, 3, 4, 5, 6, 7, 8]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode a rawSlice array [1, 2, 3]', () => { + const coder = encoding.v1.rawSlice; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3]); + const expected = [1, 2, 3]; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toEqual(11); + }); + + it('should throw when decoding an empty rawSlice array', async () => { + const coder = encoding.v1.rawSlice; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid raw slice data - malformed bytes.', { + data, + }) + ); + }); + + it('should throw when decoding a rawSlice array with not enough data', async () => { + const coder = encoding.v1.rawSlice; + const data = new Uint8Array([ + // Length = 0 + 0, 0, 0, 0, 0, 0, 0, 3, + // Data = [1, 2] + 1, 2, + ]); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid raw slice data - malformed bytes.', { + data, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/std-string.test.ts b/packages/abi/test/encoding/v1/std-string.test.ts new file mode 100644 index 00000000000..6e7bba6afab --- /dev/null +++ b/packages/abi/test/encoding/v1/std-string.test.ts @@ -0,0 +1,93 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('std_string', () => { + describe('encode', () => { + it('should encode an empty string [""]', () => { + const coder = encoding.v1.stdString; + const value = ''; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [hello world]', () => { + const coder = encoding.v1.stdString; + const value = 'hello world'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [H3llo W0rld]', () => { + const coder = encoding.v1.stdString; + const value = 'H3llo W0rld'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 11, 72, 51, 108, 108, 111, 32, 87, 48, 114, 108, 100, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [abcdefghijklmnopqrstuvwxyz1234567890]', () => { + const coder = encoding.v1.stdString; + const value = 'abcdefghijklmnopqrstuvwxyz1234567890'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 36, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 48, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode a rawSlice array [1, 2, 3]', () => { + const coder = encoding.v1.stdString; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 4, 102, 117, 101, 108]); + const expected = 'fuel'; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toEqual(12); + }); + + it('should throw when decoding a string with empty bytes', async () => { + const coder = encoding.v1.stdString; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid std string data - malformed bytes.') + ); + }); + + it('should throw when decoding a string with empty byte data', async () => { + const coder = encoding.v1.stdString; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 255, 255, 1]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid std string data - malformed bytes.') + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/str.test.ts b/packages/abi/test/encoding/v1/str.test.ts new file mode 100644 index 00000000000..93710b3eb22 --- /dev/null +++ b/packages/abi/test/encoding/v1/str.test.ts @@ -0,0 +1,93 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('str', () => { + describe('encode', () => { + it('should encode an empty string [""]', () => { + const coder = encoding.v1.str; + const value = ''; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [hello world]', () => { + const coder = encoding.v1.str; + const value = 'hello world'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [H3llo W0rld]', () => { + const coder = encoding.v1.str; + const value = 'H3llo W0rld'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 11, 72, 51, 108, 108, 111, 32, 87, 48, 114, 108, 100, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode an empty string [abcdefghijklmnopqrstuvwxyz1234567890]', () => { + const coder = encoding.v1.str; + const value = 'abcdefghijklmnopqrstuvwxyz1234567890'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 36, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 48, + ]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode a rawSlice array [1, 2, 3]', () => { + const coder = encoding.v1.str; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 4, 102, 117, 101, 108]); + const expected = 'fuel'; + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toEqual(12); + }); + + it('throws when decoding a string with empty bytes', async () => { + const coder = encoding.v1.str; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid str data - malformed bytes.') + ); + }); + + it('throws when decoding a string with empty byte data', async () => { + const coder = encoding.v1.str; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 255, 255, 1]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid str data - malformed bytes.') + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/string.test.ts b/packages/abi/test/encoding/v1/string.test.ts new file mode 100644 index 00000000000..b626cbe83fd --- /dev/null +++ b/packages/abi/test/encoding/v1/string.test.ts @@ -0,0 +1,95 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { CoderFactoryParameters } from '../../../src'; +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('string', () => { + describe('fromAbi', () => { + it('should throw when the array type is not valid', async () => { + const factory = vi.fn(); + const swayType = 'invalid[123]'; + + await expectToThrowFuelError( + () => encoding.v1.string.factory({ type: { swayType } } as CoderFactoryParameters, factory), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'Unable to find string coder for the provided type "invalid[123]".', + { swayType } + ) + ); + }); + + it('should get the coder for a valid array type', () => { + const factory = vi.fn(); + const swayType = 'str[1]'; + + const coder = encoding.v1.string.factory( + { type: { swayType } } as CoderFactoryParameters, + factory + ); + + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode a string', () => { + const coder = encoding.v1.string(4); + const value = 'fuel'; + const expected = new Uint8Array([102, 117, 101, 108]); + + const actual = coder.encode(value); + + expect(actual).toEqual(expected); + }); + + it('should throw when encoding a string [mismatch length]', async () => { + const coder = encoding.v1.string(2); + const value = 'fuel'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid string value - unexpected length.') + ); + }); + }); + + describe('decode', () => { + it('should decode a string', () => { + const coder = encoding.v1.string(4); + const data = new Uint8Array([102, 117, 101, 108]); + const expected = 'fuel'; + + const [actual, offset] = coder.decode(data, 0); + + expect(actual).toEqual(expected); + expect(offset).toEqual(4); + }); + + it('should throw when decoding a string [too few bytes]', async () => { + const coder = encoding.v1.string(4); + const data = new Uint8Array([0, 0]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid string data - unexpected length.') + ); + }); + + it('should throw when decoding a string [too few bytes w/ offset]', async () => { + const coder = encoding.v1.string(4); + const offset = 10; + const data = new Uint8Array([0, 0, 0, 0]); + + await expectToThrowFuelError( + () => coder.decode(data, offset), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid string data - unexpected length.') + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/struct.test.ts b/packages/abi/test/encoding/v1/struct.test.ts new file mode 100644 index 00000000000..9d5379a2e5b --- /dev/null +++ b/packages/abi/test/encoding/v1/struct.test.ts @@ -0,0 +1,141 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding } from '../../../src'; +import { U32_MAX, U64_MAX } from '../../utils/constants'; + +/** + * @group node + * @group browser + */ +describe('struct', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const swayType = 'struct MyStruct'; + const components: AbiTypeComponent[] | undefined = undefined; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.struct.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided struct type is missing ABI components.', + { swayType, components } + ) + ); + }); + + it('should get the coder for a valid struct type', () => { + const components: AbiTypeComponent[] = [{}] as AbiTypeComponent[]; + const factory = vi.fn(); + + const coder = encoding.v1.struct.factory( + { type: { components } } as CoderFactoryParameters, + factory + ); + + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode a struct [{ a: boolean, b: u64 }]', () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const expected = new Uint8Array([1, 0, 0, 0, 0, 255, 255, 255, 255]); + const value = { a: true, b: U32_MAX }; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw when encoding [missing element]', async () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const value = { a: true }; + + await expectToThrowFuelError( + // @ts-expect-error - expected missing field + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct value - malformed object.', { + value, + paths: [{ path: 'b', error: 'Field not present.' }], + }) + ); + }); + + it('should throw when encoding [missing and extra elements]', async () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const value = { a: true, c: 1 }; + + await expectToThrowFuelError( + // @ts-expect-error - expected missing field + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct value - malformed object.', { + value, + paths: [{ path: 'b', error: 'Field not present.' }], + }) + ); + }); + + it('should throw when encoding [invalid element]', async () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const value = { a: true, b: U64_MAX.add(1) }; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - value exceeds maximum.', { + type: 'u64', + value: U64_MAX.add(1).toString(), + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a struct [{ a: boolean, b: u64 }]', () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const expected = { a: true, b: expect.toEqualBn(U32_MAX) }; + const data = new Uint8Array([1, 0, 0, 0, 0, 255, 255, 255, 255]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toEqual(9); + }); + + it('should throw when decoding empty bytes', async () => { + const coder = encoding.v1.struct({ + a: encoding.v1.bool, + b: encoding.v1.u64, + }); + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid bool data - not enough data.', { + data, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/tuple.test.ts b/packages/abi/test/encoding/v1/tuple.test.ts new file mode 100644 index 00000000000..383e2c9a002 --- /dev/null +++ b/packages/abi/test/encoding/v1/tuple.test.ts @@ -0,0 +1,90 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding } from '../../../src'; +import { U64_MAX } from '../../utils/constants'; + +/** + * @group node + * @group browser + */ +describe('tuple', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const swayType = '(u8, bool)'; + const components: AbiTypeComponent[] | undefined = undefined; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.tuple.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided tuple type is missing ABI components.', + { swayType, components } + ) + ); + }); + + it('should get the coder for a valid tuple type', () => { + const components: AbiTypeComponent[] = [{}] as AbiTypeComponent[]; + const factory = vi.fn(); + + const coder = encoding.v1.tuple.factory( + { type: { components } } as CoderFactoryParameters, + factory + ); + + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode a tuple [boolean, u64]', () => { + const coder = encoding.v1.tuple([encoding.v1.bool, encoding.v1.u64]); + const expected = new Uint8Array([1, 255, 255, 255, 255, 255, 255, 255, 255]); + const value = [true, U64_MAX]; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw when encoding [too few inputs]', async () => { + const coder = encoding.v1.tuple([encoding.v1.bool, encoding.v1.u64]); + const value = [true]; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid tuple value - unexpected length.') + ); + }); + }); + + describe('decode', () => { + it('should decode a tuple [boolean, u64]', () => { + const coder = encoding.v1.tuple([encoding.v1.bool, encoding.v1.u64]); + const expected = [true, U64_MAX]; + const data = new Uint8Array([1, 255, 255, 255, 255, 255, 255, 255, 255]); + + const [actual, actualOffset] = coder.decode(data, 0); + + expect(actual).toStrictEqual(expected); + expect(actualOffset).toEqual(9); + }); + + it('throws when decoding bytes [empty]', async () => { + const coder = encoding.v1.tuple([encoding.v1.bool, encoding.v1.u64]); + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid tuple data - malformed data.') + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/u16.test.ts b/packages/abi/test/encoding/v1/u16.test.ts new file mode 100644 index 00000000000..3a4ef257547 --- /dev/null +++ b/packages/abi/test/encoding/v1/u16.test.ts @@ -0,0 +1,114 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('u16', () => { + describe('encode', () => { + it('should encode a u16 [min = 0]', () => { + const coder = encoding.v1.u16; + const expected = new Uint8Array([0, 0]); + const value = 0; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a u16 [max = 65535]`, () => { + const coder = encoding.v1.u16; + const expected = new Uint8Array([255, 255]); + const value = 65535; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should fail to encode value [min - 1 = -1]', async () => { + const coder = encoding.v1.u16; + const value = -1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u16 value - value is less than zero.', + { + value: '-1', + type: 'u16', + } + ) + ); + }); + + it(`should fail to encode value [max + 1 = 65536]`, async () => { + const coder = encoding.v1.u16; + const value = 65536; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16 value - value exceeds maximum.', { + value: '65536', + type: 'u16', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a u16 [min]', () => { + const coder = encoding.v1.u16; + const expected = 0; + const data = new Uint8Array([0, 0]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(2); + }); + + it(`should decode a u16 [max = 65535]`, () => { + const coder = encoding.v1.u16; + const expected = 65535; + const data = new Uint8Array([255, 255]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(2); + }); + + it('should throw when decoding invalid u16 data [empty]', async () => { + const coder = encoding.v1.u16; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u16 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u16', + expectedLength: 2, + }) + ); + }); + + it('should throw when decoding invalid u16 data [empty with offset]', async () => { + const coder = encoding.v1.u16; + const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(data, 8), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u16 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u16', + expectedLength: 2, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/u256.test.ts b/packages/abi/test/encoding/v1/u256.test.ts new file mode 100644 index 00000000000..dea0d0101e3 --- /dev/null +++ b/packages/abi/test/encoding/v1/u256.test.ts @@ -0,0 +1,210 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; +import { bn } from '@fuel-ts/math'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('u256', () => { + describe('encode', () => { + it('should encode a u256 [min = 0]', () => { + const coder = encoding.v1.u256; + const expected = new Uint8Array(32).fill(0); + const value = 0; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a u256 [max = 115792089237316195423570985008687907853269984665640564039457584007913129639935]`, () => { + const coder = encoding.v1.u256; + const expected = new Uint8Array(32).fill(255); + const value = + '115792089237316195423570985008687907853269984665640564039457584007913129639935'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a u64 [max safe integer]', () => { + const coder = encoding.v1.u256; + const value: number = Number.MAX_SAFE_INTEGER; + const expected = new Uint8Array([ + 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, 31, 255, 255, + 255, 255, 255, 255, + ]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', async () => { + const coder = encoding.v1.u256; + const value: number = Number.MAX_SAFE_INTEGER + 1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u256 type - number value is too large. Number can only safely handle up to 53 bits.' + ) + ); + }); + + it('should encode a u64 [very big number - as string]', () => { + const coder = encoding.v1.u256; + const value: string = '76472027892439376'; + const expected = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15, 174, 231, + 121, 200, 89, 80, + ]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', async () => { + const coder = encoding.v1.u256; + const value: number = 76472027892439376; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u256 type - number value is too large. Number can only safely handle up to 53 bits.' + ) + ); + }); + + it('should fail to encode value [boolean]', async () => { + const coder = encoding.v1.u256; + const value = true; + + await expectToThrowFuelError( + // @ts-expect-error: a boolean value boolean + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256 value - expected a BNInput.', { + value, + }) + ); + }); + + it('should fail to encode value [not a BNInput]', async () => { + const coder = encoding.v1.u256; + const value = 'not a BNInput'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256 value - expected a BNInput.', { + value, + }) + ); + }); + + it('should fail to encode value [min - 1 = -1]', async () => { + const coder = encoding.v1.u256; + const value = -1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u256 value - value is less than zero.', + { + value: '-1', + type: 'u256', + } + ) + ); + }); + + it(`should fail to encode value [max + 1 = 115792089237316195423570985008687907853269984665640564039457584007913129639936]`, async () => { + const coder = encoding.v1.u256; + const value = + '115792089237316195423570985008687907853269984665640564039457584007913129639936'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256 value - value exceeds maximum.', { + value: '115792089237316195423570985008687907853269984665640564039457584007913129639936', + type: 'u256', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a u256 [min]', () => { + const coder = encoding.v1.u256; + const expected = expect.toEqualBn(0); + const data = new Uint8Array(32).fill(0); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(32); + }); + + it(`should decode a u256 [max = 115792089237316195423570985008687907853269984665640564039457584007913129639935]`, () => { + const coder = encoding.v1.u256; + const expected = expect.toEqualBn( + '115792089237316195423570985008687907853269984665640564039457584007913129639935' + ); + const data = new Uint8Array(32).fill(255); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(32); + }); + + it('should decode a u64 [very big number]', () => { + const coder = encoding.v1.u256; + const data = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15, 174, 231, + 121, 200, 89, 80, + ]); + const expectedValue = bn('76472027892439376'); + + const [actualValue, actualLength] = coder.decode(data, 0); + + expect(actualValue).toEqualBn(expectedValue); + expect(actualLength).toEqual(32); + }); + + it('should throw when decoding invalid u256 data [empty]', async () => { + const coder = encoding.v1.u256; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u256 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u256', + expectedLength: 32, + }) + ); + }); + + it('should throw when decoding invalid u256 data [empty with offset]', async () => { + const coder = encoding.v1.u256; + const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(data, 8), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u256 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u256', + expectedLength: 32, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/u32.test.ts b/packages/abi/test/encoding/v1/u32.test.ts new file mode 100644 index 00000000000..d81395c529f --- /dev/null +++ b/packages/abi/test/encoding/v1/u32.test.ts @@ -0,0 +1,114 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('u32', () => { + describe('encode', () => { + it('should encode a u32 [min = 0]', () => { + const coder = encoding.v1.u32; + const expected = new Uint8Array([0, 0, 0, 0]); + const value = 0; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a u32 [max = 4294967295]`, () => { + const coder = encoding.v1.u32; + const expected = new Uint8Array([255, 255, 255, 255]); + const value = 4294967295; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should fail to encode value [min - 1 = -1]', async () => { + const coder = encoding.v1.u32; + const value = -1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u32 value - value is less than zero.', + { + value: '-1', + type: 'u32', + } + ) + ); + }); + + it(`should fail to encode value [max + 1 = 4294967296]`, async () => { + const coder = encoding.v1.u32; + const value = 4294967296; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32 value - value exceeds maximum.', { + value: '4294967296', + type: 'u32', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a u32 [min]', () => { + const coder = encoding.v1.u32; + const expected = 0; + const data = new Uint8Array([0, 0, 0, 0]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(4); + }); + + it(`should decode a u32 [max = 4294967295]`, () => { + const coder = encoding.v1.u32; + const expected = 4294967295; + const data = new Uint8Array([255, 255, 255, 255]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(4); + }); + + it('should throw when decoding invalid u32 data [empty]', async () => { + const coder = encoding.v1.u32; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u32 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u32', + expectedLength: 4, + }) + ); + }); + + it('should throw when decoding invalid u32 data [empty with offset]', async () => { + const coder = encoding.v1.u32; + const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(data, 8), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u32 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u32', + expectedLength: 4, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/u64.test.ts b/packages/abi/test/encoding/v1/u64.test.ts new file mode 100644 index 00000000000..80cb5b407f0 --- /dev/null +++ b/packages/abi/test/encoding/v1/u64.test.ts @@ -0,0 +1,197 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; +import { bn } from '@fuel-ts/math'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('u64', () => { + describe('encode', () => { + it('should encode a u64 [min = 0]', () => { + const coder = encoding.v1.u64; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + const value = 0; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it(`should encode a u64 [max = 18446744073709551615]`, () => { + const coder = encoding.v1.u64; + const expected = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]); + const value = '18446744073709551615'; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a u64 [max safe integer]', () => { + const coder = encoding.v1.u64; + const value: number = Number.MAX_SAFE_INTEGER; + const expected = new Uint8Array([0, 31, 255, 255, 255, 255, 255, 255]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', async () => { + const coder = encoding.v1.u64; + const value: number = Number.MAX_SAFE_INTEGER + 1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u64 type - number value is too large. Number can only safely handle up to 53 bits.' + ) + ); + }); + + it('should encode a u64 [very big number - as string]', () => { + const coder = encoding.v1.u64; + const value: string = '76472027892439376'; + const expected = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); + + const data = coder.encode(value); + + expect(data).toEqual(expected); + }); + + it('should throw an error when encoding [number more than max safe integer]', async () => { + const coder = encoding.v1.u64; + const value: number = 76472027892439376; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u64 type - number value is too large. Number can only safely handle up to 53 bits.' + ) + ); + }); + + it('should fail to encode value [boolean]', async () => { + const coder = encoding.v1.u64; + const value = true; + + await expectToThrowFuelError( + // @ts-expect-error: a boolean value boolean + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - expected a BNInput.', { + value, + }) + ); + }); + + it('should fail to encode value [not a BNInput]', async () => { + const coder = encoding.v1.u64; + const value = 'not a BNInput'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - expected a BNInput.', { + value, + }) + ); + }); + + it('should fail to encode value [min - 1 = -1]', async () => { + const coder = encoding.v1.u64; + const value = -1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u64 value - value is less than zero.', + { + value: '-1', + type: 'u64', + } + ) + ); + }); + + it(`should fail to encode value [max + 1 = 18446744073709551616]`, async () => { + const coder = encoding.v1.u64; + const value = '18446744073709551616'; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - value exceeds maximum.', { + value: '18446744073709551616', + type: 'u64', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a u64 [min]', () => { + const coder = encoding.v1.u64; + const expected = expect.toEqualBn(0); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(8); + }); + + it(`should decode a u64 [max = 18446744073709551615]`, () => { + const coder = encoding.v1.u64; + const expected = expect.toEqualBn('18446744073709551615'); + const data = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(8); + }); + + it('should decode a u64 [very big number]', () => { + const coder = encoding.v1.u64; + const data = new Uint8Array([1, 15, 174, 231, 121, 200, 89, 80]); + const expectedValue = bn('76472027892439376'); + + const [actualValue, actualLength] = coder.decode(data, 0); + + expect(actualValue).toEqualBn(expectedValue); + expect(actualLength).toEqual(8); + }); + + it('should throw when decoding invalid u64 data [empty]', async () => { + const coder = encoding.v1.u64; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u64 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u64', + expectedLength: 8, + }) + ); + }); + + it('should throw when decoding invalid u64 data [empty with offset]', async () => { + const coder = encoding.v1.u64; + const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(data, 8), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u64 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u64', + expectedLength: 8, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/u8.test.ts b/packages/abi/test/encoding/v1/u8.test.ts new file mode 100644 index 00000000000..1a54a1d8f2f --- /dev/null +++ b/packages/abi/test/encoding/v1/u8.test.ts @@ -0,0 +1,110 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('u8', () => { + describe('encode', () => { + it('should encode a u8 [min = 0]', () => { + const coder = encoding.v1.u8; + const expected = new Uint8Array([0]); + const value = 0; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a u8 [max = 255]', () => { + const coder = encoding.v1.u8; + const expected = new Uint8Array([255]); + const value = 255; + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should fail to encode value [-1]', async () => { + const coder = encoding.v1.u8; + const value = -1; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value is less than zero.', { + value: '-1', + type: 'u8', + }) + ); + }); + + it('should fail to encode value [256]', async () => { + const coder = encoding.v1.u8; + const value = 256; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value exceeds maximum.', { + value: '256', + type: 'u8', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a u8 [min]', () => { + const coder = encoding.v1.u8; + const expected = 0; + const data = new Uint8Array([0]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(1); + }); + + it('should decode a u8 [max]', () => { + const coder = encoding.v1.u8; + const expected = 255; + const data = new Uint8Array([255]); + + const [actual, offset] = coder.decode(data); + + expect(actual).toStrictEqual(expected); + expect(offset).toStrictEqual(1); + }); + + it('should throw when decoding invalid u8 data [empty]', async () => { + const coder = encoding.v1.u8; + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u8 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u8', + expectedLength: 1, + }) + ); + }); + + it('should throw when decoding invalid u8 data [empty with offset]', async () => { + const coder = encoding.v1.u8; + const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(data, 8), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u8 data - unexpected length.', { + data: new Uint8Array([]), + type: 'u8', + expectedLength: 1, + }) + ); + }); + }); +}); diff --git a/packages/abi/test/encoding/v1/vector.test.ts b/packages/abi/test/encoding/v1/vector.test.ts new file mode 100644 index 00000000000..2493867eb52 --- /dev/null +++ b/packages/abi/test/encoding/v1/vector.test.ts @@ -0,0 +1,212 @@ +import { FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import type { AbiTypeComponent, CoderFactoryParameters } from '../../../src'; +import { encoding, MAX_BYTES } from '../../../src'; + +const isBrowser = typeof window !== 'undefined'; + +/** + * @group node + * @group browser + */ +describe('vector', () => { + describe('fromAbi', () => { + it('should throw when a component is not provided', async () => { + const swayType = 'struct std::vec::Vec'; + const components: AbiTypeComponent[] | undefined = undefined; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.vector.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided vector type is missing ABI components.', + { swayType, components } + ) + ); + }); + + // NEDIM TODO: decide on if the vector should stay as-is or be modified in ResolvableType + it.skip('should throw when a "buf" component is not provided', async () => { + const swayType = 'struct std::vec::Vec'; + const components: AbiTypeComponent[] = []; + const factory = vi.fn(); + + await expectToThrowFuelError( + () => + encoding.v1.vector.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ), + new FuelError( + FuelError.CODES.CODER_NOT_FOUND, + 'The provided vector type is missing ABI component "buf".', + { swayType, components } + ) + ); + }); + + it('should get the coder for a valid vector type', () => { + const swayType = 'struct std::vec::Vec'; + const components: AbiTypeComponent[] = [ + { + name: 'buf', + type: { + swayType: 'u64', + concreteTypeId: 'some_hash', + }, + }, + ]; + const factory = vi.fn(); + + const coder = encoding.v1.vector.factory( + { type: { swayType, components } } as CoderFactoryParameters, + factory + ); + + expect(coder).toBeDefined(); + }); + }); + + describe('encode', () => { + it('should encode a vector of booleans [true, false, true, false, true, true]', () => { + const coder = encoding.v1.vector(encoding.v1.bool); + const value = [true, false, true, false, true, true]; + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 1, 0, 1, 1]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should encode a vector of numbers [8, 6, 7]', () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const value = Uint8Array.from([8, 6, 7]); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 8, 6, 7]); + + const actual = coder.encode(value); + + expect(actual).toStrictEqual(expected); + }); + + it('should throw when encoding non array input', async () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const value = 'Nope'; + + await expectToThrowFuelError( + // @ts-expect-error - testing invalid input + () => coder.encode(value), + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid vector value - expected array value, or a Uint8Array.', + { value } + ) + ); + }); + + it('should throw when encoding a vector with a value that cannot be encoded', async () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const value = [256]; + + await expectToThrowFuelError( + () => coder.encode(value), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value exceeds maximum.', { + type: 'u8', + value: '256', + }) + ); + }); + }); + + describe('decode', () => { + it('should decode a vector of booleans [true, false, true, false, true, true]', () => { + const coder = encoding.v1.vector(encoding.v1.bool); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 1, 0, 1, 1]); + const expected = [true, false, true, false, true, true]; + + const [actual, newOffset] = coder.decode(data, 0); + + expect(actual).toStrictEqual(expected); + expect(newOffset).toEqual(14); + }); + + it('should decode a vector of numbers [8, 6, 7]', () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 3, 8, 6, 7]); + const expected = [8, 6, 7]; + + const [actual, newOffset] = coder.decode(data, 0); + + expect(actual).toStrictEqual(expected); + expect(newOffset).toEqual(11); + }); + + it('should decode a vector of numbers [empty]', () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + const expected: number[] = []; + + const [actual, newOffset] = coder.decode(data, 0); + + expect(actual).toStrictEqual(expected); + expect(newOffset).toEqual(8); + }); + + it('should throw when decoding empty vector', async () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const data = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid vector data - malformed bytes.', { + data, + }) + ); + }); + + it('should throw when decoding vector with element data missing', async () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 2, 1]); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError(FuelError.CODES.DECODE_ERROR, 'Invalid u8 data - unexpected length.', { + data: new Uint8Array([]), + expectedLength: 1, + type: 'u8', + }) + ); + }); + + /** + * TODO: this test is failing on browser, need to investigate why. + * + * RangeError: Array buffer allocation failed + */ + it.skipIf(isBrowser)( + 'should throw when decoding an array over the max vec size [VM constraints]', + async () => { + const coder = encoding.v1.vector(encoding.v1.u8); + const data = new Uint8Array(MAX_BYTES + 1); + + await expectToThrowFuelError( + () => coder.decode(data, 0), + new FuelError( + FuelError.CODES.DECODE_ERROR, + 'Invalid vector data - exceeds maximum bytes.', + { + data, + length: data.length, + maxLength: MAX_BYTES, + } + ) + ); + } + ); + }); +}); diff --git a/packages/abi/test/encoding/v1/void.test.ts b/packages/abi/test/encoding/v1/void.test.ts new file mode 100644 index 00000000000..8f82ef1a1ce --- /dev/null +++ b/packages/abi/test/encoding/v1/void.test.ts @@ -0,0 +1,32 @@ +import { encoding } from '../../../src'; + +/** + * @group node + * @group browser + */ +describe('void', () => { + describe('encode', () => { + it('should encode a void', () => { + const coder = encoding.v1.void; + const value = undefined; + const expected = new Uint8Array([]); + + const actual = coder.encode(value); + + expect(actual).toEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode a void', () => { + const coder = encoding.v1.void; + const data = new Uint8Array([]); + const expected = undefined; + + const [actual, offset] = coder.decode(data, 0); + + expect(actual).toEqual(expected); + expect(offset).toEqual(0); + }); + }); +}); diff --git a/packages/abi/test/fixtures/v1.ts b/packages/abi/test/fixtures/v1.ts new file mode 100644 index 00000000000..5b1c6e0da21 --- /dev/null +++ b/packages/abi/test/fixtures/v1.ts @@ -0,0 +1,2836 @@ +import type { AbiSpecificationV1 } from '../../src'; + +export const v1: AbiSpecificationV1 = { + programType: 'contract', + specVersion: '1', + encodingVersion: '1', + concreteTypes: [ + { + type: '()', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + type: '(b256, bool)', + concreteTypeId: 'd5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640', + metadataTypeId: 0, + }, + { + type: '(bool, u64)', + concreteTypeId: 'c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be', + metadataTypeId: 8, + }, + { + type: '(str[5], bool)', + concreteTypeId: 'a1e229302ed2f092752a6bc4fbe66bb9305e0802b1b01ecc5e1d59356702e956', + metadataTypeId: 1, + }, + { + type: '(str[5], str[5])', + concreteTypeId: '30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e', + metadataTypeId: 2, + }, + { + type: '(struct data_structures::StructDoubleGeneric<[b256; 3],u8>, [struct data_structures::StructDoubleGeneric; 4], (str[5], bool), struct data_structures::StructSimple)', + concreteTypeId: '343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e', + metadataTypeId: 13, + }, + { + type: '(struct data_structures::StructSimple, struct std::vec::Vec)', + concreteTypeId: '5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c', + metadataTypeId: 3, + }, + { + type: '(struct std::asset_id::AssetId, struct std::asset_id::AssetId, bool)', + concreteTypeId: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + metadataTypeId: 10, + }, + { + type: '(struct std::vec::Vec, b256)', + concreteTypeId: '52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94', + metadataTypeId: 5, + }, + { + type: '(struct std::vec::Vec, struct std::vec::Vec)', + concreteTypeId: '87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd', + metadataTypeId: 6, + }, + { + type: '(u32, struct std::vec::Vec, struct std::vec::Vec)', + concreteTypeId: '18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000', + metadataTypeId: 9, + }, + { + type: '(u64, struct data_structures::StructSimple)', + concreteTypeId: '0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc', + metadataTypeId: 4, + }, + { + type: '(u8, struct data_structures::StructSingleGeneric>, str[3])', + concreteTypeId: '6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f', + metadataTypeId: 11, + }, + { + type: '(u8, u8, u8)', + concreteTypeId: '79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4', + metadataTypeId: 12, + }, + { + type: '[b256; 3]', + concreteTypeId: '81342782c917fcfd178741cb2b3a12ea1ebeaa57253fc4ee6700b4d7d6ab32d3', + metadataTypeId: 17, + }, + { + type: '[struct data_structures::StructDoubleGeneric,str[1]>; 2]', + concreteTypeId: 'b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006', + metadataTypeId: 15, + }, + { + type: '[struct data_structures::StructDoubleGeneric; 4]', + concreteTypeId: 'b22807669faa58263e636f6e2d194df8ddbc6686bb4ea14ee28005fa30adbe85', + metadataTypeId: 22, + }, + { + type: '[struct data_structures::StructSimple; 3]', + concreteTypeId: '38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237', + metadataTypeId: 18, + }, + { + type: '[struct std::vec::Vec; 1]', + concreteTypeId: '593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980', + metadataTypeId: 14, + }, + { + type: '[u8; 4]', + concreteTypeId: 'f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4', + metadataTypeId: 23, + }, + { + type: 'b256', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + type: 'bool', + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + type: 'enum abi-library::ExternalEnum', + concreteTypeId: '9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42', + metadataTypeId: 25, + }, + { + type: 'enum data_structures::EnumDoubleGeneric', + concreteTypeId: 'd0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307', + metadataTypeId: 26, + typeArguments: [ + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + ], + }, + { + type: 'enum data_structures::EnumWithBuiltinType', + concreteTypeId: '2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61', + metadataTypeId: 27, + }, + { + type: 'enum data_structures::EnumWithNative', + concreteTypeId: '58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93', + metadataTypeId: 28, + }, + { + type: 'enum data_structures::EnumWithStructs', + concreteTypeId: '9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669', + metadataTypeId: 29, + }, + { + type: 'enum data_structures::EnumWithVector', + concreteTypeId: '0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26', + metadataTypeId: 30, + }, + { + type: 'enum std::identity::Identity', + concreteTypeId: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + metadataTypeId: 31, + }, + { + type: 'enum std::option::Option', + concreteTypeId: '25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e', + metadataTypeId: 32, + typeArguments: ['ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1'], + }, + { + type: 'enum std::option::Option', + concreteTypeId: '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + metadataTypeId: 32, + typeArguments: ['c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b'], + }, + { + type: 'enum std::result::Result', + concreteTypeId: '9891b1ee451eed790368ea3969e3c8f550efa87de489b5d7b933e2290800791b', + metadataTypeId: 33, + typeArguments: [ + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + '338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50', + ], + }, + { + type: 'enum std::result::Result', + concreteTypeId: 'b3131b4c08c16cfa55b3150d587c3afa3e4cdebe0399f3f599fa160baaa64e0c', + metadataTypeId: 33, + typeArguments: [ + '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + ], + }, + { + type: 'raw untyped slice', + concreteTypeId: '1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570', + }, + { + type: 'str', + concreteTypeId: '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + }, + { + type: 'str[10]', + concreteTypeId: '338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50', + }, + { + type: 'str[5]', + concreteTypeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + { + type: 'struct abi-library::ExternalStruct', + concreteTypeId: 'c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978', + metadataTypeId: 45, + }, + { + type: 'struct data_structures::Configurables', + concreteTypeId: '69d4f1cc5ce793681d98a55ab013f42ab56260131d39af6c1e71a5f3531557bc', + metadataTypeId: 46, + }, + { + type: 'struct data_structures::StructA', + concreteTypeId: 'db8b04f624965fbfd7eb7dc3fc3c6a54a71d0019b37d4011a9350d1870136c9d', + metadataTypeId: 47, + }, + { + type: 'struct data_structures::StructB', + concreteTypeId: '9f074fde9cb9194b90bd208c8c95e709bfb1a5c736b063302e5639ce4daad5aa', + metadataTypeId: 48, + }, + { + type: 'struct data_structures::StructC', + concreteTypeId: 'f219acbc9e3b812457419966b5454d10d51594afecacb87fb7745c9311b90012', + metadataTypeId: 49, + }, + { + type: 'struct data_structures::StructD>>', + concreteTypeId: 'd0494e36b8daeafdf02dfbd1f65f82c66df872fb235c7fd2707fcd4147c6c292', + metadataTypeId: 50, + typeArguments: [ + 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + '722eb56989dc44c372c470eb3a6ddb2f91e3924c1c4a0806d21e414046599d35', + ], + }, + { + type: 'struct data_structures::StructDoubleGeneric<[b256; 3],u8>', + concreteTypeId: '7bdc2c1e9c4b8576fdf5be24c5c6569cba3a8feaba3755ed2b95d646a33c73e2', + metadataTypeId: 51, + typeArguments: [ + '81342782c917fcfd178741cb2b3a12ea1ebeaa57253fc4ee6700b4d7d6ab32d3', + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + ], + }, + { + type: 'struct data_structures::StructDoubleGeneric,u32>', + concreteTypeId: '08dbec793087c5686c1a493513b158a999bb653126ee51151dfa85fa683edce5', + metadataTypeId: 51, + typeArguments: [ + '4946973fc1adce1f6b23e80f9fad29b44e6a4ab25f2b45f3fab95114cfcd33a0', + 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + ], + }, + { + type: 'struct data_structures::StructDoubleGeneric', + concreteTypeId: '4946973fc1adce1f6b23e80f9fad29b44e6a4ab25f2b45f3fab95114cfcd33a0', + metadataTypeId: 51, + typeArguments: [ + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + ], + }, + { + type: 'struct data_structures::StructF>', + concreteTypeId: '722eb56989dc44c372c470eb3a6ddb2f91e3924c1c4a0806d21e414046599d35', + metadataTypeId: 53, + typeArguments: ['49f761c61dce644e212b8182e30557d35b6b4ad46693140be677eee0d6ef2733'], + }, + { + type: 'struct data_structures::StructG', + concreteTypeId: 'dfd8875bb49716b14dd336285ba667f953ed9aec4e918c0d7a2eb19ff644d60e', + metadataTypeId: 54, + }, + { + type: 'struct data_structures::StructGenericWithEnum', + concreteTypeId: '8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2', + metadataTypeId: 55, + typeArguments: [ + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + ], + }, + { + type: 'struct data_structures::StructSimple', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + metadataTypeId: 56, + }, + { + type: 'struct data_structures::StructSingleGeneric<(bool, u64)>', + concreteTypeId: 'fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698', + metadataTypeId: 57, + typeArguments: ['c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be'], + }, + { + type: 'struct data_structures::StructSingleGeneric', + concreteTypeId: '7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6', + metadataTypeId: 57, + typeArguments: ['c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b'], + }, + { + type: 'struct data_structures::StructWithEnumArray', + concreteTypeId: 'd5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e', + metadataTypeId: 58, + }, + { + type: 'struct data_structures::StructWithGenericArray', + concreteTypeId: '29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad', + metadataTypeId: 59, + typeArguments: ['7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b'], + }, + { + type: 'struct data_structures::StructWithImplicitGenerics', + concreteTypeId: '549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60', + metadataTypeId: 60, + typeArguments: [ + '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + ], + }, + { + type: 'struct data_structures::StructWithMultiOption', + concreteTypeId: 'aa87500bb34c8bb09ffd60ab55cb1725898c366c58d3ff3aaaf8c9b532934fd1', + metadataTypeId: 61, + }, + { + type: 'struct data_structures::StructWithNestedArray', + concreteTypeId: 'e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948', + metadataTypeId: 62, + }, + { + type: 'struct data_structures::StructWithNestedStruct', + concreteTypeId: '8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a', + metadataTypeId: 63, + }, + { + type: 'struct data_structures::StructWithNestedTuple', + concreteTypeId: 'd042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5', + metadataTypeId: 64, + }, + { + type: 'struct data_structures::StructWithSingleOption', + concreteTypeId: '089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c', + metadataTypeId: 65, + }, + { + type: 'struct data_structures::StructWithVector', + concreteTypeId: 'eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621', + metadataTypeId: 66, + }, + { + type: 'struct std::address::Address', + concreteTypeId: 'f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308', + metadataTypeId: 67, + }, + { + type: 'struct std::asset_id::AssetId', + concreteTypeId: 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + metadataTypeId: 68, + }, + { + type: 'struct std::b512::B512', + concreteTypeId: '745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322', + metadataTypeId: 69, + }, + { + type: 'struct std::bytes::Bytes', + concreteTypeId: 'cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb', + metadataTypeId: 70, + }, + { + type: 'struct std::contract_id::ContractId', + concreteTypeId: '29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54', + metadataTypeId: 72, + }, + { + type: 'struct std::string::String', + concreteTypeId: '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + metadataTypeId: 73, + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: '6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31', + metadataTypeId: 75, + typeArguments: ['b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: '49f761c61dce644e212b8182e30557d35b6b4ad46693140be677eee0d6ef2733', + metadataTypeId: 75, + typeArguments: ['dfd8875bb49716b14dd336285ba667f953ed9aec4e918c0d7a2eb19ff644d60e'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: '9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468', + metadataTypeId: 75, + typeArguments: ['ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: 'c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d', + metadataTypeId: 75, + typeArguments: ['aa87500bb34c8bb09ffd60ab55cb1725898c366c58d3ff3aaaf8c9b532934fd1'], + }, + { + type: 'struct std::vec::Vec>', + concreteTypeId: 'e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e', + metadataTypeId: 75, + typeArguments: ['13c38f4111bad6468fad4f8ea82fd744546b63be49db9439fb3d94e14ae2bb3a'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: '13c38f4111bad6468fad4f8ea82fd744546b63be49db9439fb3d94e14ae2bb3a', + metadataTypeId: 75, + typeArguments: ['d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: 'd5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4', + metadataTypeId: 75, + typeArguments: ['1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0'], + }, + { + type: 'struct std::vec::Vec', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + metadataTypeId: 75, + typeArguments: ['c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b'], + }, + { + type: 'struct std::vm::evm::evm_address::EvmAddress', + concreteTypeId: '05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2', + metadataTypeId: 76, + }, + { + type: 'u16', + concreteTypeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + { + type: 'u256', + concreteTypeId: '1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e', + }, + { + type: 'u32', + concreteTypeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + { + type: 'u64', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + type: 'u8', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + metadataTypes: [ + { + type: '(_, _)', + metadataTypeId: 0, + components: [ + { + name: '__tuple_element', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + name: '__tuple_element', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 1, + components: [ + { + name: '__tuple_element', + typeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + { + name: '__tuple_element', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 2, + components: [ + { + name: '__tuple_element', + typeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + { + name: '__tuple_element', + typeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 3, + components: [ + { + name: '__tuple_element', + typeId: 56, + }, + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 4, + components: [ + { + name: '__tuple_element', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: '__tuple_element', + typeId: 56, + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 5, + components: [ + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + name: '__tuple_element', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 6, + components: [ + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 7, + components: [ + { + name: '__tuple_element', + typeId: 34, + }, + { + name: '__tuple_element', + typeId: 35, + }, + ], + }, + { + type: '(_, _)', + metadataTypeId: 8, + components: [ + { + name: '__tuple_element', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + name: '__tuple_element', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: '(_, _, _)', + metadataTypeId: 9, + components: [ + { + name: '__tuple_element', + typeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + name: '__tuple_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + ], + }, + { + type: '(_, _, _)', + metadataTypeId: 10, + components: [ + { + name: '__tuple_element', + typeId: 68, + }, + { + name: '__tuple_element', + typeId: 68, + }, + { + name: '__tuple_element', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + }, + { + type: '(_, _, _)', + metadataTypeId: 11, + components: [ + { + name: '__tuple_element', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '__tuple_element', + typeId: 57, + typeArguments: [ + { + name: '', + typeId: 57, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + ], + }, + { + name: '__tuple_element', + typeId: 44, + }, + ], + }, + { + type: '(_, _, _)', + metadataTypeId: 12, + components: [ + { + name: '__tuple_element', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '__tuple_element', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '__tuple_element', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + type: '(_, _, _, _)', + metadataTypeId: 13, + components: [ + { + name: '__tuple_element', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 17, + }, + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + name: '__tuple_element', + typeId: 22, + }, + { + name: '__tuple_element', + typeId: 1, + }, + { + name: '__tuple_element', + typeId: 56, + }, + ], + }, + { + type: '[_; 1]', + metadataTypeId: 14, + components: [ + { + name: '__array_element', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + ], + }, + ], + }, + { + type: '[_; 2]', + metadataTypeId: 15, + components: [ + { + name: '__array_element', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 57, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + name: '', + typeId: 43, + }, + ], + }, + ], + }, + { + type: '[_; 2]', + metadataTypeId: 16, + components: [ + { + name: '__array_element', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: '[_; 3]', + metadataTypeId: 17, + components: [ + { + name: '__array_element', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: '[_; 3]', + metadataTypeId: 18, + components: [ + { + name: '__array_element', + typeId: 56, + }, + ], + }, + { + type: '[_; 3]', + metadataTypeId: 19, + components: [ + { + name: '__array_element', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 36, + }, + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: '[_; 3]', + metadataTypeId: 20, + components: [ + { + name: '__array_element', + typeId: 28, + }, + ], + }, + { + type: '[_; 3]', + metadataTypeId: 21, + components: [ + { + name: '__array_element', + typeId: 34, + }, + ], + }, + { + type: '[_; 4]', + metadataTypeId: 22, + components: [ + { + name: '__array_element', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: '', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + }, + ], + }, + { + type: '[_; 4]', + metadataTypeId: 23, + components: [ + { + name: '__array_element', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + type: '[_; 5]', + metadataTypeId: 24, + components: [ + { + name: '__array_element', + typeId: 32, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: 'enum abi-library::ExternalEnum', + metadataTypeId: 25, + components: [ + { + name: 'A', + typeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'B', + typeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + }, + { + type: 'enum data_structures::EnumDoubleGeneric', + metadataTypeId: 26, + components: [ + { + name: 'a', + typeId: 38, + }, + { + name: 'b', + typeId: 39, + }, + ], + typeParameters: [38, 39], + }, + { + type: 'enum data_structures::EnumWithBuiltinType', + metadataTypeId: 27, + components: [ + { + name: 'a', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + name: 'b', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'enum data_structures::EnumWithNative', + metadataTypeId: 28, + components: [ + { + name: 'Checked', + typeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'Pending', + typeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + }, + { + type: 'enum data_structures::EnumWithStructs', + metadataTypeId: 29, + components: [ + { + name: 'a', + typeId: 28, + }, + { + name: 'b', + typeId: 56, + }, + { + name: 'c', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: '', + typeId: 56, + }, + ], + }, + ], + }, + { + type: 'enum data_structures::EnumWithVector', + metadataTypeId: 30, + components: [ + { + name: 'a', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'b', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: 'enum std::identity::Identity', + metadataTypeId: 31, + components: [ + { + name: 'Address', + typeId: 67, + }, + { + name: 'ContractId', + typeId: 72, + }, + ], + }, + { + type: 'enum std::option::Option', + metadataTypeId: 32, + components: [ + { + name: 'None', + typeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'Some', + typeId: 37, + }, + ], + typeParameters: [37], + }, + { + type: 'enum std::result::Result', + metadataTypeId: 33, + components: [ + { + name: 'Ok', + typeId: 37, + }, + { + name: 'Err', + typeId: 34, + }, + ], + typeParameters: [37, 34], + }, + { + type: 'generic E', + metadataTypeId: 34, + }, + { + type: 'generic F', + metadataTypeId: 35, + }, + { + type: 'generic K', + metadataTypeId: 36, + }, + { + type: 'generic T', + metadataTypeId: 37, + }, + { + type: 'generic T1', + metadataTypeId: 38, + }, + { + type: 'generic T2', + metadataTypeId: 39, + }, + { + type: 'generic U', + metadataTypeId: 40, + }, + { + type: 'generic V', + metadataTypeId: 41, + }, + { + type: 'raw untyped ptr', + metadataTypeId: 42, + }, + { + type: 'str[1]', + metadataTypeId: 43, + }, + { + type: 'str[3]', + metadataTypeId: 44, + }, + { + type: 'struct abi-library::ExternalStruct', + metadataTypeId: 45, + components: [ + { + name: 'value', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'struct data_structures::Configurables', + metadataTypeId: 46, + components: [ + { + name: 'U8_VALUE', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'BOOL_VALUE', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + name: 'B256_VALUE', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + name: 'OPTION_U8_VALUE', + typeId: 32, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + name: 'GENERIC_STRUCT_VALUE', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '', + typeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + ], + }, + { + name: '', + typeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + ], + }, + ], + }, + { + type: 'struct data_structures::StructA', + metadataTypeId: 47, + components: [ + { + name: 'propA1', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + type: 'struct data_structures::StructB', + metadataTypeId: 48, + components: [ + { + name: 'propB1', + typeId: 47, + }, + { + name: 'propB2', + typeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + ], + }, + { + type: 'struct data_structures::StructC', + metadataTypeId: 49, + components: [ + { + name: 'propC1', + typeId: 47, + }, + { + name: 'propC2', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 48, + }, + ], + }, + { + name: 'propC3', + typeId: 50, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: '', + typeId: 53, + typeArguments: [ + { + name: '', + typeId: 43, + }, + ], + }, + ], + }, + ], + }, + { + type: 'struct data_structures::StructD', + metadataTypeId: 50, + components: [ + { + name: 'propD1', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 52, + typeArguments: [ + { + name: '', + typeId: 37, + }, + ], + }, + ], + }, + { + name: 'propD2', + typeId: 40, + }, + { + name: 'propD3', + typeId: 41, + }, + ], + typeParameters: [37, 40, 41], + }, + { + type: 'struct data_structures::StructDoubleGeneric', + metadataTypeId: 51, + components: [ + { + name: 'a', + typeId: 38, + }, + { + name: 'b', + typeId: 39, + }, + ], + typeParameters: [38, 39], + }, + { + type: 'struct data_structures::StructE', + metadataTypeId: 52, + components: [ + { + name: 'propE1', + typeId: 47, + }, + { + name: 'propE2', + typeId: 48, + }, + { + name: 'propE3', + typeId: 37, + }, + ], + typeParameters: [37], + }, + { + type: 'struct data_structures::StructF', + metadataTypeId: 53, + components: [ + { + name: 'propF1', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: 'propF2', + typeId: 37, + }, + ], + typeParameters: [37], + }, + { + type: 'struct data_structures::StructG', + metadataTypeId: 54, + components: [ + { + name: 'propG1', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + type: 'struct data_structures::StructGenericWithEnum', + metadataTypeId: 55, + components: [ + { + name: 'a', + typeId: 38, + }, + { + name: 'b', + typeId: 26, + typeArguments: [ + { + name: '', + typeId: 38, + }, + { + name: '', + typeId: 39, + }, + ], + }, + ], + typeParameters: [38, 39], + }, + { + type: 'struct data_structures::StructSimple', + metadataTypeId: 56, + components: [ + { + name: 'a', + typeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + name: 'b', + typeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + ], + }, + { + type: 'struct data_structures::StructSingleGeneric', + metadataTypeId: 57, + components: [ + { + name: 'a', + typeId: 37, + }, + ], + typeParameters: [37], + }, + { + type: 'struct data_structures::StructWithEnumArray', + metadataTypeId: 58, + components: [ + { + name: 'a', + typeId: 20, + }, + ], + }, + { + type: 'struct data_structures::StructWithGenericArray', + metadataTypeId: 59, + components: [ + { + name: 'a', + typeId: 19, + }, + ], + typeParameters: [36], + }, + { + type: 'struct data_structures::StructWithImplicitGenerics', + metadataTypeId: 60, + components: [ + { + name: 'a', + typeId: 21, + }, + { + name: 'b', + typeId: 7, + }, + ], + typeParameters: [34, 35], + }, + { + type: 'struct data_structures::StructWithMultiOption', + metadataTypeId: 61, + components: [ + { + name: 'a', + typeId: 24, + }, + ], + }, + { + type: 'struct data_structures::StructWithNestedArray', + metadataTypeId: 62, + components: [ + { + name: 'a', + typeId: 15, + }, + ], + }, + { + type: 'struct data_structures::StructWithNestedStruct', + metadataTypeId: 63, + components: [ + { + name: 'a', + typeId: 51, + typeArguments: [ + { + name: '', + typeId: 57, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + { + name: '', + typeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + ], + }, + ], + }, + { + type: 'struct data_structures::StructWithNestedTuple', + metadataTypeId: 64, + components: [ + { + name: 'a', + typeId: 11, + }, + ], + }, + { + type: 'struct data_structures::StructWithSingleOption', + metadataTypeId: 65, + components: [ + { + name: 'a', + typeId: 32, + typeArguments: [ + { + name: '', + typeId: 61, + }, + ], + }, + ], + }, + { + type: 'struct data_structures::StructWithVector', + metadataTypeId: 66, + components: [ + { + name: 'a', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'b', + typeId: 75, + typeArguments: [ + { + name: '', + typeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + }, + ], + }, + { + type: 'struct std::address::Address', + metadataTypeId: 67, + components: [ + { + name: 'bits', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::asset_id::AssetId', + metadataTypeId: 68, + components: [ + { + name: 'bits', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::b512::B512', + metadataTypeId: 69, + components: [ + { + name: 'bits', + typeId: 16, + }, + ], + }, + { + type: 'struct std::bytes::Bytes', + metadataTypeId: 70, + components: [ + { + name: 'buf', + typeId: 71, + }, + { + name: 'len', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'struct std::bytes::RawBytes', + metadataTypeId: 71, + components: [ + { + name: 'ptr', + typeId: 42, + }, + { + name: 'cap', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + }, + { + type: 'struct std::contract_id::ContractId', + metadataTypeId: 72, + components: [ + { + name: 'bits', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + { + type: 'struct std::string::String', + metadataTypeId: 73, + components: [ + { + name: 'bytes', + typeId: 70, + }, + ], + }, + { + type: 'struct std::vec::RawVec', + metadataTypeId: 74, + components: [ + { + name: 'ptr', + typeId: 42, + }, + { + name: 'cap', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + typeParameters: [37], + }, + { + type: 'struct std::vec::Vec', + metadataTypeId: 75, + components: [ + { + name: 'buf', + typeId: 74, + typeArguments: [ + { + name: '', + typeId: 37, + }, + ], + }, + { + name: 'len', + typeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + typeParameters: [37], + }, + { + type: 'struct std::vm::evm::evm_address::EvmAddress', + metadataTypeId: 76, + components: [ + { + name: 'bits', + typeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + }, + ], + functions: [ + { + inputs: [], + name: 'attributes_doc_comment', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'doc-comment', + arguments: [' This is a doc'], + }, + { + name: 'doc-comment', + arguments: [' This is another doc comment'], + }, + ], + }, + { + inputs: [], + name: 'attributes_inline_always', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'inline', + arguments: ['always'], + }, + ], + }, + { + inputs: [], + name: 'attributes_inline_never', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'inline', + arguments: ['never'], + }, + ], + }, + { + inputs: [], + name: 'attributes_none', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + { + inputs: [], + name: 'attributes_payable', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'payable', + arguments: [], + }, + ], + }, + { + inputs: [], + name: 'attributes_storage_read', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'storage', + arguments: ['read'], + }, + ], + }, + { + inputs: [], + name: 'attributes_storage_read_write', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'storage', + arguments: ['read', 'write'], + }, + ], + }, + { + inputs: [], + name: 'attributes_storage_write', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'storage', + arguments: ['write'], + }, + ], + }, + { + inputs: [], + name: 'attributes_test', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: [ + { + name: 'test', + arguments: [], + }, + ], + }, + { + inputs: [], + name: 'configurables', + output: '69d4f1cc5ce793681d98a55ab013f42ab56260131d39af6c1e71a5f3531557bc', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + name: 'y', + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + name: 'multi_arg_b256_bool', + output: 'd5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '7bdc2c1e9c4b8576fdf5be24c5c6569cba3a8feaba3755ed2b95d646a33c73e2', + }, + { + name: 'y', + concreteTypeId: 'b22807669faa58263e636f6e2d194df8ddbc6686bb4ea14ee28005fa30adbe85', + }, + { + name: 'z', + concreteTypeId: 'a1e229302ed2f092752a6bc4fbe66bb9305e0802b1b01ecc5e1d59356702e956', + }, + { + name: 'a', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + }, + ], + name: 'multi_arg_complex', + output: '343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + { + name: 'y', + concreteTypeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + ], + name: 'multi_arg_str_str', + output: '30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + }, + { + name: 'y', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + ], + name: 'multi_arg_struct_vector', + output: '5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + { + name: 'y', + concreteTypeId: 'd5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4', + }, + { + name: 'z', + concreteTypeId: 'd5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4', + }, + ], + name: 'multi_arg_u32_vector_vector', + output: '18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: 'y', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + }, + ], + name: 'multi_arg_u64_struct', + output: '0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + name: 'y', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + name: 'multi_arg_u64_u64', + output: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + { + name: 'y', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + name: 'multi_arg_vector_b256', + output: '52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + { + name: 'y', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + ], + name: 'multi_arg_vector_vector', + output: '87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308', + }, + ], + name: 'types_address', + output: 'f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + }, + ], + name: 'types_alias_tuple_with_native_types', + output: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4', + }, + ], + name: 'types_array', + output: 'f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237', + }, + ], + name: 'types_array_struct', + output: '38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006', + }, + ], + name: 'types_array_with_generic_struct', + output: 'b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980', + }, + ], + name: 'types_array_with_vector', + output: '593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + ], + name: 'types_asset_id', + output: 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + ], + name: 'types_b256', + output: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322', + }, + ], + name: 'types_b512', + output: '745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + ], + name: 'types_bool', + output: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb', + }, + ], + name: 'types_bytes', + output: 'cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54', + }, + ], + name: 'types_contract_id', + output: '29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93', + }, + ], + name: 'types_enum', + output: '58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42', + }, + ], + name: 'types_enum_external', + output: '9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61', + }, + ], + name: 'types_enum_with_builtin_type', + output: '2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669', + }, + ], + name: 'types_enum_with_structs', + output: '9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26', + }, + ], + name: 'types_enum_with_vector', + output: '0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2', + }, + ], + name: 'types_evm_address', + output: '05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307', + }, + ], + name: 'types_generic_enum', + output: 'd0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + }, + ], + name: 'types_identity_address', + output: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + }, + ], + name: 'types_identity_contract_id', + output: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + }, + ], + name: 'types_option', + output: '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e', + }, + ], + name: 'types_option_struct', + output: '25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570', + }, + ], + name: 'types_raw_slice', + output: '1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'b3131b4c08c16cfa55b3150d587c3afa3e4cdebe0399f3f599fa160baaa64e0c', + }, + ], + name: 'types_result', + output: '9891b1ee451eed790368ea3969e3c8f550efa87de489b5d7b933e2290800791b', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + }, + ], + name: 'types_std_string', + output: '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + ], + name: 'types_str', + output: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + }, + ], + name: 'types_str_slice', + output: '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2', + }, + ], + name: 'types_struct_double_generic', + output: '8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978', + }, + ], + name: 'types_struct_external', + output: 'c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6', + }, + ], + name: 'types_struct_generic', + output: '7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + }, + ], + name: 'types_struct_simple', + output: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad', + }, + ], + name: 'types_struct_with_array', + output: '29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e', + }, + ], + name: 'types_struct_with_array_of_enums', + output: 'd5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd0494e36b8daeafdf02dfbd1f65f82c66df872fb235c7fd2707fcd4147c6c292', + }, + ], + name: 'types_struct_with_complex_nested_struct', + output: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60', + }, + ], + name: 'types_struct_with_implicit_generics', + output: '549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'db8b04f624965fbfd7eb7dc3fc3c6a54a71d0019b37d4011a9350d1870136c9d', + }, + { + name: 'y', + concreteTypeId: '9f074fde9cb9194b90bd208c8c95e709bfb1a5c736b063302e5639ce4daad5aa', + }, + { + name: 'z', + concreteTypeId: 'f219acbc9e3b812457419966b5454d10d51594afecacb87fb7745c9311b90012', + }, + ], + name: 'types_struct_with_multiple_struct_params', + output: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948', + }, + ], + name: 'types_struct_with_nested_array', + output: 'e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a', + }, + ], + name: 'types_struct_with_nested_struct', + output: '8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5', + }, + ], + name: 'types_struct_with_nested_tuple', + output: 'd042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c', + }, + ], + name: 'types_struct_with_single_option', + output: '089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698', + }, + ], + name: 'types_struct_with_tuple', + output: 'fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621', + }, + ], + name: 'types_struct_with_vector', + output: 'eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4', + }, + ], + name: 'types_tuple', + output: '79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f', + }, + ], + name: 'types_tuple_complex', + output: '6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + }, + ], + name: 'types_tuple_with_native_types', + output: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + ], + name: 'types_u16', + output: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e', + }, + ], + name: 'types_u256', + output: '1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + ], + name: 'types_u32', + output: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + ], + name: 'types_u64', + output: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + name: 'types_u8', + output: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'y', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'z', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'a', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + name: 'types_value_then_value_then_void_then_void', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'y', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + name: 'types_value_then_void', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + name: 'y', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'z', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + name: 'types_value_then_void_then_value', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31', + }, + ], + name: 'types_vector_boolean', + output: '6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e', + }, + ], + name: 'types_vector_inside_vector', + output: 'e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: 'c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d', + }, + ], + name: 'types_vector_option', + output: 'c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + ], + name: 'types_vector_u8', + output: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468', + }, + ], + name: 'types_vector_with_struct', + output: '9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + name: 'types_void', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + { + inputs: [ + { + name: 'x', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + { + name: 'y', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + ], + name: 'types_void_then_value', + output: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + attributes: null, + }, + ], + loggedTypes: [ + { + logId: '8961848586872524460', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + }, + { + logId: '13213829929622723620', + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + }, + { + logId: '15417698811679754926', + concreteTypeId: 'd5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640', + }, + { + logId: '3764736462721235256', + concreteTypeId: '343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e', + }, + { + logId: '10098701174489624218', + concreteTypeId: '8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a', + }, + { + logId: '3459380067145079360', + concreteTypeId: '30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e', + }, + { + logId: '6826186604658872822', + concreteTypeId: '5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c', + }, + { + logId: '1730312528330453470', + concreteTypeId: '18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000', + }, + { + logId: '38494492241415594', + concreteTypeId: '0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc', + }, + { + logId: '1515152261580153489', + concreteTypeId: '1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0', + }, + { + logId: '5972461853438630448', + concreteTypeId: '52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94', + }, + { + logId: '9774045287303884155', + concreteTypeId: '87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd', + }, + { + logId: '17696813611398264200', + concreteTypeId: 'f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308', + }, + { + logId: '12204227005198389688', + concreteTypeId: 'a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6', + }, + { + logId: '17477056209248181856', + concreteTypeId: 'f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4', + }, + { + logId: '4103440364041737946', + concreteTypeId: '38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237', + }, + { + logId: '13264875749739450689', + concreteTypeId: 'b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006', + }, + { + logId: '6429795790595785041', + concreteTypeId: '593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980', + }, + { + logId: '13866877265493744985', + concreteTypeId: 'c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974', + }, + { + logId: '8385180437869151632', + concreteTypeId: '745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322', + }, + { + logId: '14832741149864513620', + concreteTypeId: 'cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb', + }, + { + logId: '3008693953818743129', + concreteTypeId: '29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54', + }, + { + logId: '6390060985836259181', + concreteTypeId: '58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93', + }, + { + logId: '11107063318498994310', + concreteTypeId: '9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42', + }, + { + logId: '2393365693554672043', + concreteTypeId: '2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61', + }, + { + logId: '11445580549060683374', + concreteTypeId: '9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669', + }, + { + logId: '176438282157896578', + concreteTypeId: '0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26', + }, + { + logId: '406535131101199095', + concreteTypeId: '05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2', + }, + { + logId: '15054851639520017915', + concreteTypeId: 'd0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307', + }, + { + logId: '12356980511120185571', + concreteTypeId: 'ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335', + }, + { + logId: '3287912245613454270', + concreteTypeId: '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + }, + { + logId: '2693553771067460161', + concreteTypeId: '25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e', + }, + { + logId: '2169745815365986704', + concreteTypeId: '1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570', + }, + { + logId: '11132648958528852192', + concreteTypeId: '9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c', + }, + { + logId: '9549741647838268318', + concreteTypeId: '84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7', + }, + { + logId: '9909809838135789262', + concreteTypeId: '8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2', + }, + { + logId: '14098361245275318101', + concreteTypeId: 'c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978', + }, + { + logId: '8988117408309506793', + concreteTypeId: '7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6', + }, + { + logId: '17263266271595476800', + concreteTypeId: 'ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1', + }, + { + logId: '2991584087911992109', + concreteTypeId: '29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad', + }, + { + logId: '15359085500973571070', + concreteTypeId: 'd5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e', + }, + { + logId: '6096764540904137535', + concreteTypeId: '549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60', + }, + { + logId: '16681458389498941754', + concreteTypeId: 'e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948', + }, + { + logId: '9678575818972079706', + concreteTypeId: '8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a', + }, + { + logId: '15006799511514667686', + concreteTypeId: 'd042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5', + }, + { + logId: '621263945896771922', + concreteTypeId: '089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c', + }, + { + logId: '18160646294966696875', + concreteTypeId: 'fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698', + }, + { + logId: '16916744526725816430', + concreteTypeId: 'eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621', + }, + { + logId: '8728991397092492258', + concreteTypeId: '79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4', + }, + { + logId: '8036493118938929554', + concreteTypeId: '6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f', + }, + { + logId: '2992671284987479467', + concreteTypeId: '29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef', + }, + { + logId: '1970142151624111756', + concreteTypeId: '1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e', + }, + { + logId: '15520703124961489725', + concreteTypeId: 'd7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc', + }, + { + logId: '14454674236531057292', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + }, + { + logId: '7752900403879318547', + concreteTypeId: '6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31', + }, + { + logId: '16171443785104013487', + concreteTypeId: 'e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e', + }, + { + logId: '13897586369399989020', + concreteTypeId: 'c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d', + }, + { + logId: '2855558404146077188', + concreteTypeId: '27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e', + }, + { + logId: '10477818057471029201', + concreteTypeId: '9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468', + }, + { + logId: '3330666440490685604', + concreteTypeId: '2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d', + }, + ], + messagesTypes: [], + configurables: [ + { + name: 'U8_VALUE', + concreteTypeId: 'c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b', + offset: 113392, + }, + { + name: 'BOOL_VALUE', + concreteTypeId: 'b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903', + offset: 113360, + }, + { + name: 'B256_VALUE', + concreteTypeId: '7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b', + offset: 113328, + }, + { + name: 'OPTION_U8_VALUE', + concreteTypeId: '2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1', + offset: 113376, + }, + { + name: 'GENERIC_STRUCT_VALUE', + concreteTypeId: '08dbec793087c5686c1a493513b158a999bb653126ee51151dfa85fa683edce5', + offset: 113368, + }, + ], +}; diff --git a/packages/abi/test/utils/constants.ts b/packages/abi/test/utils/constants.ts new file mode 100644 index 00000000000..986af402c33 --- /dev/null +++ b/packages/abi/test/utils/constants.ts @@ -0,0 +1,58 @@ +import { bn } from '@fuel-ts/math'; + +export const U8_MAX = 2 ** 8 - 1; +export const U8_MAX_ENCODED = new Uint8Array([255]); +export const U8_MIN_ENCODED = new Uint8Array([0]); +export const U16_MAX = 2 ** 16 - 1; +export const U16_MAX_ENCODED = new Uint8Array([255, 255]); +export const U16_MIN_ENCODED = new Uint8Array([0, 0]); +export const U32_MAX = 2 ** 32 - 1; +export const U32_MAX_ENCODED = new Uint8Array([255, 255, 255, 255]); +export const U32_MIN_ENCODED = new Uint8Array([0, 0, 0, 0]); +export const U64_MAX = bn(2).pow(64).sub(1); +export const U64_MAX_ENCODED = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]); +export const U64_MIN_ENCODED = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); +export const U256_MAX = bn(2).pow(256).sub(1); +export const U256_MAX_ENCODED = new Uint8Array(32).fill(255); +export const U256_MIN_ENCODED = new Uint8Array(32).fill(0); + +export const EMPTY_8_BYTE_ARRAY = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); +export const ENUM_FIRST_INDEX = EMPTY_8_BYTE_ARRAY; +export const ENUM_SECOND_INDEX = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); +export const ENUM_THIRD_INDEX = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 2]); + +export const STRING_MIN_DECODED = ''; +export const STRING_MIN_ENCODED = new Uint8Array(); +export const STRING_MAX_DECODED = 'a'.repeat(U8_MAX); +export const STRING_MAX_ENCODED = new Uint8Array([ + ...Array.from(Array(U8_MAX + 1).fill(97, 0, U8_MAX)), +]); + +export const B256_DECODED = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; +export const B256_ENCODED = new Uint8Array([ + 213, 87, 156, 70, 223, 204, 127, 24, 32, 112, 19, 230, 91, 68, 228, 203, 78, 44, 34, 152, 244, + 172, 69, 123, 168, 248, 39, 67, 243, 30, 147, 11, +]); +export const B256_ZERO_DECODED = + '0x0000000000000000000000000000000000000000000000000000000000000000'; +export const B256_ZERO_ENCODED = new Uint8Array(32); + +export const BYTE_MIN_DECODED = 0; +export const BYTE_MIN_ENCODED = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); +export const BYTE_MAX_DECODED = U8_MAX; +export const BYTE_MAX_ENCODED = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); + +export const BOOL_TRUE_ENCODED = new Uint8Array([1]); +export const BOOL_FALSE_ENCODED = new Uint8Array([0]); + +export const B512_DECODED = + '0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2'; +export const B512_ENCODED = new Uint8Array([ + 142, 157, 218, 111, 119, 147, 116, 90, 197, 170, 207, 158, 144, 124, 174, 48, 178, 160, 31, 223, + 13, 35, 183, 117, 10, 133, 198, 164, 79, 202, 12, 41, 240, 144, 111, 157, 31, 30, 146, 230, 161, + 251, 60, 61, 206, 243, 204, 59, 60, 219, 170, 226, 126, 71, 185, 217, 164, 198, 164, 252, 228, + 207, 22, 178, +]); +export const B512_ZERO_DECODED = + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; +export const B512_ZERO_ENCODED = new Uint8Array(64); diff --git a/packages/abi/tsconfig.dts.json b/packages/abi/tsconfig.dts.json new file mode 100644 index 00000000000..ccca2ac1f3e --- /dev/null +++ b/packages/abi/tsconfig.dts.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"], + "exclude": ["**/*.test.ts"] +} diff --git a/packages/abi/tsconfig.json b/packages/abi/tsconfig.json new file mode 100644 index 00000000000..b22c89a4b35 --- /dev/null +++ b/packages/abi/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": ["src", "test"] +} diff --git a/packages/abi/tsdoc.json b/packages/abi/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/abi/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/abi/tsup.config.ts b/packages/abi/tsup.config.ts new file mode 100644 index 00000000000..48e24be8656 --- /dev/null +++ b/packages/abi/tsup.config.ts @@ -0,0 +1,11 @@ +import { indexBinAndCliConfig } from '@internal/tsup'; +import type { Options } from 'tsup'; + +const configs: Options = { + ...indexBinAndCliConfig, + loader: { + '.hbs': 'text', + }, +}; + +export default configs; diff --git a/packages/abi/typedoc.json b/packages/abi/typedoc.json new file mode 100644 index 00000000000..a8ec6b825f0 --- /dev/null +++ b/packages/abi/typedoc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "readme": "none" +} diff --git a/packages/abi/typegen.js b/packages/abi/typegen.js new file mode 100755 index 00000000000..ae13c3e8f43 --- /dev/null +++ b/packages/abi/typegen.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('./dist/bin.js'); diff --git a/packages/account/package.json b/packages/account/package.json index ded879a66fa..81b207a25be 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@fuel-ts/abi-coder": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/crypto": "workspace:*", "@fuel-ts/errors": "workspace:*", diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index e41050c1b41..0a190e3ed21 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -1,4 +1,4 @@ -import { UTXO_ID_LEN } from '@fuel-ts/abi-coder'; +import { UTXO_ID_LEN } from '@fuel-ts/abi'; import type { WithAddress } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { randomBytes } from '@fuel-ts/crypto'; diff --git a/packages/account/src/connectors/types/data-type.ts b/packages/account/src/connectors/types/data-type.ts index f4a6f85fa6c..4ca8fdbd7ed 100644 --- a/packages/account/src/connectors/types/data-type.ts +++ b/packages/account/src/connectors/types/data-type.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { RequireAtLeastOne } from 'type-fest'; import type { TransactionRequest } from '../../providers'; @@ -43,7 +43,7 @@ export type SelectNetworkArguments = RequireAtLeastOne = { bytecode: BytesLike; provider: Provider; - abi: JsonAbi; + abi: AbiSpecification; data?: TData; configurableConstants?: TConfigurables; }; @@ -47,7 +47,7 @@ export class Predicate< > extends Account { bytes: Uint8Array; predicateData: TData = [] as unknown as TData; - interface: Interface; + interface: AbiCoder; initialBytecode: Uint8Array; configurableConstants: TConfigurables | undefined; /** @@ -160,7 +160,7 @@ export class Predicate< ) { return new Predicate({ bytecode: this.initialBytecode, - abi: this.interface.jsonAbi, + abi: this.interface.specification, provider: this.provider, data: overrides.data ?? this.predicateData, configurableConstants: overrides.configurableConstants ?? this.configurableConstants, @@ -177,13 +177,13 @@ export class Predicate< */ private static processPredicateData( bytes: BytesLike, - jsonAbi: JsonAbi, + jsonAbi: AbiSpecification, configurableConstants?: { [name: string]: unknown } ) { let predicateBytes = arrayify(bytes); - const abiInterface: Interface = new Interface(jsonAbi); + const abiCoder: AbiCoder = AbiCoder.fromAbi(jsonAbi); - if (abiInterface.functions.main === undefined) { + if (abiCoder.functions.main === undefined) { throw new FuelError( ErrorCode.ABI_MAIN_METHOD_MISSING, 'Cannot use ABI without "main" function.' @@ -194,13 +194,13 @@ export class Predicate< predicateBytes = Predicate.setConfigurableConstants( predicateBytes, configurableConstants, - abiInterface + abiCoder ); } return { predicateBytes, - predicateInterface: abiInterface, + predicateInterface: abiCoder, }; } @@ -246,18 +246,18 @@ export class Predicate< * * @param bytes - The bytes of the predicate. * @param configurableConstants - Configurable constants to be set. - * @param abiInterface - The ABI interface of the predicate. + * @param abiCoder - The ABI interface of the predicate. * @returns The mutated bytes with the configurable constants set. */ private static setConfigurableConstants( bytes: Uint8Array, configurableConstants: { [name: string]: unknown }, - abiInterface: Interface + abiCoder: AbiCoder ) { const mutatedBytes = bytes; try { - if (Object.keys(abiInterface.configurables).length === 0) { + if (Object.keys(abiCoder.configurables).length === 0) { throw new FuelError( ErrorCode.INVALID_CONFIGURABLE_CONSTANTS, 'Predicate has no configurable constants to be set' @@ -265,16 +265,16 @@ export class Predicate< } Object.entries(configurableConstants).forEach(([key, value]) => { - if (!abiInterface?.configurables[key]) { + if (!abiCoder?.configurables[key]) { throw new FuelError( ErrorCode.CONFIGURABLE_NOT_FOUND, `No configurable constant named '${key}' found in the Predicate` ); } - const { offset } = abiInterface.configurables[key]; + const { offset } = abiCoder.configurables[key]; - const encoded = abiInterface.encodeConfigurable(key, value as InputValue); + const encoded = abiCoder.getConfigurable(key).encode(value as InputValue); mutatedBytes.set(encoded, offset); }); @@ -339,7 +339,7 @@ export class Predicate< async deploy(account: Account) { return deployScriptOrPredicate({ deployer: account, - abi: this.interface.jsonAbi, + abi: this.interface.specification, bytecode: this.bytes, loaderInstanceCallback: (loaderBytecode, newAbi) => new Predicate({ diff --git a/packages/account/src/providers/transaction-request/input.ts b/packages/account/src/providers/transaction-request/input.ts index 15100c2e5c6..3446b24f0b7 100644 --- a/packages/account/src/providers/transaction-request/input.ts +++ b/packages/account/src/providers/transaction-request/input.ts @@ -1,4 +1,4 @@ -import { BYTES_32, UTXO_ID_LEN } from '@fuel-ts/abi-coder'; +import { BYTES_32, UTXO_ID_LEN } from '@fuel-ts/abi'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BigNumberish } from '@fuel-ts/math'; diff --git a/packages/account/src/providers/transaction-request/script-transaction-request.ts b/packages/account/src/providers/transaction-request/script-transaction-request.ts index e00a1b942e8..9d9d544ea41 100644 --- a/packages/account/src/providers/transaction-request/script-transaction-request.ts +++ b/packages/account/src/providers/transaction-request/script-transaction-request.ts @@ -1,5 +1,5 @@ -import type { InputValue, JsonAbi } from '@fuel-ts/abi-coder'; -import { Interface } from '@fuel-ts/abi-coder'; +import type { InputValue, AbiSpecification } from '@fuel-ts/abi'; +import { AbiCoder } from '@fuel-ts/abi'; import { addressify } from '@fuel-ts/address'; import type { ContractIdLike } from '@fuel-ts/address'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; @@ -248,8 +248,8 @@ export class ScriptTransactionRequest extends BaseTransactionRequest { * @param args - The input arguments. * @returns The current instance of the `ScriptTransactionRequest`. */ - setData(abi: JsonAbi, args: InputValue[]): ScriptTransactionRequest { - const abiInterface = new Interface(abi); + setData(abi: AbiSpecification, args: InputValue[]): ScriptTransactionRequest { + const abiInterface = AbiCoder.fromAbi(abi); this.scriptData = abiInterface.functions.main.encodeArguments(args); return this; } diff --git a/packages/account/src/providers/transaction-request/transaction-request.ts b/packages/account/src/providers/transaction-request/transaction-request.ts index d38eaed6a94..23f6f4edf6b 100644 --- a/packages/account/src/providers/transaction-request/transaction-request.ts +++ b/packages/account/src/providers/transaction-request/transaction-request.ts @@ -1,4 +1,4 @@ -import { UTXO_ID_LEN } from '@fuel-ts/abi-coder'; +import { UTXO_ID_LEN } from '@fuel-ts/abi'; import { Address, addressify } from '@fuel-ts/address'; import type { AddressLike } from '@fuel-ts/address'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; diff --git a/packages/account/src/providers/transaction-request/types.ts b/packages/account/src/providers/transaction-request/types.ts index a1719c0d423..e8ce1b3c4cd 100644 --- a/packages/account/src/providers/transaction-request/types.ts +++ b/packages/account/src/providers/transaction-request/types.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { TransactionType } from '@fuel-ts/transactions'; import type { @@ -36,8 +36,8 @@ export type TransactionRequestLike = | ({ type: TransactionType.Upload } & UploadTransactionRequestLike); export type JsonAbisFromAllCalls = { - main: JsonAbi; - otherContractsAbis: Record; + main: AbiSpecification; + otherContractsAbis: Record; }; /** diff --git a/packages/account/src/providers/transaction-response/getDecodedLogs.ts b/packages/account/src/providers/transaction-response/getDecodedLogs.ts index 91bafef69c5..af36dc9cc92 100644 --- a/packages/account/src/providers/transaction-response/getDecodedLogs.ts +++ b/packages/account/src/providers/transaction-response/getDecodedLogs.ts @@ -1,5 +1,5 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; -import { Interface, BigNumberCoder } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; +import { AbiCoder, encoding } from '@fuel-ts/abi'; import { ReceiptType } from '@fuel-ts/transactions'; import type { TransactionResultReceipt } from './transaction-response'; @@ -7,8 +7,8 @@ import type { TransactionResultReceipt } from './transaction-response'; /** @hidden */ export function getDecodedLogs( receipts: Array, - mainAbi: JsonAbi, - externalAbis: Record = {} + mainAbi: AbiSpecification, + externalAbis: Record = {} ): T[] { /** * This helper decodes logs from transaction receipts. @@ -28,14 +28,13 @@ export function getDecodedLogs( */ return receipts.reduce((logs: T[], receipt) => { if (receipt.type === ReceiptType.LogData || receipt.type === ReceiptType.Log) { - const interfaceToUse = new Interface(externalAbis[receipt.id] || mainAbi); + const abiCoderToUse = AbiCoder.fromAbi(externalAbis[receipt.id] || mainAbi); const data = - receipt.type === ReceiptType.Log - ? new BigNumberCoder('u64').encode(receipt.ra) - : receipt.data; + receipt.type === ReceiptType.Log ? encoding.v1.u64.encode(receipt.ra) : receipt.data; - const [decodedLog] = interfaceToUse.decodeLog(data, receipt.rb.toString()); + const log = abiCoderToUse.getLog(receipt.rb.toString()); + const decodedLog = log.decode(data) as T; logs.push(decodedLog); } diff --git a/packages/account/src/providers/transaction-summary/call.ts b/packages/account/src/providers/transaction-summary/call.ts index e4b56da2320..6d121a228b4 100644 --- a/packages/account/src/providers/transaction-summary/call.ts +++ b/packages/account/src/providers/transaction-summary/call.ts @@ -1,9 +1,10 @@ -import { Interface, type JsonAbi } from '@fuel-ts/abi-coder'; +import { AbiCoder } from '@fuel-ts/abi'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { BN } from '@fuel-ts/math'; import type { ReceiptCall } from '@fuel-ts/transactions'; type GetFunctionCallProps = { - abi: JsonAbi; + abi: AbiSpecification; receipt: ReceiptCall; rawPayload?: string; maxInputs: BN; @@ -18,10 +19,10 @@ export interface FunctionCall { } export const getFunctionCall = ({ abi, receipt }: GetFunctionCallProps): FunctionCall => { - const abiInterface = new Interface(abi); + const abiCoder = AbiCoder.fromAbi(abi); const callFunctionSelector = receipt.param1.toHex(8); - const functionFragment = abiInterface.getFunction(callFunctionSelector); - const inputs = functionFragment.jsonFn.inputs; + const functionFragment = abiCoder.getFunction(callFunctionSelector); + const inputs = functionFragment.inputs; const encodedArgs = receipt.param2.toHex(); let argumentsProvided; diff --git a/packages/account/src/providers/transaction-summary/types.ts b/packages/account/src/providers/transaction-summary/types.ts index b3372035ba8..64fb73d1a19 100644 --- a/packages/account/src/providers/transaction-summary/types.ts +++ b/packages/account/src/providers/transaction-summary/types.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { B256Address } from '@fuel-ts/address'; import type { BN, BNInput } from '@fuel-ts/math'; import type { Input, Output, Transaction, TransactionType } from '@fuel-ts/transactions'; @@ -154,7 +154,7 @@ export type ReceiptParam = { receipts: TransactionResultReceipt[]; }; -export type AbiMap = Record; +export type AbiMap = Record; export type RawPayloadParam = { rawPayload?: string; diff --git a/packages/account/src/providers/utils/receipts.test.ts b/packages/account/src/providers/utils/receipts.test.ts index b6f31bc00c0..f19a18a0afb 100644 --- a/packages/account/src/providers/utils/receipts.test.ts +++ b/packages/account/src/providers/utils/receipts.test.ts @@ -1,11 +1,8 @@ -import { BN, bn } from '@fuel-ts/math'; +import { BN } from '@fuel-ts/math'; import type { - ReceiptBurn, ReceiptCall, ReceiptLog, ReceiptLogData, - ReceiptMessageOut, - ReceiptMint, ReceiptPanic, ReceiptReturn, ReceiptReturnData, @@ -14,8 +11,7 @@ import type { ReceiptTransfer, ReceiptTransferOut, } from '@fuel-ts/transactions'; -import { getMintedAssetId, getMessageId, ReceiptType } from '@fuel-ts/transactions'; -import { arrayify } from '@fuel-ts/utils'; +import { ReceiptType } from '@fuel-ts/transactions'; import { MOCK_GQL_RECEIPT_FRAGMENT, @@ -207,75 +203,4 @@ describe('assembleReceiptByType', () => { expect(receipt.result).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.result)); expect(receipt.gasUsed).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.gasUsed)); }); - - it('should return a ReceiptMessageOut when GqlReceiptType.MessageOut is provided', () => { - const receipt = assembleReceiptByType({ - ...MOCK_GQL_RECEIPT_FRAGMENT, - receiptType: GqlReceiptType.MessageOut, - }) as ReceiptMessageOut; - - const sender = MOCK_GQL_RECEIPT_FRAGMENT.sender || ''; - const recipient = MOCK_GQL_RECEIPT_FRAGMENT.recipient || ''; - const nonce = MOCK_GQL_RECEIPT_FRAGMENT.nonce || ''; - const amount = bn(MOCK_GQL_RECEIPT_FRAGMENT.amount); - const data = arrayify(MOCK_GQL_RECEIPT_FRAGMENT.data || ''); - const digest = MOCK_GQL_RECEIPT_FRAGMENT.digest; - const len = Number(MOCK_GQL_RECEIPT_FRAGMENT.len); - - const messageId = getMessageId({ - sender, - recipient, - nonce, - amount, - data, - }); - - expect(receipt.type).toBe(ReceiptType.MessageOut); - expect(receipt.amount).toStrictEqual(amount); - expect(receipt.digest).toStrictEqual(digest); - expect(receipt.messageId).toStrictEqual(messageId); - expect(receipt.nonce).toStrictEqual(nonce); - expect(receipt.recipient).toStrictEqual(recipient); - expect(receipt.sender).toStrictEqual(sender); - expect(receipt.len).toStrictEqual(len); - expect(receipt.data).toStrictEqual(data); - }); - - it('should return a ReceiptMint when GqlReceiptType.Mint is provided', () => { - const contractId = MOCK_GQL_RECEIPT_FRAGMENT.id || ''; - const subId = MOCK_GQL_RECEIPT_FRAGMENT.subId || ''; - const assetId = getMintedAssetId(contractId, subId); - - const receipt = assembleReceiptByType({ - ...MOCK_GQL_RECEIPT_FRAGMENT, - receiptType: GqlReceiptType.Mint, - }) as ReceiptMint; - - expect(receipt.type).toBe(ReceiptType.Mint); - expect(receipt.contractId).toStrictEqual(contractId); - expect(receipt.subId).toStrictEqual(subId); - expect(receipt.assetId).toStrictEqual(assetId); - expect(receipt.is).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.is)); - expect(receipt.pc).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.pc)); - expect(receipt.val).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.val)); - }); - - it('should return a ReceiptBurn when GqlReceiptType.Burn is provided', () => { - const contractId = MOCK_GQL_RECEIPT_FRAGMENT.id || ''; - const subId = MOCK_GQL_RECEIPT_FRAGMENT.subId || ''; - const assetId = getMintedAssetId(contractId, subId); - - const receipt = assembleReceiptByType({ - ...MOCK_GQL_RECEIPT_FRAGMENT, - receiptType: GqlReceiptType.Burn, - }) as ReceiptBurn; - - expect(receipt.type).toBe(ReceiptType.Burn); - expect(receipt.contractId).toStrictEqual(contractId); - expect(receipt.subId).toStrictEqual(subId); - expect(receipt.assetId).toStrictEqual(assetId); - expect(receipt.is).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.is)); - expect(receipt.pc).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.pc)); - expect(receipt.val).toStrictEqual(new BN(MOCK_GQL_RECEIPT_FRAGMENT.val)); - }); }); diff --git a/packages/account/src/test-utils/launchNode.ts b/packages/account/src/test-utils/launchNode.ts index c68e7b13ca8..dbbd1b68d17 100644 --- a/packages/account/src/test-utils/launchNode.ts +++ b/packages/account/src/test-utils/launchNode.ts @@ -1,4 +1,4 @@ -import { BYTES_32 } from '@fuel-ts/abi-coder'; +import { BYTES_32 } from '@fuel-ts/abi'; import { randomBytes, randomUUID } from '@fuel-ts/crypto'; import { FuelError } from '@fuel-ts/errors'; import type { SnapshotConfigs } from '@fuel-ts/utils'; diff --git a/packages/account/src/test-utils/resources.ts b/packages/account/src/test-utils/resources.ts index 688d985d698..a7fde002509 100644 --- a/packages/account/src/test-utils/resources.ts +++ b/packages/account/src/test-utils/resources.ts @@ -1,4 +1,4 @@ -import { BYTES_32, UTXO_ID_LEN } from '@fuel-ts/abi-coder'; +import { BYTES_32, UTXO_ID_LEN } from '@fuel-ts/abi'; import { Address } from '@fuel-ts/address'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { randomBytes } from '@fuel-ts/crypto'; diff --git a/packages/account/src/test-utils/transactionRequest.ts b/packages/account/src/test-utils/transactionRequest.ts index a0aa0d66019..e224b326e77 100644 --- a/packages/account/src/test-utils/transactionRequest.ts +++ b/packages/account/src/test-utils/transactionRequest.ts @@ -1,4 +1,4 @@ -import { UTXO_ID_LEN } from '@fuel-ts/abi-coder'; +import { UTXO_ID_LEN } from '@fuel-ts/abi'; import { getRandomB256 } from '@fuel-ts/address'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { randomBytes } from '@fuel-ts/crypto'; diff --git a/packages/account/src/utils/deployScriptOrPredicate.ts b/packages/account/src/utils/deployScriptOrPredicate.ts index fea92dabee6..95d7e36b3b3 100644 --- a/packages/account/src/utils/deployScriptOrPredicate.ts +++ b/packages/account/src/utils/deployScriptOrPredicate.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import { FuelError, ErrorCode } from '@fuel-ts/errors'; import { hash } from '@fuel-ts/hasher'; import { bn } from '@fuel-ts/math'; @@ -40,21 +40,21 @@ async function fundBlobTx(deployer: Account, blobTxRequest: BlobTransactionReque return deployer.fund(blobTxRequest, txCost); } -function adjustConfigurableOffsets(jsonAbi: JsonAbi, configurableOffsetDiff: number) { +function adjustConfigurableOffsets(jsonAbi: AbiSpecification, configurableOffsetDiff: number) { const { configurables: readOnlyConfigurables } = jsonAbi; - const configurables: JsonAbi['configurables'] = []; + const configurables: AbiSpecification['configurables'] = []; readOnlyConfigurables.forEach((config) => { // @ts-expect-error shut up the read-only thing configurables.push({ ...config, offset: config.offset - configurableOffsetDiff }); }); - return { ...jsonAbi, configurables } as JsonAbi; + return { ...jsonAbi, configurables } as AbiSpecification; } interface Deployer { deployer: Account; bytecode: Uint8Array; - abi: JsonAbi; - loaderInstanceCallback: (loaderBytecode: Uint8Array, newAbi: JsonAbi) => T; + abi: AbiSpecification; + loaderInstanceCallback: (loaderBytecode: Uint8Array, newAbi: AbiSpecification) => T; } export async function deployScriptOrPredicate({ diff --git a/packages/account/src/utils/formatTransferToContractScriptData.test.ts b/packages/account/src/utils/formatTransferToContractScriptData.test.ts index d00fe30eb48..21f401fd331 100644 --- a/packages/account/src/utils/formatTransferToContractScriptData.test.ts +++ b/packages/account/src/utils/formatTransferToContractScriptData.test.ts @@ -1,4 +1,4 @@ -import { BigNumberCoder } from '@fuel-ts/abi-coder'; +import { encoding } from '@fuel-ts/abi'; import { getRandomB256 } from '@fuel-ts/address'; import { bn, type BigNumberish } from '@fuel-ts/math'; import * as arrayifyMod from '@fuel-ts/utils'; @@ -49,9 +49,7 @@ describe('util', () => { it('should ensure "formatScriptDataForTransferringToContract" returns script data just fine', () => { const byte: number[] = [0, 0, 0, 0, 0, 0, 0, 1]; - const encode = vi - .spyOn(BigNumberCoder.prototype, 'encode') - .mockReturnValue(Uint8Array.from(byte)); + const encode = vi.spyOn(encoding.v1.u64, 'encode').mockReturnValue(Uint8Array.from(byte)); const arrayify = vi.spyOn(arrayifyMod, 'arrayify').mockReturnValue(Uint8Array.from(byte)); diff --git a/packages/account/src/utils/formatTransferToContractScriptData.ts b/packages/account/src/utils/formatTransferToContractScriptData.ts index 2155a3ad46a..a119f1551f8 100644 --- a/packages/account/src/utils/formatTransferToContractScriptData.ts +++ b/packages/account/src/utils/formatTransferToContractScriptData.ts @@ -1,4 +1,4 @@ -import { ASSET_ID_LEN, BigNumberCoder, CONTRACT_ID_LEN, WORD_SIZE } from '@fuel-ts/abi-coder'; +import { encoding, ASSET_ID_LEN, CONTRACT_ID_LEN, WORD_SIZE } from '@fuel-ts/abi'; import { Address } from '@fuel-ts/address'; import type { BN } from '@fuel-ts/math'; import { arrayify, concat } from '@fuel-ts/utils'; @@ -14,7 +14,7 @@ interface AssembleTransferToContractParams { export const formatTransferToContractScriptData = ( transferParams: Array ) => { - const numberCoder = new BigNumberCoder('u64'); + const numberCoder = encoding.v1.u64; return transferParams.reduce((acc, transferParam) => { const { assetId, amount, contractId } = transferParam; const encoded = numberCoder.encode(amount); diff --git a/packages/account/test/fixtures/predicate-abi.ts b/packages/account/test/fixtures/predicate-abi.ts index 91bf0b52c06..aecbd61fb21 100644 --- a/packages/account/test/fixtures/predicate-abi.ts +++ b/packages/account/test/fixtures/predicate-abi.ts @@ -1,6 +1,6 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; -export const predicateAbi: JsonAbi = { +export const predicateAbi: AbiSpecification = { programType: 'predicate', specVersion: '1', encodingVersion: '1', diff --git a/packages/account/test/fixtures/transaction-summary.ts b/packages/account/test/fixtures/transaction-summary.ts index 9b3b79cbc02..c75107cb78b 100644 --- a/packages/account/test/fixtures/transaction-summary.ts +++ b/packages/account/test/fixtures/transaction-summary.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import { bn } from '@fuel-ts/math'; import type { InputCoin, @@ -260,7 +260,7 @@ export const MOCK_ABI_MAP: AbiMap = { loggedTypes: [], messagesTypes: [], configurables: [], - } as unknown as JsonAbi, // used in skipped test + } as unknown as AbiSpecification, // 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 diff --git a/packages/contract/package.json b/packages/contract/package.json index 950ab5d0471..687e98c0b69 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -39,7 +39,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@fuel-ts/abi-coder": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/crypto": "workspace:*", "@fuel-ts/errors": "workspace:*", diff --git a/packages/contract/src/contract-factory.ts b/packages/contract/src/contract-factory.ts index 0ad2c00e955..2b36dfcd226 100644 --- a/packages/contract/src/contract-factory.ts +++ b/packages/contract/src/contract-factory.ts @@ -1,5 +1,5 @@ -import { Interface, WORD_SIZE } from '@fuel-ts/abi-coder'; -import type { JsonAbi, InputValue } from '@fuel-ts/abi-coder'; +import { AbiCoder, WORD_SIZE } from '@fuel-ts/abi'; +import type { AbiSpecification, InputValue } from '@fuel-ts/abi'; import type { Account, CreateTransactionRequestLike, @@ -54,7 +54,7 @@ export type DeployContractResult = { */ export default class ContractFactory { bytecode: BytesLike; - interface: Interface; + interface: AbiCoder; provider!: Provider | null; account!: Account | null; storageSlots: StorageSlot[]; @@ -68,17 +68,17 @@ export default class ContractFactory { */ constructor( bytecode: BytesLike, - abi: JsonAbi | Interface, + abi: AbiSpecification | AbiCoder, accountOrProvider: Account | Provider | null = null, storageSlots: StorageSlot[] = [] ) { // Force the bytecode to be a byte array this.bytecode = arrayify(bytecode); - if (abi instanceof Interface) { + if (abi instanceof AbiCoder) { this.interface = abi; } else { - this.interface = new Interface(abi); + this.interface = AbiCoder.fromAbi(abi); } /** @@ -404,7 +404,7 @@ export default class ContractFactory { const { offset } = this.interface.configurables[key]; - const encoded = this.interface.encodeConfigurable(key, value as InputValue); + const encoded = this.interface.getConfigurable(key).encode(value as InputValue); const bytes = arrayify(this.bytecode); diff --git a/packages/contract/src/loader/loader-script.test.ts b/packages/contract/src/loader/loader-script.test.ts index 31511569f21..edec0b7de26 100644 --- a/packages/contract/src/loader/loader-script.test.ts +++ b/packages/contract/src/loader/loader-script.test.ts @@ -1,4 +1,4 @@ -import { WORD_SIZE } from '@fuel-ts/abi-coder'; +import { WORD_SIZE } from '@fuel-ts/abi'; import { arrayify } from '@fuel-ts/utils'; import { getLoaderInstructions } from './loader-script'; diff --git a/packages/contract/src/loader/loader-script.ts b/packages/contract/src/loader/loader-script.ts index 6d9c64e1254..d733286fd87 100644 --- a/packages/contract/src/loader/loader-script.ts +++ b/packages/contract/src/loader/loader-script.ts @@ -1,4 +1,4 @@ -import { BYTES_32 } from '@fuel-ts/abi-coder'; +import { BYTES_32 } from '@fuel-ts/abi'; import { InstructionSet } from '@fuel-ts/program'; import { arrayify, concat } from '@fuel-ts/utils'; import * as asm from '@fuels/vm-asm'; diff --git a/packages/contract/src/loader/utils.test.ts b/packages/contract/src/loader/utils.test.ts index 6e306271160..32cb2843f76 100644 --- a/packages/contract/src/loader/utils.test.ts +++ b/packages/contract/src/loader/utils.test.ts @@ -1,4 +1,4 @@ -import { WORD_SIZE } from '@fuel-ts/abi-coder'; +import { WORD_SIZE } from '@fuel-ts/abi'; import { getContractChunks } from './utils'; diff --git a/packages/contract/src/loader/utils.ts b/packages/contract/src/loader/utils.ts index 75528aab5b4..ae85e5038e2 100644 --- a/packages/contract/src/loader/utils.ts +++ b/packages/contract/src/loader/utils.ts @@ -1,4 +1,4 @@ -import { WORD_SIZE } from '@fuel-ts/abi-coder'; +import { WORD_SIZE } from '@fuel-ts/abi'; import { concat } from '@fuel-ts/utils'; export const getContractChunks = (bytecode: Uint8Array, chunkSize: number) => { diff --git a/packages/contract/src/test-utils/launch-test-node.test.ts b/packages/contract/src/test-utils/launch-test-node.test.ts index f6667d7a0e7..77a8fdab54c 100644 --- a/packages/contract/src/test-utils/launch-test-node.test.ts +++ b/packages/contract/src/test-utils/launch-test-node.test.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import { Provider } from '@fuel-ts/account'; import * as setupTestProviderAndWalletsMod from '@fuel-ts/account/test-utils'; import { randomBytes, randomUUID } from '@fuel-ts/crypto'; @@ -15,7 +15,7 @@ import ContractFactory from '../contract-factory'; import { launchTestNode } from './launch-test-node'; -const { binHexlified, abiContents } = getForcProject({ +const { binHexlified, abiContents } = getForcProject({ projectDir: join(__dirname, '../../test/fixtures/forc-projects/simple-contract'), projectName: 'simple-contract', build: 'release', diff --git a/packages/crypto/src/browser/bufferFromString.ts b/packages/crypto/src/browser/bufferFromString.ts index 84e731fd42e..8e24a7140de 100644 --- a/packages/crypto/src/browser/bufferFromString.ts +++ b/packages/crypto/src/browser/bufferFromString.ts @@ -1,8 +1,8 @@ -import type { CryptoApi, Encoding } from '../types'; +import type { CryptoApi, BufferEncoding } from '../types'; export const bufferFromString: CryptoApi['bufferFromString'] = ( string: string, - encoding: Encoding = 'base64' + encoding: BufferEncoding = 'base64' ): Uint8Array => { switch (encoding) { case 'utf-8': { diff --git a/packages/crypto/src/browser/stringFromBuffer.ts b/packages/crypto/src/browser/stringFromBuffer.ts index 948c05bd4be..f61ed9f01ae 100644 --- a/packages/crypto/src/browser/stringFromBuffer.ts +++ b/packages/crypto/src/browser/stringFromBuffer.ts @@ -1,10 +1,10 @@ -import type { CryptoApi, Encoding } from '../types'; +import type { CryptoApi, BufferEncoding } from '../types'; import { btoa } from './crypto'; export const stringFromBuffer: CryptoApi['stringFromBuffer'] = ( buffer: Uint8Array, - encoding: Encoding = 'base64' + encoding: BufferEncoding = 'base64' ): string => { switch (encoding) { case 'utf-8': { diff --git a/packages/crypto/src/node/bufferFromString.ts b/packages/crypto/src/node/bufferFromString.ts index ae22ab28603..a9ccd50fff1 100644 --- a/packages/crypto/src/node/bufferFromString.ts +++ b/packages/crypto/src/node/bufferFromString.ts @@ -1,6 +1,6 @@ -import type { CryptoApi, Encoding } from '../types'; +import type { CryptoApi, BufferEncoding } from '../types'; export const bufferFromString: CryptoApi['bufferFromString'] = ( string: string, - encoding: Encoding = 'base64' + encoding: BufferEncoding = 'base64' ): Uint8Array => Uint8Array.from(Buffer.from(string, encoding)); diff --git a/packages/crypto/src/node/stringFromBuffer.ts b/packages/crypto/src/node/stringFromBuffer.ts index ab296f582aa..63fc4be579c 100644 --- a/packages/crypto/src/node/stringFromBuffer.ts +++ b/packages/crypto/src/node/stringFromBuffer.ts @@ -1,6 +1,6 @@ -import type { CryptoApi, Encoding } from '../types'; +import type { CryptoApi, BufferEncoding } from '../types'; export const stringFromBuffer: CryptoApi['stringFromBuffer'] = ( buffer: Uint8Array, - encoding: Encoding = 'base64' + encoding: BufferEncoding = 'base64' ): string => Buffer.from(buffer).toString(encoding); diff --git a/packages/crypto/src/types.ts b/packages/crypto/src/types.ts index 78949b56af0..79dd050e2f5 100644 --- a/packages/crypto/src/types.ts +++ b/packages/crypto/src/types.ts @@ -15,14 +15,14 @@ export interface IScryptParams { dklen: number; } -export type Encoding = 'utf-8' | 'base64' | 'hex'; +export type BufferEncoding = 'utf-8' | 'base64' | 'hex'; export interface CryptoApi { - bufferFromString(string: string, encoding?: Encoding): Uint8Array; + bufferFromString(string: string, encoding?: BufferEncoding): Uint8Array; decrypt(password: string, keystore: Keystore): Promise; encrypt(password: string, data: T): Promise; keyFromPassword(password: string, saltBuffer: Uint8Array): Uint8Array; - stringFromBuffer(buffer: Uint8Array, encoding?: Encoding): string; + stringFromBuffer(buffer: Uint8Array, encoding?: BufferEncoding): string; randomBytes(length: number): Uint8Array; scrypt(params: IScryptParams): Uint8Array; keccak256(data: Uint8Array): Uint8Array; diff --git a/packages/errors/src/error-codes.ts b/packages/errors/src/error-codes.ts index fdbec36d7cc..05295418479 100644 --- a/packages/errors/src/error-codes.ts +++ b/packages/errors/src/error-codes.ts @@ -13,6 +13,7 @@ export enum ErrorCode { TYPE_NOT_SUPPORTED = 'type-not-supported', INVALID_DECODE_VALUE = 'invalid-decode-value', JSON_ABI_ERROR = 'json-abi-error', + ABI_SPECIFICATION_INVALID = 'abi-specification-invalid', TYPE_ID_NOT_FOUND = 'type-id-not-found', BIN_FILE_NOT_FOUND = 'bin-file-not-found', CODER_NOT_FOUND = 'coder-not-found', @@ -23,6 +24,7 @@ export enum ErrorCode { CONFIG_FILE_NOT_FOUND = 'config-file-not-found', CONFIG_FILE_ALREADY_EXISTS = 'config-file-already-exists', WORKSPACE_NOT_DETECTED = 'workspace-not-detected', + MATCHER_NOT_FOUND = 'matcher-not-found', // address INVALID_ADDRESS = 'invalid-address', diff --git a/packages/fuel-gauge/package.json b/packages/fuel-gauge/package.json index 97986da0e89..1a86e34407f 100644 --- a/packages/fuel-gauge/package.json +++ b/packages/fuel-gauge/package.json @@ -7,13 +7,16 @@ "scripts": { "pretest": "pnpm build:forc", "build:forc": "pnpm fuels build --deploy", - "type:check": "tsc --noEmit" + "type:check": "tsc --noEmit", + "test": "cd ../.. && pnpm run test:filter packages/fuel-gauge", + "test:update-fixtures": "UPDATE_FIXTURES=true pnpm run test" }, "license": "Apache-2.0", "dependencies": { "fuels": "workspace:*" }, "devDependencies": { + "@fuel-ts/abi": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/errors": "workspace:*", "@fuel-ts/merkle": "workspace:*", diff --git a/packages/fuel-gauge/src/abi/abi-coder.test.ts b/packages/fuel-gauge/src/abi/abi-coder.test.ts index 5f7fe677b1a..f299712f481 100644 --- a/packages/fuel-gauge/src/abi/abi-coder.test.ts +++ b/packages/fuel-gauge/src/abi/abi-coder.test.ts @@ -1,14 +1,11 @@ -import { bn, FuelError, getRandomB256 } from 'fuels'; +import { bn, ContractFactory, FuelError, getRandomB256 } from 'fuels'; import type { AssetId, BigNumberish, EvmAddress, RawSlice, WalletUnlocked } from 'fuels'; import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils'; import { AbiContractFactory } from '../../test/typegen'; import type { AbiContract } from '../../test/typegen'; -import { - EnumWithNativeInput, - EnumWithNativeOutput, - ExternalEnumInput, -} from '../../test/typegen/contracts/AbiContract'; +import type { Option, Result } from '../../test/typegen/common'; +import { EnumWithNative, ExternalEnum } from '../../test/typegen/contracts/AbiContractTypes'; import type { EnumWithBuiltinTypeInput, EnumWithBuiltinTypeOutput, @@ -32,8 +29,7 @@ import type { StructWithEnumArrayOutput, StructWithSingleOptionOutput, StructWithSingleOptionInput, -} from '../../test/typegen/contracts/AbiContract'; -import type { Option, Result, Vec } from '../../test/typegen/contracts/common'; +} from '../../test/typegen/contracts/AbiContractTypes'; import { U16_MAX, @@ -65,7 +61,8 @@ describe('AbiCoder', () => { const { contracts, wallets } = launched; wallet = wallets[0]; - contract = contracts[0] as AbiContract; + contract = contracts[0]; + cleanup = launched.cleanup; }); @@ -104,7 +101,8 @@ describe('AbiCoder', () => { }, }; - const { waitForResult: waitForDeploy } = await AbiContractFactory.deploy(wallet, { + const factory = new ContractFactory(AbiContractFactory.bytecode, contract.interface, wallet); + const { waitForResult: waitForDeploy } = await factory.deploy({ configurableConstants: NEW_CONFIGURABLES, }); @@ -129,6 +127,68 @@ describe('AbiCoder', () => { }); }); + describe('attributes', () => { + it('should have storage read attribute', () => { + const fn = contract.interface.getFunction('attributes_storage_read'); + expect(fn.attributes).toEqual([{ name: 'storage', arguments: ['read'] }]); + }); + + it('should have storage write attribute', () => { + const fn = contract.interface.getFunction('attributes_storage_write'); + expect(fn.attributes).toEqual([{ name: 'storage', arguments: ['write'] }]); + }); + + it('should have storage read and write attribute', () => { + const fn = contract.interface.getFunction('attributes_storage_read_write'); + expect(fn.attributes).toEqual([{ name: 'storage', arguments: ['read', 'write'] }]); + }); + + it('should have payable attribute', () => { + const fn = contract.interface.getFunction('attributes_payable'); + expect(fn.attributes).toEqual([{ name: 'payable' }]); + }); + + it('should have test attribute', () => { + const fn = contract.interface.getFunction('attributes_test'); + expect(fn.attributes).toEqual([{ name: 'test' }]); + }); + + it('should have inline never attribute', () => { + const fn = contract.interface.getFunction('attributes_inline_never'); + expect(fn.attributes).toEqual([{ name: 'inline', arguments: 'never' }]); + }); + + it('should have inline always attribute', () => { + const fn = contract.interface.getFunction('attributes_inline_always'); + expect(fn.attributes).toEqual([{ name: 'inline', arguments: 'always' }]); + }); + + it('should have doc attribute', () => { + const fn = contract.interface.getFunction('attributes_doc_comment'); + expect(fn.attributes).toEqual([ + { name: 'doc-comment', arguments: [' This is a doc'] }, + { name: 'doc-comment', arguments: [' This is another doc comment'] }, + ]); + }); + }); + + describe('isReadOnly', () => { + it('should return true for a storage read function', () => { + const fn = contract.interface.getFunction('attributes_storage_read'); + expect(fn.isReadOnly()).toBe(true); + }); + + it('should return true for a function that does not use storage', () => { + const fn = contract.interface.getFunction('attributes_none'); + expect(fn.isReadOnly()).toBe(true); + }); + + it('should return false for a storage write function', () => { + const fn = contract.interface.getFunction('attributes_storage_write'); + expect(fn.isReadOnly()).toBe(false); + }); + }); + describe('types_u8', () => { it('should encode/decode just fine', async () => { const input = 8; @@ -160,7 +220,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u8(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value is less than zero.', { + type: 'u8', + value: input.toString(), + }) ); }); @@ -169,7 +232,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u8(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8, too many bytes.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8 value - value exceeds maximum.', { + type: 'u8', + value: input.toString(), + }) ); }); }); @@ -203,7 +269,14 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u16(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16.') + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u16 value - value is less than zero.', + { + type: 'u16', + value: input.toString(), + } + ) ); }); @@ -212,7 +285,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u16(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16, too many bytes.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16 value - value exceeds maximum.', { + type: 'u16', + value: input.toString(), + }) ); }); }); @@ -246,7 +322,14 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u32(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32.') + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u32 value - value is less than zero.', + { + type: 'u32', + value: input.toString(), + } + ) ); }); @@ -255,7 +338,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u32(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32, too many bytes.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32 value - value exceeds maximum.', { + type: 'u32', + value: input.toString(), + }) ); }); }); @@ -292,7 +378,14 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u64(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64.') + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u64 value - value is less than zero.', + { + type: 'u64', + value: input.toString(), + } + ) ); }); @@ -301,7 +394,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u64(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64 value - value exceeds maximum.', { + type: 'u64', + value: input.toString(), + }) ); }); }); @@ -338,7 +434,14 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u256(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256.') + new FuelError( + FuelError.CODES.ENCODE_ERROR, + 'Invalid u256 value - value is less than zero.', + { + type: 'u256', + value: input.toString(), + } + ) ); }); @@ -347,7 +450,10 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_u256(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256 value - value exceeds maximum.', { + type: 'u256', + value: input.toString(), + }) ); }); }); @@ -424,7 +530,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b256(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value: input, + }) ); }); @@ -433,7 +541,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b256(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value: input, + }) ); }); @@ -442,7 +552,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b256(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value: input, + }) ); }); }); @@ -476,7 +588,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b512(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.', { + value: input, + }) ); }); @@ -485,7 +599,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b512(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.', { + value: input, + }) ); }); @@ -494,7 +610,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_b512(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b512 value - malformed hex value.', { + value: input, + }) ); }); }); @@ -570,7 +688,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_str(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Value length mismatch during encode.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid string value - unexpected length.', { + value: input, + }) ); }); @@ -579,7 +699,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_str(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Value length mismatch during encode.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid string value - unexpected length.', { + value: input, + }) ); }); }); @@ -702,7 +824,9 @@ describe('AbiCoder', () => { await expectToThrowFuelError( () => contract.functions.types_array(input).call(), - new FuelError(FuelError.CODES.ENCODE_ERROR, 'Types/values length mismatch.') + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid array value - unexpected length.', { + value: input, + }) ); }); }); @@ -793,7 +917,7 @@ describe('AbiCoder', () => { describe('types_array_with_vector', () => { it('should encode/decode just fine', async () => { - const input = [[1, 2, 3]] as [Vec]; + const input = [[1, 2, 3]] as [BigNumberish[]]; const expected = [[3, 2, 1]]; const { waitForResult } = await contract.functions.types_array_with_vector(input).call(); @@ -991,9 +1115,9 @@ describe('AbiCoder', () => { expect(fn.name).toBe('types_struct_with_implicit_generics'); expect(fn.signature).toEqual( - 'types_struct_with_implicit_generics(s(a[b256;3],(b256,u8)))' + 'types_struct_with_implicit_generics(s(a[b256;3],(b256,u8)))' ); - expect(fn.selector).toEqual('0x0000000099d41855'); + expect(fn.selector).toEqual('0x0000000098941324'); expect(fn.selectorBytes).toEqual( new Uint8Array([ 0, 0, 0, 0, 0, 0, 0, 35, 116, 121, 112, 101, 115, 95, 115, 116, 114, 117, 99, 116, 95, @@ -1006,10 +1130,7 @@ describe('AbiCoder', () => { }); describe('types_struct_with_array', () => { - /** - * TODO: This is causing a generic to be left into the parsed format. - */ - it.skip('should encode/decode just fine', async () => { + it('should encode/decode just fine', async () => { // Inputs const inputB256: string = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; @@ -1091,14 +1212,10 @@ describe('AbiCoder', () => { describe('types_struct_with_array_of_enums', () => { it.todo('should encode/decode just fine', async () => { const input: StructWithEnumArrayInput = { - a: [EnumWithNativeInput.Checked, EnumWithNativeInput.Checked, EnumWithNativeInput.Checked], + a: [EnumWithNative.Checked, EnumWithNative.Checked, EnumWithNative.Checked], }; const expected: StructWithEnumArrayOutput = { - a: [ - EnumWithNativeOutput.Pending, - EnumWithNativeOutput.Pending, - EnumWithNativeOutput.Pending, - ], + a: [EnumWithNative.Pending, EnumWithNative.Pending, EnumWithNative.Pending], }; const { waitForResult } = await contract.functions @@ -1246,9 +1363,6 @@ describe('AbiCoder', () => { .call(); await waitForResult(); - // const { value, logs } = await waitForResult(); - // expect(value).toStrictEqual(expected); - // expect(logs).toStrictEqual([expected]); }); it('should have function properties', () => { @@ -1470,8 +1584,8 @@ describe('AbiCoder', () => { */ describe('types_enum', () => { it('should encode/decode just fine', async () => { - const input = EnumWithNativeInput.Checked; - const expected = EnumWithNativeInput.Pending; + const input = EnumWithNative.Checked; + const expected = EnumWithNative.Pending; const { waitForResult } = await contract.functions.types_enum(input).call(); @@ -1579,8 +1693,8 @@ describe('AbiCoder', () => { describe('types_enum_external', () => { it('should encode/decode just fine', async () => { - const input = ExternalEnumInput.A; - const expected = ExternalEnumInput.B; + const input = ExternalEnum.A; + const expected = ExternalEnum.B; const { waitForResult } = await contract.functions.types_enum_external(input).call(); @@ -1607,7 +1721,7 @@ describe('AbiCoder', () => { describe('types_enum_with_structs', () => { it('should encode/decode just fine', async () => { - const input = { a: EnumWithNativeInput.Checked }; + const input = { a: EnumWithNative.Checked }; const expected = { b: { a: true, b: 10 } }; const { waitForResult } = await contract.functions.types_enum_with_structs(input).call(); @@ -1759,8 +1873,8 @@ describe('AbiCoder', () => { describe('types_vector_option', () => { it('should encode/decode just fine', async () => { - const input: Vec = [{ a: [1, 2, 3, 4, 5] }]; - const expected: Vec = [{ a: [5, 4, 3, 2, 1] }]; + const input: StructWithMultiOptionInput[] = [{ a: [1, 2, 3, 4, 5] }]; + const expected: StructWithMultiOptionOutput[] = [{ a: [5, 4, 3, 2, 1] }]; const { waitForResult } = await contract.functions.types_vector_option(input).call(); diff --git a/packages/fuel-gauge/src/abi/abi-gen.test.ts b/packages/fuel-gauge/src/abi/abi-gen.test.ts new file mode 100644 index 00000000000..12750557b77 --- /dev/null +++ b/packages/fuel-gauge/src/abi/abi-gen.test.ts @@ -0,0 +1,169 @@ +import { runTypegen } from '@fuel-ts/abi/cli'; +import { randomUUID } from 'crypto'; +import { cpSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync } from 'fs'; +import { FuelError } from 'fuels'; +import { expectToThrowFuelError } from 'fuels/test-utils'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +import { AbiProjectsEnum, autoUpdateFixture, getAbiForcProject } from './utils'; + +function generateTmpDir(fromDir?: string) { + const dir = join(tmpdir(), 'fuels', randomUUID()); + + mkdirSync(dir, { recursive: true }); + + if (fromDir) { + cpSync(fromDir, dir, { recursive: true }); + } + + return { path: dir, [Symbol.dispose]: () => rmdirSync(dir, { recursive: true }) }; +} + +/** + * @group node + */ +describe('AbiGen', () => { + beforeAll(() => {}); + + test('Generates all files correctly', () => { + const fixtureResultMap = new Map([ + ['index', 'index.ts'], + ['common', 'common.ts'], + + ['contracts/contract-index', 'contracts/index.ts'], + ['contracts/contract', 'contracts/AbiContract.ts'], + ['contracts/contract-types', 'contracts/AbiContractTypes.ts'], + ['contracts/contract-factory', 'contracts/AbiContractFactory.ts'], + ['contracts/contract-bytecode', 'contracts/AbiContract-bytecode.ts'], + ['contracts/contract-abi', 'contracts/AbiContract-abi.ts'], + ['contracts/contract-storage-slots', 'contracts/AbiContract-storage-slots.ts'], + + ['predicates/predicate-index', 'predicates/index.ts'], + ['predicates/predicate', 'predicates/AbiPredicate.ts'], + ['predicates/predicate-types', 'predicates/AbiPredicateTypes.ts'], + ['predicates/predicate-abi', 'predicates/AbiPredicate-abi.ts'], + + ['scripts/script-index', 'scripts/index.ts'], + ['scripts/script', 'scripts/AbiScript.ts'], + ['scripts/script-types', 'scripts/AbiScriptTypes.ts'], + ['scripts/script-abi', 'scripts/AbiScript-abi.ts'], + ]); + + const { buildDir: contractDir } = getAbiForcProject(AbiProjectsEnum.ABI_CONTRACT); + const { buildDir: predicateDir } = getAbiForcProject(AbiProjectsEnum.ABI_PREDICATE); + const { abiPath: scriptAbiPath } = getAbiForcProject(AbiProjectsEnum.ABI_SCRIPT); + + using output = generateTmpDir(); + + runTypegen({ + inputs: [contractDir, predicateDir, scriptAbiPath], + output: output.path, + }); + + fixtureResultMap.forEach((filename, fixture) => { + const fixtureFile = join( + process.cwd(), + `packages/fuel-gauge/src/abi/fixtures/${fixture}.txt` + ); + const expected = readFileSync(fixtureFile).toString(); + const generated = readFileSync(join(output.path, filename)).toString(); + + autoUpdateFixture(fixtureFile, generated); + + expect(generated, `Generated file ${filename} does not match expected`).toEqual(expected); + }); + }); + + test('logs if no abi json file found and skips path', () => { + const logSpy = vi.spyOn(console, 'log').mockImplementationOnce(() => {}); + const { buildDir: scriptBuildDir, name: scriptName } = getAbiForcProject( + AbiProjectsEnum.ABI_SCRIPT + ); + const { buildDir: predicateBuildDir } = getAbiForcProject(AbiProjectsEnum.ABI_PREDICATE); + using scriptDir = generateTmpDir(scriptBuildDir); + + rmSync(join(scriptDir.path, `${scriptName}-abi.json`)); + + using predicateDir = generateTmpDir(predicateBuildDir); + + runTypegen({ + inputs: [scriptDir.path, predicateDir.path], + output: scriptDir.path, + }); + + expect(logSpy).toHaveBeenCalledWith( + `No abi file found in ${scriptDir.path}, skipping this path.` + ); + const outputDirContents = readdirSync(scriptDir.path); + + expect(outputDirContents).not.toContain('scripts'); + expect(outputDirContents).toContain('predicates'); + }); + + test('skips contract factory and bytecode generation when bytecode is missing and logs it', () => { + const { buildDir, name } = getAbiForcProject(AbiProjectsEnum.ABI_CONTRACT); + using tmpDir = generateTmpDir(buildDir); + + rmSync(join(tmpDir.path, `${name}.bin`)); + + const spy = vi.spyOn(console, 'log').mockImplementationOnce(() => {}); + + runTypegen({ + inputs: [tmpDir.path], + output: tmpDir.path, + }); + + expect(spy).toHaveBeenCalledWith( + `No bytecode found for contract at ${tmpDir.path}, will not generate ContractFactory for it.` + ); + + const contractsOutputs = readdirSync(join(tmpDir.path, 'contracts')); + + expect(contractsOutputs).toContain('index.ts'); + expect(contractsOutputs).toContain('AbiContract.ts'); + expect(contractsOutputs).toContain('AbiContractTypes.ts'); + expect(contractsOutputs).toContain('AbiContract-abi.ts'); + expect(contractsOutputs).toContain('AbiContract-storage-slots.ts'); + expect(contractsOutputs).not.toContain('AbiContractFactory.ts'); + expect(contractsOutputs).not.toContain('AbiContract-bytecode.ts'); + }); + + test('throws when missing bytecode for script', async () => { + const { buildDir, name } = getAbiForcProject(AbiProjectsEnum.ABI_SCRIPT); + using tmpDir = generateTmpDir(buildDir); + + rmSync(join(tmpDir.path, `${name}.bin`)); + + await expectToThrowFuelError( + () => + runTypegen({ + inputs: [tmpDir.path], + output: tmpDir.path, + }), + new FuelError( + FuelError.CODES.BIN_FILE_NOT_FOUND, + `For scripts, the bytecode is required. No bytecode found for script at ${tmpDir.path}.` + ) + ); + }); + + test('throws when missing bytecode for predicate', async () => { + const { buildDir, name } = getAbiForcProject(AbiProjectsEnum.ABI_PREDICATE); + using tmpDir = generateTmpDir(buildDir); + + rmSync(join(tmpDir.path, `${name}.bin`)); + + await expectToThrowFuelError( + () => + runTypegen({ + inputs: [tmpDir.path], + output: tmpDir.path, + }), + new FuelError( + FuelError.CODES.BIN_FILE_NOT_FOUND, + `For predicates, the bytecode is required. No bytecode found for predicate at ${tmpDir.path}.` + ) + ); + }); +}); diff --git a/packages/fuel-gauge/src/abi/abi-parser.json b/packages/fuel-gauge/src/abi/abi-parser.json new file mode 100644 index 00000000000..373715abe6c --- /dev/null +++ b/packages/fuel-gauge/src/abi/abi-parser.json @@ -0,0 +1,1732 @@ +{ + "metadataTypes": [ + { + "metadataTypeId": 0, + "swayType": "(_, _)", + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "generic F", + "metadata": { + "metadataTypeId": 5 + } + } + } + ] + }, + { + "metadataTypeId": 1, + "swayType": "(_, _)", + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "struct StructWithImplicitGenerics", + "metadata": { + "metadataTypeId": 12, + "typeArguments": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + { + "swayType": "b256", + "metadata": { + "metadataTypeId": 3 + } + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "generic F", + "metadata": { + "metadataTypeId": 5 + } + } + } + ] + } + } + ] + } + } + ] + }, + { + "metadataTypeId": 2, + "swayType": "[_; 3]", + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + } + ] + }, + { + "metadataTypeId": 3, + "swayType": "b256" + }, + { + "metadataTypeId": 4, + "swayType": "generic E" + }, + { + "metadataTypeId": 5, + "swayType": "generic F" + }, + { + "metadataTypeId": 6, + "swayType": "generic T" + }, + { + "metadataTypeId": 8, + "swayType": "struct DoubleGeneric", + "components": [ + { + "name": "a", + "type": { + "swayType": "generic T", + "metadata": { + "metadataTypeId": 6 + } + } + }, + { + "name": "b", + "type": { + "swayType": "generic F", + "metadata": { + "metadataTypeId": 5 + } + } + } + ], + "typeParameters": [ + { + "metadataTypeId": 6, + "swayType": "generic T" + }, + { + "metadataTypeId": 5, + "swayType": "generic F" + } + ] + }, + { + "metadataTypeId": 9, + "swayType": "struct GenericStruct", + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "generic T", + "metadata": { + "metadataTypeId": 6 + } + } + } + ], + "typeParameters": [ + { + "metadataTypeId": 6, + "swayType": "generic T" + } + ] + }, + { + "metadataTypeId": 10, + "swayType": "struct NestedGenericStruct", + "components": [ + { + "name": "a", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + } + ] + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + } + }, + { + "name": "c", + "type": { + "swayType": "struct DoubleGeneric", + "metadata": { + "metadataTypeId": 8, + "typeArguments": [ + { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + }, + { + "name": "b", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ], + "typeParameters": [ + { + "metadataTypeId": 4, + "swayType": "generic E" + } + ] + }, + { + "metadataTypeId": 11, + "swayType": "struct SimpleStruct", + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + }, + { + "metadataTypeId": 12, + "swayType": "struct StructWithImplicitGenerics", + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "generic E", + "metadata": { + "metadataTypeId": 4 + } + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "generic F", + "metadata": { + "metadataTypeId": 5 + } + } + } + ] + } + } + ], + "typeParameters": [ + { + "metadataTypeId": 4, + "swayType": "generic E" + }, + { + "metadataTypeId": 5, + "swayType": "generic F" + } + ] + }, + { + "metadataTypeId": 13, + "swayType": "struct std::bytes::Bytes" + }, + { + "metadataTypeId": 16, + "swayType": "struct std::vec::Vec", + "components": [ + { + "name": "", + "type": { + "swayType": "generic T", + "metadata": { + "metadataTypeId": 6 + } + } + } + ], + "typeParameters": [ + { + "metadataTypeId": 6, + "swayType": "generic T" + } + ] + }, + { + "metadataTypeId": 17, + "swayType": "u32" + }, + { + "metadataTypeId": 18, + "swayType": "u64" + } + ], + "concreteTypes": [ + { + "concreteTypeId": "9dc54ad1b685b6abf04fbcc93696e440452944466e2dfd64b5df956a13ad2027", + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 1 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "struct StructWithImplicitGenerics", + "metadata": { + "metadataTypeId": 12, + "typeArguments": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + { + "swayType": "b256", + "metadata": { + "metadataTypeId": 3 + } + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "b256", + "metadata": { + "metadataTypeId": 3 + } + } + } + ] + } + } + ] + } + } + ] + }, + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + { + "concreteTypeId": "8fa64aacdb756049c3c90d3a5fa8d1a7ebedefc8dc2f347e67bb26ef4c7140a7", + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "688b6ed7fc2c45e135ea9a2ce11e3f5313a4c057ba5d616e3381937605ea81e4", + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct SimpleStruct", + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + } + ] + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "struct std::vec::Vec", + "concreteTypeId": "688b6ed7fc2c45e135ea9a2ce11e3f5313a4c057ba5d616e3381937605ea81e4", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct SimpleStruct", + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + } + ] + } + } + ] + }, + { + "concreteTypeId": "cc3087210794115a9b7e470ad5b5d554808a3a88aa003ae80bae7b0dc4505f50", + "swayType": "struct NestedGenericStruct", + "metadata": { + "metadataTypeId": 10, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + } + }, + { + "name": "c", + "type": { + "swayType": "struct DoubleGeneric", + "metadata": { + "metadataTypeId": 8, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + }, + { + "name": "b", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + }, + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + }, + { + "concreteTypeId": "426345c4ea93db9f08eeb9fe6047ef0273294bfb1140600a0660be9f2a08d750", + "swayType": "struct StructWithImplicitGenerics", + "metadata": { + "metadataTypeId": 12, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + }, + { + "concreteTypeId": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", + "swayType": "struct std::bytes::Bytes", + "metadata": { + "metadataTypeId": 13 + } + }, + { + "concreteTypeId": "688b6ed7fc2c45e135ea9a2ce11e3f5313a4c057ba5d616e3381937605ea81e4", + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct SimpleStruct", + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + } + ] + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ], + "encodingVersion": "1", + "programType": "contract", + "functions": [ + { + "name": "bytes", + "output": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + "inputs": [ + { + "name": "arg", + "type": { + "concreteTypeId": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", + "swayType": "struct std::bytes::Bytes", + "metadata": { + "metadataTypeId": 13 + } + } + } + ] + }, + { + "name": "generic_structs", + "output": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + "inputs": [ + { + "name": "arg1", + "type": { + "concreteTypeId": "8fa64aacdb756049c3c90d3a5fa8d1a7ebedefc8dc2f347e67bb26ef4c7140a7", + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "688b6ed7fc2c45e135ea9a2ce11e3f5313a4c057ba5d616e3381937605ea81e4", + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct SimpleStruct", + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + } + ] + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "struct std::vec::Vec", + "concreteTypeId": "688b6ed7fc2c45e135ea9a2ce11e3f5313a4c057ba5d616e3381937605ea81e4", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "swayType": "struct SimpleStruct", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct SimpleStruct", + "concreteTypeId": "75f7f7a06026cab5d7a70984d1fde56001e83505e3a091ff9722b92d7f56d8be", + "metadata": { + "metadataTypeId": 11 + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + } + ] + } + } + ] + } + }, + { + "name": "arg2", + "type": { + "concreteTypeId": "cc3087210794115a9b7e470ad5b5d554808a3a88aa003ae80bae7b0dc4505f50", + "swayType": "struct NestedGenericStruct", + "metadata": { + "metadataTypeId": 10, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "struct std::vec::Vec", + "metadata": { + "metadataTypeId": 16, + "typeArguments": [ + { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + ] + }, + "components": [ + { + "name": "", + "type": { + "swayType": "struct GenericStruct", + "metadata": { + "metadataTypeId": 9, + "typeArguments": [ + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "b", + "type": { + "swayType": "u32", + "metadata": { + "metadataTypeId": 17 + } + } + }, + { + "name": "c", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + } + }, + { + "name": "c", + "type": { + "swayType": "struct DoubleGeneric", + "metadata": { + "metadataTypeId": 8, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + }, + { + "name": "b", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + } + } + ] + }, + { + "name": "implicit_generic_struct", + "output": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + "inputs": [ + { + "name": "arg1", + "type": { + "concreteTypeId": "426345c4ea93db9f08eeb9fe6047ef0273294bfb1140600a0660be9f2a08d750", + "swayType": "struct StructWithImplicitGenerics", + "metadata": { + "metadataTypeId": 12, + "typeArguments": [ + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "swayType": "u16" + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + } + ] + } + } + ] + } + }, + { + "name": "arg2", + "type": { + "concreteTypeId": "9dc54ad1b685b6abf04fbcc93696e440452944466e2dfd64b5df956a13ad2027", + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 1 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "struct StructWithImplicitGenerics", + "metadata": { + "metadataTypeId": 12, + "typeArguments": [ + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + }, + { + "swayType": "b256", + "metadata": { + "metadataTypeId": 3 + } + } + ] + }, + "components": [ + { + "name": "a", + "type": { + "swayType": "[_; 3]", + "metadata": { + "metadataTypeId": 2 + }, + "components": [ + { + "name": "__array_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + } + ] + } + }, + { + "name": "b", + "type": { + "swayType": "(_, _)", + "metadata": { + "metadataTypeId": 0 + }, + "components": [ + { + "name": "__tuple_element", + "type": { + "swayType": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + }, + { + "name": "__tuple_element", + "type": { + "swayType": "b256", + "metadata": { + "metadataTypeId": 3 + } + } + } + ] + } + } + ] + } + } + ] + } + } + ] + } + ], + "loggedTypes": [ + { + "logId": "13213829929622723620", + "type": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + } + } + ], + "messageTypes": [ + { + "messageId": "0", + "type": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + } + }, + { + "messageId": "1", + "type": { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "swayType": "bool" + } + } + ], + "configurables": [ + { + "name": "U8_VALUE", + "offset": 5360, + "type": { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "swayType": "u8" + } + } + ] +} diff --git a/packages/fuel-gauge/src/abi/abi-parser.test.ts b/packages/fuel-gauge/src/abi/abi-parser.test.ts new file mode 100644 index 00000000000..07498ae19ce --- /dev/null +++ b/packages/fuel-gauge/src/abi/abi-parser.test.ts @@ -0,0 +1,51 @@ +import { AbiParser, type AbiSpecification } from 'fuels'; + +import { Parser } from '../../test/typegen'; + +import expected from './abi-parser.json'; + +/** + * @group node + * @group browser + */ +describe('AbiParser', () => { + test('parses encoding version as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.encodingVersion).toEqual(expected.encodingVersion); + }); + + test('parses program type as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.programType).toEqual(expected.programType); + }); + + test('parses metadata types as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.metadataTypes).toEqual(expected.metadataTypes); + }); + + test('parses concrete types as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.concreteTypes).toEqual(expected.concreteTypes); + }); + + test('parses functions as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.functions).toEqual(expected.functions); + }); + + test('parses logged types as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.loggedTypes).toEqual(expected.loggedTypes); + }); + + test('parses message types as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.messageTypes).toEqual(expected.messageTypes); + }); + + test('parses configurables as expected', () => { + const parsed = AbiParser.parse(Parser.abi as AbiSpecification); + expect(parsed.configurables).toEqual(expected.configurables); + }); +}); diff --git a/packages/fuel-gauge/src/abi/abi-script.test.ts b/packages/fuel-gauge/src/abi/abi-script.test.ts index c6eb5eecb05..d8be4a79432 100644 --- a/packages/fuel-gauge/src/abi/abi-script.test.ts +++ b/packages/fuel-gauge/src/abi/abi-script.test.ts @@ -8,7 +8,6 @@ import { type AssetIdInput, type ScriptWithComplexArgsInputs, } from '../../test/typegen/scripts/ScriptWithComplexArgs'; -import type { Vec } from '../../test/typegen/scripts/common'; /** * @group browser @@ -52,9 +51,11 @@ describe('abi-script', () => { const arg1 = 100; const arg2 = { bits: getRandomB256() }; const arg3 = 100; - const arg4 = [[{ bits: getRandomB256() }, { bits: getRandomB256() }, true]] as Vec< - [AssetIdInput, AssetIdInput, boolean] - >; + const arg4 = [[{ bits: getRandomB256() }, { bits: getRandomB256() }, true]] as [ + AssetIdInput, + AssetIdInput, + boolean, + ][]; const arg5 = { Address: { bits: getRandomB256() } }; const arg6 = 100; const expected = [ diff --git a/packages/fuel-gauge/src/abi/fixtures/common.txt b/packages/fuel-gauge/src/abi/fixtures/common.txt new file mode 100644 index 00000000000..d2f0bd72ea0 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/common.txt @@ -0,0 +1,53 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { AbiCoderFunction, InvokeFunction } from 'fuels'; + +/** + * Mimics Sway Enum. + * Requires one and only one Key-Value pair and raises error if more are provided. + */ +export type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +/** + * Mimics Sway Option type. + */ +export type Option = T | undefined; + +/** + * Mimics Sway Result enum type. + * Ok represents the success case, while Err represents the error case. + */ +export type Result = Enum<{ Ok: T; Err: E }>; + +/** + * Mimics Sway array type. For example, [u64; 10] is converted to ArrayOfLength. + */ +export type ArrayOfLength< + T, + Length extends number, + Arr extends unknown[] = [], +> = Arr['length'] extends Length ? Arr : ArrayOfLength; + +interface Types { + functions: Record; + configurables: Partial>; +} + +export type ProgramFunctionMapper = { + [K in keyof T]: InvokeFunction; +}; + +export type InterfaceFunctionMapper = { + [K in keyof T]: AbiCoderFunction; +}; \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-abi.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-abi.txt new file mode 100644 index 00000000000..e721288abc0 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-abi.txt @@ -0,0 +1,2923 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { AbiSpecification } from 'fuels'; + +export const abi: AbiSpecification = { + "programType": "contract", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "()", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "type": "(b256, bool)", + "concreteTypeId": "d5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640", + "metadataTypeId": 0 + }, + { + "type": "(bool, u64)", + "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", + "metadataTypeId": 8 + }, + { + "type": "(str[5], bool)", + "concreteTypeId": "a1e229302ed2f092752a6bc4fbe66bb9305e0802b1b01ecc5e1d59356702e956", + "metadataTypeId": 1 + }, + { + "type": "(str[5], str[5])", + "concreteTypeId": "30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e", + "metadataTypeId": 2 + }, + { + "type": "(struct data_structures::StructDoubleGeneric<[b256; 3],u8>, [struct data_structures::StructDoubleGeneric; 4], (str[5], bool), struct data_structures::StructSimple)", + "concreteTypeId": "343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e", + "metadataTypeId": 13 + }, + { + "type": "(struct data_structures::StructSimple, struct std::vec::Vec)", + "concreteTypeId": "5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c", + "metadataTypeId": 3 + }, + { + "type": "(struct std::asset_id::AssetId, struct std::asset_id::AssetId, bool)", + "concreteTypeId": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6", + "metadataTypeId": 10 + }, + { + "type": "(struct std::vec::Vec, b256)", + "concreteTypeId": "52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94", + "metadataTypeId": 5 + }, + { + "type": "(struct std::vec::Vec, struct std::vec::Vec)", + "concreteTypeId": "87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd", + "metadataTypeId": 6 + }, + { + "type": "(u32, struct std::vec::Vec, struct std::vec::Vec)", + "concreteTypeId": "18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000", + "metadataTypeId": 9 + }, + { + "type": "(u64, struct data_structures::StructSimple)", + "concreteTypeId": "0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc", + "metadataTypeId": 4 + }, + { + "type": "(u8, struct data_structures::StructSingleGeneric>, str[3])", + "concreteTypeId": "6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f", + "metadataTypeId": 11 + }, + { + "type": "(u8, u8, u8)", + "concreteTypeId": "79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4", + "metadataTypeId": 12 + }, + { + "type": "[b256; 3]", + "concreteTypeId": "81342782c917fcfd178741cb2b3a12ea1ebeaa57253fc4ee6700b4d7d6ab32d3", + "metadataTypeId": 17 + }, + { + "type": "[struct data_structures::StructDoubleGeneric,str[1]>; 2]", + "concreteTypeId": "b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006", + "metadataTypeId": 15 + }, + { + "type": "[struct data_structures::StructDoubleGeneric; 4]", + "concreteTypeId": "b22807669faa58263e636f6e2d194df8ddbc6686bb4ea14ee28005fa30adbe85", + "metadataTypeId": 22 + }, + { + "type": "[struct data_structures::StructSimple; 3]", + "concreteTypeId": "38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237", + "metadataTypeId": 18 + }, + { + "type": "[struct std::vec::Vec; 1]", + "concreteTypeId": "593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980", + "metadataTypeId": 14 + }, + { + "type": "[u8; 4]", + "concreteTypeId": "f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4", + "metadataTypeId": 23 + }, + { + "type": "b256", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "type": "bool", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "type": "enum abi-library::ExternalEnum", + "concreteTypeId": "9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42", + "metadataTypeId": 25 + }, + { + "type": "enum data_structures::EnumDoubleGeneric", + "concreteTypeId": "d0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307", + "metadataTypeId": 26, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + ] + }, + { + "type": "enum data_structures::EnumWithBuiltinType", + "concreteTypeId": "2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61", + "metadataTypeId": 27 + }, + { + "type": "enum data_structures::EnumWithNative", + "concreteTypeId": "58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93", + "metadataTypeId": 28 + }, + { + "type": "enum data_structures::EnumWithStructs", + "concreteTypeId": "9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669", + "metadataTypeId": 29 + }, + { + "type": "enum data_structures::EnumWithVector", + "concreteTypeId": "0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26", + "metadataTypeId": 30 + }, + { + "type": "enum std::identity::Identity", + "concreteTypeId": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335", + "metadataTypeId": 31 + }, + { + "type": "enum std::option::Option", + "concreteTypeId": "25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e", + "metadataTypeId": 32, + "typeArguments": [ + "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + ] + }, + { + "type": "enum std::option::Option", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", + "metadataTypeId": 32, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "enum std::result::Result", + "concreteTypeId": "9891b1ee451eed790368ea3969e3c8f550efa87de489b5d7b933e2290800791b", + "metadataTypeId": 33, + "typeArguments": [ + "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50" + ] + }, + { + "type": "enum std::result::Result", + "concreteTypeId": "b3131b4c08c16cfa55b3150d587c3afa3e4cdebe0399f3f599fa160baaa64e0c", + "metadataTypeId": 33, + "typeArguments": [ + "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + ] + }, + { + "type": "raw untyped slice", + "concreteTypeId": "1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570" + }, + { + "type": "str", + "concreteTypeId": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a" + }, + { + "type": "str[10]", + "concreteTypeId": "338a25cb65b9251663dcce6362b744fe10aa849758299590f4efed5dd299bf50" + }, + { + "type": "str[5]", + "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + }, + { + "type": "struct abi-library::ExternalStruct", + "concreteTypeId": "c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978", + "metadataTypeId": 45 + }, + { + "type": "struct data_structures::Configurables", + "concreteTypeId": "69d4f1cc5ce793681d98a55ab013f42ab56260131d39af6c1e71a5f3531557bc", + "metadataTypeId": 46 + }, + { + "type": "struct data_structures::StructA", + "concreteTypeId": "db8b04f624965fbfd7eb7dc3fc3c6a54a71d0019b37d4011a9350d1870136c9d", + "metadataTypeId": 47 + }, + { + "type": "struct data_structures::StructB", + "concreteTypeId": "9f074fde9cb9194b90bd208c8c95e709bfb1a5c736b063302e5639ce4daad5aa", + "metadataTypeId": 48 + }, + { + "type": "struct data_structures::StructC", + "concreteTypeId": "f219acbc9e3b812457419966b5454d10d51594afecacb87fb7745c9311b90012", + "metadataTypeId": 49 + }, + { + "type": "struct data_structures::StructD>>", + "concreteTypeId": "d0494e36b8daeafdf02dfbd1f65f82c66df872fb235c7fd2707fcd4147c6c292", + "metadataTypeId": 50, + "typeArguments": [ + "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "722eb56989dc44c372c470eb3a6ddb2f91e3924c1c4a0806d21e414046599d35" + ] + }, + { + "type": "struct data_structures::StructDoubleGeneric<[b256; 3],u8>", + "concreteTypeId": "7bdc2c1e9c4b8576fdf5be24c5c6569cba3a8feaba3755ed2b95d646a33c73e2", + "metadataTypeId": 51, + "typeArguments": [ + "81342782c917fcfd178741cb2b3a12ea1ebeaa57253fc4ee6700b4d7d6ab32d3", + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "struct data_structures::StructDoubleGeneric,u32>", + "concreteTypeId": "08dbec793087c5686c1a493513b158a999bb653126ee51151dfa85fa683edce5", + "metadataTypeId": 51, + "typeArguments": [ + "4946973fc1adce1f6b23e80f9fad29b44e6a4ab25f2b45f3fab95114cfcd33a0", + "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + ] + }, + { + "type": "struct data_structures::StructDoubleGeneric", + "concreteTypeId": "4946973fc1adce1f6b23e80f9fad29b44e6a4ab25f2b45f3fab95114cfcd33a0", + "metadataTypeId": 51, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + ] + }, + { + "type": "struct data_structures::StructF>", + "concreteTypeId": "722eb56989dc44c372c470eb3a6ddb2f91e3924c1c4a0806d21e414046599d35", + "metadataTypeId": 53, + "typeArguments": [ + "49f761c61dce644e212b8182e30557d35b6b4ad46693140be677eee0d6ef2733" + ] + }, + { + "type": "struct data_structures::StructG", + "concreteTypeId": "dfd8875bb49716b14dd336285ba667f953ed9aec4e918c0d7a2eb19ff644d60e", + "metadataTypeId": 54 + }, + { + "type": "struct data_structures::StructGenericWithEnum", + "concreteTypeId": "8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2", + "metadataTypeId": 55, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + ] + }, + { + "type": "struct data_structures::StructSimple", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1", + "metadataTypeId": 56 + }, + { + "type": "struct data_structures::StructSingleGeneric<(bool, u64)>", + "concreteTypeId": "fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698", + "metadataTypeId": 57, + "typeArguments": [ + "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be" + ] + }, + { + "type": "struct data_structures::StructSingleGeneric", + "concreteTypeId": "7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6", + "metadataTypeId": 57, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "struct data_structures::StructWithEnumArray", + "concreteTypeId": "d5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e", + "metadataTypeId": 58 + }, + { + "type": "struct data_structures::StructWithGenericArray", + "concreteTypeId": "29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad", + "metadataTypeId": 59, + "typeArguments": [ + "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + ] + }, + { + "type": "struct data_structures::StructWithImplicitGenerics", + "concreteTypeId": "549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60", + "metadataTypeId": 60, + "typeArguments": [ + "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "struct data_structures::StructWithMultiOption", + "concreteTypeId": "aa87500bb34c8bb09ffd60ab55cb1725898c366c58d3ff3aaaf8c9b532934fd1", + "metadataTypeId": 61 + }, + { + "type": "struct data_structures::StructWithNestedArray", + "concreteTypeId": "e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948", + "metadataTypeId": 62 + }, + { + "type": "struct data_structures::StructWithNestedStruct", + "concreteTypeId": "8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a", + "metadataTypeId": 63 + }, + { + "type": "struct data_structures::StructWithNestedTuple", + "concreteTypeId": "d042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5", + "metadataTypeId": 64 + }, + { + "type": "struct data_structures::StructWithSingleOption", + "concreteTypeId": "089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c", + "metadataTypeId": 65 + }, + { + "type": "struct data_structures::StructWithVector", + "concreteTypeId": "eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621", + "metadataTypeId": 66 + }, + { + "type": "struct std::address::Address", + "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308", + "metadataTypeId": 67 + }, + { + "type": "struct std::asset_id::AssetId", + "concreteTypeId": "c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974", + "metadataTypeId": 68 + }, + { + "type": "struct std::b512::B512", + "concreteTypeId": "745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322", + "metadataTypeId": 69 + }, + { + "type": "struct std::bytes::Bytes", + "concreteTypeId": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", + "metadataTypeId": 70 + }, + { + "type": "struct std::contract_id::ContractId", + "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", + "metadataTypeId": 72 + }, + { + "type": "struct std::string::String", + "concreteTypeId": "9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c", + "metadataTypeId": 73 + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31", + "metadataTypeId": 75, + "typeArguments": [ + "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "49f761c61dce644e212b8182e30557d35b6b4ad46693140be677eee0d6ef2733", + "metadataTypeId": 75, + "typeArguments": [ + "dfd8875bb49716b14dd336285ba667f953ed9aec4e918c0d7a2eb19ff644d60e" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468", + "metadataTypeId": 75, + "typeArguments": [ + "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d", + "metadataTypeId": 75, + "typeArguments": [ + "aa87500bb34c8bb09ffd60ab55cb1725898c366c58d3ff3aaaf8c9b532934fd1" + ] + }, + { + "type": "struct std::vec::Vec>", + "concreteTypeId": "e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e", + "metadataTypeId": 75, + "typeArguments": [ + "13c38f4111bad6468fad4f8ea82fd744546b63be49db9439fb3d94e14ae2bb3a" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "13c38f4111bad6468fad4f8ea82fd744546b63be49db9439fb3d94e14ae2bb3a", + "metadataTypeId": 75, + "typeArguments": [ + "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "d5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4", + "metadataTypeId": 75, + "typeArguments": [ + "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + ] + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e", + "metadataTypeId": 75, + "typeArguments": [ + "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + ] + }, + { + "type": "struct std::vm::evm::evm_address::EvmAddress", + "concreteTypeId": "05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2", + "metadataTypeId": 76 + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u256", + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" + }, + { + "type": "u32", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ + { + "type": "(_, _)", + "metadataTypeId": 0, + "components": [ + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 1, + "components": [ + { + "name": "__tuple_element", + "typeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + }, + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 2, + "components": [ + { + "name": "__tuple_element", + "typeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + }, + { + "name": "__tuple_element", + "typeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 3, + "components": [ + { + "name": "__tuple_element", + "typeId": 56 + }, + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 4, + "components": [ + { + "name": "__tuple_element", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "__tuple_element", + "typeId": 56 + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 5, + "components": [ + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "name": "__tuple_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 6, + "components": [ + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 7, + "components": [ + { + "name": "__tuple_element", + "typeId": 34 + }, + { + "name": "__tuple_element", + "typeId": 35 + } + ] + }, + { + "type": "(_, _)", + "metadataTypeId": 8, + "components": [ + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "__tuple_element", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "(_, _, _)", + "metadataTypeId": 9, + "components": [ + { + "name": "__tuple_element", + "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "name": "__tuple_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + } + ] + }, + { + "type": "(_, _, _)", + "metadataTypeId": 10, + "components": [ + { + "name": "__tuple_element", + "typeId": 68 + }, + { + "name": "__tuple_element", + "typeId": 68 + }, + { + "name": "__tuple_element", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ] + }, + { + "type": "(_, _, _)", + "metadataTypeId": 11, + "components": [ + { + "name": "__tuple_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "__tuple_element", + "typeId": 57, + "typeArguments": [ + { + "name": "", + "typeId": 57, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + } + ] + }, + { + "name": "__tuple_element", + "typeId": 44 + } + ] + }, + { + "type": "(_, _, _)", + "metadataTypeId": 12, + "components": [ + { + "name": "__tuple_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "__tuple_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "__tuple_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "type": "(_, _, _, _)", + "metadataTypeId": 13, + "components": [ + { + "name": "__tuple_element", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": 17 + }, + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "name": "__tuple_element", + "typeId": 22 + }, + { + "name": "__tuple_element", + "typeId": 1 + }, + { + "name": "__tuple_element", + "typeId": 56 + } + ] + }, + { + "type": "[_; 1]", + "metadataTypeId": 14, + "components": [ + { + "name": "__array_element", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + } + ] + } + ] + }, + { + "type": "[_; 2]", + "metadataTypeId": 15, + "components": [ + { + "name": "__array_element", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": 57, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "name": "", + "typeId": 43 + } + ] + } + ] + }, + { + "type": "[_; 2]", + "metadataTypeId": 16, + "components": [ + { + "name": "__array_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "[_; 3]", + "metadataTypeId": 17, + "components": [ + { + "name": "__array_element", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "[_; 3]", + "metadataTypeId": 18, + "components": [ + { + "name": "__array_element", + "typeId": 56 + } + ] + }, + { + "type": "[_; 3]", + "metadataTypeId": 19, + "components": [ + { + "name": "__array_element", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": 36 + }, + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "[_; 3]", + "metadataTypeId": 20, + "components": [ + { + "name": "__array_element", + "typeId": 28 + } + ] + }, + { + "type": "[_; 3]", + "metadataTypeId": 21, + "components": [ + { + "name": "__array_element", + "typeId": 34 + } + ] + }, + { + "type": "[_; 4]", + "metadataTypeId": 22, + "components": [ + { + "name": "__array_element", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ] + } + ] + }, + { + "type": "[_; 4]", + "metadataTypeId": 23, + "components": [ + { + "name": "__array_element", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "type": "[_; 5]", + "metadataTypeId": 24, + "components": [ + { + "name": "__array_element", + "typeId": 32, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "enum abi-library::ExternalEnum", + "metadataTypeId": 25, + "components": [ + { + "name": "A", + "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "B", + "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ] + }, + { + "type": "enum data_structures::EnumDoubleGeneric", + "metadataTypeId": 26, + "components": [ + { + "name": "a", + "typeId": 38 + }, + { + "name": "b", + "typeId": 39 + } + ], + "typeParameters": [ + 38, + 39 + ] + }, + { + "type": "enum data_structures::EnumWithBuiltinType", + "metadataTypeId": 27, + "components": [ + { + "name": "a", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "b", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "enum data_structures::EnumWithNative", + "metadataTypeId": 28, + "components": [ + { + "name": "Checked", + "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "Pending", + "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ] + }, + { + "type": "enum data_structures::EnumWithStructs", + "metadataTypeId": 29, + "components": [ + { + "name": "a", + "typeId": 28 + }, + { + "name": "b", + "typeId": 56 + }, + { + "name": "c", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "", + "typeId": 56 + } + ] + } + ] + }, + { + "type": "enum data_structures::EnumWithVector", + "metadataTypeId": 30, + "components": [ + { + "name": "a", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "b", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "enum std::identity::Identity", + "metadataTypeId": 31, + "components": [ + { + "name": "Address", + "typeId": 67 + }, + { + "name": "ContractId", + "typeId": 72 + } + ] + }, + { + "type": "enum std::option::Option", + "metadataTypeId": 32, + "components": [ + { + "name": "None", + "typeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "Some", + "typeId": 37 + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "enum std::result::Result", + "metadataTypeId": 33, + "components": [ + { + "name": "Ok", + "typeId": 37 + }, + { + "name": "Err", + "typeId": 34 + } + ], + "typeParameters": [ + 37, + 34 + ] + }, + { + "type": "generic E", + "metadataTypeId": 34 + }, + { + "type": "generic F", + "metadataTypeId": 35 + }, + { + "type": "generic K", + "metadataTypeId": 36 + }, + { + "type": "generic T", + "metadataTypeId": 37 + }, + { + "type": "generic T1", + "metadataTypeId": 38 + }, + { + "type": "generic T2", + "metadataTypeId": 39 + }, + { + "type": "generic U", + "metadataTypeId": 40 + }, + { + "type": "generic V", + "metadataTypeId": 41 + }, + { + "type": "raw untyped ptr", + "metadataTypeId": 42 + }, + { + "type": "str[1]", + "metadataTypeId": 43 + }, + { + "type": "str[3]", + "metadataTypeId": 44 + }, + { + "type": "struct abi-library::ExternalStruct", + "metadataTypeId": 45, + "components": [ + { + "name": "value", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "struct data_structures::Configurables", + "metadataTypeId": 46, + "components": [ + { + "name": "U8_VALUE", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "BOOL_VALUE", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "B256_VALUE", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "OPTION_U8_VALUE", + "typeId": 32, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "name": "GENERIC_STRUCT_VALUE", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "", + "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + ] + }, + { + "name": "", + "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + } + ] + } + ] + }, + { + "type": "struct data_structures::StructA", + "metadataTypeId": 47, + "components": [ + { + "name": "propA1", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "type": "struct data_structures::StructB", + "metadataTypeId": 48, + "components": [ + { + "name": "propB1", + "typeId": 47 + }, + { + "name": "propB2", + "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + ] + }, + { + "type": "struct data_structures::StructC", + "metadataTypeId": 49, + "components": [ + { + "name": "propC1", + "typeId": 47 + }, + { + "name": "propC2", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": 48 + } + ] + }, + { + "name": "propC3", + "typeId": 50, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "", + "typeId": 53, + "typeArguments": [ + { + "name": "", + "typeId": 43 + } + ] + } + ] + } + ] + }, + { + "type": "struct data_structures::StructD", + "metadataTypeId": 50, + "components": [ + { + "name": "propD1", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": 52, + "typeArguments": [ + { + "name": "", + "typeId": 37 + } + ] + } + ] + }, + { + "name": "propD2", + "typeId": 40 + }, + { + "name": "propD3", + "typeId": 41 + } + ], + "typeParameters": [ + 37, + 40, + 41 + ] + }, + { + "type": "struct data_structures::StructDoubleGeneric", + "metadataTypeId": 51, + "components": [ + { + "name": "a", + "typeId": 38 + }, + { + "name": "b", + "typeId": 39 + } + ], + "typeParameters": [ + 38, + 39 + ] + }, + { + "type": "struct data_structures::StructE", + "metadataTypeId": 52, + "components": [ + { + "name": "propE1", + "typeId": 47 + }, + { + "name": "propE2", + "typeId": 48 + }, + { + "name": "propE3", + "typeId": 37 + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "struct data_structures::StructF", + "metadataTypeId": 53, + "components": [ + { + "name": "propF1", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "propF2", + "typeId": 37 + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "struct data_structures::StructG", + "metadataTypeId": 54, + "components": [ + { + "name": "propG1", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "type": "struct data_structures::StructGenericWithEnum", + "metadataTypeId": 55, + "components": [ + { + "name": "a", + "typeId": 38 + }, + { + "name": "b", + "typeId": 26, + "typeArguments": [ + { + "name": "", + "typeId": 38 + }, + { + "name": "", + "typeId": 39 + } + ] + } + ], + "typeParameters": [ + 38, + 39 + ] + }, + { + "type": "struct data_structures::StructSimple", + "metadataTypeId": 56, + "components": [ + { + "name": "a", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "b", + "typeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + } + ] + }, + { + "type": "struct data_structures::StructSingleGeneric", + "metadataTypeId": 57, + "components": [ + { + "name": "a", + "typeId": 37 + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "struct data_structures::StructWithEnumArray", + "metadataTypeId": 58, + "components": [ + { + "name": "a", + "typeId": 20 + } + ] + }, + { + "type": "struct data_structures::StructWithGenericArray", + "metadataTypeId": 59, + "components": [ + { + "name": "a", + "typeId": 19 + } + ], + "typeParameters": [ + 36 + ] + }, + { + "type": "struct data_structures::StructWithImplicitGenerics", + "metadataTypeId": 60, + "components": [ + { + "name": "a", + "typeId": 21 + }, + { + "name": "b", + "typeId": 7 + } + ], + "typeParameters": [ + 34, + 35 + ] + }, + { + "type": "struct data_structures::StructWithMultiOption", + "metadataTypeId": 61, + "components": [ + { + "name": "a", + "typeId": 24 + } + ] + }, + { + "type": "struct data_structures::StructWithNestedArray", + "metadataTypeId": 62, + "components": [ + { + "name": "a", + "typeId": 15 + } + ] + }, + { + "type": "struct data_structures::StructWithNestedStruct", + "metadataTypeId": 63, + "components": [ + { + "name": "a", + "typeId": 51, + "typeArguments": [ + { + "name": "", + "typeId": 57, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + }, + { + "name": "", + "typeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + ] + } + ] + }, + { + "type": "struct data_structures::StructWithNestedTuple", + "metadataTypeId": 64, + "components": [ + { + "name": "a", + "typeId": 11 + } + ] + }, + { + "type": "struct data_structures::StructWithSingleOption", + "metadataTypeId": 65, + "components": [ + { + "name": "a", + "typeId": 32, + "typeArguments": [ + { + "name": "", + "typeId": 61 + } + ] + } + ] + }, + { + "type": "struct data_structures::StructWithVector", + "metadataTypeId": 66, + "components": [ + { + "name": "a", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "b", + "typeId": 75, + "typeArguments": [ + { + "name": "", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ] + } + ] + }, + { + "type": "struct std::address::Address", + "metadataTypeId": 67, + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "struct std::asset_id::AssetId", + "metadataTypeId": 68, + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "struct std::b512::B512", + "metadataTypeId": 69, + "components": [ + { + "name": "bits", + "typeId": 16 + } + ] + }, + { + "type": "struct std::bytes::Bytes", + "metadataTypeId": 70, + "components": [ + { + "name": "buf", + "typeId": 71 + }, + { + "name": "len", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "struct std::bytes::RawBytes", + "metadataTypeId": 71, + "components": [ + { + "name": "ptr", + "typeId": 42 + }, + { + "name": "cap", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "struct std::contract_id::ContractId", + "metadataTypeId": 72, + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "struct std::string::String", + "metadataTypeId": 73, + "components": [ + { + "name": "bytes", + "typeId": 70 + } + ] + }, + { + "type": "struct std::vec::RawVec", + "metadataTypeId": 74, + "components": [ + { + "name": "ptr", + "typeId": 42 + }, + { + "name": "cap", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "struct std::vec::Vec", + "metadataTypeId": 75, + "components": [ + { + "name": "buf", + "typeId": 74, + "typeArguments": [ + { + "name": "", + "typeId": 37 + } + ] + }, + { + "name": "len", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 37 + ] + }, + { + "type": "struct std::vm::evm::evm_address::EvmAddress", + "metadataTypeId": 76, + "components": [ + { + "name": "bits", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + } + ], + "functions": [ + { + "inputs": [], + "name": "attributes_doc_comment", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " This is a doc" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " This is another doc comment" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_inline_always", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "inline", + "arguments": [ + "always" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_inline_never", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "inline", + "arguments": [ + "never" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_none", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + }, + { + "inputs": [], + "name": "attributes_payable", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "payable", + "arguments": [] + } + ] + }, + { + "inputs": [], + "name": "attributes_storage_read", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_storage_read_write", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_storage_write", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "attributes_test", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": [ + { + "name": "test", + "arguments": [] + } + ] + }, + { + "inputs": [], + "name": "configurables", + "output": "69d4f1cc5ce793681d98a55ab013f42ab56260131d39af6c1e71a5f3531557bc", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "name": "y", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "name": "multi_arg_b256_bool", + "output": "d5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "7bdc2c1e9c4b8576fdf5be24c5c6569cba3a8feaba3755ed2b95d646a33c73e2" + }, + { + "name": "y", + "concreteTypeId": "b22807669faa58263e636f6e2d194df8ddbc6686bb4ea14ee28005fa30adbe85" + }, + { + "name": "z", + "concreteTypeId": "a1e229302ed2f092752a6bc4fbe66bb9305e0802b1b01ecc5e1d59356702e956" + }, + { + "name": "a", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + } + ], + "name": "multi_arg_complex", + "output": "343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + }, + { + "name": "y", + "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + } + ], + "name": "multi_arg_str_str", + "output": "30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + }, + { + "name": "y", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + } + ], + "name": "multi_arg_struct_vector", + "output": "5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "name": "y", + "concreteTypeId": "d5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4" + }, + { + "name": "z", + "concreteTypeId": "d5bfe1d4e1ace20166c9b50cadd47e862020561bde24f5189cfc2723f5ed76f4" + } + ], + "name": "multi_arg_u32_vector_vector", + "output": "18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "y", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + } + ], + "name": "multi_arg_u64_struct", + "output": "0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "name": "y", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "name": "multi_arg_u64_u64", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + }, + { + "name": "y", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "name": "multi_arg_vector_b256", + "output": "52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + }, + { + "name": "y", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + } + ], + "name": "multi_arg_vector_vector", + "output": "87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308" + } + ], + "name": "types_address", + "output": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6" + } + ], + "name": "types_alias_tuple_with_native_types", + "output": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4" + } + ], + "name": "types_array", + "output": "f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237" + } + ], + "name": "types_array_struct", + "output": "38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006" + } + ], + "name": "types_array_with_generic_struct", + "output": "b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980" + } + ], + "name": "types_array_with_vector", + "output": "593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974" + } + ], + "name": "types_asset_id", + "output": "c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ], + "name": "types_b256", + "output": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322" + } + ], + "name": "types_b512", + "output": "745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + } + ], + "name": "types_bool", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb" + } + ], + "name": "types_bytes", + "output": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" + } + ], + "name": "types_contract_id", + "output": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93" + } + ], + "name": "types_enum", + "output": "58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42" + } + ], + "name": "types_enum_external", + "output": "9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61" + } + ], + "name": "types_enum_with_builtin_type", + "output": "2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669" + } + ], + "name": "types_enum_with_structs", + "output": "9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26" + } + ], + "name": "types_enum_with_vector", + "output": "0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2" + } + ], + "name": "types_evm_address", + "output": "05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307" + } + ], + "name": "types_generic_enum", + "output": "d0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335" + } + ], + "name": "types_identity_address", + "output": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335" + } + ], + "name": "types_identity_contract_id", + "output": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" + } + ], + "name": "types_option", + "output": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e" + } + ], + "name": "types_option_struct", + "output": "25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570" + } + ], + "name": "types_raw_slice", + "output": "1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "b3131b4c08c16cfa55b3150d587c3afa3e4cdebe0399f3f599fa160baaa64e0c" + } + ], + "name": "types_result", + "output": "9891b1ee451eed790368ea3969e3c8f550efa87de489b5d7b933e2290800791b", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c" + } + ], + "name": "types_std_string", + "output": "9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + } + ], + "name": "types_str", + "output": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a" + } + ], + "name": "types_str_slice", + "output": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2" + } + ], + "name": "types_struct_double_generic", + "output": "8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978" + } + ], + "name": "types_struct_external", + "output": "c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6" + } + ], + "name": "types_struct_generic", + "output": "7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + } + ], + "name": "types_struct_simple", + "output": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad" + } + ], + "name": "types_struct_with_array", + "output": "29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e" + } + ], + "name": "types_struct_with_array_of_enums", + "output": "d5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d0494e36b8daeafdf02dfbd1f65f82c66df872fb235c7fd2707fcd4147c6c292" + } + ], + "name": "types_struct_with_complex_nested_struct", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60" + } + ], + "name": "types_struct_with_implicit_generics", + "output": "549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "db8b04f624965fbfd7eb7dc3fc3c6a54a71d0019b37d4011a9350d1870136c9d" + }, + { + "name": "y", + "concreteTypeId": "9f074fde9cb9194b90bd208c8c95e709bfb1a5c736b063302e5639ce4daad5aa" + }, + { + "name": "z", + "concreteTypeId": "f219acbc9e3b812457419966b5454d10d51594afecacb87fb7745c9311b90012" + } + ], + "name": "types_struct_with_multiple_struct_params", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948" + } + ], + "name": "types_struct_with_nested_array", + "output": "e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a" + } + ], + "name": "types_struct_with_nested_struct", + "output": "8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5" + } + ], + "name": "types_struct_with_nested_tuple", + "output": "d042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c" + } + ], + "name": "types_struct_with_single_option", + "output": "089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698" + } + ], + "name": "types_struct_with_tuple", + "output": "fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621" + } + ], + "name": "types_struct_with_vector", + "output": "eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4" + } + ], + "name": "types_tuple", + "output": "79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f" + } + ], + "name": "types_tuple_complex", + "output": "6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6" + } + ], + "name": "types_tuple_with_native_types", + "output": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + } + ], + "name": "types_u16", + "output": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" + } + ], + "name": "types_u256", + "output": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + } + ], + "name": "types_u32", + "output": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "name": "types_u64", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "name": "types_u8", + "output": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "y", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "z", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "a", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "name": "types_value_then_value_then_void_then_void", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "y", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "name": "types_value_then_void", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "y", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "z", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "name": "types_value_then_void_then_value", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31" + } + ], + "name": "types_vector_boolean", + "output": "6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e" + } + ], + "name": "types_vector_inside_vector", + "output": "e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d" + } + ], + "name": "types_vector_option", + "output": "c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + } + ], + "name": "types_vector_u8", + "output": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468" + } + ], + "name": "types_vector_with_struct", + "output": "9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "name": "types_void", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + }, + { + "name": "y", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "name": "types_void_then_value", + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "attributes": null + } + ], + "loggedTypes": [ + { + "logId": "8961848586872524460", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "logId": "13213829929622723620", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "logId": "15417698811679754926", + "concreteTypeId": "d5f6ab61fc224aae1bf15a89ab88840ed54e312a76a9735d1f60d4d0d1fae640" + }, + { + "logId": "3764736462721235256", + "concreteTypeId": "343f07ddcd75b9385bc193e0419f2e89c75fad67cbf4ad1b36a01a136620817e" + }, + { + "logId": "10098701174489624218", + "concreteTypeId": "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a" + }, + { + "logId": "3459380067145079360", + "concreteTypeId": "30022fd7ad3fda4035d30e4d86b705d4870924d4b4fe054624d2561fa12bb33e" + }, + { + "logId": "6826186604658872822", + "concreteTypeId": "5ebb7c8cdd38d1f676f9c7089a2da12b27114ee3771c2047f3295d4d30f8fd2c" + }, + { + "logId": "1730312528330453470", + "concreteTypeId": "18034e13b18b71de3c7e12f8f10a7bd48a23870e0dbb46eaf10faeb26d70f000" + }, + { + "logId": "38494492241415594", + "concreteTypeId": "0088c28967dbcdaa34626c7e915e44b2afe72f12415f0e31edc0b5ce70e7c6dc" + }, + { + "logId": "1515152261580153489", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "logId": "5972461853438630448", + "concreteTypeId": "52e2726988c7da304606fbe4ed696efac04beb29e9a22e15778f8a0539c9cb94" + }, + { + "logId": "9774045287303884155", + "concreteTypeId": "87a4626758542d7b6a03099839e440a052a4d5a00e3abfdf22bcc564ca19a4fd" + }, + { + "logId": "17696813611398264200", + "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308" + }, + { + "logId": "12204227005198389688", + "concreteTypeId": "a95e1fcceb1451b8a76471f593f66c4a52ca04bde3c227c746ad7aaf988de5c6" + }, + { + "logId": "17477056209248181856", + "concreteTypeId": "f28afa065fc5de602456160c4155d4de7d9a61e85a995d209a14eab0b34bd6b4" + }, + { + "logId": "4103440364041737946", + "concreteTypeId": "38f2594527b516dab2c81b31356901226242d7c32554877e36797c6b23969237" + }, + { + "logId": "13264875749739450689", + "concreteTypeId": "b8164e36cce9d14142824b5cc55aebc1272036775b966af82c49c78aff114006" + }, + { + "logId": "6429795790595785041", + "concreteTypeId": "593b39347cc381516d8ed1f8e5e628a8d455bd3f833bd9dfdd5165ba16f9f980" + }, + { + "logId": "13866877265493744985", + "concreteTypeId": "c0710b6731b1dd59799cf6bef33eee3b3b04a2e40e80a0724090215bbf2ca974" + }, + { + "logId": "8385180437869151632", + "concreteTypeId": "745e252e80bec590efc3999ae943f07ccea4d5b45b00bb6575499b64abdd3322" + }, + { + "logId": "14832741149864513620", + "concreteTypeId": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb" + }, + { + "logId": "3008693953818743129", + "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" + }, + { + "logId": "6390060985836259181", + "concreteTypeId": "58ae0e9c51da476db1149dd48b1cda83a12187df4c049f8df5021f0b1696fb93" + }, + { + "logId": "11107063318498994310", + "concreteTypeId": "9a24373d8ce7688609717fd5a9b75360cd8a6bdb224ae095f0c05cc891cadd42" + }, + { + "logId": "2393365693554672043", + "concreteTypeId": "2136f16aedeec1ab7f1d912c57cc0566e86c36f20a2cb313e3d679cead6a0e61" + }, + { + "logId": "11445580549060683374", + "concreteTypeId": "9ed6dede3ae1e66e0f951e860e863f77fb9b9499f4666a1123bf244c4a201669" + }, + { + "logId": "176438282157896578", + "concreteTypeId": "0272d5aecccd33822994b7be1494b72ec9ad860e4cb51f043deda7ac1e2cae26" + }, + { + "logId": "406535131101199095", + "concreteTypeId": "05a44d8c3e00faf7ed545823b7a2b32723545d8715d87a0ab3cf65904948e8d2" + }, + { + "logId": "15054851639520017915", + "concreteTypeId": "d0ed93cd57cc3dfb1c119b22bf63f5d215122402536127bf17087ca6d8186307" + }, + { + "logId": "12356980511120185571", + "concreteTypeId": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335" + }, + { + "logId": "3287912245613454270", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" + }, + { + "logId": "2693553771067460161", + "concreteTypeId": "25616ce23be3ca41fd26f8c546c053ec256f8fb5593036f60c9c417e86dcc92e" + }, + { + "logId": "2169745815365986704", + "concreteTypeId": "1e1c7c52c1c7a9901681337f8669555f62aac58911332c9ff6b4ea8e73786570" + }, + { + "logId": "11132648958528852192", + "concreteTypeId": "9a7f1d3e963c10e0a4ea70a8e20a4813d1dc5682e28f74cb102ae50d32f7f98c" + }, + { + "logId": "9549741647838268318", + "concreteTypeId": "84877f6e98274b9e4721db68b4c0bdb9e52b8e9572c5bd7811c07a41ced882c7" + }, + { + "logId": "9909809838135789262", + "concreteTypeId": "8986b78b19c146ced98454ffbe32d17f1e9e468128ba8dcb2a32f16aaf208db2" + }, + { + "logId": "14098361245275318101", + "concreteTypeId": "c3a770db33c4e755ad3ba4586b9c10520511fb80b767feb57dd41da1a88f6978" + }, + { + "logId": "8988117408309506793", + "concreteTypeId": "7cbc352969caf2e9caa716d89c3be65e707447e2a197c779cc4ef382d0602de6" + }, + { + "logId": "17263266271595476800", + "concreteTypeId": "ef937135956e37401e0bc90406ca8becda92d1b4e387fe938ddef8d27ee192a1" + }, + { + "logId": "2991584087911992109", + "concreteTypeId": "29843de0bbb48b2d3c601b61823f2e106cfa5833e18b482571f1fa58b507a7ad" + }, + { + "logId": "15359085500973571070", + "concreteTypeId": "d5266ee32061dbfec8c96f2ba8a054243875e4e6a586104d6366b11e3bc86f2e" + }, + { + "logId": "6096764540904137535", + "concreteTypeId": "549c0f0c43c9e33f7e958e0473d84e78eca4737f9f159c64614ca5dff2d91b60" + }, + { + "logId": "16681458389498941754", + "concreteTypeId": "e7807205e98b513a8beeb5bcf446f0b2d684d0dce6bfeff0f324fa31df1b8948" + }, + { + "logId": "9678575818972079706", + "concreteTypeId": "8651356d9584265a78cb58de01c22d405dfc7006ea2f5f74fddcbe3f047f109a" + }, + { + "logId": "15006799511514667686", + "concreteTypeId": "d042dca573565aa653542415397934b3e95452917664e04d27c32a22091aa9a5" + }, + { + "logId": "621263945896771922", + "concreteTypeId": "089f2c4466ef415255917812d05776ebcb386be53e5f94bdad1ca8095f02845c" + }, + { + "logId": "18160646294966696875", + "concreteTypeId": "fc0793960700fbabd2722134cff2a546743fc832b98d89aac1ec30fc669fd698" + }, + { + "logId": "16916744526725816430", + "concreteTypeId": "eac45984af86a06e11e1c5ff744bc1242e004db8404308cb7e574b4c2afaf621" + }, + { + "logId": "8728991397092492258", + "concreteTypeId": "79239b6d6f2383e2cfbaf4da7fdf7ee7fb59b7bf517acfff2d9433e9e76e8fc4" + }, + { + "logId": "8036493118938929554", + "concreteTypeId": "6f875be99a39d9920569678a34ffce676a6c3e14b958910db250b9cb4957157f" + }, + { + "logId": "2992671284987479467", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "logId": "1970142151624111756", + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e" + }, + { + "logId": "15520703124961489725", + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc" + }, + { + "logId": "14454674236531057292", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "logId": "7752900403879318547", + "concreteTypeId": "6b97d5d738359413c9fac402aced252c23902c28382469ffe27f07381e9f6f31" + }, + { + "logId": "16171443785104013487", + "concreteTypeId": "e06c82714c52b8afd2293d5d37d05783d09d71c956311c6050ac012cab06364e" + }, + { + "logId": "13897586369399989020", + "concreteTypeId": "c0de252b9f65a31c6d03071b4b18a935c88c5bb0b2401a447fd30d342fd5a04d" + }, + { + "logId": "2855558404146077188", + "concreteTypeId": "27a0fb3d3a821e04e7a3f17ab6a617f0eb10f11e6eeb0f2c0ff9e6237207319e" + }, + { + "logId": "10477818057471029201", + "concreteTypeId": "9168b00268bbefd158090041178f058b032504f76c4b9644157d5d6b5b183468" + }, + { + "logId": "3330666440490685604", + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" + } + ], + "messagesTypes": [], + "configurables": [ + { + "name": "U8_VALUE", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "offset": 120208 + }, + { + "name": "BOOL_VALUE", + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "offset": 120176 + }, + { + "name": "B256_VALUE", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "offset": 120144 + }, + { + "name": "OPTION_U8_VALUE", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", + "offset": 120192 + }, + { + "name": "GENERIC_STRUCT_VALUE", + "concreteTypeId": "08dbec793087c5686c1a493513b158a999bb653126ee51151dfa85fa683edce5", + "offset": 120184 + } + ] +}; diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-bytecode.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-bytecode.txt new file mode 100644 index 00000000000..633e20d079f --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-bytecode.txt @@ -0,0 +1,14 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { decompressBytecode } from "fuels"; + +export const bytecode = decompressBytecode("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"); \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-factory.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-factory.txt new file mode 100644 index 00000000000..e5137e5143c --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-factory.txt @@ -0,0 +1,35 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { ContractFactory } from 'fuels'; +import type { Account, Provider, DeployContractOptions } from 'fuels'; +import { AbiContract } from './AbiContract'; +import { bytecode } from './AbiContract-bytecode'; +import { abi } from './AbiContract-abi'; +import { storageSlots } from './AbiContract-storage-slots'; + +export class AbiContractFactory extends ContractFactory { + + static readonly bytecode = bytecode; + static readonly storageSlots = storageSlots; + + constructor(accountOrProvider: Account | Provider) { + super(bytecode, abi, accountOrProvider, AbiContractFactory.storageSlots); + } + + static deploy ( + wallet: Account, + options: DeployContractOptions = {} + ) { + const factory = new AbiContractFactory(wallet); + return factory.deploy(options); + } +} diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-index.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-index.txt new file mode 100644 index 00000000000..efaa702960c --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-index.txt @@ -0,0 +1,13 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +export { AbiContract } from './AbiContract'; +export { AbiContractFactory } from './AbiContractFactory'; diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-storage-slots.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-storage-slots.txt new file mode 100644 index 00000000000..c97394029a2 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-storage-slots.txt @@ -0,0 +1,14 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { StorageSlot } from 'fuels'; + +export const storageSlots: StorageSlot[] = []; \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract-types.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-types.txt new file mode 100644 index 00000000000..8f05fe291b2 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract-types.txt @@ -0,0 +1,409 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { BN, BigNumberish, Bytes, EvmAddress, RawSlice, StdString, StrSlice } from 'fuels'; +import type { ArrayOfLength, Enum, Option, Result } from '../common'; + +export enum EnumWithNative { Checked = 'Checked', Pending = 'Pending' }; +export enum ExternalEnum { A = 'A', B = 'B' }; + +export type AddressInput = { bits: string }; +export type AddressOutput = AddressInput; +export type AssetIdInput = { bits: string }; +export type AssetIdOutput = AssetIdInput; +export type ConfigurablesInput = { U8_VALUE: BigNumberish, BOOL_VALUE: boolean, B256_VALUE: string, OPTION_U8_VALUE: Option, GENERIC_STRUCT_VALUE: StructDoubleGenericInput, BigNumberish> }; +export type ConfigurablesOutput = { U8_VALUE: number, BOOL_VALUE: boolean, B256_VALUE: string, OPTION_U8_VALUE: Option, GENERIC_STRUCT_VALUE: StructDoubleGenericOutput, number> }; +export type ContractIdInput = { bits: string }; +export type ContractIdOutput = ContractIdInput; +export type EnumDoubleGenericInput = Enum<{ a: T1, b: T2 }>; +export type EnumDoubleGenericOutput = EnumDoubleGenericInput; +export type EnumWithBuiltinTypeInput = Enum<{ a: boolean, b: BigNumberish }>; +export type EnumWithBuiltinTypeOutput = Enum<{ a: boolean, b: BN }>; +export type EnumWithStructsInput = Enum<{ a: EnumWithNative, b: StructSimpleInput, c: StructDoubleGenericInput }>; +export type EnumWithStructsOutput = Enum<{ a: EnumWithNative, b: StructSimpleOutput, c: StructDoubleGenericOutput }>; +export type EnumWithVectorInput = Enum<{ a: BigNumberish, b: BigNumberish[] }>; +export type EnumWithVectorOutput = Enum<{ a: number, b: number[] }>; +export type ExternalStructInput = { value: BigNumberish }; +export type ExternalStructOutput = { value: BN }; +export type IdentityInput = Enum<{ Address: AddressInput, ContractId: ContractIdInput }>; +export type IdentityOutput = Enum<{ Address: AddressOutput, ContractId: ContractIdOutput }>; +export type StructAInput = { propA1: BigNumberish }; +export type StructAOutput = { propA1: number }; +export type StructBInput = { propB1: StructAInput, propB2: BigNumberish }; +export type StructBOutput = { propB1: StructAOutput, propB2: number }; +export type StructCInput = { propC1: StructAInput, propC2: StructBInput[], propC3: StructDInput> }; +export type StructCOutput = { propC1: StructAOutput, propC2: StructBOutput[], propC3: StructDOutput> }; +export type StructDInput = { propD1: StructEInput[], propD2: U, propD3: V }; +export type StructDOutput = { propD1: StructEOutput[], propD2: U, propD3: V }; +export type StructDoubleGenericInput = { a: T1, b: T2 }; +export type StructDoubleGenericOutput = StructDoubleGenericInput; +export type StructEInput = { propE1: StructAInput, propE2: StructBInput, propE3: T }; +export type StructEOutput = { propE1: StructAOutput, propE2: StructBOutput, propE3: T }; +export type StructFInput = { propF1: BigNumberish, propF2: T }; +export type StructFOutput = { propF1: BN, propF2: T }; +export type StructGInput = { propG1: BigNumberish }; +export type StructGOutput = { propG1: number }; +export type StructGenericWithEnumInput = { a: T1, b: EnumDoubleGenericInput }; +export type StructGenericWithEnumOutput = { a: T1, b: EnumDoubleGenericOutput }; +export type StructSimpleInput = { a: boolean, b: BigNumberish }; +export type StructSimpleOutput = { a: boolean, b: number }; +export type StructSingleGenericInput = { a: T }; +export type StructSingleGenericOutput = StructSingleGenericInput; +export type StructWithEnumArrayInput = { a: ArrayOfLength }; +export type StructWithEnumArrayOutput = StructWithEnumArrayInput; +export type StructWithGenericArrayInput = { a: ArrayOfLength, 3> }; +export type StructWithGenericArrayOutput = { a: ArrayOfLength, 3> }; +export type StructWithImplicitGenericsInput = { a: ArrayOfLength, b: [E, F] }; +export type StructWithImplicitGenericsOutput = StructWithImplicitGenericsInput; +export type StructWithMultiOptionInput = { a: ArrayOfLength, 5> }; +export type StructWithMultiOptionOutput = { a: ArrayOfLength, 5> }; +export type StructWithNestedArrayInput = { a: ArrayOfLength, string>, 2> }; +export type StructWithNestedArrayOutput = { a: ArrayOfLength, string>, 2> }; +export type StructWithNestedStructInput = { a: StructDoubleGenericInput, BigNumberish> }; +export type StructWithNestedStructOutput = { a: StructDoubleGenericOutput, number> }; +export type StructWithNestedTupleInput = { a: [BigNumberish, StructSingleGenericInput>, string] }; +export type StructWithNestedTupleOutput = { a: [number, StructSingleGenericOutput>, string] }; +export type StructWithSingleOptionInput = { a: Option }; +export type StructWithSingleOptionOutput = { a: Option }; +export type StructWithVectorInput = { a: BigNumberish, b: BigNumberish[] }; +export type StructWithVectorOutput = { a: number, b: number[] }; + +export interface AbiContractTypes { + functions: { + attributes_doc_comment: { + inputs: []; + output: void; + }; + attributes_inline_always: { + inputs: []; + output: void; + }; + attributes_inline_never: { + inputs: []; + output: void; + }; + attributes_none: { + inputs: []; + output: void; + }; + attributes_payable: { + inputs: []; + output: void; + }; + attributes_storage_read: { + inputs: []; + output: void; + }; + attributes_storage_read_write: { + inputs: []; + output: void; + }; + attributes_storage_write: { + inputs: []; + output: void; + }; + attributes_test: { + inputs: []; + output: void; + }; + configurables: { + inputs: []; + output: ConfigurablesOutput; + }; + multi_arg_b256_bool: { + inputs: [x: string, y: boolean]; + output: [string, boolean]; + }; + multi_arg_complex: { + inputs: [x: StructDoubleGenericInput, BigNumberish>, y: ArrayOfLength, 4>, z: [string, boolean], a: StructSimpleInput]; + output: [StructDoubleGenericOutput, number>, ArrayOfLength, 4>, [string, boolean], StructSimpleOutput]; + }; + multi_arg_str_str: { + inputs: [x: string, y: string]; + output: [string, string]; + }; + multi_arg_struct_vector: { + inputs: [x: StructSimpleInput, y: BigNumberish[]]; + output: [StructSimpleOutput, number[]]; + }; + multi_arg_u32_vector_vector: { + inputs: [x: BigNumberish, y: BigNumberish[], z: BigNumberish[]]; + output: [number, BN[], BN[]]; + }; + multi_arg_u64_struct: { + inputs: [x: BigNumberish, y: StructSimpleInput]; + output: [BN, StructSimpleOutput]; + }; + multi_arg_u64_u64: { + inputs: [x: BigNumberish, y: BigNumberish]; + output: BN; + }; + multi_arg_vector_b256: { + inputs: [x: BigNumberish[], y: string]; + output: [number[], string]; + }; + multi_arg_vector_vector: { + inputs: [x: BigNumberish[], y: BigNumberish[]]; + output: [number[], number[]]; + }; + types_address: { + inputs: [x: AddressInput]; + output: AddressOutput; + }; + types_alias_tuple_with_native_types: { + inputs: [x: [AssetIdInput, AssetIdInput, boolean]]; + output: [AssetIdOutput, AssetIdOutput, boolean]; + }; + types_array: { + inputs: [x: ArrayOfLength]; + output: ArrayOfLength; + }; + types_array_struct: { + inputs: [x: ArrayOfLength]; + output: ArrayOfLength; + }; + types_array_with_generic_struct: { + inputs: [x: ArrayOfLength, string>, 2>]; + output: ArrayOfLength, string>, 2>; + }; + types_array_with_vector: { + inputs: [x: ArrayOfLength]; + output: ArrayOfLength; + }; + types_asset_id: { + inputs: [x: AssetIdInput]; + output: AssetIdOutput; + }; + types_b256: { + inputs: [x: string]; + output: string; + }; + types_b512: { + inputs: [x: string]; + output: string; + }; + types_bool: { + inputs: [x: boolean]; + output: boolean; + }; + types_bytes: { + inputs: [x: Bytes]; + output: Bytes; + }; + types_contract_id: { + inputs: [x: ContractIdInput]; + output: ContractIdOutput; + }; + types_enum: { + inputs: [x: EnumWithNative]; + output: EnumWithNative; + }; + types_enum_external: { + inputs: [x: ExternalEnum]; + output: ExternalEnum; + }; + types_enum_with_builtin_type: { + inputs: [x: EnumWithBuiltinTypeInput]; + output: EnumWithBuiltinTypeOutput; + }; + types_enum_with_structs: { + inputs: [x: EnumWithStructsInput]; + output: EnumWithStructsOutput; + }; + types_enum_with_vector: { + inputs: [x: EnumWithVectorInput]; + output: EnumWithVectorOutput; + }; + types_evm_address: { + inputs: [x: EvmAddress]; + output: EvmAddress; + }; + types_generic_enum: { + inputs: [x: EnumDoubleGenericInput]; + output: EnumDoubleGenericOutput; + }; + types_identity_address: { + inputs: [x: IdentityInput]; + output: IdentityOutput; + }; + types_identity_contract_id: { + inputs: [x: IdentityInput]; + output: IdentityOutput; + }; + types_option: { + inputs: [x?: Option]; + output: Option; + }; + types_option_struct: { + inputs: [x?: Option]; + output: Option; + }; + types_raw_slice: { + inputs: [x: RawSlice]; + output: RawSlice; + }; + types_result: { + inputs: [x: Result]; + output: Result; + }; + types_std_string: { + inputs: [x: StdString]; + output: StdString; + }; + types_str: { + inputs: [x: string]; + output: string; + }; + types_str_slice: { + inputs: [x: StrSlice]; + output: StrSlice; + }; + types_struct_double_generic: { + inputs: [x: StructGenericWithEnumInput]; + output: StructGenericWithEnumOutput; + }; + types_struct_external: { + inputs: [x: ExternalStructInput]; + output: ExternalStructOutput; + }; + types_struct_generic: { + inputs: [x: StructSingleGenericInput]; + output: StructSingleGenericOutput; + }; + types_struct_simple: { + inputs: [x: StructSimpleInput]; + output: StructSimpleOutput; + }; + types_struct_with_array: { + inputs: [x: StructWithGenericArrayInput]; + output: StructWithGenericArrayOutput; + }; + types_struct_with_array_of_enums: { + inputs: [x: StructWithEnumArrayInput]; + output: StructWithEnumArrayOutput; + }; + types_struct_with_complex_nested_struct: { + inputs: [x: StructDInput>]; + output: boolean; + }; + types_struct_with_implicit_generics: { + inputs: [x: StructWithImplicitGenericsInput]; + output: StructWithImplicitGenericsOutput; + }; + types_struct_with_multiple_struct_params: { + inputs: [x: StructAInput, y: StructBInput, z: StructCInput]; + output: boolean; + }; + types_struct_with_nested_array: { + inputs: [x: StructWithNestedArrayInput]; + output: StructWithNestedArrayOutput; + }; + types_struct_with_nested_struct: { + inputs: [x: StructWithNestedStructInput]; + output: StructWithNestedStructOutput; + }; + types_struct_with_nested_tuple: { + inputs: [x: StructWithNestedTupleInput]; + output: StructWithNestedTupleOutput; + }; + types_struct_with_single_option: { + inputs: [x: StructWithSingleOptionInput]; + output: StructWithSingleOptionOutput; + }; + types_struct_with_tuple: { + inputs: [x: StructSingleGenericInput<[boolean, BigNumberish]>]; + output: StructSingleGenericOutput<[boolean, BN]>; + }; + types_struct_with_vector: { + inputs: [x: StructWithVectorInput]; + output: StructWithVectorOutput; + }; + types_tuple: { + inputs: [x: [BigNumberish, BigNumberish, BigNumberish]]; + output: [number, number, number]; + }; + types_tuple_complex: { + inputs: [x: [BigNumberish, StructSingleGenericInput>, string]]; + output: [number, StructSingleGenericOutput>, string]; + }; + types_tuple_with_native_types: { + inputs: [x: [AssetIdInput, AssetIdInput, boolean]]; + output: [AssetIdOutput, AssetIdOutput, boolean]; + }; + types_u16: { + inputs: [x: BigNumberish]; + output: number; + }; + types_u256: { + inputs: [x: BigNumberish]; + output: BN; + }; + types_u32: { + inputs: [x: BigNumberish]; + output: number; + }; + types_u64: { + inputs: [x: BigNumberish]; + output: BN; + }; + types_u8: { + inputs: [x: BigNumberish]; + output: number; + }; + types_value_then_value_then_void_then_void: { + inputs: [x: BigNumberish, y: BigNumberish, z?: undefined, a?: undefined]; + output: void; + }; + types_value_then_void: { + inputs: [x: BigNumberish, y?: undefined]; + output: void; + }; + types_value_then_void_then_value: { + inputs: [x: BigNumberish, y: undefined, z: BigNumberish]; + output: void; + }; + types_vector_boolean: { + inputs: [x: boolean[]]; + output: boolean[]; + }; + types_vector_inside_vector: { + inputs: [x: BigNumberish[][]]; + output: number[][]; + }; + types_vector_option: { + inputs: [x: StructWithMultiOptionInput[]]; + output: StructWithMultiOptionOutput[]; + }; + types_vector_u8: { + inputs: [x: BigNumberish[]]; + output: number[]; + }; + types_vector_with_struct: { + inputs: [x: StructSimpleInput[]]; + output: StructSimpleOutput[]; + }; + types_void: { + inputs: [x?: undefined]; + output: void; + }; + types_void_then_value: { + inputs: [x: undefined, y: BigNumberish]; + output: void; + }; + }; + configurables: Partial<{ + U8_VALUE: BigNumberish; + BOOL_VALUE: boolean; + B256_VALUE: string; + OPTION_U8_VALUE: Option; + GENERIC_STRUCT_VALUE: StructDoubleGenericInput, BigNumberish>; + }>; +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/contracts/contract.txt b/packages/fuel-gauge/src/abi/fixtures/contracts/contract.txt new file mode 100644 index 00000000000..5c2883ea841 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/contracts/contract.txt @@ -0,0 +1,42 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { Contract, AbiCoder } from "fuels"; +import type { Address, Account, Provider } from 'fuels'; +import type { AbiContractTypes as Types } from './AbiContractTypes'; +import type { InterfaceFunctionMapper, ProgramFunctionMapper } from '../common'; +import { abi } from './AbiContract-abi'; + +export * from './AbiContractTypes'; + +export type AbiContractConfigurables = Types['configurables']; + +export class AbiContractAbiCoder extends AbiCoder { + declare functions: InterfaceFunctionMapper; + + constructor() { + super(abi); + } +} + +export class AbiContract extends Contract { + declare interface: AbiContractAbiCoder; + declare functions: ProgramFunctionMapper; + + public static readonly abi = abi; + + constructor( + id: string | Address, + accountOrProvider: Account | Provider, + ) { + super(id, abi, accountOrProvider); + } +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/index.txt b/packages/fuel-gauge/src/abi/fixtures/index.txt new file mode 100644 index 00000000000..a20ac585213 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/index.txt @@ -0,0 +1,14 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +export * from './contracts'; +export * from './predicates'; +export * from './scripts'; diff --git a/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-abi.txt b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-abi.txt new file mode 100644 index 00000000000..ee9735b7bd6 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-abi.txt @@ -0,0 +1,285 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { AbiSpecification } from 'fuels'; + +export const abi: AbiSpecification = { + "programType": "predicate", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "b256", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "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 Configurables", + "concreteTypeId": "2770b4001b55ea9452a58b5063175844ad76bfd1bc30288792fe8962ef9d4169", + "metadataTypeId": 7 + }, + { + "type": "struct MyGenericStruct", + "concreteTypeId": "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "metadataTypeId": 8, + "typeArguments": [ + "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + ] + }, + { + "type": "struct Validation", + "concreteTypeId": "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa", + "metadataTypeId": 9 + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b", + "metadataTypeId": 11, + "typeArguments": [ + "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa" + ] + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ + { + "type": "()", + "metadataTypeId": 0 + }, + { + "type": "enum MyGenericEnum", + "metadataTypeId": 1, + "components": [ + { + "name": "a", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "enum std::option::Option", + "metadataTypeId": 2, + "components": [ + { + "name": "None", + "typeId": 0 + }, + { + "name": "Some", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "enum std::result::Result", + "metadataTypeId": 3, + "components": [ + { + "name": "Ok", + "typeId": 5 + }, + { + "name": "Err", + "typeId": 4 + } + ], + "typeParameters": [ + 5, + 4 + ] + }, + { + "type": "generic E", + "metadataTypeId": 4 + }, + { + "type": "generic T", + "metadataTypeId": 5 + }, + { + "type": "raw untyped ptr", + "metadataTypeId": 6 + }, + { + "type": "struct Configurables", + "metadataTypeId": 7, + "components": [ + { + "name": "U8_VALUE", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "B256_VALUE", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "struct MyGenericStruct", + "metadataTypeId": 8, + "components": [ + { + "name": "a", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "struct Validation", + "metadataTypeId": 9, + "components": [ + { + "name": "has_account", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "total_complete", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "struct std::vec::RawVec", + "metadataTypeId": 10, + "components": [ + { + "name": "ptr", + "typeId": 6 + }, + { + "name": "cap", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "struct std::vec::Vec", + "metadataTypeId": 11, + "components": [ + { + "name": "buf", + "typeId": 10, + "typeArguments": [ + { + "name": "", + "typeId": 5 + } + ] + }, + { + "name": "len", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 5 + ] + } + ], + "functions": [ + { + "inputs": [ + { + "name": "configurables", + "concreteTypeId": "2770b4001b55ea9452a58b5063175844ad76bfd1bc30288792fe8962ef9d4169" + }, + { + "name": "vec", + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b" + }, + { + "name": "enm", + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0" + }, + { + "name": "opt", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" + }, + { + "name": "res", + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da" + } + ], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "attributes": null + } + ], + "loggedTypes": [], + "messagesTypes": [], + "configurables": [ + { + "name": "U8_VALUE", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "offset": 1936 + }, + { + "name": "B256_VALUE", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "offset": 1904 + } + ] +}; diff --git a/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-index.txt b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-index.txt new file mode 100644 index 00000000000..c3352040e5a --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-index.txt @@ -0,0 +1,12 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +export { AbiPredicate } from './AbiPredicate'; diff --git a/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-types.txt b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-types.txt new file mode 100644 index 00000000000..4c8c57d7ecd --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate-types.txt @@ -0,0 +1,32 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { BN, BigNumberish } from 'fuels'; +import type { Enum, Option, Result } from '../common'; + + +export type ConfigurablesInput = { U8_VALUE: BigNumberish, B256_VALUE: string }; +export type ConfigurablesOutput = { U8_VALUE: number, B256_VALUE: string }; +export type MyGenericEnumInput = Enum<{ a: T }>; +export type MyGenericEnumOutput = MyGenericEnumInput; +export type MyGenericStructInput = { a: T }; +export type MyGenericStructOutput = MyGenericStructInput; +export type ValidationInput = { has_account: boolean, total_complete: BigNumberish }; +export type ValidationOutput = { has_account: boolean, total_complete: BN }; + +export interface AbiPredicateTypes { + inputs: [configurables: ConfigurablesInput, vec: ValidationInput[], enm: MyGenericEnumInput, opt: Option, res: Result, BigNumberish>]; + output: boolean; + configurables: Partial<{ + U8_VALUE: BigNumberish; + B256_VALUE: string; + }>; +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/predicates/predicate.txt b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate.txt new file mode 100644 index 00000000000..4f6d898662e --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/predicates/predicate.txt @@ -0,0 +1,42 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { Predicate } from 'fuels'; +import type { PredicateParams } from 'fuels'; +import { abi } from './AbiPredicate-abi'; +import { bytecode } from './AbiPredicate-bytecode'; +import type { AbiPredicateTypes as Types } from './AbiPredicateTypes'; + +export * from './AbiPredicateTypes'; + +export type AbiPredicateInputs = Types['inputs']; +export type AbiPredicateOutput = Types['output']; +export type AbiPredicateConfigurables = Types['configurables']; + +export type AbiPredicateParameters = Omit< + PredicateParams< + Types['inputs'], + Types['configurables'] + >, + 'abi' | 'bytecode' +>; + +export class AbiPredicate extends Predicate< + Types['inputs'], + Types['configurables'] +> { + public static readonly abi = abi; + public static readonly bytecode = bytecode; + + constructor(params: AbiPredicateParameters) { + super({ abi, bytecode, ...params }); + } +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/scripts/script-abi.txt b/packages/fuel-gauge/src/abi/fixtures/scripts/script-abi.txt new file mode 100644 index 00000000000..d943129fd58 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/scripts/script-abi.txt @@ -0,0 +1,285 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { AbiSpecification } from 'fuels'; + +export const abi: AbiSpecification = { + "programType": "script", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "b256", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + }, + { + "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 Configurables", + "concreteTypeId": "2770b4001b55ea9452a58b5063175844ad76bfd1bc30288792fe8962ef9d4169", + "metadataTypeId": 7 + }, + { + "type": "struct MyGenericStruct", + "concreteTypeId": "c397d34a45fb343f8315bb5af5eed88da9f13347c765e2d86089d99dbf952ef2", + "metadataTypeId": 8, + "typeArguments": [ + "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a" + ] + }, + { + "type": "struct Validation", + "concreteTypeId": "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa", + "metadataTypeId": 9 + }, + { + "type": "struct std::vec::Vec", + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b", + "metadataTypeId": 11, + "typeArguments": [ + "c7cf8c2be429c961ccb5c32a2951a58f1bb2a4f748ffac2206d4d1761082beaa" + ] + }, + { + "type": "u16", + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef" + }, + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + }, + { + "type": "u8", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + } + ], + "metadataTypes": [ + { + "type": "()", + "metadataTypeId": 0 + }, + { + "type": "enum MyGenericEnum", + "metadataTypeId": 1, + "components": [ + { + "name": "a", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "enum std::option::Option", + "metadataTypeId": 2, + "components": [ + { + "name": "None", + "typeId": 0 + }, + { + "name": "Some", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "enum std::result::Result", + "metadataTypeId": 3, + "components": [ + { + "name": "Ok", + "typeId": 5 + }, + { + "name": "Err", + "typeId": 4 + } + ], + "typeParameters": [ + 5, + 4 + ] + }, + { + "type": "generic E", + "metadataTypeId": 4 + }, + { + "type": "generic T", + "metadataTypeId": 5 + }, + { + "type": "raw untyped ptr", + "metadataTypeId": 6 + }, + { + "type": "struct Configurables", + "metadataTypeId": 7, + "components": [ + { + "name": "U8_VALUE", + "typeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b" + }, + { + "name": "B256_VALUE", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" + } + ] + }, + { + "type": "struct MyGenericStruct", + "metadataTypeId": 8, + "components": [ + { + "name": "a", + "typeId": 5 + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "struct Validation", + "metadataTypeId": 9, + "components": [ + { + "name": "has_account", + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" + }, + { + "name": "total_complete", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ] + }, + { + "type": "struct std::vec::RawVec", + "metadataTypeId": 10, + "components": [ + { + "name": "ptr", + "typeId": 6 + }, + { + "name": "cap", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 5 + ] + }, + { + "type": "struct std::vec::Vec", + "metadataTypeId": 11, + "components": [ + { + "name": "buf", + "typeId": 10, + "typeArguments": [ + { + "name": "", + "typeId": 5 + } + ] + }, + { + "name": "len", + "typeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "typeParameters": [ + 5 + ] + } + ], + "functions": [ + { + "inputs": [ + { + "name": "configurables", + "concreteTypeId": "2770b4001b55ea9452a58b5063175844ad76bfd1bc30288792fe8962ef9d4169" + }, + { + "name": "vec", + "concreteTypeId": "9b3ded85b5c6e502acc8b7834d5d6df0460764a7a47837eb2b32d4566c4d477b" + }, + { + "name": "enm", + "concreteTypeId": "9d8b215a39e5f5f10fc294290b6ea401edbd53056cfe6e0c9331157abdbc87d0" + }, + { + "name": "opt", + "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1" + }, + { + "name": "res", + "concreteTypeId": "85dace7aaa469c8bb476be79ddec34883ef101b3cde470636f47e299bfcdc3da" + } + ], + "name": "main", + "output": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "attributes": null + } + ], + "loggedTypes": [], + "messagesTypes": [], + "configurables": [ + { + "name": "U8_VALUE", + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "offset": 2360 + }, + { + "name": "B256_VALUE", + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "offset": 2328 + } + ] +}; diff --git a/packages/fuel-gauge/src/abi/fixtures/scripts/script-index.txt b/packages/fuel-gauge/src/abi/fixtures/scripts/script-index.txt new file mode 100644 index 00000000000..39aff230e0b --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/scripts/script-index.txt @@ -0,0 +1,12 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +export { AbiScript } from './AbiScript'; diff --git a/packages/fuel-gauge/src/abi/fixtures/scripts/script-types.txt b/packages/fuel-gauge/src/abi/fixtures/scripts/script-types.txt new file mode 100644 index 00000000000..84e911f7f88 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/scripts/script-types.txt @@ -0,0 +1,32 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { BN, BigNumberish } from 'fuels'; +import type { Enum, Option, Result } from '../common'; + + +export type ConfigurablesInput = { U8_VALUE: BigNumberish, B256_VALUE: string }; +export type ConfigurablesOutput = { U8_VALUE: number, B256_VALUE: string }; +export type MyGenericEnumInput = Enum<{ a: T }>; +export type MyGenericEnumOutput = MyGenericEnumInput; +export type MyGenericStructInput = { a: T }; +export type MyGenericStructOutput = MyGenericStructInput; +export type ValidationInput = { has_account: boolean, total_complete: BigNumberish }; +export type ValidationOutput = { has_account: boolean, total_complete: BN }; + +export interface AbiScriptTypes { + inputs: [configurables: ConfigurablesInput, vec: ValidationInput[], enm: MyGenericEnumInput, opt: Option, res: Result, BigNumberish>]; + output: boolean; + configurables: Partial<{ + U8_VALUE: BigNumberish; + B256_VALUE: string; + }>; +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/fixtures/scripts/script.txt b/packages/fuel-gauge/src/abi/fixtures/scripts/script.txt new file mode 100644 index 00000000000..2eeb2cdaa03 --- /dev/null +++ b/packages/fuel-gauge/src/abi/fixtures/scripts/script.txt @@ -0,0 +1,34 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import { Script } from 'fuels'; +import type { Account } from 'fuels'; +import { abi } from './AbiScript-abi'; +import { bytecode } from './AbiScript-bytecode'; +import type { AbiScriptTypes as Types } from './AbiScriptTypes'; + +export * from './AbiScriptTypes'; + +export type AbiScriptInputs = Types['inputs']; +export type AbiScriptOutput = Types['output']; +export type AbiScriptConfigurables = Types['configurables']; + +export class AbiScript extends Script< + Types['inputs'], + Types['output'] +> { + public static readonly abi = abi; + public static readonly bytecode = bytecode; + + constructor(wallet: Account) { + super(bytecode, abi, wallet); + } +} \ No newline at end of file diff --git a/packages/fuel-gauge/src/abi/utils.ts b/packages/fuel-gauge/src/abi/utils.ts new file mode 100644 index 00000000000..00e44dbf3ef --- /dev/null +++ b/packages/fuel-gauge/src/abi/utils.ts @@ -0,0 +1,32 @@ +import { getForcProject } from '@fuel-ts/utils/test-utils'; +import { writeFileSync } from 'fs'; +import { join } from 'path'; + +export enum AbiProjectsEnum { + ABI_CONTRACT = 'abi-contract', + ABI_PREDICATE = 'abi-predicate', + ABI_SCRIPT = 'abi-script', +} + +const forcProjectsDir = join(__dirname, '../../test/fixtures/forc-projects'); + +export const getAbiForcProject = (project: AbiProjectsEnum) => { + const result = getForcProject({ + projectDir: join(forcProjectsDir, project), + projectName: project, + build: 'release', + }); + return result; +}; + +export function autoUpdateFixture(path: string, contents: string) { + if (process.env.UPDATE_FIXTURES === 'true') { + if (!/fixtures/.test(path)) { + throw new Error(`This path may no be a fixture: ${path}`); + } + const { log } = console; + log('Updated fixture', path); + writeFileSync(path, contents); + } + return contents; +} diff --git a/packages/fuel-gauge/src/blob-deploy.test.ts b/packages/fuel-gauge/src/blob-deploy.test.ts index 83ce551c2a3..1cdf50d6722 100644 --- a/packages/fuel-gauge/src/blob-deploy.test.ts +++ b/packages/fuel-gauge/src/blob-deploy.test.ts @@ -22,9 +22,10 @@ describe('deploying blobs', () => { ): Record { const configurables: Record = {}; - Object.entries(program.interface.configurables).forEach(([key, { offset, concreteTypeId }]) => { + Object.entries(program.interface.configurables).forEach(([key, { offset }]) => { const data = program.bytes.slice(offset); - configurables[key] = program.interface.decodeType(concreteTypeId, data)[0]; + const coder = program.interface.getConfigurable(key); + configurables[key] = coder.decode(data); }); return configurables; @@ -236,7 +237,7 @@ describe('deploying blobs', () => { const predicate = new Predicate({ data: [configurable.FEE, configurable.ADDRESS], bytecode: loaderPredicate.bytes, - abi: loaderPredicate.interface.jsonAbi, + abi: loaderPredicate.interface.specification, provider, configurableConstants: configurable, }); diff --git a/packages/fuel-gauge/src/contract-factory.test.ts b/packages/fuel-gauge/src/contract-factory.test.ts index bfd3f176507..2f590b2f8b0 100644 --- a/packages/fuel-gauge/src/contract-factory.test.ts +++ b/packages/fuel-gauge/src/contract-factory.test.ts @@ -1,7 +1,7 @@ import type { Account, TransactionResult } from '@fuel-ts/account'; import { FuelError, ErrorCode } from '@fuel-ts/errors'; import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; -import { BN, bn, toHex, Interface, ContractFactory, arrayify, concat } from 'fuels'; +import { BN, bn, toHex, AbiCoder, ContractFactory, arrayify, concat } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { @@ -25,7 +25,7 @@ describe('Contract Factory', () => { using contract = await launchTestContract({ factory: StorageTestContractFactory, }); - expect(contract.interface).toBeInstanceOf(Interface); + expect(contract.interface).toBeInstanceOf(AbiCoder); const { waitForResult } = await contract.functions.initialize_counter(41).call(); const { value: valueInitial } = await waitForResult(); @@ -46,7 +46,7 @@ describe('Contract Factory', () => { factory: StorageTestContractFactory, }); - expect(contract.interface).toBeInstanceOf(Interface); + expect(contract.interface).toBeInstanceOf(AbiCoder); const call1 = await contract.functions.initialize_counter(100).call(); await call1.waitForResult(); @@ -135,7 +135,7 @@ describe('Contract Factory', () => { using contract = await launchTestContract({ factory: StorageTestContractFactory, - storageSlots: StorageTestContract.storageSlots, + storageSlots: StorageTestContractFactory.storageSlots, }); const call1 = await contract.functions.return_var1().call(); @@ -203,7 +203,7 @@ describe('Contract Factory', () => { const { waitForResult } = await factory.deploy({ storageSlots: [ - ...StorageTestContract.storageSlots, // initializing from storage_slots.json + ...StorageTestContractFactory.storageSlots, // initializing from storage_slots.json { key: '0000000000000000000000000000000000000000000000000000000000000001', value: b256 }, // Initializing manual value ], }); @@ -510,7 +510,7 @@ describe('Contract Factory', () => { ); const deploy = await factory.deployAsBlobTx({ - storageSlots: StorageTestContract.storageSlots, + storageSlots: StorageTestContractFactory.storageSlots, }); const { contract } = await deploy.waitForResult(); diff --git a/packages/fuel-gauge/src/coverage-contract.test.ts b/packages/fuel-gauge/src/coverage-contract.test.ts index 876c7ca207f..8cd440410f1 100644 --- a/packages/fuel-gauge/src/coverage-contract.test.ts +++ b/packages/fuel-gauge/src/coverage-contract.test.ts @@ -12,9 +12,11 @@ import { import { TestMessage, type LaunchTestNodeOptions } from 'fuels/test-utils'; import { CoverageContractFactory } from '../test/typegen/contracts'; -import type { MixedNativeEnumInput } from '../test/typegen/contracts/CoverageContract'; -import { SmallEnumInput } from '../test/typegen/contracts/Vectors'; -import type { Vec } from '../test/typegen/contracts/common'; +import { + ColorEnum, + type MixedNativeEnumInput, +} from '../test/typegen/contracts/CoverageContractTypes'; +import { SmallEnum } from '../test/typegen/contracts/VectorsTypes'; import { launchTestContract } from './utils'; @@ -26,18 +28,6 @@ const B256 = '0x000000000000000000000000000000000000000000000000000000000000002a const B512 = '0x059bc9c43ea1112f3eb2bd30415de72ed24c1c4416a1316f0f48cc6f958073f42a6d8c12e4829826316d8dcf444498717b5a2fbf27defac367271065f6a1d4a5'; -enum ColorEnumInput { - Red = 'Red', - Green = 'Green', - Blue = 'Blue', -} - -enum ColorEnumOutput { - Red = 'Red', - Green = 'Green', - Blue = 'Blue', -} - enum MixedNativeEnum { Native = 'Native', NotNative = 12, @@ -100,7 +90,7 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { expect(result.value).toStrictEqual(expectedValue); - expectedValue = SmallEnumInput.Empty; + expectedValue = SmallEnum.Empty; call = await contractInstance.functions.get_empty_enum().call(); result = await call.waitForResult(); @@ -321,7 +311,7 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { it('should test enum < 8 byte variable type', async () => { using contractInstance = await setupContract(); - const INPUT = SmallEnumInput.Empty; + const INPUT = SmallEnum.Empty; const { waitForResult } = await contractInstance.functions.echo_enum_small(INPUT).call(); const { value } = await waitForResult(); expect(value).toStrictEqual(INPUT); @@ -713,8 +703,9 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { it('should test native enum [Red->Green]', async () => { using contractInstance = await setupContract(); - const INPUT: ColorEnumInput = ColorEnumInput.Red; - const OUTPUT: ColorEnumOutput = ColorEnumOutput.Green; + const INPUT = ColorEnum.Red; + const OUTPUT = ColorEnum.Green; + const { waitForResult } = await contractInstance.functions.color_enum(INPUT).call(); const { value } = await waitForResult(); @@ -724,8 +715,8 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { it('should test native enum [Green->Blue]', async () => { using contractInstance = await setupContract(); - const INPUT: ColorEnumInput = ColorEnumInput.Green; - const OUTPUT: ColorEnumOutput = ColorEnumOutput.Blue; + const INPUT = ColorEnum.Green; + const OUTPUT = ColorEnum.Blue; const { waitForResult } = await contractInstance.functions.color_enum(INPUT).call(); const { value } = await waitForResult(); @@ -735,8 +726,8 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { it('should test native enum [Blue->Red]', async () => { using contractInstance = await setupContract(); - const INPUT: ColorEnumInput = ColorEnumInput.Blue; - const OUTPUT: ColorEnumOutput = ColorEnumOutput.Red; + const INPUT = ColorEnum.Blue; + const OUTPUT = ColorEnum.Red; const { waitForResult } = await contractInstance.functions.color_enum(INPUT).call(); const { value } = await waitForResult(); @@ -817,10 +808,10 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { expect(isStatusSuccess).toBeTruthy(); }); - it('should support array in vec', async () => { + it('should support vec in array', async () => { using contractInstance = await setupContract(); - const INPUT: [Vec, Vec] = [ + const INPUT: [Array, Array] = [ [0, 1, 2], [0, 1, 2], ]; @@ -864,14 +855,14 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { contractInstance.functions.echo_b256_middle(INPUT_A, INPUT_B, INPUT_C, INPUT_D), contractInstance.functions.echo_u8(13), contractInstance.functions.echo_u8(23), - contractInstance.functions.echo_enum_small(SmallEnumInput.Empty), + contractInstance.functions.echo_enum_small(SmallEnum.Empty), contractInstance.functions.echo_b256_middle(INPUT_B, INPUT_A, INPUT_C, INPUT_D), ]) .call(); const { value: results } = await waitForResult(); - expect(results).toStrictEqual([INPUT_B, 13, 23, SmallEnumInput.Empty, INPUT_A]); + expect(results).toStrictEqual([INPUT_B, 13, 23, SmallEnum.Empty, INPUT_A]); }); it('should handle multiple calls [with vectors + stack data first]', async () => { @@ -886,7 +877,7 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { .multiCall([ contractInstance.functions.echo_u8(1), contractInstance.functions.echo_u8(2), - contractInstance.functions.echo_enum_small(SmallEnumInput.Empty), + contractInstance.functions.echo_enum_small(SmallEnum.Empty), contractInstance.functions.echo_b256_middle(INPUT_A, INPUT_B, INPUT_C, INPUT_D), contractInstance.functions.echo_b256_middle(INPUT_B, INPUT_A, INPUT_C, INPUT_D), ]) @@ -894,7 +885,7 @@ describe('Coverage Contract', { timeout: 15_000 }, () => { const { value: results } = await waitForResult(); - expect(results).toStrictEqual([1, 2, SmallEnumInput.Empty, INPUT_B, INPUT_A]); + expect(results).toStrictEqual([1, 2, SmallEnum.Empty, INPUT_B, INPUT_A]); }); it('should handle an enum from a library', async () => { diff --git a/packages/fuel-gauge/src/mapped-error-messages.test.ts b/packages/fuel-gauge/src/mapped-error-messages.test.ts index 82c88a49e51..498593c6b50 100644 --- a/packages/fuel-gauge/src/mapped-error-messages.test.ts +++ b/packages/fuel-gauge/src/mapped-error-messages.test.ts @@ -15,7 +15,11 @@ describe('mapped error messages', () => { const emptyWallet = Wallet.generate({ provider: contract.provider }); - const emptyWalletContract = new Contract(contract.id, contract.interface.jsonAbi, emptyWallet); + const emptyWalletContract = new Contract( + contract.id, + contract.interface.specification, + emptyWallet + ); await expectToThrowFuelError(() => emptyWalletContract.functions.return_void().call(), { code: ErrorCode.NOT_ENOUGH_FUNDS, diff --git a/packages/fuel-gauge/src/min-gas.test.ts b/packages/fuel-gauge/src/min-gas.test.ts index 788244ada8d..b5678a9cf92 100644 --- a/packages/fuel-gauge/src/min-gas.test.ts +++ b/packages/fuel-gauge/src/min-gas.test.ts @@ -5,9 +5,9 @@ import { Address, hexlify, getGasUsedFromReceipts, - BigNumberCoder, ContractFactory, GAS_USED_MODIFIER, + encoding, } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; @@ -42,7 +42,7 @@ describe('Minimum gas tests', () => { ); const { transactionRequest: request } = contractFactory.createTransactionRequest({ - storageSlots: CoverageContract.storageSlots, + storageSlots: CoverageContractFactory.storageSlots, }); const resources = await provider.getResourcesToSpend(wallet.address, [ @@ -83,7 +83,7 @@ describe('Minimum gas tests', () => { const request = new ScriptTransactionRequest({ script: ComplexScript.bytecode, - scriptData: hexlify(new BigNumberCoder('u64').encode(bn(2000))), + scriptData: hexlify(encoding.v1.u64.encode(bn(2000))), }); request.addCoinOutput(Address.fromRandom(), bn(100), await provider.getBaseAssetId()); @@ -187,7 +187,7 @@ describe('Minimum gas tests', () => { */ const request = new ScriptTransactionRequest({ script: ComplexScript.bytecode, - scriptData: hexlify(new BigNumberCoder('u64').encode(bn(2000))), + scriptData: hexlify(encoding.v1.u64.encode(bn(2000))), }); // add predicate transfer diff --git a/packages/fuel-gauge/src/options.test.ts b/packages/fuel-gauge/src/options.test.ts index 15b1c3be5db..6e7cfd30178 100644 --- a/packages/fuel-gauge/src/options.test.ts +++ b/packages/fuel-gauge/src/options.test.ts @@ -1,9 +1,9 @@ import type { BigNumberish } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; +import type { Option } from '../test/typegen/common'; import type { DeepStructInput } from '../test/typegen/contracts/Options'; import { OptionsFactory } from '../test/typegen/contracts/OptionsFactory'; -import type { Option } from '../test/typegen/contracts/common'; import { launchTestContract } from './utils'; diff --git a/packages/fuel-gauge/src/predicate/predicate-duplication.test.ts b/packages/fuel-gauge/src/predicate/predicate-duplication.test.ts index 07cd15a05d5..f51f30f01fd 100644 --- a/packages/fuel-gauge/src/predicate/predicate-duplication.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-duplication.test.ts @@ -34,7 +34,7 @@ describe('Predicate.fromInstance', () => { const newPredicate: PredicateWithConfigurable = basePredicate.toNewInstance(); expect(newPredicate.predicateData).toEqual(basePredicate.predicateData); - expect(newPredicate.interface).toEqual(basePredicate.interface); + expect(newPredicate.interface.specification).toEqual(basePredicate.interface.specification); expect(newPredicate.provider).toEqual(basePredicate.provider); expect(newPredicate.bytes).toEqual(basePredicate.bytes); }); @@ -56,7 +56,7 @@ describe('Predicate.fromInstance', () => { const newPredicate: PredicateWithConfigurable = basePredicate.toNewInstance({ data }); expect(newPredicate.predicateData).toEqual(data); - expect(newPredicate.interface).toEqual(basePredicate.interface); + expect(newPredicate.interface.specification).toEqual(basePredicate.interface.specification); expect(newPredicate.provider).toEqual(basePredicate.provider); expect(newPredicate.bytes).toEqual(basePredicate.bytes); expect(newPredicate.predicateData).not.toEqual(basePredicate.predicateData); @@ -81,7 +81,7 @@ describe('Predicate.fromInstance', () => { }); expect(newPredicate.predicateData).toEqual(basePredicate.predicateData); - expect(newPredicate.interface).toEqual(basePredicate.interface); + expect(newPredicate.interface.specification).toEqual(basePredicate.interface.specification); expect(newPredicate.provider).toEqual(basePredicate.provider); expect(newPredicate.bytes).toEqual( new PredicateWithConfigurable({ @@ -116,7 +116,7 @@ describe('Predicate.fromInstance', () => { }); expect(newPredicate.predicateData).toEqual(data); - expect(newPredicate.interface).toEqual(basePredicate.interface); + expect(newPredicate.interface.specification).toEqual(basePredicate.interface.specification); expect(newPredicate.provider).toEqual(basePredicate.provider); expect(newPredicate.bytes).toEqual( new PredicateWithConfigurable({ diff --git a/packages/fuel-gauge/src/recipes.test.ts b/packages/fuel-gauge/src/recipes.test.ts index 6fa4daec1ed..32e5343bb04 100644 --- a/packages/fuel-gauge/src/recipes.test.ts +++ b/packages/fuel-gauge/src/recipes.test.ts @@ -34,13 +34,13 @@ describe('recipes', () => { .proxy_target() .call(); const firstTarget = await waitForFirstTarget(); - expect(firstTarget.value.bits).toEqual(targetAddress); + expect(firstTarget.value?.bits).toEqual(targetAddress); const anotherProxy = new Src14OwnedProxy(proxyAddress, wallet); const { waitForResult: waitForAnotherTarget } = await anotherProxy.functions .proxy_target() .call(); const anotherTarget = await waitForAnotherTarget(); - expect(anotherTarget.value.bits).toEqual(targetAddress); + expect(anotherTarget.value?.bits).toEqual(targetAddress); }); }); diff --git a/packages/fuel-gauge/src/reentrant-contract-calls.test.ts b/packages/fuel-gauge/src/reentrant-contract-calls.test.ts index d7d56989587..914ab20888d 100644 --- a/packages/fuel-gauge/src/reentrant-contract-calls.test.ts +++ b/packages/fuel-gauge/src/reentrant-contract-calls.test.ts @@ -71,7 +71,7 @@ describe('Reentrant Contract Calls', () => { StorageTestContractFactory.bytecode, StorageTestContract.abi, wallet - ).deploy({ storageSlots: StorageTestContract.storageSlots }); + ).deploy({ storageSlots: StorageTestContractFactory.storageSlots }); const { contract: storageContract } = await deploy.waitForResult(); diff --git a/packages/fuel-gauge/src/script-with-configurable.test.ts b/packages/fuel-gauge/src/script-with-configurable.test.ts index 3fdb883659c..c2004172124 100644 --- a/packages/fuel-gauge/src/script-with-configurable.test.ts +++ b/packages/fuel-gauge/src/script-with-configurable.test.ts @@ -36,7 +36,9 @@ describe('Script With Configurable', () => { wallets: [wallet], } = launched; - const configurableConstants: Required = { FEE: 71 }; + const configurableConstants: Required = { + FEE: 71, + }; expect(configurableConstants.FEE).not.toEqual(defaultValues.FEE); @@ -57,7 +59,9 @@ describe('Script With Configurable', () => { wallets: [wallet], } = launched; - const configurableConstants: Required = { FEE: 35 }; + const configurableConstants: Required = { + FEE: 35, + }; const script = new ScriptWithConfigurable(wallet); diff --git a/packages/fuel-gauge/src/script-with-options.test.ts b/packages/fuel-gauge/src/script-with-options.test.ts index e47cd37c08f..ac0143e6734 100644 --- a/packages/fuel-gauge/src/script-with-options.test.ts +++ b/packages/fuel-gauge/src/script-with-options.test.ts @@ -3,7 +3,7 @@ import { bn } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { ScriptWithOptions } from '../test/typegen'; -import type { Option } from '../test/typegen/contracts/common'; +import type { Option } from '../test/typegen/common'; /** * @group node diff --git a/packages/fuel-gauge/src/script-with-vectors.test.ts b/packages/fuel-gauge/src/script-with-vectors.test.ts index 77c4daa804c..a24723eda5b 100644 --- a/packages/fuel-gauge/src/script-with-vectors.test.ts +++ b/packages/fuel-gauge/src/script-with-vectors.test.ts @@ -7,7 +7,7 @@ import { ScriptWithVectorAdvanced, ScriptWithVectorMixed, } from '../test/typegen'; -import { StateErrorInput, UserErrorInput } from '../test/typegen/scripts/ScriptWithVectorAdvanced'; +import { StateError, UserError } from '../test/typegen/scripts/ScriptWithVectorAdvanced'; /** * @group node @@ -144,15 +144,15 @@ describe('Script With Vectors', () => { ]; const errors = [ - { StateError: StateErrorInput.Void }, - { StateError: StateErrorInput.Pending }, - { StateError: StateErrorInput.Completed }, - { UserError: UserErrorInput.InsufficientPermissions }, - { UserError: UserErrorInput.Unauthorized }, - { UserError: UserErrorInput.Unauthorized }, - { UserError: UserErrorInput.Unauthorized }, - { UserError: UserErrorInput.Unauthorized }, - { UserError: UserErrorInput.Unauthorized }, + { StateError: StateError.Void }, + { StateError: StateError.Pending }, + { StateError: StateError.Completed }, + { UserError: UserError.InsufficientPermissions }, + { UserError: UserError.Unauthorized }, + { UserError: UserError.Unauthorized }, + { UserError: UserError.Unauthorized }, + { UserError: UserError.Unauthorized }, + { UserError: UserError.Unauthorized }, ]; const vectorOfStructs = [ diff --git a/packages/fuel-gauge/src/storage-test-contract.test.ts b/packages/fuel-gauge/src/storage-test-contract.test.ts index 64d59f4cafc..300b533f014 100644 --- a/packages/fuel-gauge/src/storage-test-contract.test.ts +++ b/packages/fuel-gauge/src/storage-test-contract.test.ts @@ -15,7 +15,7 @@ describe('StorageTestContract', () => { wallets: [wallet], } = launched; - const { storageSlots } = StorageTestContract; + const { storageSlots } = StorageTestContractFactory; const factory = new ContractFactory( StorageTestContractFactory.bytecode, @@ -96,7 +96,7 @@ describe('StorageTestContract', () => { }); it('should automatically load storage slots', async () => { - const { storageSlots } = StorageTestContract; + const { storageSlots } = StorageTestContractFactory; const expectedStorageSlots = storageSlots.map(({ key, value }) => ({ key: `0x${key}`, value: `0x${value}`, @@ -123,7 +123,7 @@ describe('StorageTestContract', () => { }); it('should allow for overriding storage slots', async () => { - const { storageSlots } = StorageTestContract; + const { storageSlots } = StorageTestContractFactory; expect(storageSlots.length).toBeGreaterThan(2); const modifiedStorageSlots = storageSlots.slice(1).map(({ key }) => ({ @@ -176,7 +176,7 @@ describe('StorageTestContract', () => { }); test('automatically loads storage slots when using deployAsCreateTx', async () => { - const { storageSlots } = StorageTestContract; + const { storageSlots } = StorageTestContractFactory; const expectedStorageSlots = storageSlots.map(({ key, value }) => ({ key: `0x${key}`, value: `0x${value}`, @@ -197,7 +197,7 @@ describe('StorageTestContract', () => { }); test('automatically loads storage slots when using deployAsBlobTx', async () => { - const { storageSlots } = StorageTestContract; + const { storageSlots } = StorageTestContractFactory; const expectedStorageSlots = storageSlots.map(({ key, value }) => ({ key: `0x${key}`, value: `0x${value}`, diff --git a/packages/fuel-gauge/src/str-slice.test.ts b/packages/fuel-gauge/src/str-slice.test.ts index 0d505efd452..4e047260603 100644 --- a/packages/fuel-gauge/src/str-slice.test.ts +++ b/packages/fuel-gauge/src/str-slice.test.ts @@ -2,10 +2,7 @@ import { bn } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { StrSliceContractFactory, ScriptStrSlice } from '../test/typegen'; -import { - PredicateStrSlice, - type PredicateStrSliceInputs, -} from '../test/typegen/predicates/PredicateStrSlice'; +import { PredicateStrSlice } from '../test/typegen/predicates/PredicateStrSlice'; /** * @group node @@ -37,10 +34,9 @@ describe('str slice', () => { provider, } = launched; - const predicateData: PredicateStrSliceInputs = ['predicate-input']; const predicate = new PredicateStrSlice({ provider, - data: predicateData, + data: ['predicate-input'], }); const baseAssetId = await provider.getBaseAssetId(); diff --git a/packages/fuel-gauge/src/token-test-contract.test.ts b/packages/fuel-gauge/src/token-test-contract.test.ts index 4baef1dd0ef..ca68879aad8 100644 --- a/packages/fuel-gauge/src/token-test-contract.test.ts +++ b/packages/fuel-gauge/src/token-test-contract.test.ts @@ -199,7 +199,9 @@ describe('TokenTestContract', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore () => token.functions.transfer_to_address(addressParameter, assetId, 50).call(), - new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256.') + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256 value - malformed hex value.', { + value: userWallet.address, + }) ); }); }); diff --git a/packages/fuel-gauge/src/vectors.test.ts b/packages/fuel-gauge/src/vectors.test.ts index 9c94d76f40d..c3b1df2f34c 100644 --- a/packages/fuel-gauge/src/vectors.test.ts +++ b/packages/fuel-gauge/src/vectors.test.ts @@ -2,8 +2,7 @@ import { bn, randomBytes, hexlify } from 'fuels'; import type { BigNumberish, BN } from 'fuels'; import { VectorsFactory } from '../test/typegen/contracts'; -import { SmallEnumInput } from '../test/typegen/contracts/CoverageContract'; -import type { Vec } from '../test/typegen/contracts/common'; +import { SmallEnum } from '../test/typegen/contracts/CoverageContract'; import { launchTestContract } from './utils'; @@ -120,7 +119,7 @@ describe('Vector Tests', () => { it('should test (u8, u8) vector input/output', async () => { using contractInstance = await setupContract(); - const INPUT: Vec<[BigNumberish, BigNumberish]> = [ + const INPUT: Array<[BigNumberish, BigNumberish]> = [ [1, 2], [3, 4], [5, 6], @@ -137,7 +136,7 @@ describe('Vector Tests', () => { it('should test (u64, u64) vector input/output', async () => { using contractInstance = await setupContract(); - const INPUT: Vec<[BigNumberish, BigNumberish]> = [ + const INPUT: Array<[BigNumberish, BigNumberish]> = [ [111, 2222], [333, 4445], [5555, 6], @@ -168,7 +167,7 @@ describe('Vector Tests', () => { it('should test [u64; 5] vector input/output', async () => { using contractInstance = await setupContract(); - const INPUT: Vec<[BigNumberish, BigNumberish, BigNumberish, BigNumberish, BigNumberish]> = [ + const INPUT: Array<[BigNumberish, BigNumberish, BigNumberish, BigNumberish, BigNumberish]> = [ [1, 2, 3, 4, 5], [500, 600, 700, 9000, 9999], [11500, 22600, 33700, 55000, 669999], @@ -183,7 +182,7 @@ describe('Vector Tests', () => { it('should test [bool; 2] vector input/output', async () => { using contractInstance = await setupContract(); - const INPUT: Vec<[boolean, boolean]> = [ + const INPUT: Array<[boolean, boolean]> = [ [true, true], [true, false], [false, true], @@ -283,12 +282,12 @@ describe('Vector Tests', () => { it('should test SmallEnum vector input/output', async () => { using contractInstance = await setupContract(); - const INPUT: Vec = [ - SmallEnumInput.Empty, - SmallEnumInput.Empty, - SmallEnumInput.Empty, - SmallEnumInput.Empty, - SmallEnumInput.Empty, + const INPUT: Array = [ + SmallEnum.Empty, + SmallEnum.Empty, + SmallEnum.Empty, + SmallEnum.Empty, + SmallEnum.Empty, ]; const { waitForResult } = await contractInstance.functions @@ -404,7 +403,7 @@ describe('Vector Tests', () => { it('should test Vec and b256 tuple input/output', async () => { using contractInstance = await setupContract(); - const INPUT: [Vec, string] = [[1, 8, 3, 2, 55, 215], hexlify(randomBytes(32))]; + const INPUT: [Array, string] = [[1, 8, 3, 2, 55, 215], hexlify(randomBytes(32))]; const { waitForResult } = await contractInstance.functions .echo_vector_and_b256_tuple(...INPUT) @@ -417,7 +416,7 @@ describe('Vector Tests', () => { it('should test two vectors tuple input/output', async () => { using contractInstance = await setupContract(); - const INPUT: [Vec, Vec] = [ + const INPUT: [Array, Array] = [ [219, 229], [1, 254, 55], ]; @@ -433,7 +432,7 @@ describe('Vector Tests', () => { it('should test u32 and three different vectors tuple input/output', async () => { using contractInstance = await setupContract(); - const INPUT: [BigNumberish, Vec, Vec, Vec] = [ + const INPUT: [BigNumberish, Array, Array, Array] = [ 91000, [true, true, false], [95000, 153333], diff --git a/packages/fuel-gauge/src/void.test.ts b/packages/fuel-gauge/src/void.test.ts index bdd24c8003a..a46105e67ce 100644 --- a/packages/fuel-gauge/src/void.test.ts +++ b/packages/fuel-gauge/src/void.test.ts @@ -1,8 +1,8 @@ import { launchTestNode } from 'fuels/test-utils'; import { VoidFactory } from '../test/typegen'; -import type { NativeEnumInput } from '../test/typegen/contracts/Void'; -import type { Option } from '../test/typegen/contracts/common'; +import type { Option } from '../test/typegen/common'; +import { NativeEnum } from '../test/typegen/contracts/VoidTypes'; /** * @group node @@ -35,12 +35,10 @@ describe('Void Tests', () => { contracts: [voidContract], } = launched; - const enumValue: NativeEnumInput = 'C' as NativeEnumInput; - - const { waitForResult } = await voidContract.functions.echo_native_enum(enumValue).call(); + const { waitForResult } = await voidContract.functions.echo_native_enum(NativeEnum.C).call(); const { value } = await waitForResult(); - expect(value).toEqual(enumValue); + expect(value).toEqual(NativeEnum.C); }); it('should handle input arguments of type [42, void]', async () => { diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml index 7a4dc974141..a01e7b9390a 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml +++ b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml @@ -1,6 +1,9 @@ [workspace] members = [ "abi-contract", + "abi-script", + "abi-predicate", + "parser", "advanced-logging", "advanced-logging-abi", "advanced-logging-other-contract", diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/main.sw index dcce801f68d..1b1679a2562 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/main.sw +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/main.sw @@ -26,6 +26,23 @@ fn divide(numerator: u64, denominator: u64) -> Result { abi AbiContract { fn configurables() -> Configurables; + fn attributes_none() -> (); + #[storage(read)] + fn attributes_storage_read() -> (); + #[storage(write)] + fn attributes_storage_write() -> (); + #[storage(read, write)] + fn attributes_storage_read_write() -> (); + #[payable] + fn attributes_payable() -> (); + #[test] + fn attributes_test() -> (); + #[inline(never)] + fn attributes_inline_never() -> (); + #[inline(always)] + fn attributes_inline_always() -> (); + fn attributes_doc_comment() -> (); + fn types_u8(x: u8) -> u8; fn types_u16(x: u16) -> u16; fn types_u32(x: u32) -> u32; @@ -145,6 +162,51 @@ impl AbiContract for Contract { } } + fn attributes_none() -> () { + () + } + + #[storage(read)] + fn attributes_storage_read() -> () { + () + } + + #[storage(write)] + fn attributes_storage_write() -> () { + () + } + + #[storage(read, write)] + fn attributes_storage_read_write() -> () { + () + } + + #[payable] + fn attributes_payable() -> () { + () + } + + #[test] + fn attributes_test() -> () { + () + } + + #[inline(never)] + fn attributes_inline_never() -> () { + () + } + + #[inline(always)] + fn attributes_inline_always() -> () { + () + } + + /// This is a doc + /// This is another doc comment + fn attributes_doc_comment() -> () { + () + } + fn types_u8(x: u8) -> u8 { assert_eq(x, 8); diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/Forc.toml new file mode 100644 index 00000000000..07df6e22027 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/Forc.toml @@ -0,0 +1,4 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "abi-predicate" diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/src/main.sw new file mode 100644 index 00000000000..1759925ec32 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-predicate/src/main.sw @@ -0,0 +1,34 @@ +predicate; + +struct Validation { + has_account: bool, + total_complete: u64, +} + +enum MyGenericEnum { + a: T, +} + +struct MyGenericStruct { + a: T, +} + +struct Configurables { + U8_VALUE: u8, + B256_VALUE: b256, +} + +configurable { + U8_VALUE: u8 = 10, + B256_VALUE: b256 = 0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96, +} + +fn main( + configurables: Configurables, + vec: Vec, + enm: MyGenericEnum, + opt: Option, + res: Result, u64>, +) -> bool { + U8_VALUE == configurables.U8_VALUE && B256_VALUE == configurables.B256_VALUE +} diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/Forc.toml new file mode 100644 index 00000000000..324740dd9f8 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/Forc.toml @@ -0,0 +1,4 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "abi-script" diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/src/main.sw new file mode 100644 index 00000000000..e57711aa736 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-script/src/main.sw @@ -0,0 +1,34 @@ +script; + +struct Validation { + has_account: bool, + total_complete: u64, +} + +enum MyGenericEnum { + a: T, +} + +struct MyGenericStruct { + a: T, +} + +struct Configurables { + U8_VALUE: u8, + B256_VALUE: b256, +} + +configurable { + U8_VALUE: u8 = 10, + B256_VALUE: b256 = 0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96, +} + +fn main( + configurables: Configurables, + vec: Vec, + enm: MyGenericEnum, + opt: Option, + res: Result, u64>, +) -> bool { + U8_VALUE == configurables.U8_VALUE && B256_VALUE == configurables.B256_VALUE +} diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/parser/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/parser/Forc.toml new file mode 100644 index 00000000000..591f549398f --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/parser/Forc.toml @@ -0,0 +1,4 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "parser" diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/parser/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/parser/src/main.sw new file mode 100644 index 00000000000..44d4262645a --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/parser/src/main.sw @@ -0,0 +1,69 @@ +contract; +use std::bytes::Bytes; +use std::message::send_typed_message; + +struct GenericStruct { + a: bool, + b: u32, + c: T, +} + +pub struct DoubleGeneric { + pub a: T, + pub b: F, +} + +struct NestedGenericStruct { + a: Vec>, + b: Vec>, + c: DoubleGeneric, +} + +struct SimpleStruct { + a: bool, +} + +pub struct StructWithImplicitGenerics { + pub a: [E; 3], + pub b: (E, F), +} + +configurable { + U8_VALUE: u8 = 10, +} + +abi VoidContract { + fn generic_structs( + arg1: GenericStruct>, + arg2: NestedGenericStruct, + ) -> bool; + fn implicit_generic_struct( + arg1: StructWithImplicitGenerics, + arg2: (bool, StructWithImplicitGenerics), + ) -> bool; + fn bytes(arg: Bytes) -> bool; +} + +impl VoidContract for Contract { + fn generic_structs( + arg1: GenericStruct>, + arg2: NestedGenericStruct, + ) -> bool { + log(arg1.a); + send_typed_message( + 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20, + arg1.a, + 123, + ); + true + } + fn implicit_generic_struct( + arg1: StructWithImplicitGenerics, + arg2: (bool, StructWithImplicitGenerics), + ) -> bool { + true + } + fn bytes(arg: Bytes) -> bool { + true + } +} diff --git a/packages/fuels/.gitignore b/packages/fuels/.gitignore index 289ea019274..d6ba43d5252 100644 --- a/packages/fuels/.gitignore +++ b/packages/fuels/.gitignore @@ -1,2 +1,3 @@ README.md test/__temp__* +test/fixtures/typegend \ No newline at end of file diff --git a/packages/fuels/package.json b/packages/fuels/package.json index d22bfe12ee3..53401ed192e 100644 --- a/packages/fuels/package.json +++ b/packages/fuels/package.json @@ -62,8 +62,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@fuel-ts/abi-coder": "workspace:*", - "@fuel-ts/abi-typegen": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/contract": "workspace:*", diff --git a/packages/fuels/src/cli.ts b/packages/fuels/src/cli.ts index 9a716cbfb7d..9887df494d2 100644 --- a/packages/fuels/src/cli.ts +++ b/packages/fuels/src/cli.ts @@ -1,4 +1,4 @@ -import { configureCliOptions as configureTypegenCliOptions } from '@fuel-ts/abi-typegen/cli'; +import { configureTypegenCliOptions } from '@fuel-ts/abi/cli'; import { versions } from '@fuel-ts/versions'; import { runVersions } from '@fuel-ts/versions/cli'; import { Command, Option } from 'commander'; @@ -98,11 +98,7 @@ export const configureCli = () => { /** * Routing external commands from sub-packages' CLIs */ - - // Typegen - configureTypegenCliOptions( - program.command('typegen').description(`Generate Typescript from Sway ABI JSON files`) - ); + command = configureTypegenCliOptions(program.command(Commands.typegen)); // Versions (command = program.command('versions')) diff --git a/packages/fuels/src/cli/commands/build/generateTypes.ts b/packages/fuels/src/cli/commands/build/generateTypes.ts deleted file mode 100644 index dbcb17fe4bf..00000000000 --- a/packages/fuels/src/cli/commands/build/generateTypes.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ProgramTypeEnum } from '@fuel-ts/abi-typegen'; -import { runTypegen } from '@fuel-ts/abi-typegen/runTypegen'; -import { getBinaryVersions } from '@fuel-ts/versions/cli'; -import { writeFileSync, mkdirSync } from 'fs'; -import { globSync } from 'glob'; -import { join } from 'path'; - -import { getABIPaths } from '../../config/forcUtils'; -import { renderIndexTemplate } from '../../templates'; -import type { FuelsConfig } from '../../types'; -import { debug, log, loggingConfig } from '../../utils/logger'; - -async function generateTypesForProgramType( - config: FuelsConfig, - paths: string[], - programType: ProgramTypeEnum -) { - debug('Generating types..'); - - let filepaths = await getABIPaths(paths, config); - const pluralizedDirName = `${String(programType).toLocaleLowerCase()}s`; - const versions = getBinaryVersions(config); - - const isScript = programType === ProgramTypeEnum.SCRIPT; - const isPredicate = programType === ProgramTypeEnum.PREDICATE; - - if (isScript || isPredicate) { - const loaderFiles = paths.flatMap((dirpath) => { - const glob = `*-abi.json`; - const cwd = `${dirpath}/out`; - return globSync(glob, { cwd }).map((filename) => `${dirpath}/out/${filename}`); - }); - filepaths = filepaths.concat(loaderFiles); - } - - runTypegen({ - programType, - cwd: config.basePath, - filepaths, - output: join(config.output, pluralizedDirName), - silent: !loggingConfig.isDebugEnabled, - versions, - }); - - return pluralizedDirName; -} - -export async function generateTypes(config: FuelsConfig) { - log('Generating types..'); - - const { contracts, scripts, predicates, output } = config; - - mkdirSync(output, { recursive: true }); - - const members = [ - { type: ProgramTypeEnum.CONTRACT, programs: contracts }, - { type: ProgramTypeEnum.SCRIPT, programs: scripts }, - { type: ProgramTypeEnum.PREDICATE, programs: predicates }, - ]; - - const pluralizedDirNames = await Promise.all( - members - .filter(({ programs }) => !!programs.length) - .map(({ programs, type }) => generateTypesForProgramType(config, programs, type)) - ); - - const indexFile = await renderIndexTemplate(pluralizedDirNames); - - writeFileSync(join(config.output, 'index.ts'), indexFile); -} diff --git a/packages/fuels/src/cli/commands/build/index.test.ts b/packages/fuels/src/cli/commands/build/index.test.ts index 2983713940f..4deed12d989 100644 --- a/packages/fuels/src/cli/commands/build/index.test.ts +++ b/packages/fuels/src/cli/commands/build/index.test.ts @@ -1,9 +1,9 @@ import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; import { mockLogger } from '../../../../test/utils/mockLogger'; +import * as generateTypesMod from '../generate-types'; import { build } from '.'; import * as buildSwayProgramsMod from './buildSwayPrograms'; -import * as generateTypesMod from './generateTypes'; /** * @group node diff --git a/packages/fuels/src/cli/commands/build/index.ts b/packages/fuels/src/cli/commands/build/index.ts index 65cf4799c6d..88c77a21bc4 100644 --- a/packages/fuels/src/cli/commands/build/index.ts +++ b/packages/fuels/src/cli/commands/build/index.ts @@ -4,15 +4,15 @@ import type { FuelsConfig } from '../../types'; import { log } from '../../utils/logger'; import { deploy } from '../deploy'; import { autoStartFuelCore } from '../dev/autoStartFuelCore'; +import { generateTypes } from '../generate-types'; import { buildSwayPrograms } from './buildSwayPrograms'; -import { generateTypes } from './generateTypes'; export async function build(config: FuelsConfig, program?: Command) { log('Building..'); await buildSwayPrograms(config); - await generateTypes(config); + generateTypes(config); config.onBuild?.(config); const options = program?.opts(); diff --git a/packages/fuels/src/cli/commands/deploy/deployContracts.ts b/packages/fuels/src/cli/commands/deploy/deployContracts.ts index a7c6da13d2b..52aac577516 100644 --- a/packages/fuels/src/cli/commands/deploy/deployContracts.ts +++ b/packages/fuels/src/cli/commands/deploy/deployContracts.ts @@ -48,7 +48,7 @@ export async function deployContract( const proxyBytecode = Src14OwnedProxyFactory.bytecode; const proxyAbi = Src14OwnedProxy.abi; - const proxyStorageSlots = Src14OwnedProxy.storageSlots ?? []; + const proxyStorageSlots = Src14OwnedProxyFactory.storageSlots ?? []; const isProxyEnabled = tomlContents?.proxy?.enabled; const proxyAddress = tomlContents?.proxy?.address; diff --git a/packages/fuels/src/cli/commands/deploy/deployPredicates.ts b/packages/fuels/src/cli/commands/deploy/deployPredicates.ts index b3375d85e42..8678b3269cd 100644 --- a/packages/fuels/src/cli/commands/deploy/deployPredicates.ts +++ b/packages/fuels/src/cli/commands/deploy/deployPredicates.ts @@ -30,7 +30,7 @@ export async function deployPredicates(config: FuelsConfig) { const predicate = new Predicate({ abi, bytecode, provider: wallet.provider }); const { bytes: loaderBytecode, - interface: { jsonAbi }, + interface: { specification }, } = await (await predicate.deploy(wallet)).waitForResult(); const predicateRoot = getPredicateRoot(loaderBytecode); @@ -41,7 +41,7 @@ export async function deployPredicates(config: FuelsConfig) { path: predicatePath, predicateRoot, loaderBytecode, - abi: jsonAbi, + abi: specification, }); } diff --git a/packages/fuels/src/cli/commands/deploy/deployScripts.ts b/packages/fuels/src/cli/commands/deploy/deployScripts.ts index 557536d5987..94e1571e57e 100644 --- a/packages/fuels/src/cli/commands/deploy/deployScripts.ts +++ b/packages/fuels/src/cli/commands/deploy/deployScripts.ts @@ -31,7 +31,7 @@ export async function deployScripts(config: FuelsConfig) { const script = new Script(bytecode, abi, wallet); const { bytes: loaderBytecode, - interface: { jsonAbi }, + interface: { specification }, } = await (await script.deploy(wallet)).waitForResult(); debug(`Script deployed: ${projectName}`); @@ -39,7 +39,7 @@ export async function deployScripts(config: FuelsConfig) { scripts.push({ path: scriptPath, loaderBytecode, - abi: jsonAbi, + abi: specification, }); } diff --git a/packages/fuels/src/cli/commands/deploy/index.ts b/packages/fuels/src/cli/commands/deploy/index.ts index 28b14181d41..c223b7b5488 100644 --- a/packages/fuels/src/cli/commands/deploy/index.ts +++ b/packages/fuels/src/cli/commands/deploy/index.ts @@ -1,5 +1,5 @@ import type { FuelsConfig } from '../../types'; -import { generateTypes } from '../build/generateTypes'; +import { generateTypes } from '../generate-types'; import { deployContracts } from './deployContracts'; import { deployPredicates } from './deployPredicates'; @@ -35,9 +35,9 @@ export async function deploy(config: FuelsConfig) { /** * After deploying scripts/predicates, we need to - * re-generate factory classe with the loader coee + * re-generate factory classes with the loader code */ - await generateTypes(config); + generateTypes(config); return { contracts, diff --git a/packages/fuels/src/cli/commands/generate-types.ts b/packages/fuels/src/cli/commands/generate-types.ts new file mode 100644 index 00000000000..ac992252b67 --- /dev/null +++ b/packages/fuels/src/cli/commands/generate-types.ts @@ -0,0 +1,16 @@ +import { runTypegen } from '@fuel-ts/abi/cli'; + +import type { FuelsConfig } from '../types'; + +export function generateTypes(config: FuelsConfig) { + const { contracts, scripts, predicates, output } = config; + + const loaderPaths = scripts.concat(predicates).map((path) => `${path}/out`); + + const paths = contracts + .concat(scripts, predicates) + .map((path) => `${path}/out/${config.buildMode}`) + .concat(loaderPaths); + + runTypegen({ inputs: paths, output }); +} diff --git a/packages/fuels/src/cli/templates/index.hbs b/packages/fuels/src/cli/templates/index.hbs index 569e0ad5e15..81a242dac7b 100644 --- a/packages/fuels/src/cli/templates/index.hbs +++ b/packages/fuels/src/cli/templates/index.hbs @@ -1,3 +1,3 @@ {{#each paths}} export * from './{{this}}'; -{{/each}} +{{/each}} \ No newline at end of file diff --git a/packages/fuels/src/cli/types.ts b/packages/fuels/src/cli/types.ts index f44c32cd93e..8b005e8ac0b 100644 --- a/packages/fuels/src/cli/types.ts +++ b/packages/fuels/src/cli/types.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { DeployContractOptions } from '@fuel-ts/contract'; export enum Commands { @@ -8,6 +8,7 @@ export enum Commands { init = 'init', versions = 'versions', node = 'node', + typegen = 'typegen', } export type CommandEvent = @@ -34,6 +35,10 @@ export type CommandEvent = | { type: Commands.node; data: void; + } + | { + type: Commands.typegen; + data: void; }; export type DeployedContract = { @@ -44,7 +49,7 @@ export type DeployedContract = { export type DeployedScript = { path: string; loaderBytecode: Uint8Array; - abi: JsonAbi; + abi: AbiSpecification; }; export type DeployedPredicate = DeployedScript & { diff --git a/packages/fuels/src/index.test.ts b/packages/fuels/src/index.test.ts index 5528e63af3c..a66158a9657 100644 --- a/packages/fuels/src/index.test.ts +++ b/packages/fuels/src/index.test.ts @@ -8,7 +8,8 @@ describe('index.js', () => { expect(fuels.hexlify).toBeTruthy(); expect(fuels.arrayify).toBeTruthy(); expect(fuels.concat).toBeTruthy(); - expect(fuels.Interface).toBeTruthy(); + + expect(fuels.AbiCoder).toBeTruthy(); expect(fuels.Address).toBeTruthy(); expect(fuels.FuelError).toBeTruthy(); expect(fuels.Contract).toBeTruthy(); @@ -19,6 +20,5 @@ describe('index.js', () => { expect(fuels.TransactionType).toBeTruthy(); expect(fuels.Script).toBeTruthy(); expect(fuels.FunctionInvocationScope).toBeTruthy(); - expect(fuels.Src14OwnedProxy).toBeTruthy(); }); }); diff --git a/packages/fuels/src/index.ts b/packages/fuels/src/index.ts index d485f2a6cc4..5a8a31fd1ab 100644 --- a/packages/fuels/src/index.ts +++ b/packages/fuels/src/index.ts @@ -1,6 +1,6 @@ export { Script } from '@fuel-ts/script'; export * from './cli/index'; -export * from '@fuel-ts/abi-coder'; +export * from '@fuel-ts/abi'; export * from '@fuel-ts/address'; export * from '@fuel-ts/address/configs'; export * from '@fuel-ts/contract'; @@ -15,4 +15,6 @@ export * from '@fuel-ts/utils'; export * from '@fuel-ts/account'; export * from '@fuel-ts/transactions/configs'; export * from '@fuel-ts/account/configs'; +export * from '@fuel-ts/abi'; export * from '@fuel-ts/recipes'; +export * from '@fuel-ts/abi'; diff --git a/packages/fuels/test/features/build.test.ts b/packages/fuels/test/features/build.test.ts index f03a6388419..68e159870df 100644 --- a/packages/fuels/test/features/build.test.ts +++ b/packages/fuels/test/features/build.test.ts @@ -98,7 +98,7 @@ describe('build', { timeout: 180000 }, () => { ].map((f) => join(paths.outputDir, f)); files.forEach((file) => expect(existsSync(file), `${file} does not exist`).toBeTruthy()); - expect(readdirSync(paths.outputContractsDir)).toHaveLength(3); + expect(readdirSync(paths.outputContractsDir)).toHaveLength(7); expect(autoStartFuelCore).toHaveBeenCalledTimes(0); expect(deploy).toHaveBeenCalledTimes(0); @@ -132,7 +132,7 @@ describe('build', { timeout: 180000 }, () => { ].map((f) => join(paths.outputDir, f)); files.forEach((file) => expect(existsSync(file), `${file} does not exist`).toBeTruthy()); - expect(readdirSync(paths.outputContractsDir)).toHaveLength(9); + expect(readdirSync(paths.outputContractsDir)).toHaveLength(25); expect(autoStartFuelCore).toHaveBeenCalledTimes(0); expect(deploy).toHaveBeenCalledTimes(0); diff --git a/packages/fuels/test/features/deploy.test.ts b/packages/fuels/test/features/deploy.test.ts index c40dec080a1..1f9caa1ea20 100644 --- a/packages/fuels/test/features/deploy.test.ts +++ b/packages/fuels/test/features/deploy.test.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import type { Account } from '@fuel-ts/account'; import { Contract } from '@fuel-ts/program'; import { exec } from 'child_process'; @@ -82,7 +82,7 @@ describe('deploy', { timeout: 180000 }, () => { /** * Executes the target contract and returns the values of the functions for proxy deploys. */ - async function executeTargetContract(contractId: string, abi: JsonAbi, wallet: Account) { + async function executeTargetContract(contractId: string, abi: AbiSpecification, wallet: Account) { const targetContract = new Contract(contractId, abi, wallet); const { value: getCountValue } = await targetContract.functions.get_value().get(); diff --git a/packages/fuels/test/fixtures/fuels.config.ts b/packages/fuels/test/fixtures/fuels.config.ts index 2e25eae894c..4aeb67ebf9b 100644 --- a/packages/fuels/test/fixtures/fuels.config.ts +++ b/packages/fuels/test/fixtures/fuels.config.ts @@ -19,7 +19,7 @@ export const fuelsConfig: FuelsConfig = { ], scripts: [join(scriptsDir, 'script')], predicates: [join(predicatesDir, 'predicate')], - output: '/output', + output: './typegen', forcPath: 'fuels-forc', fuelCorePath: 'fuels-core', deployConfig: {}, @@ -27,6 +27,8 @@ export const fuelsConfig: FuelsConfig = { fuelCorePort: 4000, providerUrl: 'http://127.0.0.1:4000/v1/graphql', configPath: __filename, - forcBuildFlags: [], - buildMode: 'debug', + forcBuildFlags: ['--release'], + buildMode: 'release', }; + +export default fuelsConfig; diff --git a/packages/program/package.json b/packages/program/package.json index bac64bd1b55..573d029bc99 100644 --- a/packages/program/package.json +++ b/packages/program/package.json @@ -26,7 +26,7 @@ "license": "Apache-2.0", "dependencies": { "ramda": "0.30.1", - "@fuel-ts/abi-coder": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/errors": "workspace:*", diff --git a/packages/program/src/contract-call-script.ts b/packages/program/src/contract-call-script.ts index f43811fc8f7..0b22bcfcf9d 100644 --- a/packages/program/src/contract-call-script.ts +++ b/packages/program/src/contract-call-script.ts @@ -1,11 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - WORD_SIZE, - B256Coder, - ASSET_ID_LEN, - BigNumberCoder, - CONTRACT_ID_LEN, -} from '@fuel-ts/abi-coder'; +import { WORD_SIZE, ASSET_ID_LEN, CONTRACT_ID_LEN, encoding } from '@fuel-ts/abi'; import type { CallResult, TransactionResultCallReceipt, @@ -126,7 +120,7 @@ const scriptResultDecoder = (contractId: Address) => (result: ScriptResult) => { return []; } if (receipt.type === ReceiptType.Return) { - return [new BigNumberCoder('u64').encode((receipt as TransactionResultReturnReceipt).val)]; + return [encoding.v1.u64.encode((receipt as TransactionResultReturnReceipt).val)]; } if (receipt.type === ReceiptType.ReturnData) { const encodedScriptReturn = arrayify(receipt.data); @@ -200,15 +194,15 @@ export const getContractCallScript = ( let gasForwardedOffset = 0; // 1. Amount - scriptData.push(new BigNumberCoder('u64').encode(call.amount || 0)); + scriptData.push(encoding.v1.u64.encode(call.amount || 0)); // 2. Asset ID - scriptData.push(new B256Coder().encode(call.assetId?.toString() || ZeroBytes32)); + scriptData.push(encoding.v1.b256.encode(call.assetId?.toString() || ZeroBytes32)); // 3. Contract ID scriptData.push(call.contractId.toBytes()); // 4. Function selector offset - scriptData.push(new BigNumberCoder('u64').encode(encodedSelectorOffset)); + scriptData.push(encoding.v1.u64.encode(encodedSelectorOffset)); // 5. Encoded argument offset - scriptData.push(new BigNumberCoder('u64').encode(encodedArgsOffset)); + scriptData.push(encoding.v1.u64.encode(encodedArgsOffset)); // 6. Encoded function selector scriptData.push(call.fnSelectorBytes); // 7. Encoded arguments @@ -216,7 +210,7 @@ export const getContractCallScript = ( // 8. Gas to be forwarded if (call.gas) { - scriptData.push(new BigNumberCoder('u64').encode(call.gas)); + scriptData.push(encoding.v1.u64.encode(call.gas)); gasForwardedOffset = encodedArgsOffset + encodedArgs.byteLength; } diff --git a/packages/program/src/contract.test.ts b/packages/program/src/contract.test.ts index a99ab1dc60a..8852ff272ed 100644 --- a/packages/program/src/contract.test.ts +++ b/packages/program/src/contract.test.ts @@ -1,11 +1,11 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import { Account } from '@fuel-ts/account'; import { setupTestProviderAndWallets } from '@fuel-ts/account/test-utils'; import Contract from './contract'; const CONTRACT_ID = '0x0101010101010101010101010101010101010101010101010101010101010101'; -const ABI: JsonAbi = { +const ABI: AbiSpecification = { concreteTypes: [ { concreteTypeId: 'asdf', diff --git a/packages/program/src/contract.ts b/packages/program/src/contract.ts index 3193944fc04..267fa849af8 100644 --- a/packages/program/src/contract.ts +++ b/packages/program/src/contract.ts @@ -1,5 +1,5 @@ -import type { FunctionFragment, JsonAbi } from '@fuel-ts/abi-coder'; -import { Interface } from '@fuel-ts/abi-coder'; +import type { AbiSpecification, AbiCoderFunction } from '@fuel-ts/abi'; +import { AbiCoder } from '@fuel-ts/abi'; import type { Account, Provider } from '@fuel-ts/account'; import { Address } from '@fuel-ts/address'; import type { BytesLike } from '@fuel-ts/utils'; @@ -25,7 +25,7 @@ export default class Contract implements AbstractContract { /** * The contract's ABI interface. */ - interface!: Interface; + interface!: AbiCoder; /** * The account associated with the contract, if available. @@ -46,10 +46,10 @@ export default class Contract implements AbstractContract { */ constructor( id: string | Address, - abi: JsonAbi | Interface, + abi: AbiSpecification | AbiCoder, accountOrProvider: Account | Provider ) { - this.interface = abi instanceof Interface ? abi : new Interface(abi); + this.interface = abi instanceof AbiCoder ? abi : AbiCoder.fromAbi(abi); this.id = Address.fromAddressOrString(id); /** @@ -88,7 +88,7 @@ export default class Contract implements AbstractContract { * @param func - The function fragment to build a scope for. * @returns A function that creates a FunctionInvocationScope. */ - buildFunction(func: FunctionFragment) { + buildFunction(func: AbiCoderFunction) { return (() => { const funcInvocationScopeCreator = (...args: Array) => new FunctionInvocationScope(this, func, args); diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index 5e10e131e85..6ffbf3001da 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { InputValue, JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification, InputValue } from '@fuel-ts/abi'; import type { Provider, CoinQuantity, @@ -66,7 +66,7 @@ export class BaseInvocationScope { protected requiredCoins: CoinQuantity[] = []; protected isMultiCall: boolean = false; protected hasCallParamsGasLimit: boolean = false; // flag to check if any of the callParams has gasLimit set - protected externalAbis: Record = {}; + protected externalAbis: Record = {}; private addSignersCallback?: ( txRequest: ScriptTransactionRequest ) => Promise; @@ -297,7 +297,7 @@ export class BaseInvocationScope { addContracts(contracts: Array) { contracts.forEach((contract) => { this.transactionRequest.addContractInputAndOutput(contract.id); - this.externalAbis[contract.id.toB256()] = contract.interface.jsonAbi; + this.externalAbis[contract.id.toB256()] = contract.interface.specification; }); return this; } diff --git a/packages/program/src/functions/invocation-scope.ts b/packages/program/src/functions/invocation-scope.ts index 49556956052..4f7488eba14 100644 --- a/packages/program/src/functions/invocation-scope.ts +++ b/packages/program/src/functions/invocation-scope.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { FunctionFragment } from '@fuel-ts/abi-coder'; +import type { AbiCoderFunction } from '@fuel-ts/abi'; import type { CoinQuantity } from '@fuel-ts/account'; import { coinQuantityfy } from '@fuel-ts/account'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; @@ -18,7 +18,7 @@ export class FunctionInvocationScope< TArgs extends Array = Array, TReturn = any, > extends BaseInvocationScope { - protected func: FunctionFragment; + protected func: AbiCoderFunction; private callParameters?: CallParams; private forward?: CoinQuantity; protected args: TArgs; @@ -30,7 +30,7 @@ export class FunctionInvocationScope< * @param func - The function fragment. * @param args - The arguments. */ - constructor(program: AbstractProgram, func: FunctionFragment, args: TArgs) { + constructor(program: AbstractProgram, func: AbiCoderFunction, args: TArgs) { super(program, false); this.func = func; this.args = args || []; @@ -80,7 +80,7 @@ export class FunctionInvocationScope< this.callParameters = callParams; if (callParams?.forward) { - if (!this.func.attributes.find((attr) => attr.name === 'payable')) { + if (!this.func.attributes?.find((attr) => attr.name === 'payable')) { throw new FuelError( ErrorCode.TRANSACTION_ERROR, `The target function ${this.func.name} cannot accept forwarded funds as it's not marked as 'payable'.` diff --git a/packages/program/src/response.ts b/packages/program/src/response.ts index 54159eccb3e..5c98595ff07 100644 --- a/packages/program/src/response.ts +++ b/packages/program/src/response.ts @@ -39,7 +39,7 @@ export const extractInvocationResult = ( const decodedResults = encodedResults.map((encodedResult, i) => { const { func } = functionScopes[i].getCallConfig(); - return func.decodeOutput(encodedResult)?.[0]; + return func.decodeOutput(encodedResult); }); return (isMultiCall ? decodedResults : decodedResults?.[0]) as T; diff --git a/packages/program/src/script-request.ts b/packages/program/src/script-request.ts index 79d749c033c..05b7ed72da4 100644 --- a/packages/program/src/script-request.ts +++ b/packages/program/src/script-request.ts @@ -5,7 +5,7 @@ import { SCRIPT_FIXED_SIZE, WORD_SIZE, calculateVmTxMemory, -} from '@fuel-ts/abi-coder'; +} from '@fuel-ts/abi'; import type { TransactionResultReturnDataReceipt, TransactionResultRevertReceipt, @@ -161,8 +161,7 @@ export function callResultToInvocationResult( value = scriptResult.returnReceipt.val; } if (scriptResult.returnReceipt.type === ReceiptType.ReturnData) { - const decoded = call.func.decodeOutput(scriptResult.returnReceipt.data); - value = decoded[0]; + value = call.func.decodeOutput(scriptResult.returnReceipt.data); } return value as TReturn; diff --git a/packages/program/src/types.ts b/packages/program/src/types.ts index fc373f52f30..17eb46bbc03 100644 --- a/packages/program/src/types.ts +++ b/packages/program/src/types.ts @@ -1,5 +1,5 @@ /* eslint-disable max-classes-per-file */ -import type { FunctionFragment, JsonAbi, Interface } from '@fuel-ts/abi-coder'; +import type { AbiCoderFunction, AbiSpecification, AbiCoder } from '@fuel-ts/abi'; import type { CallResult, CoinQuantity, @@ -21,7 +21,7 @@ import type { FunctionInvocationScope } from './functions/invocation-scope'; */ export abstract class AbstractProgram { abstract account: AbstractAccount | null; - abstract interface: Pick; + abstract interface: Pick; abstract provider: Pick | null; } @@ -39,7 +39,7 @@ export type ContractCall = { amount?: BigNumberish; assetId?: BytesLike; gas?: BigNumberish; - externalContractsAbis?: Record; + externalContractsAbis?: Record; }; /** @@ -68,12 +68,12 @@ export type TxParams = Partial<{ * @template T - Type of the function's arguments. */ export type CallConfig = { - func: FunctionFragment; + func: AbiCoderFunction; program: AbstractProgram; callParameters?: CallParams; txParameters?: TxParams; forward?: CoinQuantity; - externalAbis: Record; + externalAbis: Record; args: T; }; diff --git a/packages/program/src/utils.ts b/packages/program/src/utils.ts index dac9f8e6cb0..5bbe9ebc15d 100644 --- a/packages/program/src/utils.ts +++ b/packages/program/src/utils.ts @@ -27,10 +27,11 @@ export function getAbisFromAllCalls( const { program, externalAbis } = funcScope.getCallConfig(); if (i === 0) { - acc.main = program.interface.jsonAbi; + acc.main = program.interface.specification; acc.otherContractsAbis = {}; } else { - acc.otherContractsAbis[(program).id.toB256()] = program.interface.jsonAbi; + acc.otherContractsAbis[(program).id.toB256()] = + program.interface.specification; } acc.otherContractsAbis = { ...acc.otherContractsAbis, ...externalAbis }; diff --git a/packages/recipes/package.json b/packages/recipes/package.json index 41246099cac..a25a257fb03 100644 --- a/packages/recipes/package.json +++ b/packages/recipes/package.json @@ -28,13 +28,12 @@ }, "license": "Apache-2.0", "dependencies": { + "@fuel-ts/abi": "workspace:*", "@fuel-ts/address": "workspace:*", - "@fuel-ts/abi-coder": "workspace:*", - "@fuel-ts/abi-typegen": "workspace:*", "@fuel-ts/account": "workspace:*", + "@fuel-ts/contract": "workspace:*", "@fuel-ts/program": "workspace:*", "@fuel-ts/transactions": "workspace:*", - "@fuel-ts/utils": "workspace:*", - "@fuel-ts/contract": "workspace:*" + "@fuel-ts/utils": "workspace:*" } } diff --git a/packages/recipes/scripts/build-recipes.ts b/packages/recipes/scripts/build-recipes.ts index 9df639cbcfa..0dbe3098210 100644 --- a/packages/recipes/scripts/build-recipes.ts +++ b/packages/recipes/scripts/build-recipes.ts @@ -2,30 +2,36 @@ import { execSync } from 'child_process'; import { readFileSync, writeFileSync } from 'fs'; import { join } from 'path'; -execSync(`fuels-typegen -i src/contracts/**/*-abi.json -o src/types`); +execSync(`fuels-typegen -i src/contracts/src14 -o src/types`); + +const typesPath = join(__dirname, '..', 'src', 'types'); +const supportedRecipes = ['Src14OwnedProxy'] + .map((s) => [s, `${s}Factory`, `${s}Types`, `${s}-bytecode`, `${s}-storage-slots`, `${s}-abi`]) + .flat() + .map((s) => join(typesPath, 'contracts', `${s}.ts`)) + .concat([join(typesPath, 'common.ts')]); -const supportedRecipes = ['Src14OwnedProxy'].map((s) => [s, `${s}Factory`]).flat(); const importReplacementMap = { + AbiCoder: '@fuel-ts/abi', + AbiCoderFunction: '@fuel-ts/abi', + AbiSpecification: '@fuel-ts/abi', + StrSlice: '@fuel-ts/abi', Contract: '@fuel-ts/program', ContractFactory: '@fuel-ts/contract', DeployContractOptions: '@fuel-ts/contract', - Interface: '@fuel-ts/abi-coder', Provider: '@fuel-ts/account', Account: '@fuel-ts/account', StorageSlot: '@fuel-ts/transactions', Address: '@fuel-ts/address', - FunctionFragment: '@fuel-ts/abi-coder', InvokeFunction: '@fuel-ts/program', - StrSlice: '@fuel-ts/abi-coder', decompressBytecode: '@fuel-ts/utils', }; -for (const recipe of supportedRecipes) { - const contractPath = join(__dirname, '..', 'src', 'types', `${recipe}.ts`); - let contractContents = readFileSync(contractPath, 'utf-8'); +for (const filepath of supportedRecipes) { + let contents = readFileSync(filepath, 'utf-8'); // Find all imports from 'fuels' const fuelImportsRegex = /import\s+(type\s+)?{([^}]+)}\s+from\s+['"]fuels['"];?/gs; - const matches = [...contractContents.matchAll(fuelImportsRegex)]; + const matches = [...contents.matchAll(fuelImportsRegex)]; // Extract the imported items and create new import statements const importsByPackage = new Map>(); @@ -58,18 +64,15 @@ for (const recipe of supportedRecipes) { .map(([pkg, imports]) => `import { ${Array.from(imports).join(', ')} } from '${pkg}';`) .join('\n'); + // Add new imports at the top of the file + const importRegex = /.*(?=import )/s; + contents = contents.replace(importRegex, (match) => `${match}\n${newImports}`); + // Replace all 'fuels' imports with the new imports matches.forEach((match) => { - contractContents = contractContents.replace(match[0], ''); + contents = contents.replace(match[0], ''); }); - // Add new imports at the top of the file - const versionCommentRegex = /\/\*\s*Fuels version: \d+\.\d+\.\d+\s*\*\/\s*/; - contractContents = contractContents.replace( - versionCommentRegex, - (match) => `${match}\n${newImports}` - ); - // Write the modified contents back to the file - writeFileSync(contractPath, contractContents); + writeFileSync(filepath, contents); } diff --git a/packages/recipes/src/types/common.d.ts b/packages/recipes/src/types/common.d.ts deleted file mode 100644 index ef3dec5e95c..00000000000 --- a/packages/recipes/src/types/common.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ - -/* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/consistent-type-imports */ - -/* - Fuels version: 0.97.2 -*/ - -/** - * Mimics Sway Enum. - * Requires one and only one Key-Value pair and raises error if more are provided. - */ -export type Enum = { - [K in keyof T]: Pick & { [P in Exclude]?: never }; -}[keyof T]; - -/** - * Mimics Sway Option and Vectors. - * Vectors are treated like arrays in Typescript. - */ -export type Option = T | undefined; - -export type Vec = T[]; - -/** - * Mimics Sway Result enum type. - * Ok represents the success case, while Err represents the error case. - */ -export type Result = Enum<{ Ok: T; Err: E }>; diff --git a/packages/recipes/src/types/common.ts b/packages/recipes/src/types/common.ts new file mode 100644 index 00000000000..68bf29ab981 --- /dev/null +++ b/packages/recipes/src/types/common.ts @@ -0,0 +1,53 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +import { type AbiCoderFunction } from '@fuel-ts/abi'; +import { type InvokeFunction } from '@fuel-ts/program'; + +/** + * Mimics Sway Enum. + * Requires one and only one Key-Value pair and raises error if more are provided. + */ +export type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +/** + * Mimics Sway Option type. + */ +export type Option = T | undefined; + +/** + * Mimics Sway Result enum type. + * Ok represents the success case, while Err represents the error case. + */ +export type Result = Enum<{ Ok: T; Err: E }>; + +/** + * Mimics Sway array type. For example, [u64; 10] is converted to ArrayOfLength. + */ +export type ArrayOfLength< + T, + Length extends number, + Arr extends unknown[] = [], +> = Arr['length'] extends Length ? Arr : ArrayOfLength; + +interface Types { + functions: Record; + configurables: Partial>; +} + +export type ProgramFunctionMapper = { + [K in keyof T]: InvokeFunction; +}; + +export type InterfaceFunctionMapper = { + [K in keyof T]: AbiCoderFunction; +}; diff --git a/packages/recipes/src/types/Src14OwnedProxy.ts b/packages/recipes/src/types/contracts/Src14OwnedProxy-abi.ts similarity index 80% rename from packages/recipes/src/types/Src14OwnedProxy.ts rename to packages/recipes/src/types/contracts/Src14OwnedProxy-abi.ts index 494f285063c..0637eec1537 100644 --- a/packages/recipes/src/types/Src14OwnedProxy.ts +++ b/packages/recipes/src/types/contracts/Src14OwnedProxy-abi.ts @@ -1,62 +1,16 @@ /* Autogenerated file. Do not edit manually. */ -/* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/consistent-type-imports */ +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ /* - Fuels version: 0.97.2 + Fuels version: 0.98.0 + Forc version: 0.66.5 */ -import { Contract, type InvokeFunction } from '@fuel-ts/program'; -import { Interface, type FunctionFragment, type StrSlice } from '@fuel-ts/abi-coder'; -import { type Provider, type Account } from '@fuel-ts/account'; -import { type StorageSlot } from '@fuel-ts/transactions'; -import { type Address } from '@fuel-ts/address'; -import type { Option, Enum } from './common'; +import { AbiSpecification } from '@fuel-ts/abi'; -export enum AccessErrorInput { - NotOwner = 'NotOwner', -} -export enum AccessErrorOutput { - NotOwner = 'NotOwner', -} -export type IdentityInput = Enum<{ Address: AddressInput; ContractId: ContractIdInput }>; -export type IdentityOutput = Enum<{ Address: AddressOutput; ContractId: ContractIdOutput }>; -export enum InitializationErrorInput { - CannotReinitialized = 'CannotReinitialized', -} -export enum InitializationErrorOutput { - CannotReinitialized = 'CannotReinitialized', -} -export enum SetProxyOwnerErrorInput { - CannotUninitialize = 'CannotUninitialize', -} -export enum SetProxyOwnerErrorOutput { - CannotUninitialize = 'CannotUninitialize', -} -export type StateInput = Enum<{ - Uninitialized: undefined; - Initialized: IdentityInput; - Revoked: undefined; -}>; -export type StateOutput = Enum<{ Uninitialized: void; Initialized: IdentityOutput; Revoked: void }>; - -export type AddressInput = { bits: string }; -export type AddressOutput = AddressInput; -export type ContractIdInput = { bits: string }; -export type ContractIdOutput = ContractIdInput; -export type ProxyOwnerSetInput = { new_proxy_owner: StateInput }; -export type ProxyOwnerSetOutput = { new_proxy_owner: StateOutput }; -export type ProxyTargetSetInput = { new_target: ContractIdInput }; -export type ProxyTargetSetOutput = { new_target: ContractIdOutput }; - -export type Src14OwnedProxyConfigurables = Partial<{ - INITIAL_TARGET: Option; - INITIAL_OWNER: StateInput; -}>; - -const abi = { +export const abi: AbiSpecification = { programType: 'contract', specVersion: '1', encodingVersion: '1', @@ -633,53 +587,3 @@ const abi = { }, ], }; - -const storageSlots: StorageSlot[] = [ - { - key: '7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55', - value: '0000000000000000000000000000000000000000000000000000000000000000', - }, - { - key: '7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56', - value: '0000000000000000000000000000000000000000000000000000000000000000', - }, - { - key: 'bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754', - value: '0000000000000000000000000000000000000000000000000000000000000000', - }, - { - key: 'bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755', - value: '0000000000000000000000000000000000000000000000000000000000000000', - }, -]; -export class Src14OwnedProxyInterface extends Interface { - constructor() { - super(abi); - } - - declare functions: { - proxy_target: FunctionFragment; - set_proxy_target: FunctionFragment; - proxy_owner: FunctionFragment; - initialize_proxy: FunctionFragment; - set_proxy_owner: FunctionFragment; - }; -} - -export class Src14OwnedProxy extends Contract { - static readonly abi = abi; - static readonly storageSlots = storageSlots; - - declare interface: Src14OwnedProxyInterface; - declare functions: { - proxy_target: InvokeFunction<[], Option>; - set_proxy_target: InvokeFunction<[new_target: ContractIdInput], void>; - proxy_owner: InvokeFunction<[], StateOutput>; - initialize_proxy: InvokeFunction<[], void>; - set_proxy_owner: InvokeFunction<[new_proxy_owner: StateInput], void>; - }; - - constructor(id: string | Address, accountOrProvider: Account | Provider) { - super(id, abi, accountOrProvider); - } -} diff --git a/packages/recipes/src/types/Src14OwnedProxyFactory.ts b/packages/recipes/src/types/contracts/Src14OwnedProxy-bytecode.ts similarity index 89% rename from packages/recipes/src/types/Src14OwnedProxyFactory.ts rename to packages/recipes/src/types/contracts/Src14OwnedProxy-bytecode.ts index 30762b7a0e9..a27e69cb2be 100644 --- a/packages/recipes/src/types/Src14OwnedProxyFactory.ts +++ b/packages/recipes/src/types/contracts/Src14OwnedProxy-bytecode.ts @@ -1,31 +1,15 @@ /* Autogenerated file. Do not edit manually. */ -/* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/consistent-type-imports */ +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ /* - Fuels version: 0.97.2 + Fuels version: 0.98.0 + Forc version: 0.66.5 */ -import { ContractFactory, type DeployContractOptions } from '@fuel-ts/contract'; import { decompressBytecode } from '@fuel-ts/utils'; -import { type Provider, type Account } from '@fuel-ts/account'; -import { Src14OwnedProxy } from './Src14OwnedProxy'; -const bytecode = decompressBytecode( +export const bytecode = decompressBytecode( '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' ); - -export class Src14OwnedProxyFactory extends ContractFactory { - static readonly bytecode = bytecode; - - constructor(accountOrProvider: Account | Provider) { - super(bytecode, Src14OwnedProxy.abi, accountOrProvider, Src14OwnedProxy.storageSlots); - } - - static deploy(wallet: Account, options: DeployContractOptions = {}) { - const factory = new Src14OwnedProxyFactory(wallet); - return factory.deploy(options); - } -} diff --git a/packages/recipes/src/types/contracts/Src14OwnedProxy-storage-slots.ts b/packages/recipes/src/types/contracts/Src14OwnedProxy-storage-slots.ts new file mode 100644 index 00000000000..2c9d2c2f4a9 --- /dev/null +++ b/packages/recipes/src/types/contracts/Src14OwnedProxy-storage-slots.ts @@ -0,0 +1,30 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +import { type StorageSlot } from '@fuel-ts/transactions'; + +export const storageSlots: StorageSlot[] = [ + { + key: '7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55', + value: '0000000000000000000000000000000000000000000000000000000000000000', + }, + { + key: '7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56', + value: '0000000000000000000000000000000000000000000000000000000000000000', + }, + { + key: 'bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea754', + value: '0000000000000000000000000000000000000000000000000000000000000000', + }, + { + key: 'bb79927b15d9259ea316f2ecb2297d6cc8851888a98278c0a2e03e1a091ea755', + value: '0000000000000000000000000000000000000000000000000000000000000000', + }, +]; diff --git a/packages/recipes/src/types/contracts/Src14OwnedProxy.ts b/packages/recipes/src/types/contracts/Src14OwnedProxy.ts new file mode 100644 index 00000000000..21d04bd6b1d --- /dev/null +++ b/packages/recipes/src/types/contracts/Src14OwnedProxy.ts @@ -0,0 +1,41 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +import type { Src14OwnedProxyTypes as Types } from './Src14OwnedProxyTypes'; +import type { InterfaceFunctionMapper, ProgramFunctionMapper } from '../common'; + +import { Contract } from '@fuel-ts/program'; +import { AbiCoder } from '@fuel-ts/abi'; +import { type Address } from '@fuel-ts/address'; +import { type Account, type Provider } from '@fuel-ts/account'; +import { abi } from './Src14OwnedProxy-abi'; + +export * from './Src14OwnedProxyTypes'; + +export type Src14OwnedProxyConfigurables = Types['configurables']; + +export class Src14OwnedProxyAbiCoder extends AbiCoder { + declare functions: InterfaceFunctionMapper; + + constructor() { + super(abi); + } +} + +export class Src14OwnedProxy extends Contract { + declare interface: Src14OwnedProxyAbiCoder; + declare functions: ProgramFunctionMapper; + + public static readonly abi = abi; + + constructor(id: string | Address, accountOrProvider: Account | Provider) { + super(id, abi, accountOrProvider); + } +} diff --git a/packages/recipes/src/types/contracts/Src14OwnedProxyFactory.ts b/packages/recipes/src/types/contracts/Src14OwnedProxyFactory.ts new file mode 100644 index 00000000000..ccc1e94ccf7 --- /dev/null +++ b/packages/recipes/src/types/contracts/Src14OwnedProxyFactory.ts @@ -0,0 +1,31 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +import { Src14OwnedProxy } from './Src14OwnedProxy'; +import { bytecode } from './Src14OwnedProxy-bytecode'; +import { abi } from './Src14OwnedProxy-abi'; + +import { ContractFactory, type DeployContractOptions } from '@fuel-ts/contract'; +import { type Account, type Provider } from '@fuel-ts/account'; +import { storageSlots } from './Src14OwnedProxy-storage-slots'; + +export class Src14OwnedProxyFactory extends ContractFactory { + static readonly bytecode = bytecode; + static readonly storageSlots = storageSlots; + + constructor(accountOrProvider: Account | Provider) { + super(bytecode, abi, accountOrProvider, Src14OwnedProxyFactory.storageSlots); + } + + static deploy(wallet: Account, options: DeployContractOptions = {}) { + const factory = new Src14OwnedProxyFactory(wallet); + return factory.deploy(options); + } +} diff --git a/packages/recipes/src/types/contracts/Src14OwnedProxyTypes.ts b/packages/recipes/src/types/contracts/Src14OwnedProxyTypes.ts new file mode 100644 index 00000000000..9b9cd8bb894 --- /dev/null +++ b/packages/recipes/src/types/contracts/Src14OwnedProxyTypes.ts @@ -0,0 +1,68 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +import { type StrSlice } from '@fuel-ts/abi'; +import type { Enum, Option } from '../common'; + +export enum AccessError { + NotOwner = 'NotOwner', +} +export enum InitializationError { + CannotReinitialized = 'CannotReinitialized', +} +export enum SetProxyOwnerError { + CannotUninitialize = 'CannotUninitialize', +} + +export type AddressInput = { bits: string }; +export type AddressOutput = AddressInput; +export type ContractIdInput = { bits: string }; +export type ContractIdOutput = ContractIdInput; +export type IdentityInput = Enum<{ Address: AddressInput; ContractId: ContractIdInput }>; +export type IdentityOutput = Enum<{ Address: AddressOutput; ContractId: ContractIdOutput }>; +export type ProxyOwnerSetInput = { new_proxy_owner: StateInput }; +export type ProxyOwnerSetOutput = { new_proxy_owner: StateOutput }; +export type ProxyTargetSetInput = { new_target: ContractIdInput }; +export type ProxyTargetSetOutput = { new_target: ContractIdOutput }; +export type StateInput = Enum<{ + Uninitialized: undefined; + Initialized: IdentityInput; + Revoked: undefined; +}>; +export type StateOutput = Enum<{ Uninitialized: void; Initialized: IdentityOutput; Revoked: void }>; + +export interface Src14OwnedProxyTypes { + functions: { + proxy_target: { + inputs: []; + output: Option; + }; + set_proxy_target: { + inputs: [new_target: ContractIdInput]; + output: void; + }; + proxy_owner: { + inputs: []; + output: StateOutput; + }; + initialize_proxy: { + inputs: []; + output: void; + }; + set_proxy_owner: { + inputs: [new_proxy_owner: StateInput]; + output: void; + }; + }; + configurables: Partial<{ + INITIAL_TARGET: Option; + INITIAL_OWNER: StateInput; + }>; +} diff --git a/packages/recipes/src/types/contracts/index.ts b/packages/recipes/src/types/contracts/index.ts new file mode 100644 index 00000000000..da6555813c5 --- /dev/null +++ b/packages/recipes/src/types/contracts/index.ts @@ -0,0 +1,12 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + +export { Src14OwnedProxy } from './Src14OwnedProxy'; +export { Src14OwnedProxyFactory } from './Src14OwnedProxyFactory'; diff --git a/packages/recipes/src/types/index.ts b/packages/recipes/src/types/index.ts index 15dc6b12ae3..427c4de30dc 100644 --- a/packages/recipes/src/types/index.ts +++ b/packages/recipes/src/types/index.ts @@ -1,12 +1,11 @@ /* Autogenerated file. Do not edit manually. */ -/* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/consistent-type-imports */ +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ /* - Fuels version: 0.97.2 + Fuels version: 0.98.0 + Forc version: 0.66.5 */ -export { Src14OwnedProxy } from './Src14OwnedProxy'; -export { Src14OwnedProxyFactory } from './Src14OwnedProxyFactory'; +export * from './contracts'; diff --git a/packages/script/package.json b/packages/script/package.json index 5f394a66908..3d584e604ab 100644 --- a/packages/script/package.json +++ b/packages/script/package.json @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@fuel-ts/abi-coder": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/errors": "workspace:*", "@fuel-ts/math": "workspace:*", diff --git a/packages/script/src/script.test.ts b/packages/script/src/script.test.ts index b00ebe04f37..f9b4dc2328f 100644 --- a/packages/script/src/script.test.ts +++ b/packages/script/src/script.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { JsonAbi } from '@fuel-ts/abi-coder'; -import { Interface } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; +import { AbiCoder } from '@fuel-ts/abi'; import type { Account, TransactionResponse, TransactionResult } from '@fuel-ts/account'; import { ScriptTransactionRequest } from '@fuel-ts/account'; import { setupTestProviderAndWallets } from '@fuel-ts/account/test-utils'; @@ -55,7 +55,7 @@ type MyStruct = { describe('Script', () => { let scriptRequest: ScriptRequest; beforeAll(() => { - const abiInterface = new Interface(scriptJsonAbi); + const abiInterface = AbiCoder.fromAbi(scriptJsonAbi); scriptRequest = new ScriptRequest( scriptBin, (myStruct: MyStruct) => { @@ -71,8 +71,7 @@ describe('Script', () => { throw new Error('fail'); } - const decoded = abiInterface.functions.main.decodeOutput(scriptResult.returnReceipt.data); - return (decoded as any)[0]; + return abiInterface.functions.main.decodeOutput(scriptResult.returnReceipt.data) as any; } ); }); @@ -139,7 +138,7 @@ describe('Script', () => { wallets: [wallet], } = launched; - const jsonAbiWithConfigurablesMock: JsonAbi = { + const jsonAbiWithConfigurablesMock: AbiSpecification = { ...jsonAbiMock, configurables: [ { diff --git a/packages/script/src/script.ts b/packages/script/src/script.ts index fd86461b8c9..25cf02bdc4f 100644 --- a/packages/script/src/script.ts +++ b/packages/script/src/script.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Interface } from '@fuel-ts/abi-coder'; -import type { InputValue, JsonAbi } from '@fuel-ts/abi-coder'; +import { AbiCoder } from '@fuel-ts/abi'; +import type { InputValue, AbiSpecification } from '@fuel-ts/abi'; import { deployScriptOrPredicate, type Account, type Provider } from '@fuel-ts/account'; import { FuelError } from '@fuel-ts/errors'; import type { BN } from '@fuel-ts/math'; @@ -38,7 +38,7 @@ export class Script, TOutput> extends AbstractScript { /** * The ABI interface for the script. */ - interface: Interface; + interface: AbiCoder; /** * The account associated with the script. @@ -67,10 +67,10 @@ export class Script, TOutput> extends AbstractScript { * @param abi - The ABI interface for the script. * @param account - The account associated with the script. */ - constructor(bytecode: BytesLike, abi: JsonAbi, account: Account) { + constructor(bytecode: BytesLike, abi: AbiSpecification, account: Account) { super(); this.bytes = arrayify(bytecode); - this.interface = new Interface(abi); + this.interface = AbiCoder.fromAbi(abi); this.provider = account.provider; this.account = account; @@ -107,7 +107,7 @@ export class Script, TOutput> extends AbstractScript { const { offset } = this.interface.configurables[key]; - const encoded = this.interface.encodeConfigurable(key, value as InputValue); + const encoded = this.interface.getConfigurable(key).encode(value as InputValue); this.bytes.set(encoded, offset); }); @@ -133,7 +133,7 @@ export class Script, TOutput> extends AbstractScript { deploy(account: Account) { return deployScriptOrPredicate({ deployer: account, - abi: this.interface.jsonAbi, + abi: this.interface.specification, bytecode: this.bytes, loaderInstanceCallback: (loaderBytecode, newAbi) => new Script(loaderBytecode, newAbi, this.account) as T, diff --git a/packages/script/test/fixtures/index.ts b/packages/script/test/fixtures/index.ts index ed64d80e4ac..16eb2bf5433 100644 --- a/packages/script/test/fixtures/index.ts +++ b/packages/script/test/fixtures/index.ts @@ -1,4 +1,4 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; import { getForcProject } from '@fuel-ts/utils/test-utils'; import { join } from 'path'; @@ -7,7 +7,7 @@ export enum ScriptProjectsEnum { } export const getScriptForcProject = (project: ScriptProjectsEnum) => - getForcProject({ + getForcProject({ projectDir: join(__dirname, 'forc-projects', project), projectName: project, build: 'release', diff --git a/packages/script/test/mocks.ts b/packages/script/test/mocks.ts index 61df118fbb5..b98de2a37b4 100644 --- a/packages/script/test/mocks.ts +++ b/packages/script/test/mocks.ts @@ -1,6 +1,6 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; +import type { AbiSpecification } from '@fuel-ts/abi'; -export const jsonAbiMock: JsonAbi = { +export const jsonAbiMock: AbiSpecification = { programType: 'script', specVersion: '1', encodingVersion: '1', diff --git a/packages/transactions/package.json b/packages/transactions/package.json index e5902bd7195..15265c4a40f 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -37,7 +37,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@fuel-ts/abi-coder": "workspace:*", + "@fuel-ts/abi": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/errors": "workspace:*", "@fuel-ts/hasher": "workspace:^", diff --git a/packages/transactions/src/coders/byte-array.test.ts b/packages/transactions/src/coders/byte-array.test.ts index 28edc0ad838..40e2ce611e5 100644 --- a/packages/transactions/src/coders/byte-array.test.ts +++ b/packages/transactions/src/coders/byte-array.test.ts @@ -1,6 +1,6 @@ import { arrayify, hexlify } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './byte-array'; +import { byteArray } from './byte-array'; /** * @group node @@ -10,11 +10,11 @@ describe('ByteArrayCoder', () => { it('Can encode empty byte[]', () => { const bytes = arrayify('0x'); - const encoded = hexlify(new ByteArrayCoder(bytes.length).encode(bytes)); + const encoded = hexlify(byteArray(bytes.length).encode(bytes)); expect(encoded).toEqual('0x'); - const [decoded, offset] = new ByteArrayCoder(bytes.length).decode(arrayify(encoded), 0); + const [decoded, offset] = byteArray(bytes.length).decode(arrayify(encoded), 0); expect(offset).toEqual(0); expect(decoded).toEqual(hexlify(bytes)); @@ -23,11 +23,11 @@ describe('ByteArrayCoder', () => { it('Can encode four-byte byte[]', () => { const bytes = arrayify('0xdeadbeef'); - const encoded = hexlify(new ByteArrayCoder(bytes.length).encode(bytes)); + const encoded = hexlify(byteArray(bytes.length).encode(bytes)); expect(encoded).toEqual('0xdeadbeef00000000'); - const [decoded, offset] = new ByteArrayCoder(bytes.length).decode(arrayify(encoded), 0); + const [decoded, offset] = byteArray(bytes.length).decode(arrayify(encoded), 0); expect(offset).toEqual(8); expect(decoded).toEqual(hexlify(bytes)); diff --git a/packages/transactions/src/coders/byte-array.ts b/packages/transactions/src/coders/byte-array.ts index b5ba3ec11e2..4c00ae342aa 100644 --- a/packages/transactions/src/coders/byte-array.ts +++ b/packages/transactions/src/coders/byte-array.ts @@ -1,51 +1,15 @@ -import { Coder } from '@fuel-ts/abi-coder'; +import type { Coder } from '@fuel-ts/abi'; import type { BytesLike } from '@fuel-ts/utils'; import { concat, hexlify, arrayify } from '@fuel-ts/utils'; -export class ByteArrayCoder extends Coder { - length: number; - #paddingLength: number; - - constructor(length: number) { - const paddingLength = (8 - (length % 8)) % 8; - const encodedLength = length + paddingLength; - super( - 'ByteArray', - // While this might sound like a [u8; N] coder it's actually not. - // A [u8; N] coder would pad every u8 to 8 bytes which would - // make every u8 have the same size as a u64. - // We are packing four u8s into u64s here, avoiding this padding. - `[u64; ${encodedLength / 4}]`, - encodedLength - ); - this.length = length; - this.#paddingLength = paddingLength; - } - - encode(value: BytesLike): Uint8Array { - const parts: Uint8Array[] = []; - - const data = arrayify(value); - parts.push(data); - // Write padding - if (this.#paddingLength) { - parts.push(new Uint8Array(this.#paddingLength)); - } - - return concat(parts); - } - - decode(data: Uint8Array, offset: number): [string, number] { - let decoded; - let o = offset; - - [decoded, o] = [hexlify(data.slice(o, o + this.length)), o + this.length]; - const value = decoded; - // Read padding - if (this.#paddingLength) { - [decoded, o] = [null, o + this.#paddingLength]; - } - - return [value, o]; - } -} +export const byteArray = (length: number): Coder => { + const paddingLength = (8 - (length % 8)) % 8; + return { + type: 'byte_array', + encode: (value: BytesLike) => concat([arrayify(value), new Uint8Array(paddingLength)]), + decode: (data: Uint8Array, initialOffset: number) => [ + hexlify(data.slice(initialOffset, initialOffset + length)), + initialOffset + length + paddingLength, + ], + }; +}; diff --git a/packages/transactions/src/coders/coders.ts b/packages/transactions/src/coders/coders.ts new file mode 100644 index 00000000000..357e896b5dd --- /dev/null +++ b/packages/transactions/src/coders/coders.ts @@ -0,0 +1,18 @@ +import { encoding, WORD_SIZE } from '@fuel-ts/abi'; +import type { Coder } from '@fuel-ts/abi'; + +import { pad } from './pad-coder'; + +const base = encoding.v1; + +export const coders = { + ...base, + type: (type: Type): Coder => ({ + type: 'TypeCoder', + encode: () => new Uint8Array([]), + decode: (_data: Uint8Array, offset: number) => [type, offset], + }), + u8: pad(base.u8, 1, WORD_SIZE), + u16: pad(base.u16, 2, WORD_SIZE), + u32: pad(base.u32, 4, WORD_SIZE), +}; diff --git a/packages/transactions/src/coders/input.test.ts b/packages/transactions/src/coders/input.test.ts index 66c9dc93943..fa120fbe914 100644 --- a/packages/transactions/src/coders/input.test.ts +++ b/packages/transactions/src/coders/input.test.ts @@ -96,7 +96,7 @@ describe('InputCoder', () => { await expectToThrowFuelError( () => new InputCoder().encode(input), - new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64.') + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64 value - value exceeds maximum.') ); }); diff --git a/packages/transactions/src/coders/input.ts b/packages/transactions/src/coders/input.ts index 140ab70589f..2bfd6823bd8 100644 --- a/packages/transactions/src/coders/input.ts +++ b/packages/transactions/src/coders/input.ts @@ -1,12 +1,13 @@ /* eslint-disable max-classes-per-file */ -import { Coder, B256Coder, NumberCoder, BigNumberCoder } from '@fuel-ts/abi-coder'; +import { Coder, BYTES_32 } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { sha256 } from '@fuel-ts/hasher'; import type { BN } from '@fuel-ts/math'; import type { BytesLike } from '@fuel-ts/utils'; import { concat, arrayify } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './byte-array'; +import { byteArray } from './byte-array'; +import { coders } from './coders'; import type { TxPointer } from './tx-pointer'; import { TxPointerCoder } from './tx-pointer'; @@ -57,78 +58,47 @@ export type InputCoin = { }; export class InputCoinCoder extends Coder { - constructor() { - super('InputCoin', 'struct InputCoin', 0); - } + private primary = coders.struct({ + txID: coders.b256, + outputIndex: coders.u16, + owner: coders.b256, + amount: coders.u64, + assetId: coders.b256, + txPointer: new TxPointerCoder(), + witnessIndex: coders.u16, + predicateGasUsed: coders.u64, + predicateLength: coders.u64, + predicateDataLength: coders.u64, + }); + + private secondary = (value: Pick) => + coders.struct({ + predicate: byteArray(value.predicateLength.toNumber()), + predicateData: byteArray(value.predicateDataLength.toNumber()), + }); + + override type = 'InputCoin'; encode(value: InputCoin): Uint8Array { const parts: Uint8Array[] = []; - - parts.push(new B256Coder().encode(value.txID)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputIndex)); - parts.push(new B256Coder().encode(value.owner)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); - parts.push(new B256Coder().encode(value.assetId)); - parts.push(new TxPointerCoder().encode(value.txPointer)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessIndex)); - parts.push(new BigNumberCoder('u64').encode(value.predicateGasUsed)); - parts.push(new BigNumberCoder('u64').encode(value.predicateLength)); - parts.push(new BigNumberCoder('u64').encode(value.predicateDataLength)); - parts.push(new ByteArrayCoder(value.predicateLength.toNumber()).encode(value.predicate)); - parts.push( - new ByteArrayCoder(value.predicateDataLength.toNumber()).encode(value.predicateData) - ); - + parts.push(this.primary.encode(value)); + parts.push(this.secondary(value).encode(value)); return concat(parts); } decode(data: Uint8Array, offset: number): [InputCoin, number] { let decoded; let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const txID = decoded; - [decoded, o] = new NumberCoder('u16', { padToWordSize: true }).decode(data, o); - const outputIndex = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const owner = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const amount = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const assetId = decoded; - [decoded, o] = new TxPointerCoder().decode(data, o); - const txPointer = decoded; - [decoded, o] = new NumberCoder('u16', { padToWordSize: true }).decode(data, o); - const witnessIndex = Number(decoded); - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const predicateGasUsed = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const predicateLength = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const predicateDataLength = decoded; - [decoded, o] = new ByteArrayCoder(predicateLength.toNumber()).decode(data, o); - const predicate = decoded; - [decoded, o] = new ByteArrayCoder(predicateDataLength.toNumber()).decode(data, o); - const predicateData = decoded; - - return [ - { - type: InputType.Coin, - txID, - outputIndex, - owner, - amount, - assetId, - txPointer, - witnessIndex, - predicateGasUsed, - predicateLength, - predicateDataLength, - predicate, - predicateData, - }, - o, - ]; + [decoded, o] = this.primary.decode(data, o); + const base = decoded; + [decoded, o] = this.secondary(base).decode(data, o); + const rest = decoded; + const inputCoin: InputCoin = { + type: InputType.Coin, + ...base, + ...rest, + }; + return [inputCoin, o]; } } @@ -155,52 +125,24 @@ export type InputContract = { }; export class InputContractCoder extends Coder { - constructor() { - super('InputContract', 'struct InputContract', 0); - } + private coder = coders.struct({ + type: coders.type(InputType.Contract), + txID: coders.b256, + outputIndex: coders.u16, + balanceRoot: coders.b256, + stateRoot: coders.b256, + txPointer: new TxPointerCoder(), + contractID: coders.b256, + }); + + override type = 'InputContract'; encode(value: InputContract): Uint8Array { - const parts: Uint8Array[] = []; - - parts.push(new B256Coder().encode(value.txID)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputIndex)); - parts.push(new B256Coder().encode(value.balanceRoot)); - parts.push(new B256Coder().encode(value.stateRoot)); - parts.push(new TxPointerCoder().encode(value.txPointer)); - parts.push(new B256Coder().encode(value.contractID)); - - return concat(parts); + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [InputContract, number] { - let decoded; - let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const txID = decoded; - [decoded, o] = new NumberCoder('u16', { padToWordSize: true }).decode(data, o); - const outputIndex = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const balanceRoot = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const stateRoot = decoded; - [decoded, o] = new TxPointerCoder().decode(data, o); - const txPointer = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const contractID = decoded; - - return [ - { - type: InputType.Contract, - txID, - outputIndex, - balanceRoot, - stateRoot, - txPointer, - contractID, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -245,19 +187,17 @@ export type InputMessage = { }; export class InputMessageCoder extends Coder { - constructor() { - super('InputMessage', 'struct InputMessage', 0); - } + override type = 'InputMessage'; static getMessageId( value: Pick ): string { const parts: Uint8Array[] = []; - parts.push(new ByteArrayCoder(32).encode(value.sender)); - parts.push(new ByteArrayCoder(32).encode(value.recipient)); - parts.push(new ByteArrayCoder(32).encode(value.nonce)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); + parts.push(byteArray(BYTES_32).encode(value.sender)); + parts.push(byteArray(BYTES_32).encode(value.recipient)); + parts.push(byteArray(BYTES_32).encode(value.nonce)); + parts.push(coders.u64.encode(value.amount)); parts.push(arrayify(value.data || '0x')); return sha256(concat(parts)); @@ -266,27 +206,25 @@ export class InputMessageCoder extends Coder { static encodeData(messageData?: BytesLike): Uint8Array { const bytes = arrayify(messageData || '0x'); const dataLength = bytes.length; - return new ByteArrayCoder(dataLength).encode(bytes); + return byteArray(dataLength).encode(bytes); } - encode(value: InputMessage): Uint8Array { + override encode(value: InputMessage): Uint8Array { const parts: Uint8Array[] = []; const data = InputMessageCoder.encodeData(value.data); - parts.push(new ByteArrayCoder(32).encode(value.sender)); - parts.push(new ByteArrayCoder(32).encode(value.recipient)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); - parts.push(new ByteArrayCoder(32).encode(value.nonce)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessIndex)); - parts.push(new BigNumberCoder('u64').encode(value.predicateGasUsed)); - parts.push(new BigNumberCoder('u64').encode(data.length)); - parts.push(new BigNumberCoder('u64').encode(value.predicateLength)); - parts.push(new BigNumberCoder('u64').encode(value.predicateDataLength)); - parts.push(new ByteArrayCoder(data.length).encode(data)); - parts.push(new ByteArrayCoder(value.predicateLength.toNumber()).encode(value.predicate)); - parts.push( - new ByteArrayCoder(value.predicateDataLength.toNumber()).encode(value.predicateData) - ); + parts.push(byteArray(BYTES_32).encode(value.sender)); + parts.push(byteArray(BYTES_32).encode(value.recipient)); + parts.push(coders.u64.encode(value.amount)); + parts.push(byteArray(BYTES_32).encode(value.nonce)); + parts.push(coders.u16.encode(value.witnessIndex)); + parts.push(coders.u64.encode(value.predicateGasUsed)); + parts.push(coders.u64.encode(data.length)); + parts.push(coders.u64.encode(value.predicateLength)); + parts.push(coders.u64.encode(value.predicateDataLength)); + parts.push(byteArray(data.length).encode(data)); + parts.push(byteArray(value.predicateLength.toNumber()).encode(value.predicate)); + parts.push(byteArray(value.predicateDataLength.toNumber()).encode(value.predicateData)); return concat(parts); } @@ -294,38 +232,38 @@ export class InputMessageCoder extends Coder { static decodeData(messageData: BytesLike): Uint8Array { const bytes = arrayify(messageData); const dataLength = bytes.length; - const [data] = new ByteArrayCoder(dataLength).decode(bytes, 0); + const [data] = byteArray(dataLength).decode(bytes, 0); return arrayify(data); } - decode(data: Uint8Array, offset: number): [InputMessage, number] { + override decode(data: Uint8Array, offset: number): [InputMessage, number] { let decoded; let o = offset; - [decoded, o] = new B256Coder().decode(data, o); + [decoded, o] = coders.b256.decode(data, o); const sender = decoded; - [decoded, o] = new B256Coder().decode(data, o); + [decoded, o] = coders.b256.decode(data, o); const recipient = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); + [decoded, o] = coders.u64.decode(data, o); const amount = decoded; - [decoded, o] = new B256Coder().decode(data, o); + [decoded, o] = coders.b256.decode(data, o); const nonce = decoded; - [decoded, o] = new NumberCoder('u16', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u16.decode(data, o); const witnessIndex = Number(decoded); - [decoded, o] = new BigNumberCoder('u64').decode(data, o); + [decoded, o] = coders.u64.decode(data, o); const predicateGasUsed = decoded; - [decoded, o] = new NumberCoder('u32', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u32.decode(data, o); const dataLength = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); + [decoded, o] = coders.u64.decode(data, o); const predicateLength = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); + [decoded, o] = coders.u64.decode(data, o); const predicateDataLength = decoded; - [decoded, o] = new ByteArrayCoder(dataLength).decode(data, o); + [decoded, o] = byteArray(dataLength).decode(data, o); const messageData = decoded; - [decoded, o] = new ByteArrayCoder(predicateLength.toNumber()).decode(data, o); + [decoded, o] = byteArray(predicateLength.toNumber()).decode(data, o); const predicate = decoded; - [decoded, o] = new ByteArrayCoder(predicateDataLength.toNumber()).decode(data, o); + [decoded, o] = byteArray(predicateDataLength.toNumber()).decode(data, o); const predicateData = decoded; return [ @@ -352,14 +290,12 @@ export class InputMessageCoder extends Coder { export type Input = InputCoin | InputContract | InputMessage; export class InputCoder extends Coder { - constructor() { - super('Input', 'struct Input', 0); - } + override type = 'Input'; - encode(value: Input): Uint8Array { + override encode(value: Input): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new NumberCoder('u8', { padToWordSize: true }).encode(value.type)); + parts.push(coders.u8.encode(value.type)); const { type } = value; @@ -391,7 +327,7 @@ export class InputCoder extends Coder { let decoded; let o = offset; - [decoded, o] = new NumberCoder('u8', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u8.decode(data, o); const type = decoded as InputType; switch (type) { case InputType.Coin: { diff --git a/packages/transactions/src/coders/output.ts b/packages/transactions/src/coders/output.ts index 8110df38f84..596349bc577 100644 --- a/packages/transactions/src/coders/output.ts +++ b/packages/transactions/src/coders/output.ts @@ -1,9 +1,11 @@ /* eslint-disable max-classes-per-file */ -import { Coder, B256Coder, NumberCoder, BigNumberCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BN } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; +import { coders } from './coders'; + export enum OutputType /* u8 */ { Coin = 0, Contract = 1, @@ -23,40 +25,21 @@ export type OutputCoin = { }; export class OutputCoinCoder extends Coder { - constructor() { - super('OutputCoin', 'struct OutputCoin', 0); - } - - encode(value: OutputCoin): Uint8Array { - const parts: Uint8Array[] = []; + private coder = coders.struct({ + type: coders.type(OutputType.Coin), + to: coders.b256, + amount: coders.u64, + assetId: coders.b256, + }); - parts.push(new B256Coder().encode(value.to)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); - parts.push(new B256Coder().encode(value.assetId)); + override type = 'OutputCoin'; - return concat(parts); + encode(value: OutputCoin): Uint8Array { + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [OutputCoin, number] { - let decoded; - let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const to = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const amount = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const assetId = decoded; - - return [ - { - type: OutputType.Coin, - to, - amount, - assetId, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -71,40 +54,21 @@ export type OutputContract = { }; export class OutputContractCoder extends Coder { - constructor() { - super('OutputContract', 'struct OutputContract', 0); - } - - encode(value: OutputContract): Uint8Array { - const parts: Uint8Array[] = []; + private coder = coders.struct({ + type: coders.type(OutputType.Contract), + inputIndex: coders.u8, + balanceRoot: coders.b256, + stateRoot: coders.b256, + }); - parts.push(new NumberCoder('u8', { padToWordSize: true }).encode(value.inputIndex)); - parts.push(new B256Coder().encode(value.balanceRoot)); - parts.push(new B256Coder().encode(value.stateRoot)); + override type = 'OutputCoin'; - return concat(parts); + encode(value: OutputContract): Uint8Array { + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [OutputContract, number] { - let decoded; - let o = offset; - - [decoded, o] = new NumberCoder('u8', { padToWordSize: true }).decode(data, o); - const inputIndex = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const balanceRoot = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const stateRoot = decoded; - - return [ - { - type: OutputType.Contract, - inputIndex, - balanceRoot, - stateRoot, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -119,40 +83,21 @@ export type OutputChange = { }; export class OutputChangeCoder extends Coder { - constructor() { - super('OutputChange', 'struct OutputChange', 0); - } + private coder = coders.struct({ + type: coders.type(OutputType.Change), + to: coders.b256, + amount: coders.u64, + assetId: coders.b256, + }); - encode(value: OutputChange): Uint8Array { - const parts: Uint8Array[] = []; + override type = 'OutputChange'; - parts.push(new B256Coder().encode(value.to)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); - parts.push(new B256Coder().encode(value.assetId)); - - return concat(parts); + encode(value: OutputChange): Uint8Array { + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [OutputChange, number] { - let decoded; - let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const to = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const amount = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const assetId = decoded; - - return [ - { - type: OutputType.Change, - to, - amount, - assetId, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -167,40 +112,21 @@ export type OutputVariable = { }; export class OutputVariableCoder extends Coder { - constructor() { - super('OutputVariable', 'struct OutputVariable', 0); - } - - encode(value: OutputVariable): Uint8Array { - const parts: Uint8Array[] = []; + private coder = coders.struct({ + type: coders.type(OutputType.Variable), + to: coders.b256, + amount: coders.u64, + assetId: coders.b256, + }); - parts.push(new B256Coder().encode(value.to)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); - parts.push(new B256Coder().encode(value.assetId)); + override type = 'OutputVariable'; - return concat(parts); + encode(value: OutputVariable): Uint8Array { + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [OutputVariable, number] { - let decoded; - let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const to = decoded; - [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const amount = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const assetId = decoded; - - return [ - { - type: OutputType.Variable, - to, - amount, - assetId, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -216,36 +142,20 @@ export class OutputContractCreatedCoder extends Coder< OutputContractCreated, OutputContractCreated > { - constructor() { - super('OutputContractCreated', 'struct OutputContractCreated', 0); - } + private coder = coders.struct({ + type: coders.type(OutputType.ContractCreated), + contractId: coders.b256, + stateRoot: coders.b256, + }); - encode(value: OutputContractCreated): Uint8Array { - const parts: Uint8Array[] = []; - - parts.push(new B256Coder().encode(value.contractId)); - parts.push(new B256Coder().encode(value.stateRoot)); + override type = 'OutputContractCreated'; - return concat(parts); + encode(value: OutputContractCreated): Uint8Array { + return this.coder.encode(value); } decode(data: Uint8Array, offset: number): [OutputContractCreated, number] { - let decoded; - let o = offset; - - [decoded, o] = new B256Coder().decode(data, o); - const contractId = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const stateRoot = decoded; - - return [ - { - type: OutputType.ContractCreated, - contractId, - stateRoot, - }, - o, - ]; + return this.coder.decode(data, offset); } } @@ -257,15 +167,12 @@ export type Output = | OutputContractCreated; export class OutputCoder extends Coder { - constructor() { - super('Output', ' struct Output', 0); - } + override type = 'Output'; encode(value: Output): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new NumberCoder('u8', { padToWordSize: true }).encode(value.type)); - + parts.push(coders.u8.encode(value.type)); const { type } = value; switch (type) { @@ -300,12 +207,13 @@ export class OutputCoder extends Coder { return concat(parts); } - decode(data: Uint8Array, offset: number): [Output, number] { + decode(data: Uint8Array, initialOffset: number): [Output, number] { let decoded; - let o = offset; + let o = initialOffset; - [decoded, o] = new NumberCoder('u8', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u8.decode(data, initialOffset); const type = decoded as OutputType; + switch (type) { case OutputType.Coin: { [decoded, o] = new OutputCoinCoder().decode(data, o); diff --git a/packages/transactions/src/coders/pad-coder.test.ts b/packages/transactions/src/coders/pad-coder.test.ts new file mode 100644 index 00000000000..5cc8075da98 --- /dev/null +++ b/packages/transactions/src/coders/pad-coder.test.ts @@ -0,0 +1,76 @@ +import { WORD_SIZE, encoding } from '@fuel-ts/abi'; + +import { pad } from './pad-coder'; + +/** + * @group node + * @group browser + */ +describe('padded', () => { + describe('encode', () => { + it('should encode and pad to word size [u8]', () => { + const coder = pad(encoding.v1.u8, 1, WORD_SIZE); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); + const value = 255; + + const encoded = coder.encode(value); + + expect(encoded).toEqual(expected); + }); + + it('should encode and pad to word size [u16]', () => { + const coder = pad(encoding.v1.u16, 2, WORD_SIZE); + const expected = new Uint8Array([0, 0, 0, 0, 0, 0, 255, 255]); + const value = 65535; + + const encoded = coder.encode(value); + + expect(encoded).toEqual(expected); + }); + + it('should encode and pad to word size [u32]', () => { + const coder = pad(encoding.v1.u32, 4, WORD_SIZE); + const expected = new Uint8Array([0, 0, 0, 0, 255, 255, 255, 255]); + const value = 4294967295; + + const encoded = coder.encode(value); + + expect(encoded).toEqual(expected); + }); + }); + + describe('decode', () => { + it('should decode and remove padding [u8]', () => { + const coder = pad(encoding.v1.u8, 1, WORD_SIZE); + const expected = 255; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); + + const [decoded, newOffset] = coder.decode(data, 0); + + expect(decoded).toEqual(expected); + expect(newOffset).toEqual(WORD_SIZE); + }); + + it('should decode and remove padding [u16]', () => { + const coder = pad(encoding.v1.u16, 2, WORD_SIZE); + const expected = 65535; + const data = new Uint8Array([0, 0, 0, 0, 0, 0, 255, 255]); + + const [decoded, newOffset] = coder.decode(data, 0); + + expect(decoded).toEqual(expected); + expect(newOffset).toEqual(WORD_SIZE); + }); + + it('should decode and remove padding [u32]', () => { + const coder = pad(encoding.v1.u32, 4, WORD_SIZE); + const expected = 4294967295; + const data = new Uint8Array([0, 0, 0, 0, 255, 255, 255, 255]); + + const [decoded, newOffset] = coder.decode(data, 0); + + expect(decoded).toEqual(expected); + expect(newOffset).toEqual(WORD_SIZE); + }); + }); +}); diff --git a/packages/transactions/src/coders/pad-coder.ts b/packages/transactions/src/coders/pad-coder.ts new file mode 100644 index 00000000000..5f082d63b0b --- /dev/null +++ b/packages/transactions/src/coders/pad-coder.ts @@ -0,0 +1,18 @@ +import type { Coder } from '@fuel-ts/abi'; + +export const pad = ( + coder: Coder, + length: number, + paddedLength: number +): Coder => ({ + type: coder.type, + encode: (value: TEncoded) => { + const encoded = coder.encode(value); + const padding = new Uint8Array(paddedLength - length).fill(0); + return new Uint8Array([...padding, ...encoded]); + }, + decode: (data: Uint8Array, offset: number = 0): [TDecoded, number] => { + const [decoded, newOffset] = coder.decode(data, offset + (paddedLength - length)); + return [decoded, newOffset]; + }, +}); diff --git a/packages/transactions/src/coders/policy.test.ts b/packages/transactions/src/coders/policy.test.ts index 3eda915b208..c21582f8c11 100644 --- a/packages/transactions/src/coders/policy.test.ts +++ b/packages/transactions/src/coders/policy.test.ts @@ -140,7 +140,7 @@ describe('PoliciesCoder', () => { // bitfield is 1 representing tip const policyTypes = PolicyType.Tip; - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(1); expect(policies[0]).toStrictEqual({ @@ -157,7 +157,7 @@ describe('PoliciesCoder', () => { // bitfield is 2 representing witnessLimit const policyTypes = PolicyType.WitnessLimit; - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(1); expect(policies[0]).toStrictEqual({ @@ -174,7 +174,7 @@ describe('PoliciesCoder', () => { // bitfield is 4 representing maturity const policyTypes = PolicyType.Maturity; - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(1); expect(policies[0]).toStrictEqual({ @@ -191,7 +191,7 @@ describe('PoliciesCoder', () => { // bitfield is 8 representing maxFee const policyTypes = PolicyType.MaxFee; - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(1); expect(policies[0]).toStrictEqual({ @@ -210,7 +210,7 @@ describe('PoliciesCoder', () => { const policyTypes = PolicyType.Tip + PolicyType.WitnessLimit; expect(policyTypes).toBe(3); - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(2); expect(policies[0]).toStrictEqual({ @@ -233,7 +233,7 @@ describe('PoliciesCoder', () => { const policyTypes = PolicyType.WitnessLimit + PolicyType.Maturity; expect(policyTypes).toBe(6); - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(2); expect(policies[0]).toStrictEqual({ @@ -256,7 +256,7 @@ describe('PoliciesCoder', () => { const policyTypes = PolicyType.WitnessLimit + PolicyType.MaxFee; expect(policyTypes).toBe(10); - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(2); expect(policies[0]).toStrictEqual({ @@ -287,7 +287,7 @@ describe('PoliciesCoder', () => { PolicyType.Tip + PolicyType.WitnessLimit + PolicyType.Maturity + PolicyType.MaxFee; expect(policyTypes).toBe(15); - const [policies] = new PoliciesCoder().decode(data, 0, policyTypes); + const [policies] = new PoliciesCoder(policyTypes).decode(data, 0); expect(policies).toHaveLength(4); expect(policies).toStrictEqual([ diff --git a/packages/transactions/src/coders/policy.ts b/packages/transactions/src/coders/policy.ts index aeb7c03fb8e..c9cf4bb9aad 100644 --- a/packages/transactions/src/coders/policy.ts +++ b/packages/transactions/src/coders/policy.ts @@ -1,8 +1,10 @@ -import { BigNumberCoder, Coder, NumberCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BN } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; +import { coders } from './coders'; + // Bitfield of used policy types. export enum PolicyType { Tip = 1, @@ -51,8 +53,13 @@ function validateDuplicatedPolicies(policies: Policy[]): void { } export class PoliciesCoder extends Coder { - constructor() { - super('Policies', 'array Policy', 0); + private policyTypeFilter: number; + + override type = 'Policies'; + + public constructor(policyTypeFilter: number = 0) { + super(); + this.policyTypeFilter = policyTypeFilter; } encode(policies: Policy[]): Uint8Array { @@ -66,11 +73,11 @@ export class PoliciesCoder extends Coder { case PolicyType.MaxFee: case PolicyType.Tip: case PolicyType.WitnessLimit: - parts.push(new BigNumberCoder('u64').encode(data)); + parts.push(coders.u64.encode(data)); break; case PolicyType.Maturity: - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(data)); + parts.push(coders.u32.encode(data)); break; default: { @@ -82,33 +89,30 @@ export class PoliciesCoder extends Coder { return concat(parts); } - decode(data: Uint8Array, offset: number, policyTypes: number): [Policy[], number] { + decode(data: Uint8Array, offset: number): [Policy[], number] { let o = offset; const policies: Policy[] = []; - if (policyTypes & PolicyType.Tip) { - const [tip, nextOffset] = new BigNumberCoder('u64').decode(data, o); + if (this.policyTypeFilter & PolicyType.Tip) { + const [tip, nextOffset] = coders.u64.decode(data, o); o = nextOffset; policies.push({ type: PolicyType.Tip, data: tip }); } - if (policyTypes & PolicyType.WitnessLimit) { - const [witnessLimit, nextOffset] = new BigNumberCoder('u64').decode(data, o); + if (this.policyTypeFilter & PolicyType.WitnessLimit) { + const [witnessLimit, nextOffset] = coders.u64.decode(data, o); o = nextOffset; policies.push({ type: PolicyType.WitnessLimit, data: witnessLimit }); } - if (policyTypes & PolicyType.Maturity) { - const [maturity, nextOffset] = new NumberCoder('u32', { padToWordSize: true }).decode( - data, - o - ); + if (this.policyTypeFilter & PolicyType.Maturity) { + const [maturity, nextOffset] = coders.u32.decode(data, o); o = nextOffset; policies.push({ type: PolicyType.Maturity, data: maturity }); } - if (policyTypes & PolicyType.MaxFee) { - const [maxFee, nextOffset] = new BigNumberCoder('u64').decode(data, o); + if (this.policyTypeFilter & PolicyType.MaxFee) { + const [maxFee, nextOffset] = coders.u64.decode(data, o); o = nextOffset; policies.push({ type: PolicyType.MaxFee, data: maxFee }); } diff --git a/packages/transactions/src/coders/storage-slot.ts b/packages/transactions/src/coders/storage-slot.ts index bfef11c5298..e9cd88e10d3 100644 --- a/packages/transactions/src/coders/storage-slot.ts +++ b/packages/transactions/src/coders/storage-slot.ts @@ -1,4 +1,6 @@ -import { B256Coder, StructCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; + +import { coders } from './coders'; export type StorageSlot = { /** Key (b256) */ @@ -7,14 +9,19 @@ export type StorageSlot = { value: string; }; -export class StorageSlotCoder extends StructCoder<{ - key: B256Coder; - value: B256Coder; -}> { - constructor() { - super('StorageSlot', { - key: new B256Coder(), - value: new B256Coder(), - }); +export class StorageSlotCoder extends Coder { + private coder = coders.struct({ + key: coders.b256, + value: coders.b256, + }); + + override type = 'StorageSlot'; + + encode(value: StorageSlot): Uint8Array { + return this.coder.encode(value); + } + + decode(data: Uint8Array, offset: number): [StorageSlot, number] { + return this.coder.decode(data, offset); } } diff --git a/packages/transactions/src/coders/transaction.ts b/packages/transactions/src/coders/transaction.ts index 5c6e2355cc6..e0fb8e3d854 100644 --- a/packages/transactions/src/coders/transaction.ts +++ b/packages/transactions/src/coders/transaction.ts @@ -1,11 +1,12 @@ /* eslint-disable max-classes-per-file */ -import { Coder, ArrayCoder, B256Coder, NumberCoder, BigNumberCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { type BN } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './byte-array'; +import { byteArray } from './byte-array'; +import { coders } from './coders'; import type { Input, InputContract } from './input'; import { InputCoder, InputContractCoder } from './input'; import type { Output, OutputContract } from './output'; @@ -81,27 +82,25 @@ export type TransactionScript = BaseTransactionType & { }; export class TransactionScriptCoder extends Coder { - constructor() { - super('TransactionScript', 'struct TransactionScript', 0); - } + override type = 'TransactionScript'; encode(value: TransactionScript): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new BigNumberCoder('u64').encode(value.scriptGasLimit)); - parts.push(new B256Coder().encode(value.receiptsRoot)); - parts.push(new BigNumberCoder('u64').encode(value.scriptLength)); - parts.push(new BigNumberCoder('u64').encode(value.scriptDataLength)); - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.policyTypes)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.inputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessesCount)); - parts.push(new ByteArrayCoder(value.scriptLength.toNumber()).encode(value.script)); - parts.push(new ByteArrayCoder(value.scriptDataLength.toNumber()).encode(value.scriptData)); + parts.push(coders.u64.encode(value.scriptGasLimit)); + parts.push(coders.b256.encode(value.receiptsRoot)); + parts.push(coders.u64.encode(value.scriptLength)); + parts.push(coders.u64.encode(value.scriptDataLength)); + parts.push(coders.u32.encode(value.policyTypes)); + parts.push(coders.u16.encode(value.inputsCount)); + parts.push(coders.u16.encode(value.outputsCount)); + parts.push(coders.u16.encode(value.witnessesCount)); + parts.push(byteArray(value.scriptLength.toNumber()).encode(value.script)); + parts.push(byteArray(value.scriptDataLength.toNumber()).encode(value.scriptData)); parts.push(new PoliciesCoder().encode(value.policies)); - parts.push(new ArrayCoder(new InputCoder(), value.inputsCount).encode(value.inputs)); - parts.push(new ArrayCoder(new OutputCoder(), value.outputsCount).encode(value.outputs)); - parts.push(new ArrayCoder(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); + parts.push(coders.array(new InputCoder(), value.inputsCount).encode(value.inputs)); + parts.push(coders.array(new OutputCoder(), value.outputsCount).encode(value.outputs)); + parts.push(coders.array(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); return concat(parts); } @@ -109,33 +108,33 @@ export class TransactionScriptCoder extends Coder { - constructor() { - super('TransactionCreate', 'struct TransactionCreate', 0); - } + override type = 'TransactionCreate'; encode(value: TransactionCreate): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.bytecodeWitnessIndex)); - parts.push(new B256Coder().encode(value.salt)); - parts.push(new BigNumberCoder('u64').encode(value.storageSlotsCount)); - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.policyTypes)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.inputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessesCount)); + parts.push(coders.u16.encode(value.bytecodeWitnessIndex)); + parts.push(coders.b256.encode(value.salt)); + parts.push(coders.u64.encode(value.storageSlotsCount)); + parts.push(coders.u32.encode(value.policyTypes)); + parts.push(coders.u16.encode(value.inputsCount)); + parts.push(coders.u16.encode(value.outputsCount)); + parts.push(coders.u16.encode(value.witnessesCount)); parts.push( - new ArrayCoder(new StorageSlotCoder(), value.storageSlotsCount.toNumber()).encode( - value.storageSlots - ) + coders + .array(new StorageSlotCoder(), value.storageSlotsCount.toNumber()) + .encode(value.storageSlots) ); parts.push(new PoliciesCoder().encode(value.policies)); - parts.push(new ArrayCoder(new InputCoder(), value.inputsCount).encode(value.inputs)); - parts.push(new ArrayCoder(new OutputCoder(), value.outputsCount).encode(value.outputs)); - parts.push(new ArrayCoder(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); + parts.push(coders.array(new InputCoder(), value.inputsCount).encode(value.inputs)); + parts.push(coders.array(new OutputCoder(), value.outputsCount).encode(value.outputs)); + parts.push(coders.array(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); return concat(parts); } @@ -209,32 +206,31 @@ export class TransactionCreateCoder extends Coder { - constructor() { - super('TransactionMint', 'struct TransactionMint', 0); - } + override type = 'TransactionMint'; encode(value: TransactionMint): Uint8Array { const parts: Uint8Array[] = []; @@ -290,9 +284,9 @@ export class TransactionMintCoder extends Coder { - constructor() { - super('TransactionUpgrade', 'struct TransactionUpgrade', 0); - } + override type = 'TransactionUpgrade'; encode(value: TransactionUpgrade): Uint8Array { const parts: Uint8Array[] = []; parts.push(new UpgradePurposeCoder().encode(value.upgradePurpose)); - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.policyTypes)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.inputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessesCount)); + parts.push(coders.u32.encode(value.policyTypes)); + parts.push(coders.u16.encode(value.inputsCount)); + parts.push(coders.u16.encode(value.outputsCount)); + parts.push(coders.u16.encode(value.witnessesCount)); parts.push(new PoliciesCoder().encode(value.policies)); - parts.push(new ArrayCoder(new InputCoder(), value.inputsCount).encode(value.inputs)); - parts.push(new ArrayCoder(new OutputCoder(), value.outputsCount).encode(value.outputs)); - parts.push(new ArrayCoder(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); + parts.push(coders.array(new InputCoder(), value.inputsCount).encode(value.inputs)); + parts.push(coders.array(new OutputCoder(), value.outputsCount).encode(value.outputs)); + parts.push(coders.array(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); return concat(parts); } @@ -363,21 +355,21 @@ export class TransactionUpgradeCoder extends Coder { - constructor() { - super('TransactionUpload', 'struct TransactionUpload', 0); - } + override type = 'TransactionUpload'; encode(value: TransactionUpload): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new B256Coder().encode(value.root)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessIndex)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.subsectionIndex)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.subsectionsNumber)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.proofSetCount)); - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.policyTypes)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.inputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessesCount)); - parts.push(new ArrayCoder(new B256Coder(), value.proofSetCount).encode(value.proofSet)); + parts.push(coders.b256.encode(value.root)); + parts.push(coders.u16.encode(value.witnessIndex)); + parts.push(coders.u16.encode(value.subsectionIndex)); + parts.push(coders.u16.encode(value.subsectionsNumber)); + parts.push(coders.u16.encode(value.proofSetCount)); + parts.push(coders.u32.encode(value.policyTypes)); + parts.push(coders.u16.encode(value.inputsCount)); + parts.push(coders.u16.encode(value.outputsCount)); + parts.push(coders.u16.encode(value.witnessesCount)); + parts.push(coders.array(coders.b256, value.proofSetCount).encode(value.proofSet)); parts.push(new PoliciesCoder().encode(value.policies)); - parts.push(new ArrayCoder(new InputCoder(), value.inputsCount).encode(value.inputs)); - parts.push(new ArrayCoder(new OutputCoder(), value.outputsCount).encode(value.outputs)); - parts.push(new ArrayCoder(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); + parts.push(coders.array(new InputCoder(), value.inputsCount).encode(value.inputs)); + parts.push(coders.array(new OutputCoder(), value.outputsCount).encode(value.outputs)); + parts.push(coders.array(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); return concat(parts); } @@ -450,33 +440,33 @@ export class TransactionUploadCoder extends Coder { - constructor() { - super('TransactionBlob', 'struct TransactionBlob', 0); - } + override type = 'TransactionBlob'; encode(value: TransactionBlob): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new B256Coder().encode(value.blobId)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessIndex)); - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.policyTypes)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.inputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.outputsCount)); - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(value.witnessesCount)); + parts.push(coders.b256.encode(value.blobId)); + parts.push(coders.u16.encode(value.witnessIndex)); + parts.push(coders.u32.encode(value.policyTypes)); + parts.push(coders.u16.encode(value.inputsCount)); + parts.push(coders.u16.encode(value.outputsCount)); + parts.push(coders.u16.encode(value.witnessesCount)); parts.push(new PoliciesCoder().encode(value.policies)); - parts.push(new ArrayCoder(new InputCoder(), value.inputsCount).encode(value.inputs)); - parts.push(new ArrayCoder(new OutputCoder(), value.outputsCount).encode(value.outputs)); - parts.push(new ArrayCoder(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); + parts.push(coders.array(new InputCoder(), value.inputsCount).encode(value.inputs)); + parts.push(coders.array(new OutputCoder(), value.outputsCount).encode(value.outputs)); + parts.push(coders.array(new WitnessCoder(), value.witnessesCount).encode(value.witnesses)); return concat(parts); } @@ -538,25 +526,25 @@ export class TransactionBlobCoder extends Coder = TTransactionType extends Tran }; export class TransactionCoder extends Coder { - constructor() { - super('Transaction', 'struct Transaction', 0); - } + override type = 'Transaction'; encode(value: Transaction): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new NumberCoder('u8', { padToWordSize: true }).encode(value.type)); + parts.push(coders.u8.encode(value.type)); const { type } = value; @@ -657,7 +643,7 @@ export class TransactionCoder extends Coder { let decoded; let o = offset; - [decoded, o] = new NumberCoder('u8', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u8.decode(data, o); const type = decoded as TransactionType; switch (type) { diff --git a/packages/transactions/src/coders/tx-pointer.ts b/packages/transactions/src/coders/tx-pointer.ts index 6ac2087cd86..1a210264c6b 100644 --- a/packages/transactions/src/coders/tx-pointer.ts +++ b/packages/transactions/src/coders/tx-pointer.ts @@ -1,6 +1,8 @@ -import { NumberCoder, StructCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { coders } from './coders'; + export type TxPointer = { /** Block height (u32) */ blockHeight: number; @@ -9,18 +11,23 @@ export type TxPointer = { txIndex: number; }; -export class TxPointerCoder extends StructCoder<{ - blockHeight: NumberCoder; - txIndex: NumberCoder; -}> { - constructor() { - super('TxPointer', { - blockHeight: new NumberCoder('u32', { padToWordSize: true }), - txIndex: new NumberCoder('u16', { padToWordSize: true }), - }); +const txPointerCoder = coders.struct({ + blockHeight: coders.u32, + txIndex: coders.u16, +}); + +export class TxPointerCoder extends Coder { + override type = 'TxPointer'; + + encode(value: TxPointer): Uint8Array { + return txPointerCoder.encode(value); + } + + decode(data: Uint8Array, offset: number): [TxPointer, number] { + return txPointerCoder.decode(data, offset); } - public static decodeFromGqlScalar(value: string) { + static decodeFromGqlScalar(value: string): TxPointer { // taken from https://github.com/FuelLabs/fuel-vm/blob/7366db6955589cb3444c9b2bb46e45c8539f19f5/fuel-tx/src/tx_pointer.rs#L87 if (value.length !== 12) { throw new FuelError( diff --git a/packages/transactions/src/coders/upgrade-purpose.test.ts b/packages/transactions/src/coders/upgrade-purpose.test.ts index 9c40186179a..25f3da24419 100644 --- a/packages/transactions/src/coders/upgrade-purpose.test.ts +++ b/packages/transactions/src/coders/upgrade-purpose.test.ts @@ -1,6 +1,6 @@ -import { B256Coder, NumberCoder } from '@fuel-ts/abi-coder'; import { arrayify } from '@fuel-ts/utils'; +import { coders } from './coders'; import type { UpgradePurpose } from './upgrade-purpose'; import { UpgradePurposeCoder, UpgradePurposeTypeEnum } from './upgrade-purpose'; @@ -23,8 +23,8 @@ describe('UpgradePurposeCoder', () => { }; const typeBytes = [0, 0, 0, 0, 0, 0, 0, UpgradePurposeTypeEnum.ConsensusParameters]; - const witnessIndexBytes = new NumberCoder('u8', { padToWordSize: true }).encode(witnessIndex); - const checksumBytes = new B256Coder().encode(checksum); + const witnessIndexBytes = coders.u8.encode(witnessIndex); + const checksumBytes = coders.b256.encode(checksum); const expectedEncoded = Uint8Array.from([...typeBytes, ...witnessIndexBytes, ...checksumBytes]); @@ -42,7 +42,7 @@ describe('UpgradePurposeCoder', () => { }; const typeBytes = [0, 0, 0, 0, 0, 0, 0, UpgradePurposeTypeEnum.StateTransition]; - const bytecodeRootBytes = new B256Coder().encode(bytecodeRoot); + const bytecodeRootBytes = coders.b256.encode(bytecodeRoot); const expectedEncoded = Uint8Array.from([...typeBytes, ...bytecodeRootBytes]); const encoded = new UpgradePurposeCoder().encode(upgradePurpose); diff --git a/packages/transactions/src/coders/upgrade-purpose.ts b/packages/transactions/src/coders/upgrade-purpose.ts index 33db4b8d5ad..758fde6ac42 100644 --- a/packages/transactions/src/coders/upgrade-purpose.ts +++ b/packages/transactions/src/coders/upgrade-purpose.ts @@ -1,7 +1,10 @@ -import { B256Coder, Coder, NumberCoder } from '@fuel-ts/abi-coder'; +/* eslint-disable max-classes-per-file */ +import { Coder } from '@fuel-ts/abi'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { concat } from '@fuel-ts/utils'; +import { coders } from './coders'; + export enum UpgradePurposeTypeEnum { ConsensusParameters = 0, StateTransition = 1, @@ -24,36 +27,61 @@ export interface ConsensusParameters { /** The hash of the serialized consensus parameters. */ checksum: string; } +export class ConsensusParametersCoder extends Coder { + private coder = coders.struct({ + witnessIndex: coders.u16, + checksum: coders.b256, + }); + + override type = 'ConsensusParameters'; + + encode(value: ConsensusParameters): Uint8Array { + return this.coder.encode(value); + } + + decode(data: Uint8Array, offset: number): [ConsensusParameters, number] { + return this.coder.decode(data, offset); + } +} export interface StateTransition { /** The root of the new bytecode of the state transition function. */ bytecodeRoot: string; } -export class UpgradePurposeCoder extends Coder { - constructor() { - super('UpgradePurpose', 'UpgradePurpose', 0); +export class StateTransitionCoder extends Coder { + private coder = coders.struct({ + bytecodeRoot: coders.b256, + }); + + override type = 'StateTransition'; + + encode(value: StateTransition): Uint8Array { + return this.coder.encode(value); } + decode(data: Uint8Array, offset: number): [StateTransition, number] { + return this.coder.decode(data, offset); + } +} + +export class UpgradePurposeCoder extends Coder { + override type = 'UpgradePurpose'; + encode(upgradePurposeType: UpgradePurpose): Uint8Array { const parts: Uint8Array[] = []; const { type } = upgradePurposeType; - parts.push(new NumberCoder('u8', { padToWordSize: true }).encode(type)); + parts.push(coders.u8.encode(type)); switch (type) { case UpgradePurposeTypeEnum.ConsensusParameters: { - const data = upgradePurposeType.data as ConsensusParameters; - - parts.push(new NumberCoder('u16', { padToWordSize: true }).encode(data.witnessIndex)); - parts.push(new B256Coder().encode(data.checksum)); + parts.push(new ConsensusParametersCoder().encode(upgradePurposeType.data)); break; } case UpgradePurposeTypeEnum.StateTransition: { - const data = upgradePurposeType.data as StateTransition; - - parts.push(new B256Coder().encode(data.bytecodeRoot)); + parts.push(new StateTransitionCoder().encode(upgradePurposeType.data)); break; } @@ -68,28 +96,18 @@ export class UpgradePurposeCoder extends Coder { return concat(parts); } - decode(data: Uint8Array, offset: number): [UpgradePurpose, number] { - let o = offset; - let decoded; - - [decoded, o] = new NumberCoder('u8', { padToWordSize: true }).decode(data, o); - const type = decoded as UpgradePurposeTypeEnum; + decode(data: Uint8Array, initialOffset: number): [UpgradePurpose, number] { + const [type, offset] = coders.u8.decode(data, initialOffset); switch (type) { case UpgradePurposeTypeEnum.ConsensusParameters: { - [decoded, o] = new NumberCoder('u16', { padToWordSize: true }).decode(data, o); - const witnessIndex = decoded; - [decoded, o] = new B256Coder().decode(data, o); - const checksum = decoded; - - return [{ type, data: { witnessIndex, checksum } }, o]; + const [decoded, o] = new ConsensusParametersCoder().decode(data, offset); + return [{ type, data: decoded }, o]; } case UpgradePurposeTypeEnum.StateTransition: { - [decoded, o] = new B256Coder().decode(data, o); - const bytecodeRoot = decoded; - - return [{ type, data: { bytecodeRoot } }, o]; + const [decoded, o] = new StateTransitionCoder().decode(data, offset); + return [{ type, data: decoded }, o]; } default: { diff --git a/packages/transactions/src/coders/utxo-id.ts b/packages/transactions/src/coders/utxo-id.ts index a967e626c7c..71d5786d73d 100644 --- a/packages/transactions/src/coders/utxo-id.ts +++ b/packages/transactions/src/coders/utxo-id.ts @@ -1,4 +1,6 @@ -import { B256Coder, NumberCoder, StructCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; + +import { coders } from './coders'; export type UtxoId = { /** Transaction ID (b256) */ @@ -7,14 +9,19 @@ export type UtxoId = { outputIndex: number; }; -export class UtxoIdCoder extends StructCoder<{ - transactionId: B256Coder; - outputIndex: NumberCoder; -}> { - constructor() { - super('UtxoId', { - transactionId: new B256Coder(), - outputIndex: new NumberCoder('u16', { padToWordSize: true }), - }); +const utxoIdCoder: Coder = coders.struct({ + transactionId: coders.b256, + outputIndex: coders.u16, +}); + +export class UtxoIdCoder extends Coder { + override type = 'UtxoId'; + + encode(value: UtxoId): Uint8Array { + return utxoIdCoder.encode(value); + } + + decode(data: Uint8Array, offset: number): [UtxoId, number] { + return utxoIdCoder.decode(data, offset); } } diff --git a/packages/transactions/src/coders/witness.test.ts b/packages/transactions/src/coders/witness.test.ts index a33499895ea..44c43a05e8d 100644 --- a/packages/transactions/src/coders/witness.test.ts +++ b/packages/transactions/src/coders/witness.test.ts @@ -1,7 +1,6 @@ import { arrayify, hexlify } from '@fuel-ts/utils'; -import type { Witness } from './witness'; -import { WitnessCoder } from './witness'; +import { WitnessCoder, type Witness } from './witness'; /** * @group node diff --git a/packages/transactions/src/coders/witness.ts b/packages/transactions/src/coders/witness.ts index 5121e60fc3a..b8cd4537910 100644 --- a/packages/transactions/src/coders/witness.ts +++ b/packages/transactions/src/coders/witness.ts @@ -1,7 +1,8 @@ -import { Coder, NumberCoder } from '@fuel-ts/abi-coder'; +import { Coder } from '@fuel-ts/abi'; import { concat } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './byte-array'; +import { byteArray } from './byte-array'; +import { coders } from './coders'; export type Witness = { /** Length of witness data byte array */ @@ -11,20 +12,13 @@ export type Witness = { }; export class WitnessCoder extends Coder { - constructor() { - super( - 'Witness', - // Types of dynamic length are not supported in the ABI - 'unknown', - 0 - ); - } + override type = 'Witness'; encode(value: Witness): Uint8Array { const parts: Uint8Array[] = []; - parts.push(new NumberCoder('u32', { padToWordSize: true }).encode(value.dataLength)); - parts.push(new ByteArrayCoder(value.dataLength).encode(value.data)); + parts.push(coders.u32.encode(value.dataLength)); + parts.push(byteArray(value.dataLength).encode(value.data)); return concat(parts); } @@ -33,17 +27,11 @@ export class WitnessCoder extends Coder { let decoded; let o = offset; - [decoded, o] = new NumberCoder('u32', { padToWordSize: true }).decode(data, o); + [decoded, o] = coders.u32.decode(data, o); const dataLength = decoded; - [decoded, o] = new ByteArrayCoder(dataLength).decode(data, o); + [decoded, o] = byteArray(dataLength).decode(data, o); const witnessData = decoded; - return [ - { - dataLength, - data: witnessData, - }, - o, - ]; + return [{ dataLength, data: witnessData }, o]; } } diff --git a/packages/transactions/src/receipt.test.ts b/packages/transactions/src/receipt.test.ts index 5bae4d01197..00fde520ea7 100644 --- a/packages/transactions/src/receipt.test.ts +++ b/packages/transactions/src/receipt.test.ts @@ -1,11 +1,11 @@ -import { BigNumberCoder } from '@fuel-ts/abi-coder'; +import { encoding } from '@fuel-ts/abi'; import type { AssetId } from '@fuel-ts/address'; import { getRandomB256 } from '@fuel-ts/address'; import { sha256 } from '@fuel-ts/hasher'; import { bn } from '@fuel-ts/math'; import { arrayify, concat } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './coders/byte-array'; +import { byteArray } from './coders/byte-array'; import { getMintedAssetId, createAssetId, getMessageId } from './receipt'; /** @@ -52,10 +52,10 @@ describe('getMessageId', () => { const parts: Uint8Array[] = []; - parts.push(new ByteArrayCoder(32).encode(sender)); - parts.push(new ByteArrayCoder(32).encode(recipient)); - parts.push(new ByteArrayCoder(32).encode(nonce)); - parts.push(new BigNumberCoder('u64').encode(amount)); + parts.push(byteArray(32).encode(sender)); + parts.push(byteArray(32).encode(recipient)); + parts.push(byteArray(32).encode(nonce)); + parts.push(encoding.v1.u64.encode(amount)); parts.push(data); const expected = sha256(concat(parts)); @@ -73,10 +73,10 @@ describe('getMessageId', () => { const parts: Uint8Array[] = []; - parts.push(new ByteArrayCoder(32).encode(sender)); - parts.push(new ByteArrayCoder(32).encode(recipient)); - parts.push(new ByteArrayCoder(32).encode(nonce)); - parts.push(new BigNumberCoder('u64').encode(amount)); + parts.push(byteArray(32).encode(sender)); + parts.push(byteArray(32).encode(recipient)); + parts.push(byteArray(32).encode(nonce)); + parts.push(encoding.v1.u64.encode(amount)); parts.push(data); const expected = sha256(concat(parts)); diff --git a/packages/transactions/src/receipt.ts b/packages/transactions/src/receipt.ts index eb204485006..aa2cf9fa1af 100644 --- a/packages/transactions/src/receipt.ts +++ b/packages/transactions/src/receipt.ts @@ -1,10 +1,10 @@ -import { BigNumberCoder } from '@fuel-ts/abi-coder'; +import { encoding } from '@fuel-ts/abi'; import type { AssetId } from '@fuel-ts/address'; import { sha256 } from '@fuel-ts/hasher'; import type { BN } from '@fuel-ts/math'; import { arrayify, concat } from '@fuel-ts/utils'; -import { ByteArrayCoder } from './coders/byte-array'; +import { byteArray } from './coders/byte-array'; export enum ReceiptType /* u8 */ { Call = 0, @@ -263,10 +263,10 @@ export const getMessageId = ( ): string => { const parts: Uint8Array[] = []; - parts.push(new ByteArrayCoder(32).encode(value.sender)); - parts.push(new ByteArrayCoder(32).encode(value.recipient)); - parts.push(new ByteArrayCoder(32).encode(value.nonce)); - parts.push(new BigNumberCoder('u64').encode(value.amount)); + parts.push(byteArray(32).encode(value.sender)); + parts.push(byteArray(32).encode(value.recipient)); + parts.push(byteArray(32).encode(value.nonce)); + parts.push(encoding.v1.u64.encode(value.amount)); parts.push(arrayify(value.data || '0x')); return sha256(concat(parts)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 020627fb417..4136b91882f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -508,6 +508,9 @@ importers: internal/check-imports: dependencies: + '@fuel-ts/abi': + specifier: workspace:* + version: link:../../packages/abi '@fuel-ts/abi-coder': specifier: workspace:* version: link:../../packages/abi-coder @@ -573,6 +576,39 @@ importers: internal/tsup: {} + packages/abi: + dependencies: + '@fuel-ts/crypto': + specifier: workspace:* + version: link:../crypto + '@fuel-ts/errors': + specifier: workspace:* + version: link:../errors + '@fuel-ts/hasher': + specifier: workspace:* + version: link:../hasher + '@fuel-ts/math': + specifier: workspace:* + version: link:../math + '@fuel-ts/utils': + specifier: workspace:* + version: link:../utils + '@fuel-ts/versions': + specifier: workspace:* + version: link:../versions + commander: + specifier: 12.1.0 + version: 12.1.0 + glob: + specifier: 10.4.5 + version: 10.4.5 + handlebars: + specifier: 4.7.8 + version: 4.7.8 + type-fest: + specifier: 4.26.1 + version: 4.26.1 + packages/abi-coder: dependencies: '@fuel-ts/crypto': @@ -636,9 +672,9 @@ importers: packages/account: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder + version: link:../abi '@fuel-ts/address': specifier: workspace:* version: link:../address @@ -727,9 +763,9 @@ importers: packages/contract: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -826,6 +862,9 @@ importers: specifier: workspace:* version: link:../fuels devDependencies: + '@fuel-ts/abi': + specifier: workspace:* + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -841,12 +880,9 @@ importers: packages/fuels: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder - '@fuel-ts/abi-typegen': - specifier: workspace:* - version: link:../abi-typegen + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -987,9 +1023,9 @@ importers: packages/program: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -1021,12 +1057,9 @@ importers: packages/recipes: dependencies: - '@fuel-ts/abi-coder': - specifier: workspace:* - version: link:../abi-coder - '@fuel-ts/abi-typegen': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-typegen + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -1048,9 +1081,9 @@ importers: packages/script: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder + version: link:../abi '@fuel-ts/account': specifier: workspace:* version: link:../account @@ -1076,9 +1109,9 @@ importers: packages/transactions: dependencies: - '@fuel-ts/abi-coder': + '@fuel-ts/abi': specifier: workspace:* - version: link:../abi-coder + version: link:../abi '@fuel-ts/address': specifier: workspace:* version: link:../address diff --git a/templates/nextjs/src/sway-api/common.ts b/templates/nextjs/src/sway-api/common.ts new file mode 100644 index 00000000000..d2f0bd72ea0 --- /dev/null +++ b/templates/nextjs/src/sway-api/common.ts @@ -0,0 +1,53 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { AbiCoderFunction, InvokeFunction } from 'fuels'; + +/** + * Mimics Sway Enum. + * Requires one and only one Key-Value pair and raises error if more are provided. + */ +export type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +/** + * Mimics Sway Option type. + */ +export type Option = T | undefined; + +/** + * Mimics Sway Result enum type. + * Ok represents the success case, while Err represents the error case. + */ +export type Result = Enum<{ Ok: T; Err: E }>; + +/** + * Mimics Sway array type. For example, [u64; 10] is converted to ArrayOfLength. + */ +export type ArrayOfLength< + T, + Length extends number, + Arr extends unknown[] = [], +> = Arr['length'] extends Length ? Arr : ArrayOfLength; + +interface Types { + functions: Record; + configurables: Partial>; +} + +export type ProgramFunctionMapper = { + [K in keyof T]: InvokeFunction; +}; + +export type InterfaceFunctionMapper = { + [K in keyof T]: AbiCoderFunction; +}; \ No newline at end of file diff --git a/templates/vite/src/sway-api/common.ts b/templates/vite/src/sway-api/common.ts new file mode 100644 index 00000000000..d2f0bd72ea0 --- /dev/null +++ b/templates/vite/src/sway-api/common.ts @@ -0,0 +1,53 @@ +/* Autogenerated file. Do not edit manually. */ + +/* eslint-disable eslint-comments/no-unlimited-disable */ +/* eslint-disable */ + +/* + Fuels version: 0.98.0 + Forc version: 0.66.5 +*/ + + +import type { AbiCoderFunction, InvokeFunction } from 'fuels'; + +/** + * Mimics Sway Enum. + * Requires one and only one Key-Value pair and raises error if more are provided. + */ +export type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +/** + * Mimics Sway Option type. + */ +export type Option = T | undefined; + +/** + * Mimics Sway Result enum type. + * Ok represents the success case, while Err represents the error case. + */ +export type Result = Enum<{ Ok: T; Err: E }>; + +/** + * Mimics Sway array type. For example, [u64; 10] is converted to ArrayOfLength. + */ +export type ArrayOfLength< + T, + Length extends number, + Arr extends unknown[] = [], +> = Arr['length'] extends Length ? Arr : ArrayOfLength; + +interface Types { + functions: Record; + configurables: Partial>; +} + +export type ProgramFunctionMapper = { + [K in keyof T]: InvokeFunction; +}; + +export type InterfaceFunctionMapper = { + [K in keyof T]: AbiCoderFunction; +}; \ No newline at end of file diff --git a/tsconfig.test.json b/tsconfig.test.json index 0ca9f42bd9a..96ad5020fb9 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,6 +3,6 @@ "compilerOptions": { "noEmit": true }, - "include": ["**/*.test.ts", "**/*.d.ts"], + "include": ["**/*.test.ts"], "exclude": ["node_modules", "apps/docs/src/**/*.test.ts"] } diff --git a/vitest.d.ts b/vitest.d.ts new file mode 100644 index 00000000000..de38d6e3b2a --- /dev/null +++ b/vitest.d.ts @@ -0,0 +1,12 @@ +import 'vitest'; + +interface CustomMatchers { + toEqualBn: (bn: string | number) => R; +} + +declare module 'vitest' { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + interface Assertion extends CustomMatchers {} + interface ExpectStatic extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +}