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

Commit 6f529c7

Browse files
avkosnazarhussain
authored andcommitted
fix decode function. migration guide. fix unit tests (#5210)
* fix decode function. migration guide. fix unit tests * 🏷️ Fix the contract types (#5213) * fix contract integration tests * add to changelog.md Co-authored-by: Nazar Hussain <[email protected]>
1 parent 501d76e commit 6f529c7

File tree

11 files changed

+154
-54
lines changed

11 files changed

+154
-54
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ Released with 1.0.0-beta.37 code base.
377377

378378
#### web3-eth-abi
379379

380-
1. `decodeParameters` decoding result is now the same format as the input parameter for encoding
380+
1. `internalType` was renamed to `baseType` in all abi types
381381

382382
#### web3-eth
383383

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Web3 Abi Migration Guide
2+
3+
## Breaking Changes
4+
5+
`internalType` was renamed to `baseType` in all abi types

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,20 @@ export const decodeLog = <ReturnType extends Record<string, unknown>>(
4646
}
4747
}
4848

49-
const decodedNonIndexedInputs = data
49+
const decodedNonIndexedInputs: { [key: string]: unknown; __length__: number } = data
5050
? decodeParametersWith(Object.values(nonIndexedInputs), data, true)
51-
: [];
51+
: { __length__: 0 };
5252

5353
// If topics are more than indexed inputs, that means first topic is the event signature
5454
const offset = clonedTopics.length - Object.keys(indexedInputs).length;
5555

5656
const decodedIndexedInputs = Object.values(indexedInputs).map((input, index) =>
5757
STATIC_TYPES.some(s => input.type.startsWith(s))
58-
? (decodeParameter(input.type, clonedTopics[index + offset])[0] as unknown[])
58+
? decodeParameter(input.type, clonedTopics[index + offset])
5959
: clonedTopics[index + offset],
6060
);
6161

6262
const returnValues: { [key: string]: unknown; __length__: number } = { __length__: 0 };
63-
returnValues.__length__ = 0;
6463

6564
let indexedCounter = 0;
6665
let nonIndexedCounter = 0;
@@ -74,7 +73,7 @@ export const decodeLog = <ReturnType extends Record<string, unknown>>(
7473
}
7574

7675
if (nonIndexedInputs[i]) {
77-
returnValues[i] = decodedNonIndexedInputs[nonIndexedCounter];
76+
returnValues[i] = decodedNonIndexedInputs[String(nonIndexedCounter)];
7877
nonIndexedCounter += 1;
7978
}
8079

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

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,21 @@ const formatDecodedObject = (
2929
abi: { [key: string]: unknown },
3030
input: { [key: string]: unknown },
3131
): { [key: string]: unknown } => {
32+
let index = 0;
3233
const res: { [key: string]: unknown } = {};
3334
for (const j of Object.keys(abi)) {
3435
if (typeof abi[j] === 'string') {
3536
res[j] = input[j];
37+
res[index] = input[j];
3638
}
3739
if (typeof abi[j] === 'object') {
3840
res[j] = formatDecodedObject(
3941
abi[j] as { [key: string]: unknown },
4042
input[j] as { [key: string]: unknown },
4143
);
44+
res[index] = res[j];
4245
}
46+
index += 1;
4347
}
4448

4549
return res;
@@ -106,7 +110,7 @@ export const decodeParametersWith = (
106110
abis: AbiInput[],
107111
bytes: HexString,
108112
loose: boolean,
109-
): unknown[] => {
113+
): { [key: string]: unknown; __length__: number } => {
110114
try {
111115
if (abis.length > 0 && (!bytes || bytes === '0x' || bytes === '0X')) {
112116
throw new AbiError(
@@ -117,13 +121,14 @@ export const decodeParametersWith = (
117121
'or querying a node which is not fully synced.',
118122
);
119123
}
120-
121124
const res = ethersAbiCoder.decode(
122125
mapTypes(abis).map(p => ParamType.from(p)),
123126
`0x${bytes.replace(/0x/i, '')}`,
124127
loose,
125128
);
126-
const returnList: unknown[] = [];
129+
const returnValue: { [key: string]: unknown; __length__: number } = {
130+
__length__: 0,
131+
};
127132
for (const [i, abi] of abis.entries()) {
128133
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
129134
let decodedValue = res[i];
@@ -138,7 +143,7 @@ export const decodeParametersWith = (
138143
// eslint-disable-next-line no-null/no-null
139144
decodedValue === '0x' && !isStringObject && !isStringType ? null : decodedValue;
140145

141-
if (!!abi && typeof abi === 'object' && !abi.name && !Array.isArray(abi)) {
146+
if (!!abi && typeof abi === 'object' && !Array.isArray(abi)) {
142147
// the length of the abi object will always be 1
143148
for (const j of Object.keys(abi)) {
144149
const abiObject: { [key: string]: unknown } = abi; // abi is readonly have to create a new const
@@ -151,9 +156,14 @@ export const decodeParametersWith = (
151156
}
152157
}
153158
}
154-
returnList.push(decodedValue);
159+
if (!!abi && typeof abi === 'object' && abi?.name) {
160+
returnValue[(abi as { name: string })?.name] = decodedValue;
161+
}
162+
returnValue[i] = decodedValue;
163+
returnValue.__length__ += 1;
155164
}
156-
return returnList;
165+
166+
return returnValue;
157167
} catch (err) {
158168
throw new AbiError(`Parameter decoding error: ${(err as Error).message}`);
159169
}
@@ -165,13 +175,16 @@ export const decodeParametersWith = (
165175
* @param abi
166176
* @param bytes
167177
*/
168-
export const decodeParameters = (abi: AbiInput[], bytes: HexString) =>
169-
decodeParametersWith(abi, bytes, false);
178+
export const decodeParameters = (
179+
abi: AbiInput[],
180+
bytes: HexString,
181+
): { [key: string]: unknown; __length__: number } => decodeParametersWith(abi, bytes, false);
170182

171183
/**
172184
* Should be used to decode bytes to plain param
173185
*
174186
* @param abi
175187
* @param bytes
176188
*/
177-
export const decodeParameter = (abi: AbiInput, bytes: HexString) => decodeParameters([abi], bytes);
189+
export const decodeParameter = (abi: AbiInput, bytes: HexString): unknown =>
190+
decodeParameters([abi], bytes)['0'];

packages/web3-eth-abi/src/types.ts

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,27 +53,27 @@ export type AbiBaseFragment = {
5353
export type AbiConstructorFragment = AbiBaseFragment & {
5454
readonly type: string | 'constructor';
5555
readonly stateMutability: string | 'nonpayable' | 'payable';
56-
readonly inputs: ReadonlyArray<AbiParameter>;
56+
readonly inputs?: ReadonlyArray<AbiParameter>;
5757
};
5858

5959
// https://docs.soliditylang.org/en/latest/abi-spec.html#json
6060
export type AbiFunctionFragment = AbiBaseFragment & {
6161
readonly name: string;
6262
readonly type: string | 'function';
63-
readonly stateMutability: string | 'nonpayable' | 'payable' | 'pure' | 'view';
64-
readonly inputs: ReadonlyArray<AbiParameter>;
65-
readonly outputs: ReadonlyArray<AbiParameter>;
63+
readonly stateMutability?: string | 'nonpayable' | 'payable' | 'pure' | 'view';
64+
readonly inputs?: ReadonlyArray<AbiParameter>;
65+
readonly outputs?: ReadonlyArray<AbiParameter>;
6666

6767
readonly constant?: boolean; // stateMutability == 'pure' or stateMutability == 'view'
6868
readonly payable?: boolean; // stateMutability == 'payable'
6969
};
7070

7171
export type AbiFallbackFragment = AbiBaseFragment & {
72-
readonly name?: never;
72+
readonly name: never;
7373
readonly type: string | 'fallback';
7474
readonly stateMutability: string | 'nonpayable' | 'payable' | 'pure' | 'view';
75-
readonly inputs?: never;
76-
readonly outputs?: never;
75+
readonly inputs: never;
76+
readonly outputs: never;
7777

7878
// legacy properties
7979
readonly constant?: boolean; // stateMutability == 'pure' or stateMutability == 'view'
@@ -84,7 +84,7 @@ export type AbiFallbackFragment = AbiBaseFragment & {
8484
export type AbiEventFragment = AbiBaseFragment & {
8585
readonly name: string;
8686
readonly type: string | 'event';
87-
readonly inputs: ReadonlyArray<AbiParameter>;
87+
readonly inputs?: ReadonlyArray<AbiParameter>;
8888
readonly anonymous?: boolean;
8989
};
9090

@@ -177,35 +177,42 @@ export type MatchPrimitiveType<
177177
| PrimitiveTupleType<Type, Components>
178178
| never;
179179

180-
export type ContractMethodOutputParameters<Params extends Array<unknown>> = Params extends []
181-
? []
182-
: Params extends [infer H, ...infer R]
183-
? H extends AbiParameter
184-
? // TODO: Find a way to set name for tuple item
185-
[MatchPrimitiveType<H['type'], H['components']>, ...ContractMethodOutputParameters<R>]
186-
: ContractMethodOutputParameters<R>
187-
: Params;
188-
189-
export type ContractMethodInputParameters<Params extends Array<unknown>> = Params extends []
190-
? []
191-
: Params extends [infer H, ...infer R]
192-
? H extends AbiParameter
193-
? // TODO: Find a way to set name for tuple item
194-
[MatchPrimitiveType<H['type'], H['components']>, ...ContractMethodInputParameters<R>]
195-
: ContractMethodInputParameters<R>
196-
: Params;
180+
export type ContractMethodOutputParameters<Params extends ReadonlyArray<unknown> | undefined> =
181+
Params extends readonly []
182+
? []
183+
: Params extends readonly [infer H, ...infer R]
184+
? H extends AbiParameter
185+
? // TODO: Find a way to set name for tuple item
186+
[MatchPrimitiveType<H['type'], H['components']>, ...ContractMethodOutputParameters<R>]
187+
: ContractMethodOutputParameters<R>
188+
: Params extends undefined | unknown
189+
? []
190+
: Params;
191+
192+
export type ContractMethodInputParameters<Params extends ReadonlyArray<unknown> | undefined> =
193+
Params extends readonly []
194+
? []
195+
: Params extends readonly [infer H, ...infer R]
196+
? H extends AbiParameter
197+
? // TODO: Find a way to set name for tuple item
198+
[MatchPrimitiveType<H['type'], H['components']>, ...ContractMethodInputParameters<R>]
199+
: ContractMethodInputParameters<R>
200+
: Params extends undefined | unknown
201+
? []
202+
: Params;
197203

198204
export type ContractConstructor<Abis extends ContractAbi> = {
199205
[Abi in FilterAbis<Abis, AbiConstructorFragment> as 'constructor']: {
200206
readonly Abi: Abi;
201-
readonly Inputs: ContractMethodInputParameters<[...Abi['inputs']]>;
207+
readonly Inputs: ContractMethodInputParameters<Abi['inputs']>;
202208
};
203209
}['constructor'];
204210

205211
export type ContractMethod<Abi extends AbiFunctionFragment> = {
206212
readonly Abi: Abi;
207-
readonly Inputs: ContractMethodInputParameters<[...Abi['inputs']]>;
208-
readonly Outputs: ContractMethodOutputParameters<[...Abi['outputs']]>;
213+
214+
readonly Inputs: ContractMethodInputParameters<Abi['inputs']>;
215+
readonly Outputs: ContractMethodOutputParameters<Abi['outputs']>;
209216
};
210217

211218
export type ContractMethods<Abis extends ContractAbi> = {
@@ -217,7 +224,7 @@ export type ContractMethods<Abis extends ContractAbi> = {
217224

218225
export type ContractEvent<Abi extends AbiEventFragment> = {
219226
readonly Abi: Abi;
220-
readonly Inputs: ContractMethodInputParameters<[...Abi['inputs']]>;
227+
readonly Inputs: ContractMethodInputParameters<Abi['inputs']>;
221228
};
222229

223230
export type ContractEvents<Abis extends ContractAbi> = {

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,20 +395,31 @@ export const validDecodeLogsData: {
395395
export const validEncodeDecodeParametersData: {
396396
input: Parameters<typeof encodeParameters>;
397397
output: ReturnType<typeof encodeParameters>;
398+
outputResult: any;
398399
}[] = [
399400
{
400401
input: [
401402
['uint256', 'string'],
402403
['2345675643', 'Hello!%'],
403404
],
404405
output: '0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000',
406+
outputResult: {
407+
'0': '2345675643',
408+
'1': 'Hello!%',
409+
__length__: 2,
410+
},
405411
},
406412
{
407413
input: [
408414
['uint8[]', 'bytes32'],
409415
[['34', '255'], '0x324567fff0000000000000000000000000000000000000000000000000000000'],
410416
],
411417
output: '0x0000000000000000000000000000000000000000000000000000000000000040324567fff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
418+
outputResult: {
419+
'0': ['34', '255'],
420+
'1': '0x324567fff0000000000000000000000000000000000000000000000000000000',
421+
__length__: 2,
422+
},
412423
},
413424
{
414425
input: [
@@ -438,6 +449,28 @@ export const validEncodeDecodeParametersData: {
438449
],
439450
],
440451
output: '0x00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff',
452+
outputResult: {
453+
'0': ['34', '255'],
454+
'1': {
455+
'0': '42',
456+
'1': '56',
457+
'2': {
458+
'0': '45',
459+
'1': '78',
460+
propertyOne: '45',
461+
propertyTwo: '78',
462+
},
463+
propertyOne: '42',
464+
propertyTwo: '56',
465+
ChildStruct: {
466+
'0': '45',
467+
'1': '78',
468+
propertyOne: '45',
469+
propertyTwo: '78',
470+
},
471+
},
472+
__length__: 2,
473+
},
441474
},
442475
];
443476

@@ -572,13 +605,19 @@ export const inValidEncodeParametersData: {
572605
export const validDecodeParametersData: {
573606
input: Parameters<typeof decodeParameters>;
574607
output: unknown[];
608+
outputResult: any;
575609
}[] = [
576610
{
577611
input: [
578612
['uint256', 'string'],
579613
'0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000',
580614
],
581615
output: ['2345675643', 'Hello!%'],
616+
outputResult: {
617+
'0': '2345675643',
618+
'1': 'Hello!%',
619+
__length__: 2,
620+
},
582621
},
583622
{
584623
input: [
@@ -589,6 +628,11 @@ export const validDecodeParametersData: {
589628
['34', '255'],
590629
'0x324567fff0000000000000000000000000000000000000000000000000000000',
591630
],
631+
outputResult: {
632+
'0': ['34', '255'],
633+
'1': '0x324567fff0000000000000000000000000000000000000000000000000000000',
634+
__length__: 2,
635+
},
592636
},
593637
{
594638
input: [
@@ -618,6 +662,28 @@ export const validDecodeParametersData: {
618662
},
619663
},
620664
],
665+
outputResult: {
666+
'0': ['34', '255'],
667+
'1': {
668+
'0': '42',
669+
'1': '56',
670+
'2': {
671+
'0': '45',
672+
'1': '78',
673+
propertyOne: '45',
674+
propertyTwo: '78',
675+
},
676+
propertyOne: '42',
677+
propertyTwo: '56',
678+
ChildStruct: {
679+
'0': '45',
680+
'1': '78',
681+
propertyOne: '45',
682+
propertyTwo: '78',
683+
},
684+
},
685+
__length__: 2,
686+
},
621687
},
622688
];
623689

0 commit comments

Comments
 (0)