diff --git a/.changeset/long-ducks-jump.md b/.changeset/long-ducks-jump.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/long-ducks-jump.md @@ -0,0 +1,2 @@ +--- +--- 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 d767304335a..394cb1f3f41 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 @@ -160,17 +160,17 @@ const abi = { { "name": "SHOULD_RETURN", "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 2776 + "offset": 2768 }, { "name": "AN_OPTION", "concreteTypeId": "2da102c46c7263beeed95818cd7bee801716ba8303dddafdcd0f6c9efda4a0f1", - "offset": 2752 + "offset": 2744 }, { "name": "A_GENERIC_STRUCT", "concreteTypeId": "71df88006611ffff852cf617defb70f77adaf507305088cedd41d276c783aab0", - "offset": 2768 + "offset": 2760 } ] }; diff --git a/packages/abi-typegen/test/fixtures/templates/contract/main.hbs b/packages/abi-typegen/test/fixtures/templates/contract/main.hbs index 942b70a5042..a01168c439f 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/main.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/main.hbs @@ -685,28 +685,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 +718,17 @@ const abi = { "output": "ab7cd04e05be58e3fc15d424c2c4a57f824a2a2d97d67252440a3925ebdc1335", "attributes": null }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308" + } + ], + "name": "types_address", + "output": "f597b637c3b0f588fb8d7086c6f4735caa3122b85f0423b82e489f9bb58e2308", + "attributes": null + }, { "inputs": [ { @@ -806,6 +795,17 @@ const abi = { "output": "cdd87b7d12fe505416570c294c884bca819364863efe3bf539245fa18515fbbb", "attributes": null }, + { + "inputs": [ + { + "name": "x", + "concreteTypeId": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54" + } + ], + "name": "types_contract_id", + "output": "29c10735d33b5159f0c71ee1dbd17b36a3e69e41f00fab0d42e1bd9f428d8a54", + "attributes": null + }, { "inputs": [ { @@ -905,7 +905,7 @@ const abi = { "concreteTypeId": "3597e0782bd4dbaf5c8025b40ff3a325845ee34caa713a6d664bda034a31d02a" } ], - "name": "types_option_geo", + "name": "types_option_struct", "output": "3597e0782bd4dbaf5c8025b40ff3a325845ee34caa713a6d664bda034a31d02a", "attributes": null }, @@ -1157,17 +1157,17 @@ export class MyContractInterface extends Interface { 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_address: FunctionFragment; types_array: FunctionFragment; types_asset_id: FunctionFragment; types_b256: FunctionFragment; types_b512: FunctionFragment; types_bool: FunctionFragment; types_bytes: FunctionFragment; + types_contract_id: FunctionFragment; types_empty: FunctionFragment; types_empty_then_value: FunctionFragment; types_enum: FunctionFragment; @@ -1176,7 +1176,7 @@ export class MyContractInterface extends Interface { types_generic_enum: FunctionFragment; types_generic_struct: FunctionFragment; types_option: FunctionFragment; - types_option_geo: FunctionFragment; + types_option_struct: FunctionFragment; types_raw_slice: FunctionFragment; types_result: FunctionFragment; types_std_string: FunctionFragment; @@ -1206,17 +1206,17 @@ export class MyContract extends Contract { declare interface: MyContractInterface; 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 +1225,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/script-with-configurable/main.hbs b/packages/abi-typegen/test/fixtures/templates/script-with-configurable/main.hbs index 71b9ba3cb53..fb4a272eb19 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 @@ -81,7 +81,7 @@ const abi = { { "name": "SHOULD_RETURN", "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", - "offset": 760 + "offset": 752 } ] }; diff --git a/packages/fuel-gauge/src/abi/abi-coder.test.ts b/packages/fuel-gauge/src/abi/abi-coder.test.ts new file mode 100644 index 00000000000..a8d5c1d1de1 --- /dev/null +++ b/packages/fuel-gauge/src/abi/abi-coder.test.ts @@ -0,0 +1,1493 @@ +import { bn, 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 { + EnumWithBuiltinTypeInput, + EnumWithBuiltinTypeOutput, + EnumWithVectorInput, + EnumWithVectorOutput, + IdentityInput, + IdentityOutput, + StructDoubleGenericInput, + StructSimpleInput, + StructSimpleOutput, + StructWithGenericArrayInput, + StructWithMultiOptionInput, + StructWithMultiOptionOutput, + StructCInput, + StructWithNestedArrayInput, + StructWithNestedTupleInput, + StructSingleGenericInput, + StructWithImplicitGenericsInput, + AssetIdInput, + StructWithEnumArrayInput, + StructWithEnumArrayOutput, + StructWithSingleOptionOutput, + StructWithSingleOptionInput, +} from '../../test/typegen/contracts/AbiContract'; +import type { Option, Result, Vec } from '../../test/typegen/contracts/common'; + +import { + U16_MAX, + U16_MIN, + U256_MAX, + U256_MIN, + U32_MAX, + U32_MIN, + U64_MAX, + U64_MIN, + U8_MAX, + U8_MIN, +} from './constants'; +import { toEqualBn } from './vitest.matcher'; + +expect.extend({ toEqualBn }); + +/** + * @group node + */ +describe('AbiCoder', () => { + let contract: AbiContract; + let wallet: WalletUnlocked; + let cleanup: () => void; + + beforeAll(async () => { + const launched = await launchTestNode({ + contractsConfigs: [{ factory: AbiContractFactory }], + }); + + const { contracts, wallets } = launched; + + wallet = wallets[0]; + contract = contracts[0] as AbiContract; + cleanup = launched.cleanup; + }); + + afterAll(() => { + cleanup(); + }); + + describe('configurables', () => { + it('should encode/decode just fine', async () => { + const EXPECTED = { + U8_VALUE: 10, + BOOL_VALUE: true, + B256_VALUE: '0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96', + OPTION_U8_VALUE: undefined, + GENERIC_STRUCT_VALUE: { + a: { a: 4, b: 257 }, + b: 57000, + }, + }; + + const { waitForResult } = await contract.functions.configurables().call(); + + const { value } = await waitForResult(); + expect(value).toEqual(EXPECTED); + }); + + it('should set configurables', async () => { + const NEW_CONFIGURABLES = { + U8_VALUE: 123, + BOOL_VALUE: false, + B256_VALUE: getRandomB256(), + OPTION_U8_VALUE: 11, + GENERIC_STRUCT_VALUE: { + a: { a: 234, b: 12 }, + b: 3525, + }, + }; + + const { waitForResult: waitForDeploy } = await AbiContractFactory.deploy(wallet, { + configurableConstants: NEW_CONFIGURABLES, + }); + + const { contract: contractWithConfigurables } = await waitForDeploy(); + const { waitForResult } = await contractWithConfigurables.functions.configurables().call(); + const { value } = await waitForResult(); + expect(value).toEqual(NEW_CONFIGURABLES); + }); + }); + + describe('types_u8', () => { + test('should encode/decode just fine', async () => { + const input = 8; + const expected = 255; + + const fn = contract.functions.types_u8(input); + + const { waitForResult } = await fn.call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + test('should fail to encode/decode [min - 1]', async () => { + const input = U8_MIN - 1; + + await expectToThrowFuelError( + () => contract.functions.types_u8(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8.') + ); + }); + + test('should fail to encode/decode [max + 1]', async () => { + const input = U8_MAX + 1; + + await expectToThrowFuelError( + () => contract.functions.types_u8(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u8, too many bytes.') + ); + }); + }); + + describe('types_u16', () => { + it('should encode/decode just fine', async () => { + const input = 16; + const expected = 65535; + + const { waitForResult } = await contract.functions.types_u16(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [min - 1]', async () => { + const input = U16_MIN - 1; + + await expectToThrowFuelError( + () => contract.functions.types_u16(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16.') + ); + }); + + it('should fail to encode/decode [max + 1]', async () => { + const input = U16_MAX + 1; + + await expectToThrowFuelError( + () => contract.functions.types_u16(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u16, too many bytes.') + ); + }); + }); + + describe('types_u32', () => { + it('should encode/decode just fine', async () => { + const input = 32; + const expected = 4294967295; + + const { waitForResult } = await contract.functions.types_u32(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [min - 1]', async () => { + const input = U32_MIN - 1; + + await expectToThrowFuelError( + () => contract.functions.types_u32(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32.') + ); + }); + + it('should fail to encode/decode [max + 1]', async () => { + const input = U32_MAX + 1; + + await expectToThrowFuelError( + () => contract.functions.types_u32(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u32, too many bytes.') + ); + }); + }); + + describe('types_u64', () => { + it('should encode/decode just fine', async () => { + const input = 64; + const expected = '4294967295000'; + + const { waitForResult } = await contract.functions.types_u64(input).call(); + + const { value } = await waitForResult(); + const actual = value.toString(); + expect(actual).toBe(expected); + }); + + it('should fail to encode/decode [min - 1]', async () => { + const input = U64_MIN - 1; + + await expectToThrowFuelError( + () => contract.functions.types_u64(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64.') + ); + }); + + it('should fail to encode/decode [max + 1]', async () => { + const input = U64_MAX.add(1); + + await expectToThrowFuelError( + () => contract.functions.types_u64(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u64.') + ); + }); + }); + + describe('types_u256', () => { + it('should encode/decode just fine', async () => { + const input = 256; + const expected = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + + const { waitForResult } = await contract.functions.types_u256(input).call(); + + const { value } = await waitForResult(); + const actual = value.toHex(); + expect(actual).toEqual(expected); + }); + + it('should fail to encode/decode [min - 1]', async () => { + const input = U256_MIN - 1; + + await expectToThrowFuelError( + () => contract.functions.types_u256(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256.') + ); + }); + + it('should fail to encode/decode [max + 1]', async () => { + const input = U256_MAX.add(1); + + await expectToThrowFuelError( + () => contract.functions.types_u256(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid u256.') + ); + }); + }); + + describe('types_bool', () => { + it('should encode/decode just fine', async () => { + const input = false; + const expected = true; + + const { waitForResult } = await contract.functions.types_bool(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [number]', async () => { + const input = 2; + + await expectToThrowFuelError( + () => contract.functions.types_bool(input as unknown as boolean).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid boolean value.') + ); + }); + + it('should fail to encode/decode [string]', async () => { + const input = '2'; + + await expectToThrowFuelError( + () => contract.functions.types_bool(input as unknown as boolean).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid boolean value.') + ); + }); + }); + + describe('types_b256', () => { + it('should encode/decode just fine', async () => { + const input = `0x${'a'.repeat(64)}`; + const expected = `0x${'0'.repeat(64)}`; + + const { waitForResult } = await contract.functions.types_b256(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [too short]', async () => { + const input = `0x${'a'.repeat(63)}`; + + await expectToThrowFuelError( + () => contract.functions.types_b256(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + ); + }); + + it('should fail to encode/decode [too long]', async () => { + const input = `0x${'a'.repeat(65)}`; + + await expectToThrowFuelError( + () => contract.functions.types_b256(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + ); + }); + + it('should fail to encode/decode [not a hex]', async () => { + const input = 'not a hex value'; + + await expectToThrowFuelError( + () => contract.functions.types_b256(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid b256.') + ); + }); + }); + + describe('types_b512', () => { + it('should encode/decode just fine', async () => { + const input = `0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d`; + const expected = `0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d`; + + const { waitForResult } = await contract.functions.types_b512(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [too short]', async () => { + const input = `0x${'a'.repeat(127)}`; + + await expectToThrowFuelError( + () => contract.functions.types_b512(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + ); + }); + + it('should fail to encode/decode [too long]', async () => { + const input = `0x${'a'.repeat(129)}`; + + await expectToThrowFuelError( + () => contract.functions.types_b512(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + ); + }); + + it('should fail to encode/decode [not a hex]', async () => { + const input = 'not a hex value'; + + await expectToThrowFuelError( + () => contract.functions.types_b512(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Invalid struct B512.') + ); + }); + }); + + describe('types_bytes', () => { + it('should encode/decode just fine [Uint8Array]', async () => { + const input = Uint8Array.from([1, 2, 3]); + const expected = Uint8Array.from([3, 2, 1]); + const { waitForResult } = await contract.functions.types_bytes(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + + it('should encode/decode just fine [number]', async () => { + const input = [1, 2, 3]; + const expected = Uint8Array.from([3, 2, 1]); + const { waitForResult } = await contract.functions.types_bytes(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + /** + * Strings + */ + describe('types_str', () => { + it('should encode/decode just fine [length = 5]', async () => { + const input = 'Input'; + const expected = 'Hello'; + + const { waitForResult } = await contract.functions.types_str(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + + it('should fail to encode/decode [length - 1]', async () => { + const input = 'a'.repeat(4); + + await expectToThrowFuelError( + () => contract.functions.types_str(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Value length mismatch during encode.') + ); + }); + + it('should fail to encode/decode [length + 1]', async () => { + const input = 'a'.repeat(6); + + await expectToThrowFuelError( + () => contract.functions.types_str(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Value length mismatch during encode.') + ); + }); + }); + + describe('types_str_slice', () => { + it('should encode/decode just fine', async () => { + const input = 'Input'; + const expected = 'Output'; + + const { waitForResult } = await contract.functions.types_str_slice(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + }); + + describe('types_raw_slice', () => { + it('should encode/decode just fine', async () => { + const input: RawSlice = [1, 2, 3]; + const expected: RawSlice = [4, 3, 2, 1]; + + const { waitForResult } = await contract.functions.types_raw_slice(input).call(); + const { value } = await waitForResult(); + + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_std_string', () => { + it('should encode/decode just fine', async () => { + const input = 'Input'; + const expected = 'Output'; + + const { waitForResult } = await contract.functions.types_std_string(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + }); + + /** + * Arrays + */ + describe('types_array', () => { + it('should encode/decode just fine', async () => { + const input = [1, 2, 3, 4] as [number, number, number, number]; + const expected = [4, 3, 2, 1] as [number, number, number, number]; + + const { waitForResult } = await contract.functions.types_array(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + + it('should fail to encode/decode [empty]', async () => { + const input = [] as unknown as [number, number, number, number]; + + await expectToThrowFuelError( + () => contract.functions.types_array(input).call(), + new FuelError(FuelError.CODES.ENCODE_ERROR, 'Types/values length mismatch.') + ); + }); + }); + + describe('types_array_struct', () => { + it('should encode/decode just fine', async () => { + const input = [ + { a: true, b: 10 }, + { a: true, b: 10 }, + { a: true, b: 10 }, + ] as [{ a: boolean; b: number }, { a: boolean; b: number }, { a: boolean; b: number }]; + const expected = [ + { a: false, b: 30 }, + { a: false, b: 30 }, + { a: false, b: 30 }, + ]; + + const { waitForResult } = await contract.functions.types_array_struct(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_array_with_generic_struct', () => { + it('should encode/decode just fine', async () => { + const INPUT_STRUCT = { + a: { + a: 10, + }, + b: 'A', + }; + const input = [INPUT_STRUCT, INPUT_STRUCT] as [ + StructDoubleGenericInput, string>, + StructDoubleGenericInput, string>, + ]; + + const EXPECTED_STRUCT = { + a: { + // @ts-expect-error: Custom matcher 'toEqualBn' + a: expect.toEqualBn(20), + }, + b: 'B', + }; + const expected = [EXPECTED_STRUCT, EXPECTED_STRUCT]; + + const { waitForResult } = await contract.functions + .types_array_with_generic_struct(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_array_with_vector', () => { + it('should encode/decode just fine', async () => { + const input = [[1, 2, 3]] as [Vec]; + const expected = [[3, 2, 1]]; + + const { waitForResult } = await contract.functions.types_array_with_vector(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Structs + */ + describe('types_struct_simple', () => { + it('should encode/decode just fine', async () => { + const input = { a: true, b: 10 }; + const expected = { a: false, b: 30 }; + + const { waitForResult } = await contract.functions.types_struct_simple(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_generic', () => { + it('should encode/decode just fine', async () => { + const input = { a: 10 }; + const expected = { a: 20 }; + + const { waitForResult } = await contract.functions.types_struct_generic(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_with_tuple', () => { + it('should encode/decode just fine', async () => { + const input: StructSingleGenericInput<[boolean, BigNumberish]> = { a: [true, 10] }; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected = { a: [false, expect.toEqualBn(20)] }; + + const { waitForResult } = await contract.functions.types_struct_with_tuple(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_double_generic', () => { + it('should encode/decode just fine', async () => { + const input = { a: 10, b: { a: 10 } }; + const expected = { a: 20, b: { b: 10 } }; + + const { waitForResult } = await contract.functions.types_struct_double_generic(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_external', () => { + it('should encode/decode just fine', async () => { + const input = { value: 10 }; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected = { value: expect.toEqualBn(20) }; + + const { waitForResult } = await contract.functions.types_struct_external(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_with_implicit_generics', () => { + it('should encode/decode just fine', async () => { + const INPUT_B256 = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const INPUT: StructWithImplicitGenericsInput = { + a: [INPUT_B256, INPUT_B256, INPUT_B256], + b: [INPUT_B256, 10], + }; + + const EXPECTED_B256 = '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; + + const EXPECTED: StructWithImplicitGenericsInput = { + a: [EXPECTED_B256, EXPECTED_B256, EXPECTED_B256], + b: [EXPECTED_B256, 25], + }; + + const { waitForResult } = await contract.functions + .types_struct_with_implicit_generics(INPUT) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(EXPECTED); + }); + }); + + describe('types_struct_with_array', () => { + it('should encode/decode just fine', async () => { + // Inputs + const inputB256: string = + '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const inputStruct: StructDoubleGenericInput = { + a: inputB256, + b: 10, + }; + const input: StructWithGenericArrayInput = { + a: [inputStruct, inputStruct, inputStruct], + }; + + // Expected + const expectedB256: string = + '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; + const expectedStruct: StructDoubleGenericInput = { + a: expectedB256, + b: 20, + }; + const expected: StructWithGenericArrayInput = { + a: [expectedStruct, expectedStruct, expectedStruct], + }; + + const { waitForResult } = await contract.functions.types_struct_with_array(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_with_vector', () => { + it('should encode/decode just fine', async () => { + const input = { a: 1, b: [1, 2, 3] }; + const expected = { a: 3, b: [3, 2, 1] }; + + const { waitForResult } = await contract.functions.types_struct_with_vector(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * TODO: Fix this test + * + * Currently the expected value is not correct + */ + describe.todo('types_struct_with_array_of_enums', () => { + it('should encode/decode just fine', async () => { + const input: StructWithEnumArrayInput = { + a: [EnumWithNativeInput.Checked, EnumWithNativeInput.Checked, EnumWithNativeInput.Checked], + }; + const expected: StructWithEnumArrayOutput = { + a: [ + EnumWithNativeOutput.Pending, + EnumWithNativeOutput.Pending, + EnumWithNativeOutput.Pending, + ], + }; + + const { waitForResult } = await contract.functions + .types_struct_with_array_of_enums(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_with_nested_array', () => { + it('should encode/decode just fine', async () => { + const INPUT_STRUCT = { a: { a: 10 }, b: 'A' }; + const input: StructWithNestedArrayInput = { a: [INPUT_STRUCT, INPUT_STRUCT] }; + // @ts-expect-error: Custom matcher 'toEqualBn' + const EXPECTED_STRUCT = { a: { a: expect.toEqualBn(20) }, b: 'B' }; + const EXPECTED = { a: [EXPECTED_STRUCT, EXPECTED_STRUCT] }; + + const { waitForResult } = await contract.functions + .types_struct_with_nested_array(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(EXPECTED); + }); + }); + + describe('types_struct_with_nested_tuple', () => { + it('should encode/decode just fine', async () => { + const input: StructWithNestedTupleInput = { a: [10, { a: { a: 20 } }, 'ABC'] }; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected = { a: [30, { a: { a: expect.toEqualBn(40) } }, 'CBA'] }; + + const { waitForResult } = await contract.functions + .types_struct_with_nested_tuple(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_struct_with_nested_struct', () => { + it('should encode/decode just fine', async () => { + const input = { a: { a: { a: 10 }, b: 20 } }; + const expected = { a: { a: { a: 30 }, b: 40 } }; + + const { waitForResult } = await contract.functions + .types_struct_with_nested_struct(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe.todo('types_struct_with_multiple_struct_params', () => { + it('should encode/decode just fine', async () => { + const STRUCT_A = { propA1: 10 }; + const STRUCT_B = { propB1: STRUCT_A, propB2: 20 }; + + const INPUT_X = STRUCT_A; + const INPUT_Y = STRUCT_B; + const INPUT_Z: StructCInput = { + propC1: STRUCT_A, + propC2: [STRUCT_B], + propC3: { + propD1: [{ propE1: STRUCT_A, propE2: STRUCT_B, propE3: 30 }], + propD2: 40, + propD3: { propF1: 50, propF2: 'A' }, + }, + }; + + const { waitForResult } = await contract.functions + .types_struct_with_multiple_struct_params(INPUT_X, INPUT_Y, INPUT_Z) + .call(); + + const { value } = await waitForResult(); + // expect(value).toEqual(expected); + }); + }); + + describe.todo('types_struct_with_complex_nested_struct'); + + describe('types_struct_with_single_option', () => { + it('should encode/decode just fine', async () => { + const input: StructWithSingleOptionInput = { + a: { + a: [1, undefined, 2, undefined, 3], + }, + }; + const expected: StructWithSingleOptionOutput = { + a: undefined, + }; + + const { waitForResult } = await contract.functions + .types_struct_with_single_option(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Tuples + */ + describe('types_tuple', () => { + it('should encode/decode just fine', async () => { + const input = [1, 2, 3] as [number, number, number]; + const expected = [3, 2, 1] as [number, number, number]; + + const { waitForResult } = await contract.functions.types_tuple(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_tuple_complex', () => { + it('should encode/decode just fine', async () => { + const input = [1, { a: { a: 10 } }, 'ABC'] as [ + BigNumberish, + StructSingleGenericInput>, + string, + ]; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected = [3, { a: { a: expect.toEqualBn(30) } }, 'CBA']; + + const { waitForResult } = await contract.functions.types_tuple_complex(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_tuple_with_native_types', () => { + it('should encode/decode just fine', async () => { + const A: AssetId = { + bits: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + }; + const B: AssetId = { + bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + const input = [A, B, true] as [AssetIdInput, AssetIdInput, boolean]; + const expected = [B, A, false]; + + const { waitForResult } = await contract.functions + .types_tuple_with_native_types(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_alias_tuple_with_native_types', () => { + it('should encode/decode just fine', async () => { + const A: AssetId = { + bits: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + }; + const B: AssetId = { + bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + const input = [A, B, true] as [AssetIdInput, AssetIdInput, boolean]; + const expected = [B, A, false]; + + const { waitForResult } = await contract.functions + .types_alias_tuple_with_native_types(input) + .call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Enums + */ + describe('types_enum', () => { + it('should encode/decode just fine', async () => { + const input = EnumWithNativeInput.Checked; + const expected = EnumWithNativeInput.Pending; + + const { waitForResult } = await contract.functions.types_enum(input).call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + }); + + describe('types_enum_with_builtin_type', () => { + it('should encode/decode just fine', async () => { + const input: EnumWithBuiltinTypeInput = { a: true }; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected: EnumWithBuiltinTypeOutput = { b: expect.toEqualBn(20) }; + + const { waitForResult } = await contract.functions.types_enum_with_builtin_type(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_enum_with_vector', () => { + it('should encode/decode just fine', async () => { + const input: EnumWithVectorInput = { a: 10 }; + const expected: EnumWithVectorOutput = { b: [1, 2, 3] }; + + const { waitForResult } = await contract.functions.types_enum_with_vector(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_generic_enum', () => { + it('should encode/decode just fine', async () => { + const input = { a: 10 }; + const expected = { b: 20 }; + + const { waitForResult } = await contract.functions.types_generic_enum(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_enum_external', () => { + it('should encode/decode just fine', async () => { + const input = ExternalEnumInput.A; + const expected = ExternalEnumInput.B; + + const { waitForResult } = await contract.functions.types_enum_external(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_enum_with_structs', () => { + it('should encode/decode just fine', async () => { + const input = { a: EnumWithNativeInput.Checked }; + const expected = { b: { a: true, b: 10 } }; + + const { waitForResult } = await contract.functions.types_enum_with_structs(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Vectors + */ + describe('types_vector_u8', () => { + it('should encode/decode just fine', async () => { + const input = [1, 2, 3]; + const expected = [3, 2, 1]; + + const { waitForResult } = await contract.functions.types_vector_u8(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_vector_boolean', () => { + it('should encode/decode just fine', async () => { + const input = [true, false, true, false]; + const expected = [false, true, false, true]; + + const { waitForResult } = await contract.functions.types_vector_boolean(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_vector_inside_vector', () => { + it('should encode/decode just fine', async () => { + const input = [[1, 2, 3]]; + const expected = [ + [3, 2, 1], + [6, 5, 4], + ]; + + const { waitForResult } = await contract.functions.types_vector_inside_vector(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_vector_with_struct', () => { + it('should encode/decode just fine', async () => { + const input = [{ a: true, b: 10 }]; + const expected = [{ a: false, b: 30 }]; + + const { waitForResult } = await contract.functions.types_vector_with_struct(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + 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 { waitForResult } = await contract.functions.types_vector_option(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Options + */ + describe('types_option', () => { + it('should encode/decode just fine', async () => { + const input: Option = 10; // Some + const expected: Option = undefined; // None + + const { waitForResult } = await contract.functions.types_option(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_option_struct', () => { + it('should encode/decode just fine', async () => { + const input: Option = { + a: true, + b: 10, + }; + const expected: Option = undefined; + + const { waitForResult } = await contract.functions.types_option_struct(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Native types + */ + describe('types_identity_address', () => { + it('should encode/decode just fine', async () => { + const input: IdentityInput = { + Address: { bits: '0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + }; + const expected: IdentityOutput = { + Address: { bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' }, + }; + + const { waitForResult } = await contract.functions.types_identity_address(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_identity_contract_id', () => { + it('should encode/decode just fine', async () => { + const input: IdentityInput = { + ContractId: { bits: '0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + }; + const expected: IdentityOutput = { + ContractId: { bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' }, + }; + + const { waitForResult } = await contract.functions.types_identity_contract_id(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_address', () => { + it('should encode/decode just fine', async () => { + const input = { bits: '0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }; + const expected = { + bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + + const { waitForResult } = await contract.functions.types_address(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_contract_id', () => { + it('should encode/decode just fine', async () => { + const input = { bits: '0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }; + const expected = { + bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + + const { waitForResult } = await contract.functions.types_contract_id(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_asset_id', () => { + it('should encode/decode just fine', async () => { + const input: AssetId = { + bits: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + }; + const expected: AssetId = { + bits: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + + const { waitForResult } = await contract.functions.types_asset_id(input).call(); + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + describe('types_evm_address', () => { + it('should encode/decode just fine', async () => { + const input: EvmAddress = { + bits: '0x000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + }; + const expected = { + bits: '0x000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + }; + + const { waitForResult } = await contract.functions.types_evm_address(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_result', () => { + it('should accept result just fine [Ok - 10]', async () => { + const input: Result = { + Ok: 10, + }; + const expected: Result = { + // @ts-expect-error: Custom matcher 'toEqualBn' + Ok: expect.toEqualBn(2), + }; + + const { waitForResult } = await contract.functions.types_result(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + + it('should accept result just fine [Err - divide by zero]', async () => { + const input: Result = { + Ok: 0, + }; + const expected: Result = { + Err: 'DivisError', + }; + + const { waitForResult } = await contract.functions.types_result(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + + it('should accept result just fine [Err - 10]', async () => { + const input: Result = { + Err: 10, + }; + const expected: Result = { + Err: 'InputError', + }; + + const { waitForResult } = await contract.functions.types_result(input).call(); + + const { value } = await waitForResult(); + expect(value).toEqual(expected); + }); + }); + + /** + * Void + */ + describe('types_void', () => { + it('should encode/decode just fine', async () => { + const input = undefined; + const expected = undefined; + + const { waitForResult } = await contract.functions.types_void(input).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + + it('should encode/decode just fine [omit optional args]', async () => { + const expected = undefined; + + const { waitForResult } = await contract.functions.types_void().call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('types_void_then_value', () => { + it('should encode/decode just fine', async () => { + const inputX = undefined; + const inputY = 10; + const expected = undefined; + + const { waitForResult } = await contract.functions + .types_void_then_value(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toBe(expected); + }); + }); + + describe('types_value_then_void', () => { + it('should encode/decode just fine', async () => { + const inputX = 10; + const inputY = undefined; + const { waitForResult } = await contract.functions + .types_value_then_void(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toBeUndefined(); + }); + + it('should encode/decode just fine [omitting optional args]', async () => { + const inputX = 10; + + const { waitForResult } = await contract.functions.types_value_then_void(inputX).call(); + + const { value } = await waitForResult(); + expect(value).toBeUndefined(); + }); + }); + + describe('types_value_then_void_then_value', () => { + it('should encode/decode just fine', async () => { + const inputX = 10; + const inputY = undefined; + const inputZ = 20; + + const { waitForResult } = await contract.functions + .types_value_then_void_then_value(inputX, inputY, inputZ) + .call(); + + const { value } = await waitForResult(); + expect(value).toBeUndefined(); + }); + }); + + describe('types_value_then_value_then_void_then_void', () => { + it('should encode/decode just fine', async () => { + const inputX = 10; + const inputY = 20; + const inputZ = undefined; + const inputA = undefined; + + const { waitForResult } = await contract.functions + .types_value_then_value_then_void_then_void(inputX, inputY, inputZ, inputA) + .call(); + + const { value } = await waitForResult(); + expect(value).toBeUndefined(); + }); + + it('should encode/decode just fine [omitting optional args]', async () => { + const inputX = 10; + const inputY = 20; + + const { waitForResult } = await contract.functions + .types_value_then_value_then_void_then_void(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toBeUndefined(); + }); + }); + + /** + * Multi-arg + */ + describe('multi_arg_u64_u64', () => { + it('should encode/decode just fine', async () => { + const inputX = 1; + const inputY = 2; + // @ts-expect-error: Custom matcher 'toEqualBn' + const expected = expect.toEqualBn(3); + + const { waitForResult } = await contract.functions.multi_arg_u64_u64(inputX, inputY).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_b256_bool', () => { + it('should encode/decode just fine', async () => { + const inputX = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const inputY = true; + const expected = [ + '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + false, + ]; + + const { waitForResult } = await contract.functions.multi_arg_b256_bool(inputX, inputY).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_vector_vector', () => { + it('should encode/decode just fine', async () => { + const inputX = [1, 2, 3]; + const inputY = [4, 5, 6]; + const expected = [ + [7, 8, 9], + [10, 11, 12], + ]; + + const { waitForResult } = await contract.functions + .multi_arg_vector_vector(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_vector_b256', () => { + it('should encode/decode just fine', async () => { + const inputX = [1, 2, 3]; + const inputY = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const expected = [ + [7, 8, 9], + '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + ]; + + const { waitForResult } = await contract.functions + .multi_arg_vector_b256(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_struct_vector', () => { + it('should encode/decode just fine', async () => { + const inputX = { a: true, b: 1 }; + const inputY = [1, 2, 3]; + const expected = [{ a: false, b: 2 }, [4, 5, 6]]; + + const { waitForResult } = await contract.functions + .multi_arg_struct_vector(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_u64_struct', () => { + it('should encode/decode just fine', async () => { + const inputX = bn(99); + const inputY: StructSimpleInput = { a: true, b: 51 }; + const expected = [bn(3), { a: false, b: 4 }]; + + const { waitForResult } = await contract.functions + .multi_arg_u64_struct(inputX, inputY) + .call(); + + const { value } = await waitForResult(); + expect(JSON.stringify(value)).toEqual(JSON.stringify(expected)); + }); + }); + + describe('multi_arg_str_str', () => { + it('should encode/decode just fine', async () => { + const inputX = 'Input'; + const inputY = 'False'; + + const expected = ['Fuuel', 'Niice']; + + const { waitForResult } = await contract.functions.multi_arg_str_str(inputX, inputY).call(); + + const { value } = await waitForResult(); + expect(value).toStrictEqual(expected); + }); + }); + + describe('multi_arg_u32_vector_vector', () => { + it('should encode/decode just fine', async () => { + const inputX = 1; + const inputY = [bn(10020), bn(1231231), bn(777657)]; + const inputZ = [bn(99), bn(101)]; + + const expected = [2, [bn(7), bn(8), bn(9)], [bn(10), bn(11), bn(12)]]; + + const { waitForResult } = await contract.functions + .multi_arg_u32_vector_vector(inputX, inputY, inputZ) + .call(); + + const { value } = await waitForResult(); + expect(JSON.stringify(value)).toEqual(JSON.stringify(expected)); + }); + }); + + describe('multi_arg_complex', () => { + it('should encode/decode just fine', async () => { + const inputX: StructDoubleGenericInput<[string, string, string], number> = { + a: [ + '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + '0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc', + ], + b: 10, + }; + + const inputY: [ + StructDoubleGenericInput, + StructDoubleGenericInput, + StructDoubleGenericInput, + StructDoubleGenericInput, + ] = [ + { a: bn(99), b: false }, + { a: bn(199), b: false }, + { a: bn(2000), b: false }, + { a: bn(31), b: true }, + ]; + + const inputZ: [string, boolean] = ['Input', true]; + + const inputA = { a: true, b: 10 }; + + const expectedX: StructDoubleGenericInput<[string, string, string], number> = { + a: [ + '0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ], + b: 99, + }; + + const expectedY: [ + StructDoubleGenericInput, + StructDoubleGenericInput, + StructDoubleGenericInput, + StructDoubleGenericInput, + ] = [ + { a: bn(11), b: true }, + { a: bn(99), b: true }, + { a: bn(567), b: true }, + { a: bn(971), b: false }, + ]; + + const expectedZ: [string, boolean] = ['tupni', false]; + + const expectedA = { + a: false, + b: 57, + }; + + const { waitForResult } = await contract.functions + .multi_arg_complex(inputX, inputY, inputZ, inputA) + .call(); + + const { value } = await waitForResult(); + expect(JSON.stringify(value)).toEqual( + JSON.stringify([expectedX, expectedY, expectedZ, expectedA]) + ); + }); + }); +}); diff --git a/packages/fuel-gauge/src/abi/constants.ts b/packages/fuel-gauge/src/abi/constants.ts new file mode 100644 index 00000000000..a81232d91c1 --- /dev/null +++ b/packages/fuel-gauge/src/abi/constants.ts @@ -0,0 +1,63 @@ +import { bn } from 'fuels'; + +export const U8_MIN = 0; +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_MIN = 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_MIN = 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_MIN = 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_MIN = 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/fuel-gauge/src/abi/vitest.matcher.ts b/packages/fuel-gauge/src/abi/vitest.matcher.ts new file mode 100644 index 00000000000..32a1cd3b4a8 --- /dev/null +++ b/packages/fuel-gauge/src/abi/vitest.matcher.ts @@ -0,0 +1,20 @@ +import { bn } from 'fuels'; +import type { BNInput } from 'fuels'; + +export const toEqualBn = (_received: BNInput, _argument: BNInput) => { + const received = bn(_received); + const argument = bn(_argument); + + const pass = received.eq(argument); + + if (pass) { + return { + message: () => `Expected ${received.toString()} not to equal ${argument.toString()}`, + pass: true, + }; + } + return { + message: () => `expected ${received.toString()} to equal ${argument.toString()}`, + pass: false, + }; +}; diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml index bb2f195d352..4d78a28388b 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml +++ b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "abi-contract", "advanced-logging", "advanced-logging-abi", "advanced-logging-other-contract", diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/Forc.toml new file mode 100644 index 00000000000..7e941f84b42 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "abi-contract" + +[dependencies] +abi-library = { path = "../abi-library" } diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/data_structures.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/data_structures.sw new file mode 100644 index 00000000000..192d57f8259 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/data_structures.sw @@ -0,0 +1,137 @@ +library; + +pub struct Configurables { + pub U8_VALUE: u8, + pub BOOL_VALUE: bool, + pub B256_VALUE: b256, + pub OPTION_U8_VALUE: Option, + pub GENERIC_STRUCT_VALUE: StructDoubleGeneric, u32>, +} + +pub enum EnumWithNative { + pub Checked: (), + pub Pending: (), +} + +pub enum EnumWithVector { + pub a: u8, + pub b: Vec, +} + +pub enum EnumWithBuiltinType { + pub a: bool, + pub b: u64, +} + +pub enum EnumDoubleGeneric { + pub a: T1, + pub b: T2, +} + +pub enum EnumWithStructs { + pub a: EnumWithNative, + pub b: StructSimple, + pub c: StructDoubleGeneric, +} + +pub struct StructSimple { + pub a: bool, + pub b: u32, +} + +pub struct StructWithEnumArray { + pub a: [EnumWithNative; 3], +} + +pub struct StructWithMultiOption { + pub a: [Option; 5], +} + +pub struct StructWithSingleOption { + pub a: Option, +} + +pub struct StructWithVector { + pub a: u8, + pub b: Vec, +} + +pub struct StructSingleGeneric { + pub a: T, +} + +pub struct StructDoubleGeneric { + pub a: T1, + pub b: T2, +} + +pub struct StructGenericWithEnum { + pub a: T1, + pub b: EnumDoubleGeneric, +} + +pub struct StructWithImplicitGenerics { + pub a: [E; 3], + pub b: (E, F), +} + +pub struct StructWithGenericArray { + pub a: [StructDoubleGeneric; 3], +} + +pub struct StructWithNestedArray { + pub a: [StructDoubleGeneric, str[1]>; 2], +} + +pub struct StructWithNestedTuple { + pub a: (u8, StructSingleGeneric>, str[3]), +} + +pub struct StructWithNestedStruct { + pub a: StructDoubleGeneric, u16>, +} + +pub struct StructA { + pub propA1: u8, +} + +pub struct StructB { + pub propB1: StructA, + pub propB2: u16, +} + +pub struct StructC { + pub propC1: StructA, + pub propC2: Vec, + pub propC3: StructD>, + // propC4: Vec>>, + // propC5: Vec>>>, +} + +pub struct StructD { + pub propD1: Vec>, + pub propD2: U, + pub propD3: V, +} + +pub struct StructE { + pub propE1: StructA, + pub propE2: StructB, + pub propE3: T, +} + +pub struct StructF { + pub propF1: u64, + pub propF2: T, +} + +pub struct StructG { + pub propG1: u8, +} + +pub enum MyContractError { + pub DivisionByZero: (), +} + +pub type TupleWithNativeAssets = (AssetId, AssetId, bool); + diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/equality.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/equality.sw new file mode 100644 index 00000000000..d370a24cc28 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/equality.sw @@ -0,0 +1,481 @@ +library; + +use ::data_structures::*; +use core::ops::Eq; + +impl Eq for [u8; 4] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] && self[3] == other[3] + } +} + +impl Eq for StructSimple { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for [StructSimple; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} + +impl Eq for StructSingleGeneric { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for [b256; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} + +impl Eq for (b256, u8) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Eq for str[1] { + fn eq(self, other: Self) -> bool { + from_str_array(self) == from_str_array(other) + } +} + +impl Eq for str[3] { + fn eq(self, other: Self) -> bool { + from_str_array(self) == from_str_array(other) + } +} + +impl Eq for str[5] { + fn eq(self, other: Self) -> bool { + from_str_array(self) == from_str_array(other) + } +} + +impl Eq for StructDoubleGeneric, str[1]> { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for StructDoubleGeneric { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for StructDoubleGeneric { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for [StructDoubleGeneric; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} + +impl Eq for [StructDoubleGeneric, str[1]>; 2] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} + +impl Eq for [StructDoubleGeneric; 4] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] && self[3] == other[3] + } +} + +impl Eq for StructSingleGeneric<[b256; 3]> { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructDoubleGeneric<[b256; 3], u8> { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructDoubleGeneric, u8> { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for [Vec; 1] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] + } +} + +impl Eq for (u8, u8, u8) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 + } +} + +impl Eq for StructSingleGeneric> { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for (u8, StructSingleGeneric>, str[3]) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 + } +} + +impl Eq for (AssetId, AssetId, bool) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 && self.2 == other.2 + } +} + +impl Eq for StructSingleGeneric { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for (bool, u64) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Eq for (str[5], bool) { + fn eq(self, other: Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Eq for StructSingleGeneric<(bool, u64)> { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructWithNestedArray { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for EnumDoubleGeneric { + fn eq(self, other: Self) -> bool { + match (self, other) { + (EnumDoubleGeneric::a(a), EnumDoubleGeneric::a(b)) => a == b, + (EnumDoubleGeneric::b(a), EnumDoubleGeneric::b(b)) => a == b, + _ => false, + } + } +} + +impl Eq for StructGenericWithEnum { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for StructWithNestedTuple { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructDoubleGeneric, u16> { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for StructWithNestedStruct { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructA { + fn eq(self, other: Self) -> bool { + self.propA1 == other.propA1 + } +} + +impl Eq for StructB { + fn eq(self, other: Self) -> bool { + self.propB1 == other.propB1 && self.propB2 == other.propB2 + } +} + +impl Eq for StructE { + fn eq(self, other: Self) -> bool { + self.propE1 == other.propE1 && self.propE2 == other.propE2 && self.propE3 == other.propE3 + } +} + +impl Eq for Vec> { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for StructF { + fn eq(self, other: Self) -> bool { + self.propF1 == other.propF1 && self.propF2 == other.propF2 + } +} + +impl Eq for StructD> { + fn eq(self, other: Self) -> bool { + self.propD1 == other.propD1 && self.propD2 == other.propD2 && self.propD3 == other.propD3 + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for StructC { + fn eq(self, other: Self) -> bool { + self.propC1 == other.propC1 && self.propC2 == other.propC2 && self.propC3 == other.propC3 + } +} + +impl Eq for EnumWithNative { + fn eq(self, other: Self) -> bool { + match (self, other) { + (EnumWithNative::Checked, EnumWithNative::Checked) => true, + (EnumWithNative::Pending, EnumWithNative::Pending) => true, + _ => false, + } + } +} + +impl Eq for EnumWithBuiltinType { + fn eq(self, other: Self) -> bool { + match (self, other) { + (EnumWithBuiltinType::a(a), EnumWithBuiltinType::a(b)) => a == b, + (EnumWithBuiltinType::b(a), EnumWithBuiltinType::b(b)) => a == b, + _ => false, + } + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for EnumWithVector { + fn eq(self, other: Self) -> bool { + match (self, other) { + (EnumWithVector::a(a), EnumWithVector::a(b)) => a == b, + (EnumWithVector::b(a), EnumWithVector::b(b)) => a == b, + _ => false, + } + } +} + +impl Eq for StructDoubleGeneric { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for EnumWithStructs { + fn eq(self, other: Self) -> bool { + match (self, other) { + (EnumWithStructs::a(a), EnumWithStructs::a(b)) => a == b, + (EnumWithStructs::b(a), EnumWithStructs::b(b)) => a == b, + (EnumWithStructs::c(a), EnumWithStructs::c(b)) => a == b, + _ => false, + } + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for Vec> { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for [Option; 5] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] && self[3] == other[3] && self[4] == other[4] + } +} + +impl Eq for StructWithMultiOption { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for Vec { + fn eq(self, other: Self) -> bool { + if self.len() != other.len() { + return false; + } + let mut i = 0; + while i < self.len() { + if self.get(i).unwrap() != other.get(i).unwrap() { + return false; + } + i += 1; + } + true + } +} + +impl Eq for StructWithGenericArray { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructWithImplicitGenerics { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for StructWithVector { + fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b + } +} + +impl Eq for [EnumWithNative; 3] { + fn eq(self, other: Self) -> bool { + self[0] == other[0] && self[1] == other[1] && self[2] == other[2] + } +} + +impl Eq for StructWithEnumArray { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} + +impl Eq for StructWithSingleOption { + fn eq(self, other: Self) -> bool { + self.a == other.a + } +} 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 new file mode 100644 index 00000000000..48e4e413d63 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/main.sw @@ -0,0 +1,1075 @@ +contract; + +mod data_structures; +mod equality; +mod utils; + +use data_structures::*; +use equality::*; +use utils::*; + +use abi_library::ExternalStruct; +use abi_library::ExternalEnum; +use std::vm::evm::evm_address::EvmAddress; +use std::b512::B512; +use std::string::String; +use std::bytes::Bytes; + +fn divide(numerator: u64, denominator: u64) -> Result { + if (denominator == 0) { + return Err(MyContractError::DivisionByZero); + } else { + Ok(numerator / denominator) + } +} + +abi AbiContract { + fn configurables() -> Configurables; + + fn types_u8(x: u8) -> u8; + fn types_u16(x: u16) -> u16; + fn types_u32(x: u32) -> u32; + fn types_u64(x: u64) -> u64; + fn types_u256(x: u256) -> u256; + fn types_bool(x: bool) -> bool; + fn types_b256(x: b256) -> b256; + fn types_b512(x: B512) -> B512; + fn types_bytes(x: Bytes) -> Bytes; + + fn types_str(x: str[5]) -> str[5]; + fn types_str_slice(x: str) -> str; + fn types_raw_slice(x: raw_slice) -> raw_slice; + fn types_std_string(x: String) -> String; + + fn types_array(x: [u8; 4]) -> [u8; 4]; + fn types_array_struct(x: [StructSimple; 3]) -> [StructSimple; 3]; + fn types_array_with_generic_struct( + x: [StructDoubleGeneric, str[1]>; 2], + ) -> [StructDoubleGeneric, str[1]>; 2]; + fn types_array_with_vector(x: [Vec; 1]) -> [Vec; 1]; + + fn types_struct_simple(x: StructSimple) -> StructSimple; + fn types_struct_generic(x: StructSingleGeneric) -> StructSingleGeneric; + fn types_struct_with_tuple( + x: StructSingleGeneric<(bool, u64)>, + ) -> StructSingleGeneric<(bool, u64)>; + fn types_struct_double_generic( + x: StructGenericWithEnum, + ) -> StructGenericWithEnum; + fn types_struct_external(x: ExternalStruct) -> ExternalStruct; + fn types_struct_with_implicit_generics( + x: StructWithImplicitGenerics, + ) -> StructWithImplicitGenerics; + fn types_struct_with_array(x: StructWithGenericArray) -> StructWithGenericArray; + fn types_struct_with_vector(x: StructWithVector) -> StructWithVector; + fn types_struct_with_array_of_enums(x: StructWithEnumArray) -> StructWithEnumArray; + fn types_struct_with_nested_array(x: StructWithNestedArray) -> StructWithNestedArray; + fn types_struct_with_nested_tuple(x: StructWithNestedTuple) -> StructWithNestedTuple; + fn types_struct_with_nested_struct(x: StructWithNestedStruct) -> StructWithNestedStruct; + fn types_struct_with_multiple_struct_params(x: StructA, y: StructB, z: StructC) -> bool; + fn types_struct_with_complex_nested_struct(x: StructD>>) -> bool; + fn types_struct_with_single_option(x: StructWithSingleOption) -> StructWithSingleOption; + + fn types_tuple(x: (u8, u8, u8)) -> (u8, u8, u8); + fn types_tuple_complex( + x: (u8, StructSingleGeneric>, str[3]), + ) -> (u8, StructSingleGeneric>, str[3]); + fn types_tuple_with_native_types(x: (AssetId, AssetId, bool)) -> (AssetId, AssetId, bool); + fn types_alias_tuple_with_native_types(x: TupleWithNativeAssets) -> TupleWithNativeAssets; + + fn types_enum(x: EnumWithNative) -> EnumWithNative; + fn types_enum_with_builtin_type(x: EnumWithBuiltinType) -> EnumWithBuiltinType; + fn types_enum_with_vector(x: EnumWithVector) -> EnumWithVector; + fn types_generic_enum(x: EnumDoubleGeneric) -> EnumDoubleGeneric; + fn types_enum_external(x: ExternalEnum) -> ExternalEnum; + fn types_enum_with_structs(x: EnumWithStructs) -> EnumWithStructs; + + fn types_vector_u8(x: Vec) -> Vec; + fn types_vector_boolean(x: Vec) -> Vec; + fn types_vector_inside_vector(x: Vec>) -> Vec>; + fn types_vector_with_struct(x: Vec) -> Vec; + fn types_vector_option(x: Vec) -> Vec; + + fn types_option(x: Option) -> Option; + fn types_option_struct(x: Option) -> Option; + + fn types_identity_address(x: Identity) -> Identity; + fn types_identity_contract_id(x: Identity) -> Identity; + fn types_address(x: Address) -> Address; + fn types_contract_id(x: ContractId) -> ContractId; + fn types_asset_id(x: AssetId) -> AssetId; + fn types_evm_address(x: EvmAddress) -> EvmAddress; + fn types_result(x: Result) -> Result; + + fn types_void(x: ()) -> (); + fn types_void_then_value(x: (), y: u8) -> (); + fn types_value_then_void(x: u8, y: ()) -> (); + fn types_value_then_void_then_value(x: u8, y: (), z: u8) -> (); + fn types_value_then_value_then_void_then_void(x: u8, y: u8, z: (), a: ()) -> (); + + fn multi_arg_u64_u64(x: u64, y: u64) -> u64; + fn multi_arg_b256_bool(x: b256, y: bool) -> (b256, bool); + fn multi_arg_vector_vector(x: Vec, y: Vec) -> (Vec, Vec); + fn multi_arg_vector_b256(x: Vec, y: b256) -> (Vec, b256); + fn multi_arg_struct_vector(x: StructSimple, y: Vec) -> (StructSimple, Vec); + fn multi_arg_u64_struct(x: u64, y: StructSimple) -> (u64, StructSimple); + fn multi_arg_str_str(x: str[5], y: str[5]) -> (str[5], str[5]); + fn multi_arg_u32_vector_vector(x: u32, y: Vec, z: Vec) -> (u32, Vec, Vec); + fn multi_arg_complex( + x: StructDoubleGeneric<[b256; 3], u8>, + y: [StructDoubleGeneric; 4], + z: (str[5], bool), + a: StructSimple, + ) -> (StructDoubleGeneric<[b256; 3], u8>, [StructDoubleGeneric; 4], (str[5], bool), StructSimple); +} + +configurable { + U8_VALUE: u8 = 10, + BOOL_VALUE: bool = true, + B256_VALUE: b256 = 0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96, + OPTION_U8_VALUE: Option = Option::None, + GENERIC_STRUCT_VALUE: StructDoubleGeneric, u32> = StructDoubleGeneric { + a: StructDoubleGeneric { a: 4, b: 257 }, + b: 57000, + }, +} + +impl AbiContract for Contract { + fn configurables() -> Configurables { + Configurables { + U8_VALUE: U8_VALUE, + BOOL_VALUE: BOOL_VALUE, + B256_VALUE: B256_VALUE, + OPTION_U8_VALUE: OPTION_U8_VALUE, + GENERIC_STRUCT_VALUE: GENERIC_STRUCT_VALUE, + } + } + + fn types_u8(x: u8) -> u8 { + assert_eq(x, 8); + 255 + } + + fn types_u16(x: u16) -> u16 { + assert_eq(x, 16); + 65535 + } + + fn types_u32(x: u32) -> u32 { + assert_eq(x, 32); + 4294967295 + } + + fn types_u64(x: u64) -> u64 { + assert_eq(x, 64); + 4294967295000 + } + + fn types_u256(x: u256) -> u256 { + assert_eq(x, 256); + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu256 + } + + fn types_bool(x: bool) -> bool { + const INPUT: bool = false; + assert_eq(x, INPUT); + + const EXPECTED: bool = true; + return EXPECTED + } + + fn types_b256(x: b256) -> b256 { + const INPUT: b256 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + assert_eq(x, INPUT); + + const EXPECTED: b256 = 0x0000000000000000000000000000000000000000000000000000000000000000; + return EXPECTED + } + + fn types_b512(x: B512) -> B512 { + // HIGH_BIT and **LOW_BIT** + const HI_BITS = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c; + const LO_BITS = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; + const INPUT: B512 = B512::from((HI_BITS, LO_BITS)); + assert_eq(x, INPUT); + + // HIGH_BIT and **LOW_BIT2** + const LO_BITS2 = 0x54ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d; + const EXPECTED: B512 = B512::from((HI_BITS, LO_BITS2)); + return INPUT + } + + fn types_bytes(x: Bytes) -> Bytes { + let mut INPUT = Bytes::new(); + INPUT.push(1u8); + INPUT.push(2u8); + INPUT.push(3u8); + assert_eq(x, INPUT); + + let mut EXPECTED = Bytes::new(); + EXPECTED.push(3u8); + EXPECTED.push(2u8); + EXPECTED.push(1u8); + return EXPECTED + // let EXPECTED = Bytes::from_hex("0xabcdef9012345678"); + + } + + /** + * Strings + */ + fn types_str(x: str[5]) -> str[5] { + const INPUT: str[5] = __to_str_array("Input"); + assert_eq(x, INPUT); + + const EXPECTED: str[5] = __to_str_array("Hello"); + return EXPECTED; + } + + fn types_str_slice(x: str) -> str { + let INPUT = "Input"; + assert(x == INPUT); + + let EXPECTED = "Output"; + return EXPECTED; + } + + fn types_std_string(x: String) -> String { + let INPUT = "Input"; + assert_eq(x, String::from_ascii_str(INPUT)); + + let EXPECTED = "Output"; + return String::from_ascii_str(EXPECTED); + } + + fn types_raw_slice(x: raw_slice) -> raw_slice { + let vec: Vec = Vec::from(x); + require(vec.len() == 3, "raw slice len is not 3"); + require(vec.get(2).unwrap() == 3, "expected 3rd slice entry to be 3"); + require(vec.get(1).unwrap() == 2, "expected 2nd slice entry to be 2"); + require(vec.get(0).unwrap() == 1, "expected 1st slice entry to be 1"); + + let mut vec_expected: Vec = Vec::new(); + vec_expected.push(4); + vec_expected.push(3); + vec_expected.push(2); + vec_expected.push(1); + let EXPECTED = vec_expected.as_raw_slice(); + return EXPECTED + } + + /** + * Arrays + */ + fn types_array(x: [u8; 4]) -> [u8; 4] { + const INPUT: [u8; 4] = [1, 2, 3, 4]; + assert(x == INPUT); + + const EXPECTED: [u8; 4] = [4, 3, 2, 1]; + return EXPECTED + } + + fn types_array_struct(x: [StructSimple; 3]) -> [StructSimple; 3] { + const INPUT_STRUCT_1: StructSimple = StructSimple { a: true, b: 10 }; + const INPUT = [INPUT_STRUCT_1, INPUT_STRUCT_1, INPUT_STRUCT_1]; + assert(x == INPUT); + + const EXPECTED_STRUCT: StructSimple = StructSimple { + a: false, + b: 30, + }; + [EXPECTED_STRUCT, EXPECTED_STRUCT, EXPECTED_STRUCT] + } + + fn types_array_with_generic_struct( + x: [StructDoubleGeneric, str[1]>; 2], + ) -> [StructDoubleGeneric, str[1]>; 2] { + const INPUT_STRUCT: StructDoubleGeneric, str[1]> = StructDoubleGeneric { + a: StructSingleGeneric { a: 10 }, + b: __to_str_array("A"), + }; + const INPUT = [INPUT_STRUCT, INPUT_STRUCT]; + assert(x == INPUT); + + const EXPECTED_STRUCT: StructDoubleGeneric, str[1]> = StructDoubleGeneric { + a: StructSingleGeneric { a: 20 }, + b: __to_str_array("B"), + }; + [EXPECTED_STRUCT, EXPECTED_STRUCT] + } + + fn types_array_with_vector(x: [Vec; 1]) -> [Vec; 1] { + let INPUT_VEC = vec_u32_from([1, 2, 3]); + let INPUT = [INPUT_VEC]; + assert(x == INPUT); + + let EXPECTED_VEC: Vec = vec_u32_from([3, 2, 1]); + let EXPECTED: [Vec; 1] = [EXPECTED_VEC]; + return EXPECTED + } + + /** + * Tuples + */ + fn types_tuple(x: (u8, u8, u8)) -> (u8, u8, u8) { + const INPUT: (u8, u8, u8) = (1, 2, 3); + assert(x == INPUT); + + const EXPECTED: (u8, u8, u8) = (3, 2, 1); + return EXPECTED + } + + fn types_tuple_complex( + x: (u8, StructSingleGeneric>, str[3]), + ) -> (u8, StructSingleGeneric>, str[3]) { + let INPUT: (u8, StructSingleGeneric>, str[3]) = ( + 1, + StructSingleGeneric { + a: StructSingleGeneric { a: 10 }, + }, + __to_str_array("ABC"), + ); + assert(x == INPUT); + + let EXPECTED: (u8, StructSingleGeneric>, str[3]) = ( + 3, + StructSingleGeneric { + a: StructSingleGeneric { a: 30 }, + }, + __to_str_array("CBA"), + ); + return EXPECTED + } + + fn types_tuple_with_native_types(x: (AssetId, AssetId, bool)) -> (AssetId, AssetId, bool) { + const A = AssetId::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + const B = AssetId::from(0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB); + const C = true; + const INPUT: (AssetId, AssetId, bool) = (A, B, C); + assert(x == INPUT); + + const F = false; + const EXPECTED: (AssetId, AssetId, bool) = (B, A, F); + return EXPECTED + } + + fn types_alias_tuple_with_native_types(x: TupleWithNativeAssets) -> TupleWithNativeAssets { + const A = AssetId::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + const B = AssetId::from(0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB); + const C = true; + const INPUT: (AssetId, AssetId, bool) = (A, B, C); + assert(x == INPUT); + + const F = false; + const EXPECTED: (AssetId, AssetId, bool) = (B, A, F); + return EXPECTED + } + + /** + * Structs + */ + fn types_struct_simple(x: StructSimple) -> StructSimple { + const INPUT: StructSimple = StructSimple { a: true, b: 10 }; + assert(x == INPUT); + + const EXPECTED: StructSimple = StructSimple { + a: false, + b: 30, + }; + return EXPECTED + } + + fn types_struct_generic(x: StructSingleGeneric) -> StructSingleGeneric { + const INPUT: StructSingleGeneric = StructSingleGeneric { a: 10 }; + assert(x == INPUT); + + const EXPECTED: StructSingleGeneric = StructSingleGeneric { a: 20 }; + return EXPECTED + } + + fn types_struct_with_tuple( + x: StructSingleGeneric<(bool, u64)>, + ) -> StructSingleGeneric<(bool, u64)> { + const INPUT: StructSingleGeneric<(bool, u64)> = StructSingleGeneric { a: (true, 10) }; + assert(x == INPUT); + + const EXPECTED: StructSingleGeneric<(bool, u64)> = StructSingleGeneric { a: (false, 20) }; + return EXPECTED + } + + fn types_struct_double_generic( + x: StructGenericWithEnum, + ) -> StructGenericWithEnum { + const INPUT: StructGenericWithEnum = StructGenericWithEnum { + a: 10, + b: EnumDoubleGeneric::a(10), + }; + assert(x == INPUT); + + const EXPECTED: StructGenericWithEnum = StructGenericWithEnum { + a: 20, + b: EnumDoubleGeneric::b(10), + }; + return EXPECTED + } + + fn types_struct_external(x: ExternalStruct) -> ExternalStruct { + const INPUT: ExternalStruct = ExternalStruct { value: 10 }; + assert(x == INPUT); + + const EXPECTED: ExternalStruct = ExternalStruct { value: 20 }; + return EXPECTED + } + + fn types_struct_with_nested_array(x: StructWithNestedArray) -> StructWithNestedArray { + const INPUT_STRUCT: StructDoubleGeneric, str[1]> = StructDoubleGeneric { + a: StructSingleGeneric { a: 10 }, + b: __to_str_array("A"), + }; + const INPUT = StructWithNestedArray { + a: [INPUT_STRUCT, INPUT_STRUCT], + }; + assert(x == INPUT); + + const EXPECTED_STRUCT: StructDoubleGeneric, str[1]> = StructDoubleGeneric { + a: StructSingleGeneric { a: 20 }, + b: __to_str_array("B"), + }; + const EXPECTED = StructWithNestedArray { + a: [EXPECTED_STRUCT, EXPECTED_STRUCT], + }; + return EXPECTED + } + + fn types_struct_with_nested_tuple(x: StructWithNestedTuple) -> StructWithNestedTuple { + const INPUT: StructWithNestedTuple = StructWithNestedTuple { + a: ( + 10, + StructSingleGeneric { + a: StructSingleGeneric { a: 20 }, + }, + __to_str_array("ABC"), + ), + }; + assert(x == INPUT); + + const EXPECTED: StructWithNestedTuple = StructWithNestedTuple { + a: ( + 30, + StructSingleGeneric { + a: StructSingleGeneric { a: 40 }, + }, + __to_str_array("CBA"), + ), + }; + return EXPECTED + } + + fn types_struct_with_nested_struct(x: StructWithNestedStruct) -> StructWithNestedStruct { + const INPUT: StructWithNestedStruct = StructWithNestedStruct { + a: StructDoubleGeneric { + a: StructSingleGeneric { a: 10 }, + b: 20, + }, + }; + assert(x == INPUT); + + const EXPECTED: StructWithNestedStruct = StructWithNestedStruct { + a: StructDoubleGeneric { + a: StructSingleGeneric { a: 30 }, + b: 40, + }, + }; + return EXPECTED + } + + fn types_struct_with_multiple_struct_params(x: StructA, y: StructB, z: StructC) -> bool { + const STRUCT_A: StructA = StructA { propA1: 10 }; + assert(x == STRUCT_A); + + const STRUCT_B: StructB = StructB { + propB1: STRUCT_A, + propB2: 20, + }; + assert(y == STRUCT_B); + + // PropC2 + let mut propC2 = Vec::new(); + propC2.push(STRUCT_B); + + // PropC3 + const STRUCT_E: StructE = StructE { + propE1: STRUCT_A, + propE2: STRUCT_B, + propE3: 30, + }; + let mut propD1 = Vec::new(); + propD1.push(STRUCT_E); + + const STRUCT_F: StructF = StructF { + propF1: 50, + propF2: __to_str_array("A"), + }; + let propC3: StructD> = StructD { + propD1: propD1, + propD2: 40, + propD3: STRUCT_F, + }; + + let STRUCT_C: StructC = StructC { + propC1: STRUCT_A, + propC2: propC2, + propC3: propC3, + // propC4: [STRUCT_D], + // propC5: [STRUCT_D], + }; + + assert(z == STRUCT_C); + + return true; + + // const STRUCT_C4: StructD> = StructD { + // propD1: [StructE { propE1: STRUCT_A, propE2: STRUCT_B, propE3: 30 }], + // propD2: 40, + // propD3: StructF { propF1: 50, propF2: true }, + // }; + + // const STRUCT_C5: StructD>> = StructD> { + // propD1: [StructE { propE1: STRUCT_A, propE2: STRUCT_B, propE3: 30 }], + // propD2: 40, + // propD3: StructF { propF1: 50, propF2: [StructG { propG1: 60 }] }, + // }; + + // const STRUCT_C: StructC = StructC { + // propC1: STRUCT_A, + // propC2: [STRUCT_B], + // propC3: STRUCT_C3, + // propC4: [STRUCT_C4], + // propC5: [STRUCT_C5], + // }; + // const STRUCT_B: StructB = StructB { propB1: INPUT_X, propB2: 20 }; + // const STRUCT_C: StructC = StructC { propC1: INPUT_X, propC2: [INPUT_Y], propC3: INPUT_D, propC4: [INPUT_D], propC5: [INPUT_D] }; + // const STRUCT_D: StructD = StructD { + // propD1: [StructE { propE1: INPUT_X, propE2: INPUT_Y, propE3: 30 }], + // propD2: 40, + // propD3: StructF { propF1: 50, propF2: __to_str_array("ABC") }, + // }; + // assert(y == INPUT_Y); + } + + fn types_struct_with_implicit_generics( + x: StructWithImplicitGenerics, + ) -> StructWithImplicitGenerics { + const INPUT_B256: b256 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + const INPUT: StructWithImplicitGenerics = StructWithImplicitGenerics { + a: [INPUT_B256, INPUT_B256, INPUT_B256], + b: (INPUT_B256, 10), + }; + + assert(x == INPUT); + + const EXPECTED_B256: b256 = 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + const EXPECTED: StructWithImplicitGenerics = StructWithImplicitGenerics { + a: [EXPECTED_B256, EXPECTED_B256, EXPECTED_B256], + b: (EXPECTED_B256, 25), + }; + + EXPECTED + } + + fn types_struct_with_array(x: StructWithGenericArray) -> StructWithGenericArray { + const INPUT_B256: b256 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + const INPUT_STRUCT: StructDoubleGeneric = StructDoubleGeneric { + a: INPUT_B256, + b: 10, + }; + const INPUT: StructWithGenericArray = StructWithGenericArray { + a: [INPUT_STRUCT, INPUT_STRUCT, INPUT_STRUCT], + }; + assert(x == INPUT); + + const EXPECTED_B256: b256 = 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + const EXPECTED_STRUCT: StructDoubleGeneric = StructDoubleGeneric { + a: EXPECTED_B256, + b: 20, + }; + const EXPECTED: StructWithGenericArray = StructWithGenericArray { + a: [EXPECTED_STRUCT, EXPECTED_STRUCT, EXPECTED_STRUCT], + }; + + EXPECTED + } + + fn types_struct_with_vector(x: StructWithVector) -> StructWithVector { + let INPUT_VEC: Vec = vec_u8_from([1, 2, 3]); + let INPUT: StructWithVector = StructWithVector { + a: 1, + b: INPUT_VEC, + }; + assert(x == INPUT); + + let EXPECTED_VEC: Vec = vec_u8_from([3, 2, 1]); + let EXPECTED: StructWithVector = StructWithVector { + a: 3, + b: EXPECTED_VEC, + }; + + EXPECTED + } + + fn types_struct_with_array_of_enums(x: StructWithEnumArray) -> StructWithEnumArray { + const INPUT_ENUM = EnumWithNative::Checked; + const INPUT: StructWithEnumArray = StructWithEnumArray { + a: [INPUT_ENUM, INPUT_ENUM, INPUT_ENUM], + }; + assert(x == INPUT); + + const EXPECTED_ENUM = EnumWithNative::Pending; + const EXPECTED: StructWithEnumArray = StructWithEnumArray { + a: [EXPECTED_ENUM, EXPECTED_ENUM, EXPECTED_ENUM], + }; + + EXPECTED + } + + fn types_struct_with_complex_nested_struct(x: StructD>>) -> bool { + false + } + + fn types_struct_with_single_option(x: StructWithSingleOption) -> StructWithSingleOption { + const OPTION_ARRAY: [Option; 5] = [Option::Some(1), Option::None, Option::Some(2), Option::None, Option::Some(3)]; + const OPTION_STRUCT: Option = Option::Some(StructWithMultiOption { + a: OPTION_ARRAY + }); + const INPUT: StructWithSingleOption = StructWithSingleOption { + a: OPTION_STRUCT + }; + assert(x == INPUT); + + const EXPECTED: StructWithSingleOption = StructWithSingleOption { + a: Option::None + }; + EXPECTED + } + + /** + * Enums + */ + fn types_enum(x: EnumWithNative) -> EnumWithNative { + assert(x == EnumWithNative::Checked); + + EnumWithNative::Pending + } + + fn types_enum_with_builtin_type(x: EnumWithBuiltinType) -> EnumWithBuiltinType { + assert(x == EnumWithBuiltinType::a(true)); + + EnumWithBuiltinType::b(20) + } + + fn types_enum_with_vector(x: EnumWithVector) -> EnumWithVector { + assert(x == EnumWithVector::a(10)); + + let EXPECTED_VEC = vec_u8_from([1, 2, 3]); + return EnumWithVector::b(EXPECTED_VEC) + } + + fn types_generic_enum(x: EnumDoubleGeneric) -> EnumDoubleGeneric { + const INPUT: EnumDoubleGeneric = EnumDoubleGeneric::a(10); + assert(x == INPUT); + + const EXPECTED: EnumDoubleGeneric = EnumDoubleGeneric::b(20); + return EXPECTED + } + + fn types_enum_external(x: ExternalEnum) -> ExternalEnum { + assert_eq(x, ExternalEnum::A); + + return ExternalEnum::B; + } + + fn types_enum_with_structs(x: EnumWithStructs) -> EnumWithStructs { + const INPUT: EnumWithStructs = EnumWithStructs::a(EnumWithNative::Checked); + assert(x == INPUT); + + const EXPECTED: EnumWithStructs = EnumWithStructs::b(StructSimple { a: true, b: 10 }); + return EXPECTED + } + + /** + * Vectors + */ + fn types_vector_u8(x: Vec) -> Vec { + let INPUT = vec_u8_from([1, 2, 3]); + assert(x == INPUT); + + let EXPECTED = vec_u8_from([3, 2, 1]); + return EXPECTED + } + + fn types_vector_boolean(x: Vec) -> Vec { + let INPUT = vec_bool_from([true, false, true, false]); + assert(x == INPUT); + + let EXPECTED = vec_bool_from([false, true, false, true]); + return EXPECTED + } + + fn types_vector_inside_vector(x: Vec>) -> Vec> { + let mut INPUT = Vec::new(); + INPUT.push(vec_u32_from([1, 2, 3])); + assert(x == INPUT); + + let mut EXPECTED = Vec::new(); + EXPECTED.push(vec_u32_from([3, 2, 1])); + EXPECTED.push(vec_u32_from([6, 5, 4])); + return EXPECTED + } + + fn types_vector_with_struct(x: Vec) -> Vec { + let mut INPUT = Vec::new(); + INPUT.push(StructSimple { a: true, b: 10 }); + assert(x == INPUT); + + let mut EXPECTED = Vec::new(); + EXPECTED.push(StructSimple { + a: false, + b: 30, + }); + return EXPECTED + } + + fn types_vector_option(x: Vec) -> Vec { + let mut INPUT = Vec::new(); + INPUT.push(StructWithMultiOption { + a: [Some(1), Some(2), Some(3), Some(4), Some(5)], + }); + assert(x == INPUT); + + let mut EXPECTED = Vec::new(); + EXPECTED.push(StructWithMultiOption { + a: [Some(5), Some(4), Some(3), Some(2), Some(1)], + }); + return EXPECTED + } + + /** + * Options + */ + fn types_option(x: Option) -> Option { + const INPUT: Option = Option::Some(10); + assert(x == INPUT); + + const EXPECTED: Option = Option::None; + return EXPECTED + } + + fn types_option_struct(x: Option) -> Option { + let input_struct: StructSimple = StructSimple { + a: true, + b: 10, + }; + let input: Option = Option::Some(input_struct); + assert(x == input); + + const EXPECTED: Option = Option::None; + return EXPECTED + } + + /** + * Native types + */ + fn types_asset_id(x: AssetId) -> AssetId { + const INPUT = AssetId::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + assert(x == INPUT); + + const EXPECTED = AssetId::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + return EXPECTED + } + + fn types_identity_address(x: Identity) -> Identity { + const ADDRESS = Address::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + const INPUT = Identity::Address(ADDRESS); + assert(x == INPUT); + + const EXPECTED_ADDRESS = Address::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + const EXPECTED = Identity::Address(EXPECTED_ADDRESS); + return EXPECTED + } + + fn types_identity_contract_id(x: Identity) -> Identity { + const INPUT_CONTRACT_ID = ContractId::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + const INPUT = Identity::ContractId(INPUT_CONTRACT_ID); + assert(x == INPUT); + + const EXPECTED_ADDRESS = ContractId::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + const EXPECTED = Identity::ContractId(EXPECTED_ADDRESS); + return EXPECTED + } + + fn types_address(x: Address) -> Address { + const INPUT = Address::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + assert(x == INPUT); + + const EXPECTED = Address::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + return EXPECTED + } + + fn types_contract_id(x: ContractId) -> ContractId { + const INPUT = ContractId::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + assert(x == INPUT); + + const EXPECTED = ContractId::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + return EXPECTED + } + + fn types_evm_address(x: EvmAddress) -> EvmAddress { + let INPUT = EvmAddress::from(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA); + assert(x == INPUT); + + let EXPECTED = EvmAddress::from(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb); + return EXPECTED + } + + fn types_result(x: Result) -> Result { + if (x.is_err()) { + return Err(__to_str_array("InputError")); + } + + let result = divide(20, x.unwrap()); + match result { + Ok(value) => Ok(value), + Err(MyContractError::DivisionByZero) => Err(__to_str_array("DivisError")), + } + } + + /** + * Void + */ + fn types_void(x: ()) -> () { + x + } + + fn types_void_then_value(x: (), y: u8) -> () { + const inputY = 10; + assert(y == inputY); + () + } + + fn types_value_then_void(x: u8, y: ()) -> () { + const inputX = 10; + assert(x == inputX); + () + } + + fn types_value_then_void_then_value(x: u8, y: (), z: u8) -> () { + const inputX = 10; + assert(x == inputX); + + const inputZ = 20; + assert(z == inputZ); + () + } + + fn types_value_then_value_then_void_then_void(x: u8, y: u8, z: (), a: ()) -> () { + const inputX = 10; + assert(x == inputX); + + const inputY = 20; + assert(y == inputY); + () + } + + /** + * Multi-args + */ + fn multi_arg_u64_u64(x: u64, y: u64) -> u64 { + const INPUT_X = 1; + const INPUT_Y = 2; + assert(x == INPUT_X); + assert(y == INPUT_Y); + + const EXPECTED = 3; + return EXPECTED; + } + + fn multi_arg_b256_bool(x: b256, y: bool) -> (b256, bool) { + const INPUT_X: b256 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + const INPUT_Y: bool = true; + assert_eq(x, INPUT_X); + assert_eq(y, INPUT_Y); + + const EXPECTED: (b256, bool) = (0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, false); + return EXPECTED + } + + fn multi_arg_vector_vector(x: Vec, y: Vec) -> (Vec, Vec) { + let INPUT_X = vec_u8_from([1, 2, 3]); + let INPUT_Y = vec_u8_from([4, 5, 6]); + assert(x == INPUT_X); + assert(y == INPUT_Y); + + let EXPECTED_X = vec_u8_from([7, 8, 9]); + let EXPECTED_Y = vec_u8_from([10, 11, 12]); + (EXPECTED_X, EXPECTED_Y) + } + + fn multi_arg_vector_b256(x: Vec, y: b256) -> (Vec, b256) { + let INPUT_X = vec_u8_from([1, 2, 3]); + let INPUT_Y: b256 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + assert(x == INPUT_X); + assert(y == INPUT_Y); + + let EXPECTED_X = vec_u8_from([7, 8, 9]); + const EXPECTED_Y = 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; + let EXPECTED = (EXPECTED_X, EXPECTED_Y); + return EXPECTED + } + + fn multi_arg_struct_vector(x: StructSimple, y: Vec) -> (StructSimple, Vec) { + const INPUT_X = StructSimple { a: true, b: 1 }; + let INPUT_Y = vec_u8_from([1, 2, 3]); + assert(x == INPUT_X); + assert(y == INPUT_Y); + + const EXPECTED_X = StructSimple { a: false, b: 2 }; + let EXPECTED_Y = vec_u8_from([4, 5, 6]); + let EXPECTED = (EXPECTED_X, EXPECTED_Y); + return EXPECTED + } + + fn multi_arg_u64_struct(x: u64, y: StructSimple) -> (u64, StructSimple) { + const INPUT_X = 99u64; + let input_y = StructSimple { a: true, b: 51 }; + assert(x == INPUT_X); + assert(y == input_y); + + const EXPECTED_X = 3u64; + let expected_y = StructSimple { a: false, b: 4 }; + return (EXPECTED_X, expected_y); + } + + fn multi_arg_str_str(x: str[5], y: str[5]) -> (str[5], str[5]) { + let input_x: str = "Input"; + let input_y: str = "False"; + + assert_eq(from_str_array(x), input_x); + assert_eq(from_str_array(y), input_y); + + let EXPECTED_X: str[5] = __to_str_array("Fuuel"); + let EXPECTED_Y: str[5] = __to_str_array("Niice"); + (EXPECTED_X, EXPECTED_Y) + } + + fn multi_arg_u32_vector_vector(x: u32, y: Vec, z: Vec) -> (u32, Vec, Vec) { + const INPUT_X = 1u32; + + let mut input_y: Vec = Vec::new(); + input_y.push(10020); + input_y.push(1231231); + input_y.push(777657); + + let mut input_z: Vec = Vec::new(); + input_z.push(99); + input_z.push(101); + + assert(x == INPUT_X); + assert(y == input_y); + assert(z == input_z); + + const EXPECTED_X = 2u32; + + let mut expected_y: Vec = Vec::new(); + expected_y.push(7); + expected_y.push(8); + expected_y.push(9); + + let mut expected_z: Vec = Vec::new(); + expected_z.push(10); + expected_z.push(11); + expected_z.push(12); + + return (EXPECTED_X, expected_y, expected_z); + } + + fn multi_arg_complex( + x: StructDoubleGeneric<[b256; 3], u8>, + y: [StructDoubleGeneric; 4], + z: (str[5], bool), + a: StructSimple, + ) -> (StructDoubleGeneric<[b256; 3], u8>, [StructDoubleGeneric; 4], (str[5], bool), StructSimple) { + let input_x: StructDoubleGeneric<[b256; 3], u8> = StructDoubleGeneric { + a: [ + 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc, + ], + b: 10, + }; + + let input_y: [StructDoubleGeneric; 4] = [ + StructDoubleGeneric { + a: 99u64, + b: false, + }, + StructDoubleGeneric { + a: 199u64, + b: false, + }, + StructDoubleGeneric { + a: 2000u64, + b: false, + }, + StructDoubleGeneric { + a: 31u64, + b: true, + }, + ]; + + let input_z: (str[5], bool) = (__to_str_array("Input"), true); + + let input_a: StructSimple = StructSimple { a: true, b: 10 }; + + assert(x == input_x); + assert(y == input_y); + assert(z == input_z); + assert(a == input_a); + + let expected_x: StructDoubleGeneric<[b256; 3], u8> = StructDoubleGeneric { + a: [ + 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd, + 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, + ], + b: 99, + }; + + let expected_y: [StructDoubleGeneric; 4] = [ + StructDoubleGeneric { + a: 11u64, + b: true, + }, + StructDoubleGeneric { + a: 99u64, + b: true, + }, + StructDoubleGeneric { + a: 567u64, + b: true, + }, + StructDoubleGeneric { + a: 971u64, + b: false, + }, + ]; + + let expected_z: (str[5], bool) = (__to_str_array("tupni"), false); + + let expected_a: StructSimple = StructSimple { + a: false, + b: 57, + }; + + return (expected_x, expected_y, expected_z, expected_a); + } +} diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/utils.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/utils.sw new file mode 100644 index 00000000000..27338730220 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-contract/src/utils.sw @@ -0,0 +1,26 @@ +library; + +pub fn vec_u32_from(vals: [u32; 3]) -> Vec { + let mut vec = Vec::new(); + vec.push(vals[0]); + vec.push(vals[1]); + vec.push(vals[2]); + vec +} + +pub fn vec_u8_from(vals: [u8; 3]) -> Vec { + let mut vec = Vec::new(); + vec.push(vals[0]); + vec.push(vals[1]); + vec.push(vals[2]); + vec +} + +pub fn vec_bool_from(vals: [bool; 4]) -> Vec { + let mut vec = Vec::new(); + vec.push(vals[0]); + vec.push(vals[1]); + vec.push(vals[2]); + vec.push(vals[3]); + vec +} diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/Forc.toml new file mode 100644 index 00000000000..d28ab3240d9 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "lib.sw" +license = "Apache-2.0" +name = "abi-library" + +[dependencies] diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/src/lib.sw b/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/src/lib.sw new file mode 100644 index 00000000000..90c6c70044a --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/abi-library/src/lib.sw @@ -0,0 +1,27 @@ +library; + +// anything `pub` here will be exported as a part of this library's API +pub struct ExternalStruct { + pub value: u64, +} + +pub enum ExternalEnum { + A: (), + B: (), +} + +impl Eq for ExternalStruct { + fn eq(self, other: Self) -> bool { + self.value == other.value + } +} + +impl Eq for ExternalEnum { + fn eq(self, other: Self) -> bool { + match (self, other) { + (ExternalEnum::A, ExternalEnum::A) => true, + (ExternalEnum::B, ExternalEnum::B) => true, + _ => false, + } + } +}