diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index a532a68f77..c022c7b409 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -34,6 +34,7 @@ import { MirrorNodeClientError } from '../errors/MirrorNodeClientError'; import { formatRequestIdMessage, formatTransactionId } from '../../formatters'; import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import { + IAccountInfo, IContractCallRequest, IContractCallResponse, IContractLogsResultsParams, diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index a83b324031..3153429390 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -57,7 +57,7 @@ import { DebugService } from './services/debugService'; import { IFeeHistory } from './types/IFeeHistory'; import { ITransactionReceipt } from './types/ITransactionReceipt'; import TransactionService from './services/transactionService/transactionService'; -import { IContractCallRequest, IContractCallResponse } from './types/IMirrorNode'; +import { IAccountInfo, IContractCallRequest, IContractCallResponse } from './types/IMirrorNode'; import { IReceiptRootHash, ReceiptsRootUtils } from '../receiptsRootUtils'; const _ = require('lodash'); @@ -617,7 +617,7 @@ export class EthImpl implements Eth { transaction: IContractCallRequest, requestIdPrefix?: string, ): Promise { - this.contractCallFormat(transaction); + await this.contractCallFormat(transaction, requestIdPrefix); const callData = { ...transaction, estimate: true }; return this.mirrorNodeClient.postContractCall(callData, requestIdPrefix); } @@ -689,9 +689,9 @@ export class EthImpl implements Eth { * * @param {string} address the address of the account * @param {string} requestIdPrefix the prefix for the request ID - * @returns the account (if such exists for the given address) + * @returns {Promise} the account (if such exists for the given address) */ - private async getAccount(address: string, requestIdPrefix?: string) { + private async getAccount(address: string, requestIdPrefix?: string): Promise { const key = `${constants.CACHE_KEY.ACCOUNT}_${address}`; let account = await this.cacheService.getAsync(key, EthImpl.ethEstimateGas, requestIdPrefix); if (!account) { @@ -704,17 +704,29 @@ export class EthImpl implements Eth { /** * Perform value format precheck before making contract call towards the mirror node * @param transaction + * @param requestIdPrefix */ - contractCallFormat(transaction: IContractCallRequest): void { + async contractCallFormat(transaction: IContractCallRequest, requestIdPrefix?: string): Promise { if (transaction.value) { transaction.value = weibarHexToTinyBarInt(transaction.value); } if (transaction.gasPrice) { transaction.gasPrice = parseInt(transaction.gasPrice.toString()); + } else { + transaction.gasPrice = await this.gasPrice(requestIdPrefix).then((gasPrice) => parseInt(gasPrice)); } if (transaction.gas) { transaction.gas = parseInt(transaction.gas.toString()); } + if (!transaction.from && transaction.value && transaction.value > 0) { + if (process.env.OPERATOR_KEY_FORMAT === 'HEX_ECDSA') { + transaction.from = this.hapiService.getMainClientInstance().operatorPublicKey?.toEvmAddress(); + } else { + const operatorId = this.hapiService.getMainClientInstance().operatorAccountId!.toString(); + const operatorAccount = await this.getAccount(operatorId, requestIdPrefix); + transaction.from = operatorAccount?.evm_address; + } + } // Support either data or input. https://ethereum.github.io/execution-apis/api-documentation/ lists input but many EVM tools still use data. // We chose in the mirror node to use data field as the correct one, however for us to be able to support all tools, @@ -1649,7 +1661,7 @@ export class EthImpl implements Eth { // Get a reasonable value for "gas" if it is not specified. const gas = this.getCappedBlockGasLimit(call.gas?.toString(), requestIdPrefix); - this.contractCallFormat(call); + await this.contractCallFormat(call, requestIdPrefix); let result: string | JsonRpcError = ''; try { diff --git a/packages/relay/src/lib/types/IMirrorNode.ts b/packages/relay/src/lib/types/IMirrorNode.ts index 2b0a823f59..5db8ddc775 100644 --- a/packages/relay/src/lib/types/IMirrorNode.ts +++ b/packages/relay/src/lib/types/IMirrorNode.ts @@ -18,6 +18,28 @@ * */ +export interface IAccountInfo { + /** + * Account ID string in the form `shard.realm.num` + */ + account: string; + /** + * RFC4648 no-padding base32 encoded string of the account's alias. + */ + alias: string; + balance?: IAccountBalance; + deleted?: boolean; + ethereum_nonce?: number; + evm_address?: string; + memo?: string; +} + +export interface IAccountBalance { + balance: number; + timestamp?: string; + tokens?: { token_id: string; balance: number }[]; +} + export interface ILimitOrderParams { limit?: number; order?: string; @@ -59,7 +81,7 @@ export interface IContractCallResponse { errorMessage?: string; statusCode?: number; _status?: { - messages: Array<{ message: string; detail: string; data: string }>; + messages: Array<{ message: string; detail?: string; data?: string }>; }; } diff --git a/packages/relay/tests/lib/eth/eth_call.spec.ts b/packages/relay/tests/lib/eth/eth_call.spec.ts index fc3a3fb25c..0b9cd38d06 100644 --- a/packages/relay/tests/lib/eth/eth_call.spec.ts +++ b/packages/relay/tests/lib/eth/eth_call.spec.ts @@ -54,6 +54,7 @@ import { mockData, } from '../../helpers'; import { generateEthTestEnv } from './eth-helpers'; +import { IContractCallRequest, IContractCallResponse } from '../../../src/lib/types/IMirrorNode'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); @@ -488,9 +489,7 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: `0x00` }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); web3Mock.history.post = []; @@ -510,9 +509,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: `0x00` }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); const result = await ethImpl.call(callData, 'latest'); expect(result).to.equal('0x00'); @@ -525,10 +522,7 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: `0x00` }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); const result = await ethImpl.call(callData, 'latest'); expect(result).to.equal('0x00'); }); @@ -541,10 +535,7 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: `0x00` }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); const result = await ethImpl.call(callData, 'latest'); expect(result).to.equal('0x00'); }); @@ -560,7 +551,7 @@ describe('@ethCall Eth Call spec', async function () { block: 'latest', }; - web3Mock.onPost('contracts/call', { ...callData, estimate: false }).reply(200, { result: `0x00` }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); // Relay is called with value in Weibars @@ -576,10 +567,7 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(429, mockData.tooManyRequests); + await mockContractCall({ ...callData, block: 'latest' }, false, 429, mockData.tooManyRequests); const result = await ethImpl.call(callData, 'latest'); expect(result).to.be.not.null; expect((result as JsonRpcError).code).to.eq(-32605); @@ -594,9 +582,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(400, mockData.contractReverted); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted); const result = await ethImpl.call(callData, 'latest'); expect(result).to.be.not.null; expect((result as JsonRpcError).code).to.eq(3); @@ -613,9 +599,7 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(501, mockData.notSuported); + await mockContractCall({ ...callData, block: 'latest' }, false, 501, mockData.notSuported); sdkClientStub.submitContractCallQueryWithRetry.returns({ asBytes: function () { @@ -646,9 +630,7 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(400, mockData.contractReverted); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted); sinon.reset(); const result = await ethImpl.call(callData, 'latest'); sinon.assert.notCalled(sdkClientStub.submitContractCallQueryWithRetry); @@ -667,8 +649,7 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - - web3Mock.onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }).reply(400, { + await mockContractCall({ ...callData, block: 'latest' }, false, 400, { _status: { messages: [ { @@ -718,9 +699,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(400, mockData.invalidTransaction); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.invalidTransaction); const result = await ethImpl.call(callData, 'latest'); expect(result).to.be.not.null; expect(result).to.equal('0x'); @@ -735,9 +714,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(400, mockData.failInvalid); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.failInvalid); const result = await ethImpl.call(callData, 'latest'); expect(result).to.be.not.null; expect(result).to.equal('0x'); @@ -750,9 +727,7 @@ describe('@ethCall Eth Call spec', async function () { from: ACCOUNT_ADDRESS_1, }; - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: EXAMPLE_CONTRACT_BYTECODE }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: EXAMPLE_CONTRACT_BYTECODE }); const result = await ethImpl.call(callData, 'latest'); expect(result).to.eq(EXAMPLE_CONTRACT_BYTECODE); }); @@ -763,90 +738,133 @@ describe('@ethCall Eth Call spec', async function () { from: ACCOUNT_ADDRESS_1, }; - web3Mock - .onPost('contracts/call', { ...callData, estimate: false, block: 'latest' }) - .reply(200, { result: EXAMPLE_CONTRACT_BYTECODE }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: EXAMPLE_CONTRACT_BYTECODE }); const result = await ethImpl.call(callData, 'latest'); expect(result).to.eq(EXAMPLE_CONTRACT_BYTECODE); }); + + async function mockContractCall( + callData: IContractCallRequest, + estimate: boolean, + statusCode: number, + result: IContractCallResponse, + ) { + const formattedCallData = { ...callData, estimate }; + await ethImpl.contractCallFormat(formattedCallData); + return web3Mock.onPost('contracts/call', formattedCallData).reply(statusCode, result); + } }); describe('contractCallFormat', () => { - it('should format transaction value to tiny bar integer', () => { + const operatorId = hapiServiceInstance.getMainClientInstance().operatorAccountId; + const operatorEvmAddress = ACCOUNT_ADDRESS_1; + + beforeEach(() => { + restMock.onGet(`accounts/${operatorId!.toString()}?transactions=false`).reply(200, { + account: operatorId!.toString(), + evm_address: operatorEvmAddress, + }); + }); + + it('should format transaction value to tiny bar integer', async () => { const transaction = { value: '0x2540BE400', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.value).to.equal(1); }); - it('should parse gasPrice to integer', () => { + it('should parse gasPrice to integer', async () => { const transaction = { gasPrice: '1000000000', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.gasPrice).to.equal(1000000000); }); - it('should parse gas to integer', () => { + it('should parse gas to integer', async () => { const transaction = { gas: '50000', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.gas).to.equal(50000); }); - it('should accepts both input and data fields but copy value of input field to data field', () => { + it('should accepts both input and data fields but copy value of input field to data field', async () => { const inputValue = 'input value'; const dataValue = 'data value'; const transaction = { input: inputValue, data: dataValue, }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.data).to.eq(inputValue); expect(transaction.data).to.not.eq(dataValue); expect(transaction.input).to.be.undefined; }); - it('should not modify transaction if only data field is present', () => { + it('should not modify transaction if only data field is present', async () => { const dataValue = 'data value'; const transaction = { data: dataValue, }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.data).to.eq(dataValue); }); - it('should copy input to data if input is provided but data is not', () => { + it('should copy input to data if input is provided but data is not', async () => { const transaction = { input: 'input data', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); // @ts-ignore expect(transaction.data).to.equal('input data'); expect(transaction.input).to.be.undefined; }); - it('should not modify transaction if input and data fields are not provided', () => { + it('should not modify transaction if input and data fields are not provided', async () => { const transaction = { value: '0x2540BE400', gasPrice: '1000000000', gas: '50000', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.value).to.equal(1); expect(transaction.gasPrice).to.equal(1000000000); expect(transaction.gas).to.equal(50000); }); + + it('should populate gas price if not provided', async () => { + const transaction = { + value: '0x2540BE400', + gasPrice: undefined, + }; + + await ethImpl.contractCallFormat(transaction); + + const expectedGasPrice = await ethImpl.gasPrice(); + expect(transaction.gasPrice).to.equal(parseInt(expectedGasPrice)); + }); + + it('should populate the from field if the from field is not provided and value is provided', async () => { + const transaction = { + value: '0x2540BE400', + to: CONTRACT_ADDRESS_2, + from: undefined, + }; + + await ethImpl.contractCallFormat(transaction); + + expect(transaction.from).to.equal(operatorEvmAddress); + }); }); }); diff --git a/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts b/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts index 88a92c6868..1d7e4f70d3 100644 --- a/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts +++ b/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts @@ -31,7 +31,13 @@ import { generateEthTestEnv } from './eth-helpers'; import { Precheck } from '../../../src/lib/precheck'; import { createStubInstance, SinonStub, SinonStubbedInstance, stub } from 'sinon'; import { IContractCallRequest, IContractCallResponse } from '../../../src/lib/types/IMirrorNode'; -import { DEFAULT_NETWORK_FEES, NO_TRANSACTIONS, ONE_TINYBAR_IN_WEI_HEX, RECEIVER_ADDRESS } from './eth-config'; +import { + ACCOUNT_ADDRESS_1, + DEFAULT_NETWORK_FEES, + NO_TRANSACTIONS, + ONE_TINYBAR_IN_WEI_HEX, + RECEIVER_ADDRESS, +} from './eth-config'; import { AbiCoder, keccak256 } from 'ethers'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); @@ -47,13 +53,15 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { let { restMock, web3Mock, hapiServiceInstance, ethImpl, cacheService, mirrorNodeInstance, logger, registry } = generateEthTestEnv(); - function mockContractCall( + async function mockContractCall( callData: IContractCallRequest, estimate: boolean, statusCode: number, result: IContractCallResponse, ) { - return web3Mock.onPost('contracts/call', { ...callData, estimate }).reply(statusCode, result); + const formattedData = { ...callData, estimate }; + await ethImpl.contractCallFormat(formattedData); + return web3Mock.onPost('contracts/call', formattedData).reply(statusCode, result); } function mockGetAccount(idOrAliasOrEvmAddress: string, statusCode: number, result: any) { @@ -80,6 +88,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { currentMaxBlockRange = Number(process.env.ETH_GET_TRANSACTION_COUNT_MAX_BLOCK_RANGE); process.env.ETH_GET_TRANSACTION_COUNT_MAX_BLOCK_RANGE = '1'; restMock.onGet(`accounts/undefined${NO_TRANSACTIONS}`).reply(404); + mockGetAccount(hapiServiceInstance.getMainClientInstance().operatorAccountId!.toString(), 200, { + evm_address: ACCOUNT_ADDRESS_1, + }); }); this.afterEach(() => { @@ -97,7 +108,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gasPrice: '0x0', data: null, }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImpl.estimateGas(callData, null); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); @@ -109,7 +120,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', to: RECEIVER_ADDRESS, }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImpl.estimateGas(callData, null); expect(gas).to.equal(numberTo0x(constants.TX_CONTRACT_CALL_AVERAGE_GAS)); @@ -120,7 +131,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { data: '0x608060405234801561001057600080fd5b506040516107893803806107898339818101604052810190610032919061015a565b806000908051906020019061004892919061004f565b50506102f6565b82805461005b90610224565b90600052602060002090601f01602090048101928261007d57600085556100c4565b82601f1061009657805160ff19168380011785556100c4565b828001600101855582156100c4579182015b828111156100c35782518255916020019190600101906100a8565b5b5090506100d191906100d5565b5090565b5b808211156100ee5760008160009055506001016100d6565b5090565b6000610105610100846101c0565b61019b565b90508281526020810184848401111561011d57600080fd5b6101288482856101f1565b509392505050565b600082601f83011261014157600080fd5b81516101518482602086016100f2565b91505092915050565b60006020828403121561016c57600080fd5b600082015167ffffffffffffffff81111561018657600080fd5b61019284828501610130565b91505092915050565b60006101a56101b6565b90506101b18282610256565b919050565b6000604051905090565b600067ffffffffffffffff8211156101db576101da6102b6565b5b6101e4826102e5565b9050602081019050919050565b60005b8381101561020f5780820151818401526020810190506101f4565b8381111561021e576000848401525b50505050565b6000600282049050600182168061023c57607f821691505b602082108114156102505761024f610287565b5b50919050565b61025f826102e5565b810181811067ffffffffffffffff8211171561027e5761027d6102b6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b610484806103056000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b6100556004803603810190610050919061022c565b610075565b005b61005f61008f565b60405161006c91906102a6565b60405180910390f35b806000908051906020019061008b929190610121565b5050565b60606000805461009e9061037c565b80601f01602080910402602001604051908101604052809291908181526020018280546100ca9061037c565b80156101175780601f106100ec57610100808354040283529160200191610117565b820191906000526020600020905b8154815290600101906020018083116100fa57829003601f168201915b5050505050905090565b82805461012d9061037c565b90600052602060002090601f01602090048101928261014f5760008555610196565b82601f1061016857805160ff1916838001178555610196565b82800160010185558215610196579182015b8281111561019557825182559160200191906001019061017a565b5b5090506101a391906101a7565b5090565b5b808211156101c05760008160009055506001016101a8565b5090565b60006101d76101d2846102ed565b6102c8565b9050828152602081018484840111156101ef57600080fd5b6101fa84828561033a565b509392505050565b600082601f83011261021357600080fd5b81356102238482602086016101c4565b91505092915050565b60006020828403121561023e57600080fd5b600082013567ffffffffffffffff81111561025857600080fd5b61026484828501610202565b91505092915050565b60006102788261031e565b6102828185610329565b9350610292818560208601610349565b61029b8161043d565b840191505092915050565b600060208201905081810360008301526102c0818461026d565b905092915050565b60006102d26102e3565b90506102de82826103ae565b919050565b6000604051905090565b600067ffffffffffffffff8211156103085761030761040e565b5b6103118261043d565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b8381101561036757808201518184015260208101905061034c565b83811115610376576000848401525b50505050565b6000600282049050600182168061039457607f821691505b602082108114156103a8576103a76103df565b5b50919050565b6103b78261043d565b810181811067ffffffffffffffff821117156103d6576103d561040e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fea264697066735822122070d157c4efbb3fba4a1bde43cbba5b92b69f2fc455a650c0dfb61e9ed3d4bd6364736f6c634300080400330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b696e697469616c5f6d7367000000000000000000000000000000000000000000', from: '0x81cb089c285e5ee3a7353704fb114955037443af', }; - mockContractCall(callData, true, 200, { result: `0x61A80` }); + await mockContractCall(callData, true, 200, { result: `0x61A80` }); const gas = await ethImpl.estimateGas(callData, null); expect((gas as string).toLowerCase()).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT).toLowerCase()); @@ -132,7 +143,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', value: 1, }; - mockContractCall(callData, true, 200, { result: `0x61A80` }); + await mockContractCall(callData, true, 200, { result: `0x61A80` }); const gas = await ethImpl.estimateGas({ ...callData, value: ONE_TINYBAR_IN_WEI_HEX }, null); expect((gas as string).toLowerCase()).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT).toLowerCase()); @@ -142,7 +153,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '0x01', }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImpl.estimateGas({ data: '0x01' }, null); expect(gas).to.equal(numberTo0x(Precheck.transactionIntrinsicGasCost(callData.data!))); @@ -155,7 +166,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: '0x2540BE400', }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const gas = await ethImpl.estimateGas(callData, null); @@ -168,7 +179,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', to: RECEIVER_ADDRESS, }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const result = await ethImpl.estimateGas(callData, null); @@ -181,7 +192,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const gas = await ethImpl.estimateGas( @@ -199,7 +210,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const gasBeforeCache = await ethImpl.estimateGas( @@ -228,7 +239,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(404); const hollowAccountGasCreation = await ethImpl.estimateGas( @@ -247,7 +258,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 0, //in tinybars }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const result = await ethImpl.estimateGas( { to: RECEIVER_ADDRESS, @@ -266,13 +277,6 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas for contract create with input field and absent data field', async () => { const gasEstimation = 1357410; - const mockedCallData: IContractCallRequest = { - from: '0x81cb089c285e5ee3a7353704fb114955037443af', - to: null, - value: 0, - data: '0x81cb089c285e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af', - }; - const callData: IContractCallRequest = { from: '0x81cb089c285e5ee3a7353704fb114955037443af', to: null, @@ -280,8 +284,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { input: '0x81cb089c285e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af', }; - - mockContractCall(mockedCallData, true, 200, { result: `0x14b662` }); + await mockContractCall(callData, true, 200, { result: `0x14b662` }); const gas = await ethImpl.estimateGas(callData, null); @@ -293,7 +296,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: null, //in tinybars }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const result = await ethImpl.estimateGas( { to: RECEIVER_ADDRESS, @@ -311,7 +314,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas empty call returns transfer cost', async function () { const callData: IContractCallRequest = {}; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImpl.estimateGas({}, null); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); @@ -319,7 +322,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas empty call returns transfer cost with overridden default gas', async function () { const callData: IContractCallRequest = {}; - mockContractCall(callData, true, 200, { result: numberTo0x(defaultGasOverride) }); + await mockContractCall(callData, true, 200, { result: numberTo0x(defaultGasOverride) }); const gas = await ethImplOverridden.estimateGas({}, null); expect(gas).to.equal(numberTo0x(defaultGasOverride)); @@ -332,7 +335,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { }; const contractsCallResponse: IContractCallResponse = { errorMessage: '', statusCode: 501 }; - web3Mock.onPost('contracts/call', callData).reply(501, contractsCallResponse); + await mockContractCall(callData, true, 501, contractsCallResponse); const gas = await ethImpl.estimateGas({ data: '' }, null); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); @@ -342,7 +345,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '', }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImplOverridden.estimateGas({ data: '' }, null); expect(gas).to.equal(numberTo0x(defaultGasOverride)); @@ -354,7 +357,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: '0x1', }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); mockGetAccount(RECEIVER_ADDRESS, 200, { account: '0.0.1234', evm_address: RECEIVER_ADDRESS }); const gas = await ethImpl.estimateGas(callData, null); @@ -365,7 +368,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '0x0', }; - mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); const gas = await ethImplOverridden.estimateGas({ data: '0x' }, null); expect(gas).to.equal(numberTo0x(defaultGasOverride)); @@ -383,7 +386,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { ], }, }; - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(400, contractCallResult); + await mockContractCall(transaction, true, 400, contractCallResult); const estimatedGas = await ethImpl.estimateGas(transaction, id); @@ -393,7 +396,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas with contract revert and message does not equal executionReverted and ESTIMATE_GAS_THROWS is set to false', async function () { const estimateGasThrows = process.env.ESTIMATE_GAS_THROWS; process.env.ESTIMATE_GAS_THROWS = 'false'; - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(400, { + await mockContractCall(transaction, true, 400, { _status: { messages: [ { @@ -412,7 +415,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { }); it('should eth_estimateGas with contract revert and message equals "execution reverted: Invalid number of recipients"', async function () { - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(400, { + await mockContractCall(transaction, true, 400, { _status: { messages: [ { @@ -438,7 +441,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const encodedMessage = new AbiCoder().encode(['string'], [decodedMessage]).replace('0x', ''); const encodedCustomError = customErrorSignature + encodedMessage; - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(400, { + await mockContractCall(transaction, true, 400, { _status: { messages: [ { @@ -462,7 +465,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const encodedMessage = new AbiCoder().encode(['string'], [decodedMessage]).replace('0x', ''); const encodedGenericError = defaultErrorSignature + encodedMessage; - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(400, { + await mockContractCall(transaction, true, 400, { _status: { messages: [ { @@ -481,7 +484,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { }); it('should eth_estimateGas handles a 501 unimplemented response from the mirror node correctly by returning default gas', async function () { - web3Mock.onPost('contracts/call', { ...transaction, estimate: true }).reply(501, { + await mockContractCall(transaction, true, 501, { _status: { messages: [ { @@ -506,13 +509,13 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gas: '0xd97010', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.value).to.eq(1110); expect(transaction.gasPrice).to.eq(1000000); expect(transaction.gas).to.eq(14250000); }); - it('should accepts both input and data fields but copy value of input field to data field', () => { + it('should accepts both input and data fields but copy value of input field to data field', async () => { const inputValue = 'input value'; const dataValue = 'data value'; const transaction = { @@ -524,7 +527,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gas: '0xd97010', }; - ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction); expect(transaction.data).to.eq(inputValue); expect(transaction.data).to.not.eq(dataValue); expect(transaction.input).to.be.undefined;