Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 76427c9

Browse files
author
Alex
authored
4555/encode decode parameters (#4844)
* removing abiparameter in decodeParams * updating decode params testcases * fixing linter * updating * adding new testcases * fixing linter
1 parent 0079ef4 commit 76427c9

File tree

6 files changed

+142
-41
lines changed

6 files changed

+142
-41
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,8 @@ Released with 1.0.0-beta.37 code base.
373373
1. `setMultihash` is not supported in web3-eth-ens 4.x as its deprecated in ENS public resolver (https://github.com/ensdomains/resolvers/blob/master/contracts/PublicResolver.sol)
374374
2. `setContent` is not supported in web3-eth-ens 4.x as its deprecated in ENS public resolver (https://github.com/ensdomains/resolvers/blob/master/contracts/PublicResolver.sol)
375375
3. `getContent` is not supported in web3-eth-ens 4.x as its deprecated in ENS public resolver.
376-
4. `getMultihash` is not supported in web3-eth-ens 4.x as its deprecated in ENS public resolver.
376+
4. `getMultihash` is not supported in web3-eth-ens 4.x as its deprecated in ENS public resolver.
377+
378+
#### web3-eth-abi
379+
380+
1. `decodeParameters` decoding result is now the same format as the input parameter for encoding

packages/web3-eth-abi/src/api/logs_api.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ export const decodeLog = <ReturnType extends Record<string, unknown>>(
1717
const clonedData = data ?? '';
1818

1919
const notIndexedInputs: Array<string | AbiParameter> = [];
20-
const indexedParams: Array<string | AbiParameter> = [];
20+
const indexedParams: Array<string | unknown> = [];
2121
let topicCount = 0;
2222

2323
// TODO check for anonymous logs?
2424
for (const [i, input] of inputs.entries()) {
2525
if (input.indexed) {
2626
indexedParams[i] = STATIC_TYPES.some(s => input.type.startsWith(s))
27-
? (decodeParameter(input.type, clonedTopics[topicCount]) as AbiParameter)
27+
? (decodeParameter(input.type, clonedTopics[topicCount])[0] as unknown[])
2828
: clonedTopics[topicCount];
2929

3030
topicCount += 1;
@@ -34,18 +34,18 @@ export const decodeLog = <ReturnType extends Record<string, unknown>>(
3434
}
3535

3636
const nonIndexedData = clonedData;
37-
const notIndexedParams: Record<string, unknown> = nonIndexedData
38-
? decodeParametersWith<ReturnType>(notIndexedInputs, nonIndexedData, true)
39-
: {};
37+
const notIndexedParams = nonIndexedData
38+
? decodeParametersWith(notIndexedInputs, nonIndexedData, true)
39+
: [];
4040

4141
const returnValue: { [key: string]: unknown; __length__: number } = { __length__: 0 };
4242
returnValue.__length__ = 0;
4343

4444
for (const [i, res] of inputs.entries()) {
4545
returnValue[i] = res.type === 'string' ? '' : null;
4646

47-
if (notIndexedParams[i.toString()]) {
48-
returnValue[i] = notIndexedParams[i.toString()];
47+
if (notIndexedParams[i]) {
48+
returnValue[i] = notIndexedParams[i];
4949
}
5050
if (indexedParams[i]) {
5151
returnValue[i] = indexedParams[i];
@@ -57,6 +57,5 @@ export const decodeLog = <ReturnType extends Record<string, unknown>>(
5757

5858
returnValue.__length__ += 1;
5959
}
60-
6160
return returnValue as ReturnType & { __length__: number };
6261
};

packages/web3-eth-abi/src/api/parameters_api.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ import ethersAbiCoder from '../ethers_abi_coder';
55
import { AbiInput } from '../types';
66
import { formatParam, isAbiFragment, mapTypes, modifyParams } from '../utils';
77

8+
/**
9+
* Helper function to format the decoded object
10+
*/
11+
const formatDecodedObject = (
12+
abi: { [key: string]: unknown },
13+
input: { [key: string]: unknown },
14+
): { [key: string]: unknown } => {
15+
const res: { [key: string]: unknown } = {};
16+
for (const j of Object.keys(abi)) {
17+
if (typeof abi[j] === 'string') {
18+
res[j] = input[j];
19+
}
20+
if (typeof abi[j] === 'object') {
21+
res[j] = formatDecodedObject(
22+
abi[j] as { [key: string]: unknown },
23+
input[j] as { [key: string]: unknown },
24+
);
25+
}
26+
}
27+
28+
return res;
29+
};
30+
831
/**
932
* Should be used to encode list of params
1033
*/
@@ -54,11 +77,11 @@ export const encodeParameter = (abi: AbiInput, param: unknown): string =>
5477
/**
5578
* Should be used to decode list of params
5679
*/
57-
export const decodeParametersWith = <ReturnType extends Record<string, unknown>>(
80+
export const decodeParametersWith = (
5881
abis: AbiInput[],
5982
bytes: HexString,
6083
loose: boolean,
61-
): ReturnType & { __length__: number } => {
84+
): unknown[] => {
6285
try {
6386
if (abis.length > 0 && (!bytes || bytes === '0x' || bytes === '0X')) {
6487
throw new AbiError(
@@ -75,26 +98,29 @@ export const decodeParametersWith = <ReturnType extends Record<string, unknown>>
7598
`0x${bytes.replace(/0x/i, '')}`,
7699
loose,
77100
);
78-
79-
const returnValue: { [key: string]: unknown; __length__: number } = { __length__: 0 };
80-
returnValue.__length__ = 0;
81-
101+
const returnList: unknown[] = [];
82102
for (const [i, abi] of abis.entries()) {
83103
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
84-
let decodedValue = res[returnValue.__length__];
104+
let decodedValue = res[i];
85105
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
86106
decodedValue = decodedValue === '0x' ? null : decodedValue;
87107

88-
returnValue[i] = decodedValue;
89-
90-
if ((typeof abi === 'function' || (!!abi && typeof abi === 'object')) && abi.name) {
91-
returnValue[abi.name as string] = decodedValue;
108+
if (!!abi && typeof abi === 'object' && !abi.name && !Array.isArray(abi)) {
109+
// the length of the abi object will always be 1
110+
for (const j of Object.keys(abi)) {
111+
const abiObject: { [key: string]: unknown } = abi; // abi is readonly have to create a new const
112+
if (!!abiObject[j] && typeof abiObject[j] === 'object') {
113+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
114+
decodedValue = formatDecodedObject(
115+
abiObject[j] as { [key: string]: unknown },
116+
decodedValue as { [key: string]: unknown },
117+
);
118+
}
119+
}
92120
}
93-
94-
returnValue.__length__ += 1;
121+
returnList.push(decodedValue);
95122
}
96-
97-
return returnValue as ReturnType & { __length__: number };
123+
return returnList;
98124
} catch (err) {
99125
throw new AbiError(`Parameter decoding error: ${(err as Error).message}`);
100126
}
@@ -103,15 +129,10 @@ export const decodeParametersWith = <ReturnType extends Record<string, unknown>>
103129
/**
104130
* Should be used to decode list of params
105131
*/
106-
export const decodeParameters = <ReturnType extends Record<string, unknown>>(
107-
abi: AbiInput[],
108-
bytes: HexString,
109-
) => decodeParametersWith<ReturnType>(abi, bytes, false);
132+
export const decodeParameters = (abi: AbiInput[], bytes: HexString) =>
133+
decodeParametersWith(abi, bytes, false);
110134

111135
/**
112136
* Should be used to decode bytes to plain param
113137
*/
114-
export const decodeParameter = <ReturnType extends Record<string, unknown>>(
115-
abi: AbiInput,
116-
bytes: HexString,
117-
) => decodeParameters<ReturnType>([abi], bytes)['0'];
138+
export const decodeParameter = (abi: AbiInput, bytes: HexString) => decodeParameters([abi], bytes);

packages/web3-eth-abi/test/fixtures/data.ts

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,55 @@ export const validDecodeLogsData: {
297297
},
298298
];
299299

300+
export const validEncodeDecodeParametersData: {
301+
input: Parameters<typeof encodeParameters>;
302+
output: ReturnType<typeof encodeParameters>;
303+
}[] = [
304+
{
305+
input: [
306+
['uint256', 'string'],
307+
['2345675643', 'Hello!%'],
308+
],
309+
output: '0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000',
310+
},
311+
{
312+
input: [
313+
['uint8[]', 'bytes32'],
314+
[['34', '255'], '0x324567fff0000000000000000000000000000000000000000000000000000000'],
315+
],
316+
output: '0x0000000000000000000000000000000000000000000000000000000000000040324567fff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
317+
},
318+
{
319+
input: [
320+
[
321+
'uint8[]',
322+
{
323+
ParentStruct: {
324+
propertyOne: 'uint256',
325+
propertyTwo: 'uint256',
326+
ChildStruct: {
327+
propertyOne: 'uint256',
328+
propertyTwo: 'uint256',
329+
},
330+
},
331+
},
332+
],
333+
[
334+
['34', '255'],
335+
{
336+
propertyOne: '42',
337+
propertyTwo: '56',
338+
ChildStruct: {
339+
propertyOne: '45',
340+
propertyTwo: '78',
341+
},
342+
},
343+
],
344+
],
345+
output: '0x00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
346+
},
347+
];
348+
300349
export const validEncodeParametersData: {
301350
input: Parameters<typeof encodeParameters>;
302351
output: ReturnType<typeof encodeParameters>;
@@ -377,25 +426,24 @@ export const inValidEncodeParametersData: {
377426

378427
export const validDecodeParametersData: {
379428
input: Parameters<typeof decodeParameters>;
380-
output: ReturnType<typeof decodeParameters>;
429+
output: unknown[];
381430
}[] = [
382431
{
383432
input: [
384433
['uint256', 'string'],
385434
'0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000',
386435
],
387-
output: { '0': '2345675643', '1': 'Hello!%', __length__: 2 },
436+
output: ['2345675643', 'Hello!%'],
388437
},
389438
{
390439
input: [
391440
['uint8[]', 'bytes32'],
392441
'0x0000000000000000000000000000000000000000000000000000000000000040324567fff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
393442
],
394-
output: {
395-
'0': ['34', '255'],
396-
'1': '0x324567fff0000000000000000000000000000000000000000000000000000000',
397-
__length__: 2,
398-
},
443+
output: [
444+
['34', '255'],
445+
'0x324567fff0000000000000000000000000000000000000000000000000000000',
446+
]
399447
},
400448
{
401449
input: [
@@ -414,7 +462,17 @@ export const validDecodeParametersData: {
414462
],
415463
'0x00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
416464
],
417-
output: { '0': ['34', '255'], '1': ['42', '56', ['45', '78']], __length__: 2 },
465+
output: [
466+
['34', '255'],
467+
{
468+
propertyOne: '42',
469+
propertyTwo: '56',
470+
ChildStruct: {
471+
propertyOne: '45',
472+
propertyTwo: '78',
473+
},
474+
},
475+
],
418476
},
419477
];
420478

packages/web3-eth-abi/test/unit/api/parameters_api.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import {
44
inValidEncodeParametersData,
55
validDecodeParametersData,
66
validEncodeParametersData,
7+
validEncodeDecodeParametersData
78
} from '../../fixtures/data';
9+
import {
10+
AbiInput
11+
} from '../../../src/types'
812

913
describe('parameters_api', () => {
1014
describe('encodeParameters', () => {
@@ -62,4 +66,19 @@ describe('parameters_api', () => {
6266
);
6367
});
6468
});
69+
70+
describe('encode and decode', () => {
71+
describe('input should be the same as returned value from encode and decode', () => {
72+
it.each(validEncodeDecodeParametersData)(
73+
'%#: should pass for valid values: %j',
74+
({ input: [abi, params], output }) => {
75+
const rwAbi = abi as AbiInput[];
76+
const encodedBytes = encodeParameters(abi, params);
77+
expect(encodedBytes).toEqual(output);
78+
const decodedBytes = decodeParameters(rwAbi, encodedBytes)
79+
expect(decodedBytes).toEqual(params);
80+
},
81+
);
82+
});
83+
});
6584
});

packages/web3-eth-contract/src/encoding.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export const decodeMethodReturn = (abi: AbiFunctionFragment, returnValues?: HexS
180180
const value = returnValues.length >= 2 ? returnValues.slice(2) : returnValues;
181181
const result = decodeParameters([...abi.outputs], value);
182182

183-
if (result.__length__ === 1) {
183+
if (result.length === 1) {
184184
return result[0];
185185
}
186186

0 commit comments

Comments
 (0)