From e15756e02351c0bee12587f3ba7d43b8aaaaf1ca Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 26 Sep 2024 12:03:49 +0200 Subject: [PATCH 1/2] Not possible to send an optional empty tuple Fixes #2271 --- .../form/ContractMethodFieldInputTuple.tsx | 1 + .../contract/methods/form/utils.test.ts | 23 +++++++++++++++++++ ui/address/contract/methods/form/utils.ts | 19 ++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ui/address/contract/methods/form/ContractMethodFieldInputTuple.tsx b/ui/address/contract/methods/form/ContractMethodFieldInputTuple.tsx index 339b391d66..6f820a0dd8 100644 --- a/ui/address/contract/methods/form/ContractMethodFieldInputTuple.tsx +++ b/ui/address/contract/methods/form/ContractMethodFieldInputTuple.tsx @@ -42,6 +42,7 @@ const ContractMethodFieldInputTuple = ({ data, basePath, level, isDisabled, isOp basePath={ `${ basePath }:${ index }` } level={ level + 1 } isDisabled={ isDisabled } + isOptional={ isOptional } /> ); } diff --git a/ui/address/contract/methods/form/utils.test.ts b/ui/address/contract/methods/form/utils.test.ts index bedfc413d2..48c2f2b4a3 100644 --- a/ui/address/contract/methods/form/utils.test.ts +++ b/ui/address/contract/methods/form/utils.test.ts @@ -74,4 +74,27 @@ describe('transformFormDataToMethodArgs', () => { ], ]); }); + + it('should transform all nested empty arrays to empty arrays', () => { + const formData = { + '0': '0x1D415D28380ff51A507F7B176ca5F27833F7FffD', + '1': '0x1D415D28380ff51A507F7B176ca5F27833F7FffD', + '2': '3160', + '3': true, + // tuple array without elements + '4:0:0:0': undefined, + '4:0:1:0': undefined, + '4:0:1:1': undefined, + '4:0:1:2': undefined, + '4:0:1:3': undefined, + }; + const result = transformFormDataToMethodArgs(formData); + expect(result).toEqual([ + '0x1D415D28380ff51A507F7B176ca5F27833F7FffD', + '0x1D415D28380ff51A507F7B176ca5F27833F7FffD', + '3160', + true, + [], + ]); + }); }); diff --git a/ui/address/contract/methods/form/utils.ts b/ui/address/contract/methods/form/utils.ts index 060683172c..e067f0f74f 100644 --- a/ui/address/contract/methods/form/utils.ts +++ b/ui/address/contract/methods/form/utils.ts @@ -81,7 +81,9 @@ export function transformFormDataToMethodArgs(formData: ContractMethodFormFields _set(result, field.replaceAll(':', '.'), value); } - return filterOutEmptyItems(result); + const filteredResult = filterOutEmptyItems(result); + const mappedResult = mapEmptyNestedArrays(filteredResult); + return mappedResult; } function filterOutEmptyItems(array: Array): Array { @@ -90,11 +92,26 @@ function filterOutEmptyItems(array: Array): Array { // The only optional field is the native coin value, which is safely handled in the form submit handler. // 2. When the user adds and removes items from a field array. // In this scenario, empty items need to be filtered out to maintain the correct sequence of arguments. + // We don't use isEmptyField() function here because of the second case otherwise it will not keep the correct order of arguments. return array .map((item) => Array.isArray(item) ? filterOutEmptyItems(item) : item) .filter((item) => item !== undefined); } +function isEmptyField(field: unknown): boolean { + // the empty string is meant that the field was touched but left empty + // the undefined is meant that the field was not touched + return field === undefined || field === ''; +} + +function isEmptyNestedArray(array: Array): boolean { + return array.flat(Infinity).filter((item) => !isEmptyField(item)).length === 0; +} + +function mapEmptyNestedArrays(array: Array): Array { + return array.map((item) => Array.isArray(item) && isEmptyNestedArray(item) ? [] : item); +} + export function getFieldLabel(input: ContractAbiItemInput, isRequired?: boolean) { const name = input.name || input.internalType || ''; return `${ name } (${ input.type })${ isRequired ? '*' : '' }`; From 32be80d993c4aee66c55ad10ab8fb7a9032e91da Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 26 Sep 2024 12:15:44 +0200 Subject: [PATCH 2/2] fix args length for read/simulate method functions --- ui/address/contract/methods/useCallMethodPublicClient.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/address/contract/methods/useCallMethodPublicClient.ts b/ui/address/contract/methods/useCallMethodPublicClient.ts index fab792816d..86eccfcc5f 100644 --- a/ui/address/contract/methods/useCallMethodPublicClient.ts +++ b/ui/address/contract/methods/useCallMethodPublicClient.ts @@ -28,11 +28,14 @@ export default function useCallMethodPublicClient(): (params: Params) => Promise } const address = getAddress(addressHash); + // for write payable methods we add additional input for native coin value + // so in simulate mode we need to strip it off + const _args = args.slice(0, item.inputs.length); const params = { abi: [ item ], functionName: item.name, - args: args, + args: _args, address, account, };