From 37e26fa235a3feb51d42384f069996004505a535 Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Thu, 24 Oct 2024 17:21:48 +0100 Subject: [PATCH 1/7] refactor: first commit --- .../thor-client/contracts/contracts-module.ts | 46 ++++++++++++++----- .../contracts/model/contract-proxy.ts | 4 +- .../src/thor-client/contracts/types.d.ts | 9 +++- packages/network/src/utils/vns/index.ts | 12 +++-- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/packages/network/src/thor-client/contracts/contracts-module.ts b/packages/network/src/thor-client/contracts/contracts-module.ts index f89578aeb..e7ce24bd1 100644 --- a/packages/network/src/thor-client/contracts/contracts-module.ts +++ b/packages/network/src/thor-client/contracts/contracts-module.ts @@ -1,11 +1,11 @@ import { ABIContract, + Address, + Clause, dataUtils, Hex, - Address, - VET, Units, - Clause, + VET, type ABIFunction } from '@vechain/sdk-core'; import { type Abi } from 'abitype'; @@ -84,7 +84,7 @@ class ContractsModule { functionAbi: ABIFunction, functionData: unknown[], contractCallOptions?: ContractCallOptions - ): Promise { + ): Promise { // Simulate the transaction to get the result of the contract call const response = await this.thor.transactions.simulateTransaction( [ @@ -104,11 +104,27 @@ class ContractsModule { * * @link see [Error handling: Assert, Require, Revert and Exceptions](https://docs.soliditylang.org/en/latest/control-structures.html#error-handling-assert-require-revert-and-exceptions) */ - return decodeRevertReason(response[0].data) ?? ''; + const errorMessage = decodeRevertReason(response[0].data) ?? ''; + return { + success: false, + result: { + errorMessage + } + }; } else { // Returning an array of values. - // The viem format is a single value/JSON object (ABIFunction#decodeResult) - return functionAbi.decodeOutputAsArray(Hex.of(response[0].data)); + const encodedResult = Hex.of(response[0].data); + const objectValue = functionAbi.decodeResult( + encodedResult + ) as object; + const values = functionAbi.decodeOutputAsArray(encodedResult); + return { + success: true, + result: { + objectValue, + values + } + }; } } @@ -120,7 +136,7 @@ class ContractsModule { public async executeMultipleClausesCall( clauses: ContractClause[], options?: SimulateTransactionOptions - ): Promise> { + ): Promise { // Simulate the transaction to get the result of the contract call const response = await this.thor.transactions.simulateTransaction( clauses.map((clause) => clause.clause), @@ -128,9 +144,17 @@ class ContractsModule { ); // Returning an array of values. // The viem format is a single value/JSON object (ABIFunction#decodeResult) - return response.map((res, index) => - clauses[index].functionAbi.decodeOutputAsArray(Hex.of(res.data)) - ); + return response.map((res, index) => ({ + success: true, + result: { + objectValue: clauses[index].functionAbi.decodeResult( + Hex.of(res.data) + ) as object, + values: clauses[index].functionAbi.decodeOutputAsArray( + Hex.of(res.data) + ) + } + })); } /** diff --git a/packages/network/src/thor-client/contracts/model/contract-proxy.ts b/packages/network/src/thor-client/contracts/model/contract-proxy.ts index 747b67f21..728a87333 100644 --- a/packages/network/src/thor-client/contracts/model/contract-proxy.ts +++ b/packages/network/src/thor-client/contracts/model/contract-proxy.ts @@ -61,7 +61,7 @@ function getReadProxy( const revisionValue = extractOptionsResult.clauseAdditionalOptions?.revision; - return (await contract.thor.contracts.executeCall( + return await contract.thor.contracts.executeCall( contract.address, contract.getFunctionAbi(prop), extractOptionsResult.args, @@ -75,7 +75,7 @@ function getReadProxy( revision: revisionValue, includeABI: true } - )) as ContractCallResult; + ); }; } }); diff --git a/packages/network/src/thor-client/contracts/types.d.ts b/packages/network/src/thor-client/contracts/types.d.ts index 2caf0ea81..6c65254ac 100644 --- a/packages/network/src/thor-client/contracts/types.d.ts +++ b/packages/network/src/thor-client/contracts/types.d.ts @@ -43,7 +43,14 @@ type ContractCallOptions = SimulateTransactionOptions & ClauseOptions; /** * Represents the result of a contract call operation, encapsulating the output of the call. */ -type ContractCallResult = unknown[]; +interface ContractCallResult { + success: boolean; + result: { + objectValue?: object; + values?: unknown[]; + errorMessage?: string; + }; +} /** * Represents a contract clause, which includes the clause and the corresponding function ABI. diff --git a/packages/network/src/utils/vns/index.ts b/packages/network/src/utils/vns/index.ts index e01aac93b..4b3aaa206 100644 --- a/packages/network/src/utils/vns/index.ts +++ b/packages/network/src/utils/vns/index.ts @@ -42,14 +42,16 @@ const resolveNames = async ( const resolveUtilsAddress = NetworkContracts[genesisBlock.id].resolveUtils; // use the resolveUtils to lookup names - const [addresses] = (await thorClient.contracts.executeCall( + const callGetAddresses = await thorClient.contracts.executeCall( resolveUtilsAddress, ABIItem.ofSignature( ABIFunction, 'function getAddresses(string[] names) returns (address[] addresses)' ), [names] - )) as string[][]; + ); + + const [addresses] = callGetAddresses.result.values as string[][]; return addresses.map((address) => { // zero addresses are missing configuration entries @@ -101,14 +103,16 @@ const lookupAddresses = async ( const resolveUtilsAddress = NetworkContracts[genesisBlock.id].resolveUtils; // use the resolveUtils to lookup names - const [names] = (await thorClient.contracts.executeCall( + const callGetNames = await thorClient.contracts.executeCall( resolveUtilsAddress, ABIItem.ofSignature( ABIFunction, 'function getNames(address[] addresses) returns (string[] names)' ), [addresses] - )) as string[][]; + ); + + const [names] = callGetNames.result.values as string[][]; return names.map((name) => { // empty strings indicate a missing entry From c680e7ac5378a7c35a4d63ba00cc0356d07693c9 Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 11:08:15 +0100 Subject: [PATCH 2/7] refactor: fixed some tests --- .../methods/eth_gasPrice/eth_gasPrice.ts | 10 +- .../thor-client/contracts/contracts-module.ts | 21 ++-- .../src/thor-client/contracts/types.d.ts | 4 +- packages/network/src/utils/vns/index.ts | 4 +- .../contracts/contract.erc20.solo.test.ts | 106 +++++++++++++++--- .../contracts/contract.testnet.test.ts | 17 ++- .../tests/thor-client/contracts/fixture.ts | 32 +++++- 7 files changed, 152 insertions(+), 42 deletions(-) diff --git a/packages/network/src/provider/utils/rpc-mapper/methods/eth_gasPrice/eth_gasPrice.ts b/packages/network/src/provider/utils/rpc-mapper/methods/eth_gasPrice/eth_gasPrice.ts index 597d747b9..15e196c12 100644 --- a/packages/network/src/provider/utils/rpc-mapper/methods/eth_gasPrice/eth_gasPrice.ts +++ b/packages/network/src/provider/utils/rpc-mapper/methods/eth_gasPrice/eth_gasPrice.ts @@ -6,13 +6,11 @@ import { type ThorClient } from '../../../../../thor-client'; * @returns The current gas price in Wei unit considering that 1 VTHO equals 1e18 Wei. */ const ethGasPrice = async (thorClient: ThorClient): Promise => { - const result = BigInt( - (await Promise.resolve( - thorClient.contracts.getBaseGasPrice() - )) as string - ); + const { + result: { plain } + } = await thorClient.contracts.getBaseGasPrice(); - return '0x' + result.toString(16); + return '0x' + BigInt(plain as bigint).toString(16); }; export { ethGasPrice }; diff --git a/packages/network/src/thor-client/contracts/contracts-module.ts b/packages/network/src/thor-client/contracts/contracts-module.ts index e7ce24bd1..08d661527 100644 --- a/packages/network/src/thor-client/contracts/contracts-module.ts +++ b/packages/network/src/thor-client/contracts/contracts-module.ts @@ -112,17 +112,15 @@ class ContractsModule { } }; } else { - // Returning an array of values. + // Returning the decoded result both as plain and array. const encodedResult = Hex.of(response[0].data); - const objectValue = functionAbi.decodeResult( - encodedResult - ) as object; - const values = functionAbi.decodeOutputAsArray(encodedResult); + const plain = functionAbi.decodeResult(encodedResult) as object; + const array = functionAbi.decodeOutputAsArray(encodedResult); return { success: true, result: { - objectValue, - values + plain, + array } }; } @@ -142,15 +140,14 @@ class ContractsModule { clauses.map((clause) => clause.clause), options ); - // Returning an array of values. - // The viem format is a single value/JSON object (ABIFunction#decodeResult) + // Returning the decoded results both as plain and array. return response.map((res, index) => ({ success: true, result: { - objectValue: clauses[index].functionAbi.decodeResult( + plain: clauses[index].functionAbi.decodeResult( Hex.of(res.data) ) as object, - values: clauses[index].functionAbi.decodeOutputAsArray( + array: clauses[index].functionAbi.decodeOutputAsArray( Hex.of(res.data) ) } @@ -250,7 +247,7 @@ class ContractsModule { * * @returns The base gas price in wei. */ - public async getBaseGasPrice(): Promise { + public async getBaseGasPrice(): Promise { return await this.executeCall( BUILT_IN_CONTRACTS.PARAMS_ADDRESS, ABIContract.ofAbi(BUILT_IN_CONTRACTS.PARAMS_ABI).getFunction('get'), diff --git a/packages/network/src/thor-client/contracts/types.d.ts b/packages/network/src/thor-client/contracts/types.d.ts index 6c65254ac..bc25d82c6 100644 --- a/packages/network/src/thor-client/contracts/types.d.ts +++ b/packages/network/src/thor-client/contracts/types.d.ts @@ -46,8 +46,8 @@ type ContractCallOptions = SimulateTransactionOptions & ClauseOptions; interface ContractCallResult { success: boolean; result: { - objectValue?: object; - values?: unknown[]; + plain?: unknown; // Success result as a plain value (might be literal or object). + array?: unknown[]; // Success result as an array (values are the same as in plain). errorMessage?: string; }; } diff --git a/packages/network/src/utils/vns/index.ts b/packages/network/src/utils/vns/index.ts index 4b3aaa206..a688e2dcf 100644 --- a/packages/network/src/utils/vns/index.ts +++ b/packages/network/src/utils/vns/index.ts @@ -51,7 +51,7 @@ const resolveNames = async ( [names] ); - const [addresses] = callGetAddresses.result.values as string[][]; + const [addresses] = callGetAddresses.result.array as string[][]; return addresses.map((address) => { // zero addresses are missing configuration entries @@ -112,7 +112,7 @@ const lookupAddresses = async ( [addresses] ); - const [names] = callGetNames.result.values as string[][]; + const [names] = callGetNames.result.array as string[][]; return names.map((name) => { // empty strings indicate a missing entry diff --git a/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts b/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts index 50eea22e2..bb53c0969 100644 --- a/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts +++ b/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, test } from '@jest/globals'; +import { Address, ERC20_ABI, HexUInt } from '@vechain/sdk-core'; import { ProviderInternalBaseWallet, THOR_SOLO_URL, @@ -10,7 +11,6 @@ import { } from '../../../src'; import { TEST_ACCOUNTS } from '../../fixture'; import { erc20ContractBytecode } from './fixture'; -import { Address, ERC20_ABI, HexUInt } from '@vechain/sdk-core'; /** * Tests for the ThorClient class, specifically focusing on ERC20 contract-related functionality. @@ -131,7 +131,13 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual([BigInt(1000)]); + expect(balanceOfResult).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); }, 10000); // Set a timeout of 10000ms for this test test('Execute ERC20 contract operations with comments', async () => { @@ -168,7 +174,13 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual([BigInt(1000)]); + expect(balanceOfResult).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); }, 10000); // Set a timeout of 10000ms for this test test('Execute ERC20 contract operations with revision', async () => { @@ -205,7 +217,13 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual([BigInt(1000)]); + expect(balanceOfResult).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); }, 10000); /** @@ -269,7 +287,13 @@ describe('ThorClient - ERC20 Contracts', () => { await contract.read.balanceOf( TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address ) - ).toEqual([BigInt(1000)]); + ).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); }, 10000); /** @@ -294,9 +318,27 @@ describe('ThorClient - ERC20 Contracts', () => { contract.clause.decimals() ]); - expect(contractRead[0]).toEqual(['SampleToken']); - expect(contractRead[1]).toEqual(['ST']); - expect(contractRead[2]).toEqual([18]); + expect(contractRead[0]).toEqual({ + success: true, + result: { + plain: 'SampleToken', + array: ['SampleToken'] + } + }); + expect(contractRead[1]).toEqual({ + success: true, + result: { + plain: 'ST', + array: ['ST'] + } + }); + expect(contractRead[2]).toEqual({ + success: true, + result: { + plain: 18, + array: [18] + } + }); }, 10000); /** @@ -321,9 +363,27 @@ describe('ThorClient - ERC20 Contracts', () => { contract.clause.decimals() ]); - expect(contractRead[0]).toEqual(['SampleToken']); - expect(contractRead[1]).toEqual(['ST']); - expect(contractRead[2]).toEqual([18]); + expect(contractRead[0]).toEqual({ + success: true, + result: { + plain: 'SampleToken', + array: ['SampleToken'] + } + }); + expect(contractRead[1]).toEqual({ + success: true, + result: { + plain: 'ST', + array: ['ST'] + } + }); + expect(contractRead[2]).toEqual({ + success: true, + result: { + plain: 18, + array: [18] + } + }); }, 10000); /** @@ -377,9 +437,21 @@ describe('ThorClient - ERC20 Contracts', () => { ] ); - expect(reads[0]).toEqual([BigInt(1000)]); + expect(reads[0]).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); - expect(reads[1]).toEqual([BigInt(4000)]); + expect(reads[1]).toEqual({ + success: true, + result: { + plain: 4000n, + array: [4000n] + } + }); }, 10000); /** @@ -412,6 +484,12 @@ describe('ThorClient - ERC20 Contracts', () => { await contract.read.balanceOf( TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address ) - ).toEqual([BigInt(1000)]); + ).toEqual({ + success: true, + result: { + plain: 1000n, + array: [1000n] + } + }); }, 30000); }); diff --git a/packages/network/tests/thor-client/contracts/contract.testnet.test.ts b/packages/network/tests/thor-client/contracts/contract.testnet.test.ts index 2cabce075..2d0b7f4f7 100644 --- a/packages/network/tests/thor-client/contracts/contract.testnet.test.ts +++ b/packages/network/tests/thor-client/contracts/contract.testnet.test.ts @@ -19,7 +19,20 @@ describe('ThorClient - Gas Module', () => { */ test('Should return the base gas price of the Testnet', async () => { const baseGasPrice = await thorClient.contracts.getBaseGasPrice(); - expect(baseGasPrice).toEqual([10000000000000n]); - expect(baseGasPrice).toEqual([BigInt(10 ** 13)]); // 10^13 wei + const expected = { + success: true, + result: { + plain: 10000000000000n, + array: [10000000000000n] + } + }; + expect(baseGasPrice).toEqual(expected); + expect(baseGasPrice).toEqual({ + ...expected, + result: { + plain: BigInt(10 ** 13), + array: [BigInt(10 ** 13)] + } + }); // 10^13 wei }); }); diff --git a/packages/network/tests/thor-client/contracts/fixture.ts b/packages/network/tests/thor-client/contracts/fixture.ts index 4afc2a280..8aefcb06d 100644 --- a/packages/network/tests/thor-client/contracts/fixture.ts +++ b/packages/network/tests/thor-client/contracts/fixture.ts @@ -322,7 +322,13 @@ const erc721ContractTestCases: TestCase[] = [ 'should get the current NFT balance of the CONTRACT_MANAGER address', functionName: 'balanceOf', params: [TEST_ACCOUNTS.TRANSACTION.CONTRACT_MANAGER.address], - expected: [2n], + expected: { + success: true, + result: { + plain: 2n, + array: [2n] + } + }, reverted: false, isReadOnly: true }, @@ -331,7 +337,13 @@ const erc721ContractTestCases: TestCase[] = [ 'should get the current NFT balance of the DELEGATOR address', functionName: 'balanceOf', params: [TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address], - expected: [1n], + expected: { + success: true, + result: { + plain: 1n, + array: [1n] + } + }, reverted: false, isReadOnly: true }, @@ -360,7 +372,13 @@ const erc721ContractTestCases: TestCase[] = [ 'should get the current NFT balance of the CONTRACT_MANAGER address', functionName: 'balanceOf', params: [TEST_ACCOUNTS.TRANSACTION.CONTRACT_MANAGER.address], - expected: [1n], + expected: { + success: true, + result: { + plain: 1n, + array: [1n] + } + }, reverted: false, isReadOnly: true }, @@ -369,7 +387,13 @@ const erc721ContractTestCases: TestCase[] = [ 'should get the current NFT balance of the DELEGATOR address', functionName: 'balanceOf', params: [TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address], - expected: [2n], + expected: { + success: true, + result: { + plain: 2n, + array: [2n] + } + }, reverted: false, isReadOnly: true } From 5743496cf7060e522003e02af09675ba8889235b Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 11:30:46 +0100 Subject: [PATCH 3/7] refactor: fixed some tests --- .../contracts/contract.solo.test.ts | 54 +++-- .../tests/thor-client/contracts/fixture.ts | 198 +++++++++++++----- 2 files changed, 189 insertions(+), 63 deletions(-) diff --git a/packages/network/tests/thor-client/contracts/contract.solo.test.ts b/packages/network/tests/thor-client/contracts/contract.solo.test.ts index d02a9b151..491950a22 100644 --- a/packages/network/tests/thor-client/contracts/contract.solo.test.ts +++ b/packages/network/tests/thor-client/contracts/contract.solo.test.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { beforeEach, describe, expect, test } from '@jest/globals'; -import { ABIContract, Address, HexUInt, type DeployParams } from '@vechain/sdk-core'; +import { ABIContract, Address, type DeployParams, HexUInt } from '@vechain/sdk-core'; import { CannotFindTransaction, ContractDeploymentFailed, @@ -144,7 +144,10 @@ describe('ThorClient - Contracts', () => { // Call the get function of the deployed contract to verify that the stored value is 100 const result = await contract.read.get(); - expect(result).toEqual([100n]); + expect(result).toEqual({ + success: true, + result: { plain: 100n, array: [100n] } + }); // Assertions expect(contract.deployTransactionReceipt?.reverted).toBe(false); @@ -227,7 +230,10 @@ describe('ThorClient - Contracts', () => { const callFunctionGetResult = await contract.read.get(); - expect(callFunctionGetResult).toEqual([BigInt(123)]); + expect(callFunctionGetResult).toEqual({ + success: true, + result: { plain: 123n, array: [123n] } + }); }, 10000); /** @@ -241,7 +247,10 @@ describe('ThorClient - Contracts', () => { const contract = await factory.waitForDeployment(); await (await contract.transact.set(123n)).wait(); - expect(await contract.read.get()).toEqual([BigInt(123)]); + expect(await contract.read.get()).toEqual({ + success: true, + result: { plain: 123n, array: [123n] } + }); contract.setContractReadOptions({ caller: 'invalid address' }); @@ -258,7 +267,10 @@ describe('ThorClient - Contracts', () => { contract.clearContractTransactOptions(); await (await contract.transact.set(22323n)).wait(); - expect(await contract.read.get()).toEqual([BigInt(22323)]); + expect(await contract.read.get()).toEqual({ + success: true, + result: { plain: 22323n, array: [22323n] } + }); }, 15000); /** @@ -290,7 +302,10 @@ describe('ThorClient - Contracts', () => { const callFunctionGetResult = await contract.read.get(); - expect(callFunctionGetResult).toEqual([BigInt(123)]); + expect(callFunctionGetResult).toEqual({ + success: true, + result: { plain: 123n, array: [123n] } + }); }, 10000); /** @@ -312,7 +327,10 @@ describe('ThorClient - Contracts', () => { // Call the get function of the loaded contract to verify that the stored value is 100 let callFunctionGetResult = await loadedContract.read.get(); - expect(callFunctionGetResult).toEqual([BigInt(100)]); + expect(callFunctionGetResult).toEqual({ + success: true, + result: { plain: 100n, array: [100n] } + }); // Set the private key of the caller for signing transactions loadedContract.setSigner(contract.getSigner() as VeChainSigner); @@ -329,7 +347,10 @@ describe('ThorClient - Contracts', () => { callFunctionGetResult = await loadedContract.read.get(); // Assertion: The value should be 123 - expect(callFunctionGetResult).toEqual([BigInt(123)]); + expect(callFunctionGetResult).toEqual({ + success: true, + result: { plain: 123n, array: [123n] } + }); }, 10000); /** @@ -429,7 +450,10 @@ describe('ThorClient - Contracts', () => { await deployedDepositContract.read.getBalance( TEST_ACCOUNTS.TRANSACTION.TRANSACTION_SENDER.address ) - ).toEqual([BigInt(1000)]); + ).toEqual({ + success: true, + result: { plain: 1000n, array: [1000n] } + }); }, 10000); test('Deploy a contract that returns two values', async () => { @@ -599,7 +623,7 @@ describe('ThorClient - Contracts', () => { ), params ); - expect(response).toBe(expected); + expect(response).toStrictEqual(expected); }); } ); @@ -797,8 +821,14 @@ describe('ThorClient - Contracts', () => { test('Should return the base gas price of the Solo network', async () => { const baseGasPrice = await thorSoloClient.contracts.getBaseGasPrice(); - expect(baseGasPrice).toEqual([1000000000000000n]); - expect(baseGasPrice).toEqual([BigInt(10 ** 15)]); // 10^13 wei + expect(baseGasPrice).toEqual( { + success: true, + result: { plain: 1000000000000000n, array: [1000000000000000n] } + }); + expect(baseGasPrice).toEqual( { + success: true, + result: { plain: BigInt(10 ** 15), array: [BigInt(10 ** 15)] } + }); // 10^13 wei }); }); }); diff --git a/packages/network/tests/thor-client/contracts/fixture.ts b/packages/network/tests/thor-client/contracts/fixture.ts index 8aefcb06d..cdd428f60 100644 --- a/packages/network/tests/thor-client/contracts/fixture.ts +++ b/packages/network/tests/thor-client/contracts/fixture.ts @@ -404,7 +404,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the bool value false', functionName: 'boolData', params: [false], - expected: [false], + expected: { result: { array: [false], plain: false }, success: true }, reverted: false, isReadOnly: true }, @@ -412,7 +412,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the bool value true', functionName: 'boolData', params: [true], - expected: [true], + expected: { result: { array: [true], plain: true }, success: true }, reverted: false, isReadOnly: true }, @@ -420,7 +420,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the int value 1', functionName: 'intData', params: [1], - expected: [1n], + expected: { result: { array: [1n], plain: 1n }, success: true }, reverted: false, isReadOnly: true }, @@ -428,7 +428,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the int value -1', functionName: 'intData', params: [-1], - expected: [-1n], + expected: { result: { array: [-1n], plain: -1n }, success: true }, reverted: false, isReadOnly: true }, @@ -436,7 +436,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the int value 0', functionName: 'intData', params: [0], - expected: [0n], + expected: { result: { array: [0n], plain: 0n }, success: true }, reverted: false, isReadOnly: true }, @@ -444,7 +444,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the uint value 0', functionName: 'uintData', params: [0], - expected: [0n], + expected: { result: { array: [0n], plain: 0n }, success: true }, reverted: false, isReadOnly: true }, @@ -452,7 +452,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the uint value 1', functionName: 'uintData', params: [1], - expected: [1n], + expected: { result: { array: [1n], plain: 1n }, success: true }, reverted: false, isReadOnly: true }, @@ -461,7 +461,13 @@ const testingContractTestCases: TestCase[] = [ 'should return the address value 0x0000000000000000000000000000000000000000', functionName: 'addressData', params: ['0x0000000000000000000000000000000000000000'], - expected: ['0x0000000000000000000000000000000000000000'], + expected: { + result: { + array: ['0x0000000000000000000000000000000000000000'], + plain: '0x0000000000000000000000000000000000000000' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -472,9 +478,15 @@ const testingContractTestCases: TestCase[] = [ params: [ '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0' ], - expected: [ - '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0' - ], + expected: { + result: { + array: [ + '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0' + ], + plain: '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -482,7 +494,7 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the string value "a"', functionName: 'stringData', params: ['a'], - expected: ['a'], + expected: { result: { array: ['a'], plain: 'a' }, success: true }, reverted: false, isReadOnly: true }, @@ -490,7 +502,10 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the passed fixed array', functionName: 'fixedArrayData', params: [[123, 456, 789]], - expected: [[123n, 456n, 789n]], + expected: { + result: { array: [[123n, 456n, 789n]], plain: [123n, 456n, 789n] }, + success: true + }, reverted: false, isReadOnly: true }, @@ -498,7 +513,13 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the passed dynamic array', functionName: 'dynamicArrayData', params: [[123, 456, 789, 323, 123]], - expected: [[123n, 456n, 789n, 323n, 123n]], + expected: { + result: { + array: [[123n, 456n, 789n, 323n, 123n]], + plain: [123n, 456n, 789n, 323n, 123n] + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -506,7 +527,13 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the passed struct', functionName: 'structData', params: [{ id: 10, name: 'test' }], - expected: [[10n, 'test']], + expected: { + result: { + array: [[10n, 'test']], + plain: { id: 10n, name: 'test' } + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -514,7 +541,10 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the passed enum', functionName: 'enumData', params: [ExampleEnum.SMALL], - expected: [ExampleEnum.SMALL], + expected: { + result: { array: [ExampleEnum.SMALL], plain: ExampleEnum.SMALL }, + success: true + }, reverted: false, isReadOnly: true }, @@ -531,16 +561,31 @@ const testingContractTestCases: TestCase[] = [ { id: 10, name: 'test' }, ExampleEnum.SMALL ], - expected: [ - 1n, - '0x0000000000000000000000000000000000000000', - '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0', - 'a', - [123n, 456n, 789n], - [123n, 456n, 789n, 323n, 123n], - [10n, 'test'], - ExampleEnum.SMALL - ], + expected: { + result: { + array: [ + 1n, + '0x0000000000000000000000000000000000000000', + '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0', + 'a', + [123n, 456n, 789n], + [123n, 456n, 789n, 323n, 123n], + [10n, 'test'], + ExampleEnum.SMALL + ], + plain: [ + 1n, + '0x0000000000000000000000000000000000000000', + '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0', + 'a', + [123n, 456n, 789n], + [123n, 456n, 789n, 323n, 123n], + { id: 10n, name: 'test' }, + 0 + ] + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -548,7 +593,13 @@ const testingContractTestCases: TestCase[] = [ description: 'should return the passed multiple int values', functionName: 'multipleIntData', params: [1, 222, 333, 287274, 390343843, 123223663], - expected: [1, 222, 333, 287274n, 390343843n, 123223663n], + expected: { + result: { + array: [1, 222, 333, 287274n, 390343843n, 123223663n], + plain: [1, 222, 333, 287274n, 390343843n, 123223663n] + }, + success: true + }, reverted: false, isReadOnly: true } @@ -559,7 +610,10 @@ const testingContractNegativeTestCases: TestCase[] = [ description: 'testRequireError() test', functionName: 'testRequireError', params: [8], - expected: 'Value must be greater than 10', + expected: { + result: { errorMessage: 'Value must be greater than 10' }, + success: false + }, reverted: true, isReadOnly: true }, @@ -567,7 +621,7 @@ const testingContractNegativeTestCases: TestCase[] = [ description: 'testAssertError() test', functionName: 'testAssertError', params: [1], - expected: 'Panic(0x01)', + expected: { result: { errorMessage: 'Panic(0x01)' }, success: false }, reverted: true, isReadOnly: true }, @@ -575,7 +629,10 @@ const testingContractNegativeTestCases: TestCase[] = [ description: 'testRevertError() test', functionName: 'testRevertError', params: [4], - expected: 'Value must be at least 5', + expected: { + result: { errorMessage: 'Value must be at least 5' }, + success: false + }, reverted: true, isReadOnly: true }, @@ -583,7 +640,7 @@ const testingContractNegativeTestCases: TestCase[] = [ description: 'testOverflowError() test', functionName: 'testOverflowError', params: [255], - expected: 'Panic(0x11)', + expected: { result: { errorMessage: 'Panic(0x11)' }, success: false }, reverted: true, isReadOnly: true }, @@ -591,7 +648,7 @@ const testingContractNegativeTestCases: TestCase[] = [ description: 'testInvalidOpcodeError() test', functionName: 'testInvalidOpcodeError', params: [], - expected: '', + expected: { result: { errorMessage: '' }, success: false }, reverted: true, isReadOnly: true } @@ -602,9 +659,15 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the blockID of the given block number', functionName: 'getBlockID', params: [1], - expected: [ - '0x00000001fb5387f59d35a8e76dcce151cb229a3910ac5f4731ff55f7ca36a809' - ], + expected: { + result: { + array: [ + '0x00000001fb5387f59d35a8e76dcce151cb229a3910ac5f4731ff55f7ca36a809' + ], + plain: '0x00000001fb5387f59d35a8e76dcce151cb229a3910ac5f4731ff55f7ca36a809' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -613,7 +676,7 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ 'should return the block total score of the given block defined by the block number', functionName: 'getBlockTotalScore', params: [1], - expected: [1n], + expected: { result: { array: [1n], plain: 1n }, success: true }, reverted: false, isReadOnly: true }, @@ -621,7 +684,10 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the block time of the given block number', functionName: 'getBlockTime', params: [1], - expected: [1702231120n], + expected: { + result: { array: [1702231120n], plain: 1702231120n }, + success: true + }, reverted: false, isReadOnly: true }, @@ -629,7 +695,13 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the block signer of the given block number', functionName: 'getBlockSigner', params: [1], - expected: ['0xf077b491b355E64048cE21E3A6Fc4751eEeA77fa'], + expected: { + result: { + array: ['0xf077b491b355E64048cE21E3A6Fc4751eEeA77fa'], + plain: '0xf077b491b355E64048cE21E3A6Fc4751eEeA77fa' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -637,7 +709,13 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the total supply of VET', functionName: 'getTotalSupply', params: [], - expected: [10000000000000000000000000000n], + expected: { + result: { + array: [10000000000000000000000000000n], + plain: 10000000000000000000000000000n + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -646,7 +724,7 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ 'should return the `provedWork` of the current transaction', functionName: 'getTxProvedWork', params: [], - expected: [0n], + expected: { result: { array: [0n], plain: 0n }, success: true }, reverted: false, isReadOnly: true }, @@ -655,9 +733,15 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ 'should return the transaction ID of the current transaction', functionName: 'getTxID', params: [], - expected: [ - '0x0000000000000000000000000000000000000000000000000000000000000000' - ], + expected: { + result: { + array: [ + '0x0000000000000000000000000000000000000000000000000000000000000000' + ], + plain: '0x0000000000000000000000000000000000000000000000000000000000000000' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -665,7 +749,13 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the `blockRef` of the current transaction', functionName: 'getTxBlockRef', params: [], - expected: ['0x0000000000000000'], + expected: { + result: { + array: ['0x0000000000000000'], + plain: '0x0000000000000000' + }, + success: true + }, reverted: false, isReadOnly: true }, @@ -674,7 +764,7 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ 'should return the `expiration` of the current transaction', functionName: 'getTxExpiration', params: [], - expected: [0n], + expected: { result: { array: [0n], plain: 0n }, success: true }, reverted: false, isReadOnly: true }, @@ -682,9 +772,15 @@ const testingContractEVMExtensionTestCases: TestCase[] = [ description: 'should return the data hashed using Blake2b256', functionName: 'calculateBlake2b256', params: ['0x'], - expected: [ - '0x0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' - ], + expected: { + result: { + array: [ + '0x0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + ], + plain: '0x0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + }, + success: true + }, reverted: false, isReadOnly: true } @@ -3724,12 +3820,12 @@ export { multipleClausesTestCases, OWNER_RESTRICTION_ABI, OWNER_RESTRICTION_BYTECODE, + sampleTwoValuesReturnAbi, + sampleTwoValuesReturnBytecode, testingContractEVMExtensionTestCases, testingContractNegativeTestCases, testingContractTestCases, TESTNET_DELEGATE_URL, xAllocationAddress, - xAllocationVotingGovernorABI, - sampleTwoValuesReturnAbi, - sampleTwoValuesReturnBytecode + xAllocationVotingGovernorABI }; From dd4d403cb6d6af49ff0aa0aef4d245d2ef2aaf3d Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 12:43:37 +0100 Subject: [PATCH 4/7] feat: fixed tests --- .../src/available-errors/contract/contract.ts | 10 +++- .../contracts/model/contract-proxy.ts | 53 ++++++++++------ .../contracts/contract.erc20.solo.test.ts | 40 ++----------- .../contracts/contract.solo.test.ts | 60 ++++++++----------- 4 files changed, 73 insertions(+), 90 deletions(-) diff --git a/packages/errors/src/available-errors/contract/contract.ts b/packages/errors/src/available-errors/contract/contract.ts index 199e5d449..ae9f482eb 100644 --- a/packages/errors/src/available-errors/contract/contract.ts +++ b/packages/errors/src/available-errors/contract/contract.ts @@ -9,4 +9,12 @@ import { type ObjectErrorData } from '../types'; */ class ContractDeploymentFailed extends VechainSDKError {} -export { ContractDeploymentFailed }; +/** + * Error when calling a read function on a contract. + * + * WHEN TO USE: + * * Error will be thrown when a read (call) operation fails. + */ +class ContractCallError extends VechainSDKError {} + +export { ContractCallError, ContractDeploymentFailed }; diff --git a/packages/network/src/thor-client/contracts/model/contract-proxy.ts b/packages/network/src/thor-client/contracts/model/contract-proxy.ts index 728a87333..e45339415 100644 --- a/packages/network/src/thor-client/contracts/model/contract-proxy.ts +++ b/packages/network/src/thor-client/contracts/model/contract-proxy.ts @@ -5,7 +5,10 @@ import { Units, VET } from '@vechain/sdk-core'; -import { InvalidTransactionField } from '@vechain/sdk-errors'; +import { + ContractCallError, + InvalidTransactionField +} from '@vechain/sdk-errors'; import type { Abi, AbiEvent, @@ -17,7 +20,7 @@ import type { import { type VeChainSigner } from '../../../signer'; import { type FilterCriteria } from '../../logs'; import { type SendTransactionResult } from '../../transactions/types'; -import { type ContractCallResult, type ContractClause } from '../types'; +import { type ContractClause } from '../types'; import { type Contract } from './contract'; import { ContractFilter } from './contract-filter'; import { @@ -48,7 +51,7 @@ function getReadProxy( ExtractAbiFunction['inputs'], 'inputs' > - ): Promise => { + ): Promise => { // check if the clause comment is provided as an argument const extractOptionsResult = extractAndRemoveAdditionalOptions( @@ -61,21 +64,35 @@ function getReadProxy( const revisionValue = extractOptionsResult.clauseAdditionalOptions?.revision; - return await contract.thor.contracts.executeCall( - contract.address, - contract.getFunctionAbi(prop), - extractOptionsResult.args, - { - caller: - contract.getSigner() !== undefined - ? await contract.getSigner()?.getAddress() - : undefined, - ...contract.getContractReadOptions(), - comment: clauseComment, - revision: revisionValue, - includeABI: true - } - ); + const functionAbi = contract.getFunctionAbi(prop); + + const executeCallResult = + await contract.thor.contracts.executeCall( + contract.address, + functionAbi, + extractOptionsResult.args, + { + caller: + contract.getSigner() !== undefined + ? await contract.getSigner()?.getAddress() + : undefined, + ...contract.getContractReadOptions(), + comment: clauseComment, + revision: revisionValue, + includeABI: true + } + ); + + if (!executeCallResult.success) { + throw new ContractCallError( + functionAbi.stringSignature, + executeCallResult.result.errorMessage as string, + { + contractAddress: contract.address + } + ); + } + return executeCallResult.result.array as unknown[]; }; } }); diff --git a/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts b/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts index bb53c0969..8dce9b158 100644 --- a/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts +++ b/packages/network/tests/thor-client/contracts/contract.erc20.solo.test.ts @@ -131,13 +131,7 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual({ - success: true, - result: { - plain: 1000n, - array: [1000n] - } - }); + expect(balanceOfResult).toEqual([1000n]); }, 10000); // Set a timeout of 10000ms for this test test('Execute ERC20 contract operations with comments', async () => { @@ -174,13 +168,7 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual({ - success: true, - result: { - plain: 1000n, - array: [1000n] - } - }); + expect(balanceOfResult).toEqual([1000n]); }, 10000); // Set a timeout of 10000ms for this test test('Execute ERC20 contract operations with revision', async () => { @@ -217,13 +205,7 @@ describe('ThorClient - ERC20 Contracts', () => { // Ensure that the transfer transaction was successful and the balance is as expected expect(transactionReceiptTransfer.reverted).toBe(false); - expect(balanceOfResult).toEqual({ - success: true, - result: { - plain: 1000n, - array: [1000n] - } - }); + expect(balanceOfResult).toEqual([1000n]); }, 10000); /** @@ -287,13 +269,7 @@ describe('ThorClient - ERC20 Contracts', () => { await contract.read.balanceOf( TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address ) - ).toEqual({ - success: true, - result: { - plain: 1000n, - array: [1000n] - } - }); + ).toEqual([1000n]); }, 10000); /** @@ -484,12 +460,6 @@ describe('ThorClient - ERC20 Contracts', () => { await contract.read.balanceOf( TEST_ACCOUNTS.TRANSACTION.DELEGATOR.address ) - ).toEqual({ - success: true, - result: { - plain: 1000n, - array: [1000n] - } - }); + ).toEqual([1000n]); }, 30000); }); diff --git a/packages/network/tests/thor-client/contracts/contract.solo.test.ts b/packages/network/tests/thor-client/contracts/contract.solo.test.ts index 491950a22..a2a55a8d1 100644 --- a/packages/network/tests/thor-client/contracts/contract.solo.test.ts +++ b/packages/network/tests/thor-client/contracts/contract.solo.test.ts @@ -7,6 +7,7 @@ import { ContractDeploymentFailed, InvalidTransactionField } from '@vechain/sdk-errors'; +import { fail } from 'assert'; import { Contract, type ContractFactory, @@ -144,10 +145,7 @@ describe('ThorClient - Contracts', () => { // Call the get function of the deployed contract to verify that the stored value is 100 const result = await contract.read.get(); - expect(result).toEqual({ - success: true, - result: { plain: 100n, array: [100n] } - }); + expect(result).toEqual([100n]); // Assertions expect(contract.deployTransactionReceipt?.reverted).toBe(false); @@ -230,10 +228,7 @@ describe('ThorClient - Contracts', () => { const callFunctionGetResult = await contract.read.get(); - expect(callFunctionGetResult).toEqual({ - success: true, - result: { plain: 123n, array: [123n] } - }); + expect(callFunctionGetResult).toEqual([123n]); }, 10000); /** @@ -247,10 +242,7 @@ describe('ThorClient - Contracts', () => { const contract = await factory.waitForDeployment(); await (await contract.transact.set(123n)).wait(); - expect(await contract.read.get()).toEqual({ - success: true, - result: { plain: 123n, array: [123n] } - }); + expect(await contract.read.get()).toEqual([123n]); contract.setContractReadOptions({ caller: 'invalid address' }); @@ -267,10 +259,7 @@ describe('ThorClient - Contracts', () => { contract.clearContractTransactOptions(); await (await contract.transact.set(22323n)).wait(); - expect(await contract.read.get()).toEqual({ - success: true, - result: { plain: 22323n, array: [22323n] } - }); + expect(await contract.read.get()).toEqual([22323n]); }, 15000); /** @@ -302,10 +291,7 @@ describe('ThorClient - Contracts', () => { const callFunctionGetResult = await contract.read.get(); - expect(callFunctionGetResult).toEqual({ - success: true, - result: { plain: 123n, array: [123n] } - }); + expect(callFunctionGetResult).toEqual([123n]); }, 10000); /** @@ -327,10 +313,7 @@ describe('ThorClient - Contracts', () => { // Call the get function of the loaded contract to verify that the stored value is 100 let callFunctionGetResult = await loadedContract.read.get(); - expect(callFunctionGetResult).toEqual({ - success: true, - result: { plain: 100n, array: [100n] } - }); + expect(callFunctionGetResult).toEqual([100n]); // Set the private key of the caller for signing transactions loadedContract.setSigner(contract.getSigner() as VeChainSigner); @@ -347,10 +330,7 @@ describe('ThorClient - Contracts', () => { callFunctionGetResult = await loadedContract.read.get(); // Assertion: The value should be 123 - expect(callFunctionGetResult).toEqual({ - success: true, - result: { plain: 123n, array: [123n] } - }); + expect(callFunctionGetResult).toEqual([123n]); }, 10000); /** @@ -450,10 +430,7 @@ describe('ThorClient - Contracts', () => { await deployedDepositContract.read.getBalance( TEST_ACCOUNTS.TRANSACTION.TRANSACTION_SENDER.address ) - ).toEqual({ - success: true, - result: { plain: 1000n, array: [1000n] } - }); + ).toEqual([1000n]); }, 10000); test('Deploy a contract that returns two values', async () => { @@ -493,17 +470,28 @@ describe('ThorClient - Contracts', () => { const deployedContract = await factory.waitForDeployment(); - const secretData = await deployedContract.read.getSecretData(); + const [secretData] = await deployedContract.read.getSecretData(); - expect(secretData[0]).toEqual(42n); + expect(secretData).toEqual(42n); const loadedContract = thorSoloClient.contracts.load( deployedContract.address, OWNER_RESTRICTION_ABI, receiverSigner ); - const secretDataNotOwner = await loadedContract.read.getSecretData(); - expect(secretDataNotOwner).toEqual('Not the contract owner'); + try { + await loadedContract.read.getSecretData(); + fail('Should fail'); + } catch (error) { + if (error instanceof Error) { + expect(error.message).toEqual( + `Method 'getSecretData()' failed.` + + `\n-Reason: 'Not the contract owner'` + + `\n-Parameters: \n\t` + + `{\n "contractAddress": "${deployedContract.address}"\n}` + ); + } + } }); /** From 75508c5a5808741fb63d2d731a20026911eddb7f Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 12:52:57 +0100 Subject: [PATCH 5/7] refactor: fixed some tests --- .../network/src/thor-client/contracts/contracts-module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/network/src/thor-client/contracts/contracts-module.ts b/packages/network/src/thor-client/contracts/contracts-module.ts index 08d661527..d96f85a4b 100644 --- a/packages/network/src/thor-client/contracts/contracts-module.ts +++ b/packages/network/src/thor-client/contracts/contracts-module.ts @@ -114,7 +114,7 @@ class ContractsModule { } else { // Returning the decoded result both as plain and array. const encodedResult = Hex.of(response[0].data); - const plain = functionAbi.decodeResult(encodedResult) as object; + const plain = functionAbi.decodeResult(encodedResult); const array = functionAbi.decodeOutputAsArray(encodedResult); return { success: true, @@ -146,7 +146,7 @@ class ContractsModule { result: { plain: clauses[index].functionAbi.decodeResult( Hex.of(res.data) - ) as object, + ), array: clauses[index].functionAbi.decodeOutputAsArray( Hex.of(res.data) ) From 77b591f8fd4692cf874f873f14f0e4f3813005f6 Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 14:04:33 +0100 Subject: [PATCH 6/7] refactor: fixed docs tests --- docs/contracts.md | 32 ++++++++++++++++--- .../contracts/contract-create-ERC20-token.ts | 32 ++++++++++++++++--- docs/examples/evm-extension/evm-extension.ts | 8 ++++- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/docs/contracts.md b/docs/contracts.md index 37b3b3736..8f4d639c2 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -149,10 +149,34 @@ const multipleClausesResult = contract.clause.decimals() ]); -expect(multipleClausesResult[0]).toEqual([expectedBalance]); -expect(multipleClausesResult[1]).toEqual(['SampleToken']); -expect(multipleClausesResult[2]).toEqual(['ST']); -expect(multipleClausesResult[3]).toEqual([18]); +expect(multipleClausesResult[0]).toEqual({ + success: true, + result: { + plain: expectedBalance, + array: [expectedBalance] + } +}); +expect(multipleClausesResult[1]).toEqual({ + success: true, + result: { + plain: 'SampleToken', + array: ['SampleToken'] + } +}); +expect(multipleClausesResult[2]).toEqual({ + success: true, + result: { + plain: 'ST', + array: ['ST'] + } +}); +expect(multipleClausesResult[3]).toEqual({ + success: true, + result: { + plain: 18, + array: [18] + } +}); ``` > ⚠️ **Warning:** diff --git a/docs/examples/contracts/contract-create-ERC20-token.ts b/docs/examples/contracts/contract-create-ERC20-token.ts index 9370e90e7..602b5e65b 100644 --- a/docs/examples/contracts/contract-create-ERC20-token.ts +++ b/docs/examples/contracts/contract-create-ERC20-token.ts @@ -70,9 +70,33 @@ const multipleClausesResult = contract.clause.decimals() ]); -expect(multipleClausesResult[0]).toEqual([expectedBalance]); -expect(multipleClausesResult[1]).toEqual(['SampleToken']); -expect(multipleClausesResult[2]).toEqual(['ST']); -expect(multipleClausesResult[3]).toEqual([18]); +expect(multipleClausesResult[0]).toEqual({ + success: true, + result: { + plain: expectedBalance, + array: [expectedBalance] + } +}); +expect(multipleClausesResult[1]).toEqual({ + success: true, + result: { + plain: 'SampleToken', + array: ['SampleToken'] + } +}); +expect(multipleClausesResult[2]).toEqual({ + success: true, + result: { + plain: 'ST', + array: ['ST'] + } +}); +expect(multipleClausesResult[3]).toEqual({ + success: true, + result: { + plain: 18, + array: [18] + } +}); // END_SNIPPET: ERC20MultiClausesReadSnippet diff --git a/docs/examples/evm-extension/evm-extension.ts b/docs/examples/evm-extension/evm-extension.ts index cbe8c5422..f4c9ca13d 100644 --- a/docs/examples/evm-extension/evm-extension.ts +++ b/docs/examples/evm-extension/evm-extension.ts @@ -791,4 +791,10 @@ const totalSupply = await thorSoloClient.contracts.executeCall( // END_SNIPPET: EVMExtensionSnippet // Check the result -expect(totalSupply).toStrictEqual([10000000000000000000000000000n]); +expect(totalSupply).toStrictEqual({ + result: { + array: [10000000000000000000000000000n], + plain: 10000000000000000000000000000n + }, + success: true +}); From ebdc1a4c963ff1caa2c6d33f968f7269e5fcdbe2 Mon Sep 17 00:00:00 2001 From: Miguel Angel Rojo Fernandez Date: Fri, 25 Oct 2024 14:44:24 +0100 Subject: [PATCH 7/7] feat: refactor --- .../thor-client/contracts/contracts-module.ts | 86 +++++++++++-------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/packages/network/src/thor-client/contracts/contracts-module.ts b/packages/network/src/thor-client/contracts/contracts-module.ts index d96f85a4b..0772012f7 100644 --- a/packages/network/src/thor-client/contracts/contracts-module.ts +++ b/packages/network/src/thor-client/contracts/contracts-module.ts @@ -68,6 +68,41 @@ class ContractsModule { return new Contract(address, abi, this.thor, signer); } + /** + * Extracts the decoded contract call result from the response of a simulated transaction. + * @param {string} encodedData Data returned from the simulated transaction. + * @param {ABIFunction} functionAbi Function ABI of the contract function. + * @param {boolean} reverted Whether the transaction was reverted. + * @returns {ContractCallResult} An object containing the decoded contract call result. + */ + private getContractCallResult( + encodedData: string, + functionAbi: ABIFunction, + reverted: boolean + ): ContractCallResult { + if (reverted) { + const errorMessage = decodeRevertReason(encodedData) ?? ''; + return { + success: false, + result: { + errorMessage + } + }; + } + + // Returning the decoded result both as plain and array. + const encodedResult = Hex.of(encodedData); + const plain = functionAbi.decodeResult(encodedResult); + const array = functionAbi.decodeOutputAsArray(encodedResult); + return { + success: true, + result: { + plain, + array + } + }; + } + /** * Executes a read-only call to a smart contract function, simulating the transaction to obtain the result. * @@ -97,39 +132,18 @@ class ContractsModule { contractCallOptions ); - if (response[0].reverted) { - /** - * The decoded revert reason of the transaction. - * Solidity may revert with Error(string) or Panic(uint256). - * - * @link see [Error handling: Assert, Require, Revert and Exceptions](https://docs.soliditylang.org/en/latest/control-structures.html#error-handling-assert-require-revert-and-exceptions) - */ - const errorMessage = decodeRevertReason(response[0].data) ?? ''; - return { - success: false, - result: { - errorMessage - } - }; - } else { - // Returning the decoded result both as plain and array. - const encodedResult = Hex.of(response[0].data); - const plain = functionAbi.decodeResult(encodedResult); - const array = functionAbi.decodeOutputAsArray(encodedResult); - return { - success: true, - result: { - plain, - array - } - }; - } + return this.getContractCallResult( + response[0].data, + functionAbi, + response[0].reverted + ); } /** * Executes a read-only call to multiple smart contract functions, simulating the transaction to obtain the results. * @param clauses - An array of contract clauses to interact with the contract functions. * @param options - (Optional) Additional options for the contract call, such as the sender's address, gas limit, and gas price, which can affect the simulation's context. + * @returns A promise that resolves to an array of decoded outputs of the smart contract function calls, the format of which depends on the functions' return types. */ public async executeMultipleClausesCall( clauses: ContractClause[], @@ -141,17 +155,13 @@ class ContractsModule { options ); // Returning the decoded results both as plain and array. - return response.map((res, index) => ({ - success: true, - result: { - plain: clauses[index].functionAbi.decodeResult( - Hex.of(res.data) - ), - array: clauses[index].functionAbi.decodeOutputAsArray( - Hex.of(res.data) - ) - } - })); + return response.map((res, index) => + this.getContractCallResult( + res.data, + clauses[index].functionAbi, + res.reverted + ) + ); } /**