Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix args length and optional nested arrays in contract method form #2272

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const ContractMethodFieldInputTuple = ({ data, basePath, level, isDisabled, isOp
basePath={ `${ basePath }:${ index }` }
level={ level + 1 }
isDisabled={ isDisabled }
isOptional={ isOptional }
/>
);
}
Expand Down
23 changes: 23 additions & 0 deletions ui/address/contract/methods/form/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
[],
]);
});
});
19 changes: 18 additions & 1 deletion ui/address/contract/methods/form/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown>): Array<unknown> {
Expand All @@ -90,11 +92,26 @@ function filterOutEmptyItems(array: Array<unknown>): Array<unknown> {
// 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<unknown>): boolean {
return array.flat(Infinity).filter((item) => !isEmptyField(item)).length === 0;
}

function mapEmptyNestedArrays(array: Array<unknown>): Array<unknown> {
return array.map((item) => Array.isArray(item) && isEmptyNestedArray(item) ? [] : item);
}

export function getFieldLabel(input: ContractAbiItemInput, isRequired?: boolean) {
const name = input.name || input.internalType || '<unnamed argument>';
return `${ name } (${ input.type })${ isRequired ? '*' : '' }`;
Expand Down
5 changes: 4 additions & 1 deletion ui/address/contract/methods/useCallMethodPublicClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
Loading