diff --git a/.changeset/polite-rabbits-care.md b/.changeset/polite-rabbits-care.md new file mode 100644 index 00000000000..9d02e5246fd --- /dev/null +++ b/.changeset/polite-rabbits-care.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-typegen": patch +--- + +feat: support `()` type in typegen diff --git a/packages/abi-typegen/src/abi/functions/Function.ts b/packages/abi-typegen/src/abi/functions/Function.ts index f6ad8e87925..e479d31ff1a 100644 --- a/packages/abi-typegen/src/abi/functions/Function.ts +++ b/packages/abi-typegen/src/abi/functions/Function.ts @@ -3,6 +3,7 @@ import { TargetEnum } from '../../types/enums/TargetEnum'; import type { IType } from '../../types/interfaces/IType'; import { findType } from '../../utils/findType'; import { parseTypeArguments } from '../../utils/parseTypeArguments'; +import { EmptyType } from '../types/EmptyType'; export class Function implements IFunction { public name: string; @@ -26,33 +27,38 @@ export class Function implements IFunction { const { types } = this; // loop through all inputs - const inputs = this.rawAbiFunction.inputs.map((input) => { - const { name, type: typeId, typeArguments } = input; + const inputs = this.rawAbiFunction.inputs + .filter((input) => { + const type = findType({ types, typeId: input.type }); + return type.rawAbiType.type !== EmptyType.swayType; + }) + .map((input) => { + const { name, type: typeId, typeArguments } = input; - const type = findType({ types, typeId }); + const type = findType({ types, typeId }); - let typeDecl: string; + let typeDecl: string; - if (typeArguments) { - // recursively process child `typeArguments` - typeDecl = parseTypeArguments({ - types, - target: TargetEnum.INPUT, - parentTypeId: typeId, - typeArguments, - }); - } else { - // or just collect type declaration - typeDecl = type.attributes.inputLabel; - } + if (typeArguments) { + // recursively process child `typeArguments` + typeDecl = parseTypeArguments({ + types, + target: TargetEnum.INPUT, + parentTypeId: typeId, + typeArguments, + }); + } else { + // or just collect type declaration + typeDecl = type.attributes.inputLabel; + } - // assemble it in `[key: string]: ` fashion - if (shouldPrefixParams) { - return `${name}: ${typeDecl}`; - } + // assemble it in `[key: string]: ` fashion + if (shouldPrefixParams) { + return `${name}: ${typeDecl}`; + } - return typeDecl; - }); + return typeDecl; + }); return inputs.join(', '); } diff --git a/packages/abi-typegen/src/abi/types/EmptyType.test.ts b/packages/abi-typegen/src/abi/types/EmptyType.test.ts new file mode 100644 index 00000000000..8b2a745d4b0 --- /dev/null +++ b/packages/abi-typegen/src/abi/types/EmptyType.test.ts @@ -0,0 +1,20 @@ +import { EmptyType } from './EmptyType'; + +/** + * @group node + */ +describe('EmptyType.ts', () => { + test('should properly parse type attributes', () => { + const emptyType = new EmptyType({ + rawAbiType: { + components: null, + typeParameters: null, + typeId: 0, + type: EmptyType.swayType, + }, + }); + + expect(emptyType.attributes.inputLabel).toEqual('never'); + expect(emptyType.attributes.outputLabel).toEqual('void'); + }); +}); diff --git a/packages/abi-typegen/src/abi/types/EmptyType.ts b/packages/abi-typegen/src/abi/types/EmptyType.ts new file mode 100644 index 00000000000..29ef9a97b59 --- /dev/null +++ b/packages/abi-typegen/src/abi/types/EmptyType.ts @@ -0,0 +1,32 @@ +import type { IRawAbiTypeRoot } from '../..'; +import type { IType } from '../../types/interfaces/IType'; + +import { AType } from './AType'; + +export class EmptyType extends AType implements IType { + public static swayType = '()'; + + public name = 'empty'; + + static MATCH_REGEX: RegExp = /^\(\)$/m; + + constructor(params: { rawAbiType: IRawAbiTypeRoot }) { + super(params); + this.attributes = { + /** + * The empty type is always ignored in function inputs. If it makes + * its way into a function's inputs list, it's a bug in the typegen. + */ + inputLabel: `never`, + outputLabel: `void`, + }; + } + + static isSuitableFor(params: { type: string }) { + return EmptyType.MATCH_REGEX.test(params.type); + } + + public parseComponentsAttributes(_params: { types: IType[] }) { + return this.attributes; + } +} diff --git a/packages/abi-typegen/src/abi/types/EnumType.test.ts b/packages/abi-typegen/src/abi/types/EnumType.test.ts index bbe407502b8..3208e700c67 100644 --- a/packages/abi-typegen/src/abi/types/EnumType.test.ts +++ b/packages/abi-typegen/src/abi/types/EnumType.test.ts @@ -23,9 +23,7 @@ describe('EnumType.ts', () => { abiContents: { types: rawTypes }, } = getTypegenForcProject(project); - const types = rawTypes - .filter((t) => t.type !== '()') - .map((rawAbiType: IRawAbiTypeRoot) => makeType({ rawAbiType })); + const types = rawTypes.map((rawAbiType: IRawAbiTypeRoot) => makeType({ rawAbiType })); return { types }; } diff --git a/packages/abi-typegen/src/abi/types/EnumType.ts b/packages/abi-typegen/src/abi/types/EnumType.ts index f5014edf21a..20dae73837c 100644 --- a/packages/abi-typegen/src/abi/types/EnumType.ts +++ b/packages/abi-typegen/src/abi/types/EnumType.ts @@ -5,6 +5,7 @@ import { extractStructName } from '../../utils/extractStructName'; import { findType } from '../../utils/findType'; import { AType } from './AType'; +import { EmptyType } from './EmptyType'; export class EnumType extends AType implements IType { public static swayType = 'enum MyEnumName'; @@ -43,10 +44,10 @@ export class EnumType extends AType implements IType { public getNativeEnum(params: { types: IType[] }) { const { types } = params; - const typeHash: { [key: number]: IType['rawAbiType'] } = types.reduce( + const typeHash: { [key: number]: IType['rawAbiType']['type'] } = types.reduce( (hash, row) => ({ ...hash, - [row.rawAbiType.typeId]: row, + [row.rawAbiType.typeId]: row.rawAbiType.type, }), {} ); @@ -56,7 +57,7 @@ export class EnumType extends AType implements IType { // `components` array guaranteed to always exist for structs/enums const enumComponents = components as IRawAbiTypeComponent[]; - if (!enumComponents.every(({ type }) => !typeHash[type])) { + if (!enumComponents.every(({ type }) => typeHash[type] === EmptyType.swayType)) { return undefined; } diff --git a/packages/abi-typegen/src/abi/types/OptionType.test.ts b/packages/abi-typegen/src/abi/types/OptionType.test.ts index 2e8d80f7b3c..f85d78a8223 100644 --- a/packages/abi-typegen/src/abi/types/OptionType.test.ts +++ b/packages/abi-typegen/src/abi/types/OptionType.test.ts @@ -20,9 +20,7 @@ describe('OptionType.ts', () => { const project = getTypegenForcProject(AbiTypegenProjectsEnum.OPTION_SIMPLE); const rawTypes = project.abiContents.types; - const types = rawTypes - .filter((t) => t.type !== '()') - .map((rawAbiType: IRawAbiTypeRoot) => makeType({ rawAbiType })); + const types = rawTypes.map((rawAbiType: IRawAbiTypeRoot) => makeType({ rawAbiType })); return { types }; } diff --git a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts index 397e624733f..994c98ed92f 100644 --- a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts +++ b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts @@ -108,7 +108,7 @@ describe('parseTypeArguments.ts', () => { test('should fallback to void for null outputs', () => { const project = getTypegenForcProject(AbiTypegenProjectsEnum.FN_VOID); - const types = bundleTypes([]); + const types = bundleTypes(project.abiContents.types); const typeArguments = [project.abiContents.functions[0].output]; // should fallback to void because `typeArguments.type` will be 0, and non-existent diff --git a/packages/abi-typegen/src/utils/parseTypeArguments.ts b/packages/abi-typegen/src/utils/parseTypeArguments.ts index d64bfc4c029..f7e9820f6c1 100644 --- a/packages/abi-typegen/src/utils/parseTypeArguments.ts +++ b/packages/abi-typegen/src/utils/parseTypeArguments.ts @@ -29,17 +29,10 @@ export function parseTypeArguments(params: { // loop through all `typeArgument` items typeArguments.forEach((typeArgument) => { - let currentLabel: string; - const currentTypeId = typeArgument.type; - try { - const currentType = findType({ types, typeId: currentTypeId }); - currentLabel = currentType.attributes[attributeKey]; - } catch (_err) { - // used for functions without output - currentLabel = 'void'; - } + const currentType = findType({ types, typeId: currentTypeId }); + const currentLabel = currentType.attributes[attributeKey]; if (typeArgument.typeArguments) { // recursively process nested `typeArguments` diff --git a/packages/abi-typegen/src/utils/shouldSkipAbiType.ts b/packages/abi-typegen/src/utils/shouldSkipAbiType.ts index 8a0ce77b002..575fb98e26c 100644 --- a/packages/abi-typegen/src/utils/shouldSkipAbiType.ts +++ b/packages/abi-typegen/src/utils/shouldSkipAbiType.ts @@ -1,5 +1,5 @@ export function shouldSkipAbiType(params: { type: string }) { - const ignoreList = ['()', 'struct RawVec']; + const ignoreList = ['struct RawVec']; const shouldSkip = ignoreList.indexOf(params.type) >= 0; return shouldSkip; } diff --git a/packages/abi-typegen/src/utils/shouldSkipType.test.ts b/packages/abi-typegen/src/utils/shouldSkipType.test.ts index 6faa48fe650..68d1ce5800d 100644 --- a/packages/abi-typegen/src/utils/shouldSkipType.test.ts +++ b/packages/abi-typegen/src/utils/shouldSkipType.test.ts @@ -6,7 +6,6 @@ import { supportedTypes } from './supportedTypes'; */ describe('types.ts', () => { test('should always skip these types', () => { - expect(shouldSkipAbiType({ type: '()' })).toEqual(true); expect(shouldSkipAbiType({ type: 'struct RawVec' })).toEqual(true); }); diff --git a/packages/abi-typegen/src/utils/supportedTypes.test.ts b/packages/abi-typegen/src/utils/supportedTypes.test.ts index 32b0303a43a..ea5df9ddda8 100644 --- a/packages/abi-typegen/src/utils/supportedTypes.test.ts +++ b/packages/abi-typegen/src/utils/supportedTypes.test.ts @@ -5,6 +5,6 @@ import { supportedTypes } from './supportedTypes'; */ describe('supportedTypes.ts', () => { test('should export all supported types', () => { - expect(supportedTypes.length).toEqual(22); + expect(supportedTypes.length).toEqual(23); }); }); diff --git a/packages/abi-typegen/src/utils/supportedTypes.ts b/packages/abi-typegen/src/utils/supportedTypes.ts index 86b44710bfe..24d2985ec7a 100644 --- a/packages/abi-typegen/src/utils/supportedTypes.ts +++ b/packages/abi-typegen/src/utils/supportedTypes.ts @@ -3,6 +3,7 @@ import { B256Type } from '../abi/types/B256Type'; import { B512Type } from '../abi/types/B512Type'; import { BoolType } from '../abi/types/BoolType'; import { BytesType } from '../abi/types/BytesType'; +import { EmptyType } from '../abi/types/EmptyType'; import { EnumType } from '../abi/types/EnumType'; import { EvmAddressType } from '../abi/types/EvmAddressType'; import { GenericType } from '../abi/types/GenericType'; @@ -22,6 +23,7 @@ import { U8Type } from '../abi/types/U8Type'; import { VectorType } from '../abi/types/VectorType'; export const supportedTypes = [ + EmptyType, ArrayType, B256Type, B512Type, 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 5d419b9da2f..cd20dd93a68 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 @@ -25,6 +25,10 @@ struct StructWithSingleOption { } abi MyContract { + fn types_empty(x: ()) -> (); + fn types_empty_then_value(x: (), y: u8) -> (); + fn types_value_then_empty(x: u8, y: ()) -> (); + fn types_value_then_empty_then_value(x: u8, y: (), z: u8) -> (); fn types_u8(x: u8) -> u8; fn types_u16(x: u16) -> u16; fn types_u32(x: u32) -> u32; @@ -51,6 +55,19 @@ abi MyContract { } impl MyContract for Contract { + fn types_empty(x: ()) -> () { + x + } + fn types_empty_then_value(x: (), y: u8) -> () { + () + } + fn types_value_then_empty(x: u8, y: ()) -> () { + () + } + fn types_value_then_empty_then_value(x: u8, y: (), z: u8) -> () { + () + } + fn types_u8(x: u8) -> u8 { 255 } diff --git a/packages/abi-typegen/test/fixtures/templates/contract/dts.hbs b/packages/abi-typegen/test/fixtures/templates/contract/dts.hbs index 6774eb07bb6..ae5efb64408 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/dts.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/dts.hbs @@ -46,6 +46,8 @@ interface MyContractAbiInterface extends Interface { types_b512: FunctionFragment; types_bool: FunctionFragment; types_bytes: FunctionFragment; + types_empty: FunctionFragment; + types_empty_then_value: FunctionFragment; types_enum: FunctionFragment; types_evm_address: FunctionFragment; types_option: FunctionFragment; @@ -60,6 +62,8 @@ interface MyContractAbiInterface extends Interface { types_u32: FunctionFragment; types_u64: FunctionFragment; types_u8: FunctionFragment; + types_value_then_empty: FunctionFragment; + types_value_then_empty_then_value: FunctionFragment; types_vector_geo: FunctionFragment; types_vector_option: FunctionFragment; types_vector_u8: FunctionFragment; @@ -71,6 +75,8 @@ interface MyContractAbiInterface extends Interface { encodeFunctionData(functionFragment: 'types_b512', values: [string]): Uint8Array; encodeFunctionData(functionFragment: 'types_bool', values: [boolean]): Uint8Array; encodeFunctionData(functionFragment: 'types_bytes', values: [Bytes]): Uint8Array; + encodeFunctionData(functionFragment: 'types_empty', values: []): Uint8Array; + encodeFunctionData(functionFragment: 'types_empty_then_value', values: [BigNumberish]): Uint8Array; encodeFunctionData(functionFragment: 'types_enum', values: [MyEnumInput]): Uint8Array; encodeFunctionData(functionFragment: 'types_evm_address', values: [EvmAddress]): Uint8Array; encodeFunctionData(functionFragment: 'types_option', values: [Option]): Uint8Array; @@ -85,6 +91,8 @@ interface MyContractAbiInterface extends Interface { encodeFunctionData(functionFragment: 'types_u32', values: [BigNumberish]): Uint8Array; encodeFunctionData(functionFragment: 'types_u64', values: [BigNumberish]): Uint8Array; encodeFunctionData(functionFragment: 'types_u8', values: [BigNumberish]): Uint8Array; + encodeFunctionData(functionFragment: 'types_value_then_empty', values: [BigNumberish]): Uint8Array; + encodeFunctionData(functionFragment: 'types_value_then_empty_then_value', values: [BigNumberish, BigNumberish]): Uint8Array; encodeFunctionData(functionFragment: 'types_vector_geo', values: [Vec]): Uint8Array; encodeFunctionData(functionFragment: 'types_vector_option', values: [Vec]): Uint8Array; encodeFunctionData(functionFragment: 'types_vector_u8', values: [Vec]): Uint8Array; @@ -95,6 +103,8 @@ interface MyContractAbiInterface extends Interface { decodeFunctionData(functionFragment: 'types_b512', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_bool', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_bytes', data: BytesLike): DecodedValue; + decodeFunctionData(functionFragment: 'types_empty', data: BytesLike): DecodedValue; + decodeFunctionData(functionFragment: 'types_empty_then_value', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_enum', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_evm_address', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_option', data: BytesLike): DecodedValue; @@ -109,6 +119,8 @@ interface MyContractAbiInterface extends Interface { decodeFunctionData(functionFragment: 'types_u32', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_u64', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_u8', data: BytesLike): DecodedValue; + decodeFunctionData(functionFragment: 'types_value_then_empty', data: BytesLike): DecodedValue; + decodeFunctionData(functionFragment: 'types_value_then_empty_then_value', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_vector_geo', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_vector_option', data: BytesLike): DecodedValue; decodeFunctionData(functionFragment: 'types_vector_u8', data: BytesLike): DecodedValue; @@ -123,6 +135,8 @@ export class MyContractAbi extends Contract { types_b512: InvokeFunction<[x: string], string>; types_bool: InvokeFunction<[x: boolean], boolean>; types_bytes: InvokeFunction<[x: Bytes], Bytes>; + types_empty: InvokeFunction<[], void>; + types_empty_then_value: InvokeFunction<[y: BigNumberish], void>; types_enum: InvokeFunction<[x: MyEnumInput], MyEnumOutput>; types_evm_address: InvokeFunction<[x: EvmAddress], EvmAddress>; types_option: InvokeFunction<[x: Option], Option>; @@ -137,6 +151,8 @@ export class MyContractAbi extends Contract { types_u32: InvokeFunction<[x: BigNumberish], number>; types_u64: InvokeFunction<[x: BigNumberish], BN>; types_u8: InvokeFunction<[x: BigNumberish], number>; + types_value_then_empty: InvokeFunction<[x: BigNumberish], void>; + types_value_then_empty_then_value: InvokeFunction<[x: BigNumberish, z: BigNumberish], void>; types_vector_geo: InvokeFunction<[x: Vec], Vec>; types_vector_option: InvokeFunction<[x: Vec], Vec>; types_vector_u8: InvokeFunction<[x: Vec], Vec>; diff --git a/packages/fuel-gauge/src/call-test-contract.test.ts b/packages/fuel-gauge/src/call-test-contract.test.ts index f85c0c0b8aa..193ca1a2edd 100644 --- a/packages/fuel-gauge/src/call-test-contract.test.ts +++ b/packages/fuel-gauge/src/call-test-contract.test.ts @@ -1,4 +1,5 @@ import { ASSET_A } from '@fuel-ts/utils/test-utils'; +import type { Contract } from 'fuels'; import { BN, bn, toHex, BaseAssetId } from 'fuels'; import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; @@ -23,7 +24,7 @@ const U64_MAX = bn(2).pow(64).sub(1); describe('CallTestContract', () => { it.each([0, 1337, U64_MAX.sub(1)])('can call a contract with u64 (%p)', async (num) => { const contract = await setupContract(); - const { value } = await contract.functions.foo(num).call(); + const { value } = await contract.functions.foo(num).call(); expect(value.toHex()).toEqual(bn(num).add(1).toHex()); }); @@ -44,11 +45,19 @@ describe('CallTestContract', () => { it('can call a function with empty arguments', async () => { const contract = await setupContract(); - const { value: value0 } = await contract.functions.barfoo(0).call(); - expect(value0.toHex()).toEqual(toHex(63)); + const { value: empty } = await contract.functions.empty().call(); + expect(empty.toHex()).toEqual(toHex(63)); - const { value: value1 } = await contract.functions.foobar().call(); - expect(value1.toHex()).toEqual(toHex(63)); + const { value: emptyThenValue } = await contract.functions.empty_then_value(35).call(); + expect(emptyThenValue.toHex()).toEqual(toHex(63)); + + const { value: valueThenEmpty } = await contract.functions.value_then_empty(35).call(); + expect(valueThenEmpty.toHex()).toEqual(toHex(63)); + + const { value: valueThenEmptyThenValue } = await contract.functions + .value_then_empty_then_value(35, 35) + .call(); + expect(valueThenEmptyThenValue.toHex()).toEqual(toHex(63)); }); it('function with empty return should resolve undefined', async () => { @@ -61,7 +70,7 @@ describe('CallTestContract', () => { it.each([ [ - 'foobar_no_params', + 'no_params', { values: [], expected: bn(50), @@ -127,7 +136,9 @@ describe('CallTestContract', () => { ])( `Test call with multiple arguments and different types -> %s`, async (method, { values, expected }) => { - const contract = await setupContract(); + // Type cast to Contract because of the dynamic nature of the test + // But the function names are type-constrained to correct Contract's type + const contract = (await setupContract()) as Contract; const { value } = await contract.functions[method](...values).call(); @@ -214,7 +225,7 @@ describe('CallTestContract', () => { it('Calling a simple contract function does only one dry run', async () => { const contract = await setupContract(); const dryRunSpy = vi.spyOn(contract.provider.operations, 'dryRun'); - await contract.functions.foobar_no_params().call(); + await contract.functions.no_params().call(); expect(dryRunSpy).toHaveBeenCalledOnce(); }); @@ -222,7 +233,7 @@ describe('CallTestContract', () => { const contract = await setupContract(); const dryRunSpy = vi.spyOn(contract.provider.operations, 'dryRun'); - await contract.functions.foobar_no_params().simulate(); + await contract.functions.no_params().simulate(); expect(dryRunSpy).toHaveBeenCalledTimes(2); }); }); diff --git a/packages/fuel-gauge/src/fee.test.ts b/packages/fuel-gauge/src/fee.test.ts index c8cc08b8dfa..f413b4cb4d3 100644 --- a/packages/fuel-gauge/src/fee.test.ts +++ b/packages/fuel-gauge/src/fee.test.ts @@ -233,7 +233,7 @@ describe('Fee', () => { .multiCall([ contract.functions.sum_multparams(1, 2, 3, 4, 5), contract.functions.return_void(), - contract.functions.foobar(), + contract.functions.empty(), contract.functions.return_bytes(), ]) .txParams({ diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/call-test-contract/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/call-test-contract/src/main.sw index 75757f20dac..0ae04aff10d 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/call-test-contract/src/main.sw +++ b/packages/fuel-gauge/test/fixtures/forc-projects/call-test-contract/src/main.sw @@ -40,9 +40,11 @@ abi TestContract { fn foo(value: u64) -> u64; fn call_external_foo(param: u64, contract_id: b256) -> u64; fn boo(value: TestStruct) -> TestStruct; - fn barfoo(value: u64) -> u64; - fn foobar(value: ()) -> u64; - fn foobar_no_params() -> u64; + fn empty(empty: ()) -> u64; + fn empty_then_value(empty: (), value: u8) -> u64; + fn value_then_empty(value: u8, empty: ()) -> u64; + fn value_then_empty_then_value(value: u8, empty: (), value2: u8) -> u64; + fn no_params() -> u64; fn sum(a: u64, b: u64) -> u64; fn sum_test(a: u64, test: SumStruct) -> u64; fn sum_single(test: SumStruct) -> u64; @@ -90,13 +92,19 @@ impl TestContract for Contract { b: value.b + 1, } } - fn barfoo(value: u64) -> u64 { + fn empty(value: ()) -> u64 { 63 } - fn foobar(value: ()) -> u64 { + fn empty_then_value(empty: (), value: u8) -> u64 { 63 } - fn foobar_no_params() -> u64 { + fn value_then_empty(value: u8, empty: ()) -> u64 { + 63 + } + fn value_then_empty_then_value(value: u8, empty: (), value2: u8) -> u64 { + 63 + } + fn no_params() -> u64 { 50 } fn sum(a: u64, b: u64) -> u64 {