From 60acf7dbeb128199c30c28a2e8972787519b330a Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 19:42:17 +0200
Subject: [PATCH 1/8] move `decodeMethodParams` and `decodeMethodReturn` to
web3-eth-abi
---
.../web3-eth-abi/src/api/functions_api.ts | 58 ++++++++++++++++++-
.../src/contract-deployer-method-class.ts | 3 +-
packages/web3-eth-contract/src/contract.ts | 11 ++--
packages/web3-eth-contract/src/encoding.ts | 48 ++-------------
4 files changed, 67 insertions(+), 53 deletions(-)
diff --git a/packages/web3-eth-abi/src/api/functions_api.ts b/packages/web3-eth-abi/src/api/functions_api.ts
index 7504f352275..9807da95cfe 100644
--- a/packages/web3-eth-abi/src/api/functions_api.ts
+++ b/packages/web3-eth-abi/src/api/functions_api.ts
@@ -19,11 +19,11 @@ along with web3.js. If not, see .
*
* @module ABI
*/
-import { AbiError } from 'web3-errors';
+import { AbiError, Web3ContractError } from 'web3-errors';
import { sha3Raw } from 'web3-utils';
-import { AbiFunctionFragment } from 'web3-types';
+import { AbiConstructorFragment, AbiFunctionFragment, DecodedParams, HexString } from 'web3-types';
import { isAbiFunctionFragment, jsonInterfaceMethodToString } from '../utils.js';
-import { encodeParameters } from './parameters_api.js';
+import { decodeParameters, encodeParameters } from './parameters_api.js';
/**
* Encodes the function name to its ABI representation, which are the first 4 bytes of the sha3 of the function name including types.
@@ -143,3 +143,55 @@ export const encodeFunctionCall = (
params ?? [],
).replace('0x', '')}`;
};
+
+export const decodeMethodParams = (
+ functionsAbis: AbiFunctionFragment | AbiConstructorFragment,
+ data: HexString,
+ methodSignatureProvided = true,
+): DecodedParams & { __method__: string } => {
+ const value =
+ methodSignatureProvided && data && data.length >= 10 && data.startsWith('0x')
+ ? data.slice(10)
+ : data;
+ if (!functionsAbis.inputs) {
+ if (value !== '') {
+ throw new Web3ContractError('No inputs found in the ABI');
+ } else {
+ return {
+ __length__: 0,
+ __method__: jsonInterfaceMethodToString(functionsAbis),
+ };
+ }
+ }
+ const result = decodeParameters([...functionsAbis.inputs], value);
+ return {
+ ...result,
+ __method__: jsonInterfaceMethodToString(functionsAbis),
+ };
+};
+
+export const decodeMethodReturn = (abi: AbiFunctionFragment, returnValues?: HexString) => {
+ // If it was constructor then we need to return contract address
+ if (abi.type === 'constructor') {
+ return returnValues;
+ }
+
+ if (!returnValues) {
+ // Using "null" value intentionally to match legacy behavior
+ // eslint-disable-next-line no-null/no-null
+ return null;
+ }
+
+ const value = returnValues.length >= 2 ? returnValues.slice(2) : returnValues;
+ if (!abi.outputs) {
+ // eslint-disable-next-line no-null/no-null
+ return null;
+ }
+ const result = decodeParameters([...abi.outputs], value);
+
+ if (result.__length__ === 1) {
+ return result[0];
+ }
+
+ return result;
+};
diff --git a/packages/web3-eth-contract/src/contract-deployer-method-class.ts b/packages/web3-eth-contract/src/contract-deployer-method-class.ts
index 33018541390..b7c14f3f1d9 100644
--- a/packages/web3-eth-contract/src/contract-deployer-method-class.ts
+++ b/packages/web3-eth-contract/src/contract-deployer-method-class.ts
@@ -17,6 +17,7 @@ along with web3.js. If not, see .
import { Web3ContractError } from 'web3-errors';
import { sendTransaction, SendTransactionEvents, SendTransactionOptions } from 'web3-eth';
+import { decodeMethodParams } from 'web3-eth-abi';
import {
AbiConstructorFragment,
AbiFunctionFragment,
@@ -34,7 +35,7 @@ import {
import { format } from 'web3-utils';
import { isNullish } from 'web3-validator';
import { Web3PromiEvent } from 'web3-core';
-import { decodeMethodParams, encodeMethodABI } from './encoding.js';
+import { encodeMethodABI } from './encoding.js';
import { NonPayableTxOptions, PayableTxOptions } from './types.js';
import { getSendTxParams } from './utils.js';
// eslint-disable-next-line import/no-cycle
diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts
index a99dc27628e..83876aa600f 100644
--- a/packages/web3-eth-contract/src/contract.ts
+++ b/packages/web3-eth-contract/src/contract.ts
@@ -42,6 +42,8 @@ import {
TransactionMiddleware,
} from 'web3-eth';
import {
+ decodeMethodReturn,
+ decodeMethodParams,
encodeEventSignature,
encodeFunctionSignature,
decodeContractErrorData,
@@ -99,12 +101,7 @@ import {
ValidationSchemaInput,
Web3ValidatorError,
} from 'web3-validator';
-import {
- decodeMethodReturn,
- decodeMethodParams,
- encodeEventABI,
- encodeMethodABI,
-} from './encoding.js';
+import { encodeEventABI, encodeMethodABI } from './encoding.js';
import { ContractLogsSubscription } from './contract_log_subscription.js';
import {
ContractEventOptions,
@@ -1026,7 +1023,7 @@ export class Contract
`The ABI for the provided method signature ${methodSignature} was not found.`,
);
}
- return { ...decodeMethodParams(abi, data), __method__: jsonInterfaceMethodToString(abi) };
+ return decodeMethodParams(abi, data);
}
private _parseAndSetJsonInterface(
diff --git a/packages/web3-eth-contract/src/encoding.ts b/packages/web3-eth-contract/src/encoding.ts
index 48531277518..e701744c88f 100644
--- a/packages/web3-eth-contract/src/encoding.ts
+++ b/packages/web3-eth-contract/src/encoding.ts
@@ -30,7 +30,8 @@ import {
} from 'web3-types';
import {
- decodeParameters,
+ decodeMethodParams as decodeMethodParamsFromEthAbi,
+ decodeMethodReturn as decodeMethodReturnFromEthAbi,
encodeEventSignature,
encodeFunctionSignature,
encodeParameter,
@@ -153,44 +154,7 @@ export const encodeMethodABI = (
return `${encodeFunctionSignature(abi)}${params}`;
};
-export const decodeMethodParams = (
- abi: AbiFunctionFragment | AbiConstructorFragment,
- data: HexString,
- methodSignatureProvided = true,
-) => {
- const value =
- methodSignatureProvided && data && data.length >= 10 && data.startsWith('0x')
- ? data.slice(10)
- : data;
- if (!abi.inputs) {
- throw new Web3ContractError('No inputs found in the ABI');
- }
- const result = decodeParameters([...abi.inputs], value);
- return result;
-};
-
-export const decodeMethodReturn = (abi: AbiFunctionFragment, returnValues?: HexString) => {
- // If it was constructor then we need to return contract address
- if (abi.type === 'constructor') {
- return returnValues;
- }
-
- if (!returnValues) {
- // Using "null" value intentionally to match legacy behavior
- // eslint-disable-next-line no-null/no-null
- return null;
- }
-
- const value = returnValues.length >= 2 ? returnValues.slice(2) : returnValues;
- if (!abi.outputs) {
- // eslint-disable-next-line no-null/no-null
- return null;
- }
- const result = decodeParameters([...abi.outputs], value);
-
- if (result.__length__ === 1) {
- return result[0];
- }
-
- return result;
-};
+/** @deprecated import from ''web3-eth-abi' instead. */
+export const decodeMethodParams = decodeMethodParamsFromEthAbi;
+/** @deprecated import from ''web3-eth-abi' instead. */
+export const decodeMethodReturn = decodeMethodReturnFromEthAbi;
From f1ee17d95c9111a956e1900fbecd525130deac17 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 21:38:16 +0200
Subject: [PATCH 2/8] add unit tests
---
.../unit/decodeMethodParamsAndReturn.test.ts | 116 ++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
diff --git a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
new file mode 100644
index 00000000000..a57bca8e4ea
--- /dev/null
+++ b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
@@ -0,0 +1,116 @@
+/*
+This file is part of web3.js.
+
+web3.js is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+web3.js is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with web3.js. If not, see .
+*/
+
+import { decodeMethodParams, decodeMethodReturn } from '../../src';
+
+describe('decodeMethodParams and decodeMethodReturn tests should pass', () => {
+ it('decodeMethodParams should decode single-value data of a method', async () => {
+ const result =
+ '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+
+ const params = decodeMethodParams(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ outputs: [
+ { internalType: 'bool', name: '', type: 'bool' },
+ { internalType: 'string', name: '', type: 'string' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ );
+
+ expect(params).toMatchObject({
+ __method__: 'setGreeting(string)',
+ __length__: 1,
+ '0': 'Hello',
+ _greeting: 'Hello',
+ });
+ });
+
+ it('decodeMethodParams should decode multi-value data of a method', async () => {
+ const result =
+ '0xa413686200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000';
+
+ const params = decodeMethodParams(
+ {
+ inputs: [
+ { internalType: 'string', name: '_greeting', type: 'string' },
+ { internalType: 'string', name: '_second_greeting', type: 'string' },
+ ],
+ name: 'setGreeting',
+ outputs: [
+ { internalType: 'bool', name: '', type: 'bool' },
+ { internalType: 'string', name: '', type: 'string' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ );
+
+ expect(params).toEqual({
+ '0': 'Hello',
+ '1': 'Another Greeting',
+ __length__: 2,
+ __method__: 'setGreeting(string,string)',
+ _greeting: 'Hello',
+ _second_greeting: 'Another Greeting',
+ });
+ });
+
+ it('decodeMethodReturn should decode single-value data of a method', async () => {
+ const result =
+ '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+
+ const params = decodeMethodReturn(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ );
+
+ expect(params).toBe('Hello');
+ });
+
+ it('decodeMethodReturn should decode multi-value data of a method', async () => {
+ const result =
+ '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+
+ const params = decodeMethodReturn(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ outputs: [
+ { internalType: 'string', name: '', type: 'string' },
+ { internalType: 'bool', name: '', type: 'bool' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ );
+
+ expect(params).toEqual({ '0': 'Hello', '1': true, __length__: 2 });
+ });
+});
From 247f00c5d044b52de1690eec1544a8bcfd9ecae2 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 22:21:51 +0200
Subject: [PATCH 3/8] renaming, enhancing and documenting
---
.../web3-eth-abi/src/api/functions_api.ts | 46 ++++++++++++-------
.../unit/decodeMethodParamsAndReturn.test.ts | 20 ++++----
.../src/contract-deployer-method-class.ts | 4 +-
packages/web3-eth-contract/src/contract.ts | 10 ++--
packages/web3-eth-contract/src/encoding.ts | 12 ++---
5 files changed, 52 insertions(+), 40 deletions(-)
diff --git a/packages/web3-eth-abi/src/api/functions_api.ts b/packages/web3-eth-abi/src/api/functions_api.ts
index 9807da95cfe..b7b7757d5d5 100644
--- a/packages/web3-eth-abi/src/api/functions_api.ts
+++ b/packages/web3-eth-abi/src/api/functions_api.ts
@@ -144,8 +144,16 @@ export const encodeFunctionCall = (
).replace('0x', '')}`;
};
-export const decodeMethodParams = (
- functionsAbis: AbiFunctionFragment | AbiConstructorFragment,
+/**
+ * Decodes a function call data using its `JSON interface` object.
+ * The JSON interface spec documentation https://docs.soliditylang.org/en/latest/abi-spec.html#json
+ * @param functionsAbi - The `JSON interface` object of the function.
+ * @param data - The data to decode
+ * @param methodSignatureProvided - (Optional) if `false` do not remove the first 4 bytes that would rather contain the function signature.
+ * @returns - The data decoded according to the passed ABI.
+ */
+export const decodeFunctionCall = (
+ functionsAbi: AbiFunctionFragment | AbiConstructorFragment,
data: HexString,
methodSignatureProvided = true,
): DecodedParams & { __method__: string } => {
@@ -153,26 +161,30 @@ export const decodeMethodParams = (
methodSignatureProvided && data && data.length >= 10 && data.startsWith('0x')
? data.slice(10)
: data;
- if (!functionsAbis.inputs) {
- if (value !== '') {
- throw new Web3ContractError('No inputs found in the ABI');
- } else {
- return {
- __length__: 0,
- __method__: jsonInterfaceMethodToString(functionsAbis),
- };
- }
+ if (!functionsAbi.inputs) {
+ throw new Web3ContractError('No inputs found in the ABI');
}
- const result = decodeParameters([...functionsAbis.inputs], value);
+ const result = decodeParameters([...functionsAbi.inputs], value);
return {
...result,
- __method__: jsonInterfaceMethodToString(functionsAbis),
+ __method__: jsonInterfaceMethodToString(functionsAbi),
};
};
-export const decodeMethodReturn = (abi: AbiFunctionFragment, returnValues?: HexString) => {
+/**
+ * Decodes a function call data using its `JSON interface` object.
+ * The JSON interface spec documentation https://docs.soliditylang.org/en/latest/abi-spec.html#json
+ * @returns - The ABI encoded function call, which, means the function signature and the parameters passed.
+ * @param functionsAbi - The `JSON interface` object of the function.
+ * @param returnValues - The data (the function-returned-values) to decoded
+ * @returns - The function-returned-values decoded according to the passed ABI.
+ */
+export const decodeFunctionReturn = (
+ functionsAbi: AbiFunctionFragment,
+ returnValues?: HexString,
+) => {
// If it was constructor then we need to return contract address
- if (abi.type === 'constructor') {
+ if (functionsAbi.type === 'constructor') {
return returnValues;
}
@@ -183,11 +195,11 @@ export const decodeMethodReturn = (abi: AbiFunctionFragment, returnValues?: HexS
}
const value = returnValues.length >= 2 ? returnValues.slice(2) : returnValues;
- if (!abi.outputs) {
+ if (!functionsAbi.outputs) {
// eslint-disable-next-line no-null/no-null
return null;
}
- const result = decodeParameters([...abi.outputs], value);
+ const result = decodeParameters([...functionsAbi.outputs], value);
if (result.__length__ === 1) {
return result[0];
diff --git a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
index a57bca8e4ea..9bc824578fa 100644
--- a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
+++ b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
@@ -15,14 +15,14 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
-import { decodeMethodParams, decodeMethodReturn } from '../../src';
+import { decodeFunctionCall, decodeFunctionReturn } from '../../src';
-describe('decodeMethodParams and decodeMethodReturn tests should pass', () => {
- it('decodeMethodParams should decode single-value data of a method', async () => {
+describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () => {
+ it('decodeFunctionCall should decode single-value data of a method', async () => {
const result =
'0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
- const params = decodeMethodParams(
+ const params = decodeFunctionCall(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
name: 'setGreeting',
@@ -44,11 +44,11 @@ describe('decodeMethodParams and decodeMethodReturn tests should pass', () => {
});
});
- it('decodeMethodParams should decode multi-value data of a method', async () => {
+ it('decodeFunctionCall should decode multi-value data of a method', async () => {
const result =
'0xa413686200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000';
- const params = decodeMethodParams(
+ const params = decodeFunctionCall(
{
inputs: [
{ internalType: 'string', name: '_greeting', type: 'string' },
@@ -75,11 +75,11 @@ describe('decodeMethodParams and decodeMethodReturn tests should pass', () => {
});
});
- it('decodeMethodReturn should decode single-value data of a method', async () => {
+ it('decodeFunctionReturn should decode single-value data of a method', async () => {
const result =
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
- const params = decodeMethodReturn(
+ const params = decodeFunctionReturn(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
name: 'setGreeting',
@@ -93,11 +93,11 @@ describe('decodeMethodParams and decodeMethodReturn tests should pass', () => {
expect(params).toBe('Hello');
});
- it('decodeMethodReturn should decode multi-value data of a method', async () => {
+ it('decodeFunctionReturn should decode multi-value data of a method', async () => {
const result =
'0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
- const params = decodeMethodReturn(
+ const params = decodeFunctionReturn(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
name: 'setGreeting',
diff --git a/packages/web3-eth-contract/src/contract-deployer-method-class.ts b/packages/web3-eth-contract/src/contract-deployer-method-class.ts
index b7c14f3f1d9..08d04b16c8f 100644
--- a/packages/web3-eth-contract/src/contract-deployer-method-class.ts
+++ b/packages/web3-eth-contract/src/contract-deployer-method-class.ts
@@ -17,7 +17,7 @@ along with web3.js. If not, see .
import { Web3ContractError } from 'web3-errors';
import { sendTransaction, SendTransactionEvents, SendTransactionOptions } from 'web3-eth';
-import { decodeMethodParams } from 'web3-eth-abi';
+import { decodeFunctionCall } from 'web3-eth-abi';
import {
AbiConstructorFragment,
AbiFunctionFragment,
@@ -210,7 +210,7 @@ export class DeployerMethodClass {
public decodeData(data: HexString) {
return {
- ...decodeMethodParams(
+ ...decodeFunctionCall(
this.constructorAbi,
data.replace(this.deployData as string, ''),
false,
diff --git a/packages/web3-eth-contract/src/contract.ts b/packages/web3-eth-contract/src/contract.ts
index 83876aa600f..60602497e65 100644
--- a/packages/web3-eth-contract/src/contract.ts
+++ b/packages/web3-eth-contract/src/contract.ts
@@ -42,8 +42,8 @@ import {
TransactionMiddleware,
} from 'web3-eth';
import {
- decodeMethodReturn,
- decodeMethodParams,
+ decodeFunctionCall,
+ decodeFunctionReturn,
encodeEventSignature,
encodeFunctionSignature,
decodeContractErrorData,
@@ -1023,7 +1023,7 @@ export class Contract
`The ABI for the provided method signature ${methodSignature} was not found.`,
);
}
- return decodeMethodParams(abi, data);
+ return decodeFunctionCall(abi, data);
}
private _parseAndSetJsonInterface(
@@ -1248,7 +1248,7 @@ export class Contract
}),
encodeABI: () => encodeMethodABI(methodAbi, abiParams),
- decodeData: (data: HexString) => decodeMethodParams(methodAbi, data),
+ decodeData: (data: HexString) => decodeFunctionCall(methodAbi, data),
createAccessList: async (
options?: PayableCallOptions | NonPayableCallOptions,
@@ -1302,7 +1302,7 @@ export class Contract
block,
this.defaultReturnFormat as typeof DEFAULT_RETURN_FORMAT,
);
- return decodeMethodReturn(abi, result);
+ return decodeFunctionReturn(abi, result);
} catch (error: unknown) {
if (error instanceof ContractExecutionError) {
// this will parse the error data by trying to decode the ABI error inputs according to EIP-838
diff --git a/packages/web3-eth-contract/src/encoding.ts b/packages/web3-eth-contract/src/encoding.ts
index e701744c88f..2b347269d36 100644
--- a/packages/web3-eth-contract/src/encoding.ts
+++ b/packages/web3-eth-contract/src/encoding.ts
@@ -30,8 +30,8 @@ import {
} from 'web3-types';
import {
- decodeMethodParams as decodeMethodParamsFromEthAbi,
- decodeMethodReturn as decodeMethodReturnFromEthAbi,
+ decodeFunctionCall,
+ decodeFunctionReturn,
encodeEventSignature,
encodeFunctionSignature,
encodeParameter,
@@ -154,7 +154,7 @@ export const encodeMethodABI = (
return `${encodeFunctionSignature(abi)}${params}`;
};
-/** @deprecated import from ''web3-eth-abi' instead. */
-export const decodeMethodParams = decodeMethodParamsFromEthAbi;
-/** @deprecated import from ''web3-eth-abi' instead. */
-export const decodeMethodReturn = decodeMethodReturnFromEthAbi;
+/** @deprecated import `decodeFunctionCall` from ''web3-eth-abi' instead. */
+export const decodeMethodParams = decodeFunctionCall;
+/** @deprecated import `decodeFunctionReturn` from ''web3-eth-abi' instead. */
+export const decodeMethodReturn = decodeFunctionReturn;
From cc7a7276786ec61e1706402b662d0a312b2694a7 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 22:22:17 +0200
Subject: [PATCH 4/8] update CHANGELOG.md
---
packages/web3-eth-abi/CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/packages/web3-eth-abi/CHANGELOG.md b/packages/web3-eth-abi/CHANGELOG.md
index 9cb5bd2d7b9..7a6951386d9 100644
--- a/packages/web3-eth-abi/CHANGELOG.md
+++ b/packages/web3-eth-abi/CHANGELOG.md
@@ -195,3 +195,7 @@ Documentation:
- `decodeLog` , `decodeParametersWith` , `decodeParameters` and `decodeParameters` now accepts first immutable param as well (#7288)
## [Unreleased]
+
+### Added
+
+- added `decodeFunctionCall` and `decodeFunctionReturn`. (#7345)
From 501ee4d54219f7e2a84dceb3b6f52211aa6a8369 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 22:22:27 +0200
Subject: [PATCH 5/8] add 2 tests
---
.../unit/decodeMethodParamsAndReturn.test.ts | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
index 9bc824578fa..e0530fe703f 100644
--- a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
+++ b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
@@ -44,6 +44,55 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
});
});
+ it('decodeFunctionCall should decode data of a method without removing the method signature (if intended)', async () => {
+ const result =
+ '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+
+ const params = decodeFunctionCall(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ outputs: [
+ { internalType: 'bool', name: '', type: 'bool' },
+ { internalType: 'string', name: '', type: 'string' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ false,
+ );
+
+ expect(params).toMatchObject({
+ __method__: 'setGreeting(string)',
+ __length__: 1,
+ '0': 'Hello',
+ _greeting: 'Hello',
+ });
+ });
+
+ it('decodeFunctionCall should throw if no inputs at the ABI', async () => {
+ const result =
+ '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+
+ expect(() =>
+ decodeFunctionCall(
+ {
+ name: 'setGreeting',
+ // no `inputs` provided!
+ outputs: [
+ { internalType: 'bool', name: '', type: 'bool' },
+ { internalType: 'string', name: '', type: 'string' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ result,
+ false,
+ ),
+ ).toThrow('No inputs found in the ABI');
+ });
+
it('decodeFunctionCall should decode multi-value data of a method', async () => {
const result =
'0xa413686200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000';
From b0dd7c50dca6ca1250d35b0b663c6b318f354724 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 21 Oct 2024 22:39:11 +0200
Subject: [PATCH 6/8] add a test
---
.../web3-eth-abi/src/api/functions_api.ts | 2 +-
.../unit/decodeMethodParamsAndReturn.test.ts | 40 +++++++++++++------
2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/packages/web3-eth-abi/src/api/functions_api.ts b/packages/web3-eth-abi/src/api/functions_api.ts
index b7b7757d5d5..0ff43b35ad4 100644
--- a/packages/web3-eth-abi/src/api/functions_api.ts
+++ b/packages/web3-eth-abi/src/api/functions_api.ts
@@ -183,7 +183,7 @@ export const decodeFunctionReturn = (
functionsAbi: AbiFunctionFragment,
returnValues?: HexString,
) => {
- // If it was constructor then we need to return contract address
+ // If it is a constructor there is nothing to decode!
if (functionsAbi.type === 'constructor') {
return returnValues;
}
diff --git a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
index e0530fe703f..8b245a3b8b1 100644
--- a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
+++ b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
@@ -15,11 +15,12 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
+import { AbiFunctionFragment } from 'web3-types';
import { decodeFunctionCall, decodeFunctionReturn } from '../../src';
describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () => {
it('decodeFunctionCall should decode single-value data of a method', async () => {
- const result =
+ const data =
'0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
const params = decodeFunctionCall(
@@ -33,7 +34,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
);
expect(params).toMatchObject({
@@ -45,7 +46,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
});
it('decodeFunctionCall should decode data of a method without removing the method signature (if intended)', async () => {
- const result =
+ const data =
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
const params = decodeFunctionCall(
@@ -59,7 +60,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
false,
);
@@ -72,7 +73,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
});
it('decodeFunctionCall should throw if no inputs at the ABI', async () => {
- const result =
+ const data =
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
expect(() =>
@@ -87,14 +88,14 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
false,
),
).toThrow('No inputs found in the ABI');
});
it('decodeFunctionCall should decode multi-value data of a method', async () => {
- const result =
+ const data =
'0xa413686200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000';
const params = decodeFunctionCall(
@@ -111,7 +112,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
);
expect(params).toEqual({
@@ -125,7 +126,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
});
it('decodeFunctionReturn should decode single-value data of a method', async () => {
- const result =
+ const data =
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
const params = decodeFunctionReturn(
@@ -136,14 +137,14 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
);
expect(params).toBe('Hello');
});
it('decodeFunctionReturn should decode multi-value data of a method', async () => {
- const result =
+ const data =
'0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
const params = decodeFunctionReturn(
@@ -157,9 +158,24 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
stateMutability: 'nonpayable',
type: 'function',
},
- result,
+ data,
);
expect(params).toEqual({ '0': 'Hello', '1': true, __length__: 2 });
});
+
+ it('decodeFunctionReturn should decode nothing if it is called on a constructor', async () => {
+ const result = 'anything passed should be returned as-is';
+
+ const params = decodeFunctionReturn(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ stateMutability: 'nonpayable',
+ type: 'constructor',
+ } as unknown as AbiFunctionFragment,
+ result,
+ );
+
+ expect(params).toEqual(result);
+ });
});
From 27efc5af1f88af3abf4515c83ba7aeba9e083bab Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 22 Oct 2024 13:19:02 +0200
Subject: [PATCH 7/8] add 2 tests
---
.../unit/decodeMethodParamsAndReturn.test.ts | 52 ++++++++++++++++---
1 file changed, 44 insertions(+), 8 deletions(-)
diff --git a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
index 8b245a3b8b1..acf96c2aa41 100644
--- a/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
+++ b/packages/web3-eth-abi/test/unit/decodeMethodParamsAndReturn.test.ts
@@ -129,7 +129,7 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
const data =
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
- const params = decodeFunctionReturn(
+ const decodedResult = decodeFunctionReturn(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
name: 'setGreeting',
@@ -140,14 +140,14 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
data,
);
- expect(params).toBe('Hello');
+ expect(decodedResult).toBe('Hello');
});
it('decodeFunctionReturn should decode multi-value data of a method', async () => {
const data =
'0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
- const params = decodeFunctionReturn(
+ const decodedResult = decodeFunctionReturn(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
name: 'setGreeting',
@@ -161,21 +161,57 @@ describe('decodeFunctionCall and decodeFunctionReturn tests should pass', () =>
data,
);
- expect(params).toEqual({ '0': 'Hello', '1': true, __length__: 2 });
+ expect(decodedResult).toEqual({ '0': 'Hello', '1': true, __length__: 2 });
});
it('decodeFunctionReturn should decode nothing if it is called on a constructor', async () => {
- const result = 'anything passed should be returned as-is';
+ const data = 'anything passed should be returned as-is';
- const params = decodeFunctionReturn(
+ const decodedResult = decodeFunctionReturn(
{
inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
stateMutability: 'nonpayable',
type: 'constructor',
} as unknown as AbiFunctionFragment,
- result,
+ data,
+ );
+
+ expect(decodedResult).toEqual(data);
+ });
+
+ it('decodeFunctionReturn should return `null` if no values passed', async () => {
+ const data = '';
+
+ const decodedResult = decodeFunctionReturn(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ outputs: [
+ { internalType: 'string', name: '', type: 'string' },
+ { internalType: 'bool', name: '', type: 'bool' },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ data,
+ );
+
+ expect(decodedResult).toBeNull();
+ });
+
+ it('decodeFunctionReturn should return `null` if no function output provided', async () => {
+ const data = '0x000000';
+
+ const decodedResult = decodeFunctionReturn(
+ {
+ inputs: [{ internalType: 'string', name: '_greeting', type: 'string' }],
+ name: 'setGreeting',
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ data,
);
- expect(params).toEqual(result);
+ expect(decodedResult).toBeNull();
});
});
From 4a0e890b10621e9e1f5f8c116e4e8d7ab113d77d Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 22 Oct 2024 13:38:43 +0200
Subject: [PATCH 8/8] add examples in functions docs
---
.../web3-eth-abi/src/api/functions_api.ts | 77 ++++++++++++++++++-
1 file changed, 76 insertions(+), 1 deletion(-)
diff --git a/packages/web3-eth-abi/src/api/functions_api.ts b/packages/web3-eth-abi/src/api/functions_api.ts
index 0ff43b35ad4..8518deeab24 100644
--- a/packages/web3-eth-abi/src/api/functions_api.ts
+++ b/packages/web3-eth-abi/src/api/functions_api.ts
@@ -151,6 +151,37 @@ export const encodeFunctionCall = (
* @param data - The data to decode
* @param methodSignatureProvided - (Optional) if `false` do not remove the first 4 bytes that would rather contain the function signature.
* @returns - The data decoded according to the passed ABI.
+ * @example
+ * ```ts
+ * const data =
+ * '0xa413686200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010416e6f74686572204772656574696e6700000000000000000000000000000000';
+ * const params = decodeFunctionCall(
+ * {
+ * inputs: [
+ * { internalType: 'string', name: '_greeting', type: 'string' },
+ * { internalType: 'string', name: '_second_greeting', type: 'string' },
+ * ],
+ * name: 'setGreeting',
+ * outputs: [
+ * { internalType: 'bool', name: '', type: 'bool' },
+ * { internalType: 'string', name: '', type: 'string' },
+ * ],
+ * stateMutability: 'nonpayable',
+ * type: 'function',
+ * },
+ * data,
+ * );
+
+ * console.log(params);
+ * > {
+ * > '0': 'Hello',
+ * > '1': 'Another Greeting',
+ * > __length__: 2,
+ * > __method__: 'setGreeting(string,string)',
+ * > _greeting: 'Hello',
+ * > _second_greeting: 'Another Greeting',
+ * > }
+ * ```
*/
export const decodeFunctionCall = (
functionsAbi: AbiFunctionFragment | AbiConstructorFragment,
@@ -177,7 +208,51 @@ export const decodeFunctionCall = (
* @returns - The ABI encoded function call, which, means the function signature and the parameters passed.
* @param functionsAbi - The `JSON interface` object of the function.
* @param returnValues - The data (the function-returned-values) to decoded
- * @returns - The function-returned-values decoded according to the passed ABI.
+ * @returns - The function-returned-values decoded according to the passed ABI. If there are multiple values, it returns them as an object as the example below. But if it is a single value, it returns it only for simplicity.
+ * @example
+ * ```ts
+ * // decode a multi-value data of a method
+ * const data =
+ * '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+ * const decodedResult = decodeFunctionReturn(
+ * {
+ * inputs: [
+ * { internalType: 'string', name: '_greeting', type: 'string' }
+ * ],
+ * name: 'setGreeting',
+ * outputs: [
+ * { internalType: 'string', name: '', type: 'string' },
+ * { internalType: 'bool', name: '', type: 'bool' },
+ * ],
+ * stateMutability: 'nonpayable',
+ * type: 'function',
+ * },
+ * data,
+ * );
+
+ * console.log(decodedResult);
+ * > { '0': 'Hello', '1': true, __length__: 2 }
+ *
+ *
+ * // decode a single-value data of a method
+ * const data =
+ * '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+ * const decodedResult = decodeFunctionReturn(
+ * {
+ * inputs: [
+ * { internalType: 'string', name: '_greeting', type: 'string' }
+ * ],
+ * name: 'setGreeting',
+ * outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ * stateMutability: 'nonpayable',
+ * type: 'function',
+ * },
+ * data,
+ * );
+
+ * console.log(decodedResult);
+ * > 'Hello'
+ * ```
*/
export const decodeFunctionReturn = (
functionsAbi: AbiFunctionFragment,