Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,20 @@ export class ContractService implements IContractService {
return constants.EMPTY_HEX;
}

// Map additional Mirror Node 400 precheck failures during eth_call to a non-error empty result
// Only for entity-nonexistence style messages; do not swallow genuine EVM reverts
if (e.statusCode === 400) {
const nonExistenceMessages = new Set<string>([
'INVALID_ACCOUNT_ID',
'ACCOUNT_ID_DOES_NOT_EXIST',
'INVALID_CONTRACT_ID',
'CONTRACT_ID_DOES_NOT_EXIST',
]);
if (e.message && nonExistenceMessages.has(e.message)) {
return constants.EMPTY_HEX;
}
}

if (e.isContractReverted()) {
if (this.logger.isLevelEnabled('trace')) {
this.logger.trace(
Expand Down
33 changes: 33 additions & 0 deletions packages/relay/tests/lib/eth/eth_call.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,39 @@ describe('@ethCall Eth Call spec', async function () {
expect((result as JsonRpcError).data).to.equal(defaultErrorMessageHex);
});

it('eth_call view with non-existing from succeeds', async function () {
const NON_EXISTENT_FROM = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
const callData = {
...defaultCallData,
from: NON_EXISTENT_FROM,
to: CONTRACT_ADDRESS_2,
data: CONTRACT_CALL_DATA,
gas: MAX_GAS_LIMIT,
};

restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2));
await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails);
const result = await contractService.call(callData, 'latest', requestDetails);
expect(result).to.equal('0x00');
});

it('eth_call tx with non-existing from and value=0 succeeds', async function () {
const NON_EXISTENT_FROM = '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const callData = {
...defaultCallData,
from: NON_EXISTENT_FROM,
to: CONTRACT_ADDRESS_2,
data: CONTRACT_CALL_DATA,
gas: MAX_GAS_LIMIT,
value: null,
};

restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2));
await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails);
const result = await contractService.call(callData, 'latest', requestDetails);
expect(result).to.equal('0x00');
});

it('eth_call with wrong `to` field', async function () {
const args = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"eth_call - existing contract tx, non-existing from and positive value": {
"request": "{\"method\":\"eth_call\",\"params\":[{\"from\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\"to\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\"data\":\"0x70a082310000000000000000000000006E0d01A76C3Cf4288372a29124A26D4353EE51BE\",\"value\":\"0x2540be400\"},\"latest\"],\"id\":1,\"jsonrpc\":\"2.0\"}",
"response": "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[]}"
"response": "{\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer\"}}"
},
"eth_estimateGas - non existing contract": {
"request": "{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\"to\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\"data\":\"0x70a082310000000000000000000000006E0d01A76C3Cf4288372a29124A26D4353EE51BE\"},\"latest\"],\"id\":1,\"jsonrpc\":\"2.0\"}",
Expand Down
Loading