Skip to content

Commit

Permalink
feat: allowed eth_call to accept null/empty tx.to to simulate deployi…
Browse files Browse the repository at this point in the history
…ng smart contracts

Signed-off-by: Logan Nguyen <logan.nguyen@swirldslabs.com>
  • Loading branch information
quiet-node committed Jul 1, 2024
1 parent 635db92 commit d669260
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
11 changes: 5 additions & 6 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1553,19 +1553,18 @@ export class EthImpl implements Eth {
const callData = call.data ? call.data : call.input;
// log request
this.logger.trace(
`${requestIdPrefix} call({to=${call.to}, from=${call.from}, data=${callData}, gas=${call.gas}, ...}, blockParam=${blockParam})`,
`${requestIdPrefix} call({to=${call.to}, from=${call.from}, data=${callData}, gas=${call.gas}, gasPrice=${call.gasPrice} blockParam=${blockParam}, estimate=${call.estimate})`,
);
// log call data size and gas
// log call data size
const callDataSize = callData ? callData.length : 0;
this.logger.trace(`${requestIdPrefix} call data size: ${callDataSize}, gas: ${call.gas}`);
this.logger.trace(`${requestIdPrefix} call data size: ${callDataSize}`);
// metrics for selector
if (callDataSize >= constants.FUNCTION_SELECTOR_CHAR_LENGTH)
this.ethExecutionsCounter
.labels(EthImpl.ethCall, callData.substring(0, constants.FUNCTION_SELECTOR_CHAR_LENGTH))
.inc();

const blockNumberOrTag = await this.extractBlockNumberOrTag(blockParam, requestIdPrefix);

await this.performCallChecks(call);

// Get a reasonable value for "gas" if it is not specified.
Expand Down Expand Up @@ -1828,8 +1827,8 @@ export class EthImpl implements Eth {
* @param requestIdPrefix
*/
async performCallChecks(call: any): Promise<void> {
// The "to" address must always be 42 chars.
if (!call.to || call.to.length != 42) {
// after this mirror-node PR https://github.com/hashgraph/hedera-mirror-node/pull/8100, call.to is allowed to be empty or null
if (call.to !== null && !isValidEthereumAddress(call.to)) {
throw predefined.INVALID_CONTRACT_ADDRESS(call.to);
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/relay/tests/lib/eth/eth-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,14 @@ export const CONTRACT_ADDRESS_2 = '0x000000000000000000000000000000000000055e';
export const CONTRACT_ADDRESS_3 = '0x000000000000000000000000000000000000255c';
export const HTS_TOKEN_ADDRESS = '0x0000000000000000000000000000000002dca431';
export const ACCOUNT_ADDRESS_1 = '0x13212A14deaf2775a5b3bEcC857806D5c719d3f2';
export const NON_EXISTENT_CONTRACT_ADDRESS = `'0x55555555555555555555555555555555555555'`;
export const NON_EXISTENT_CONTRACT_ADDRESS = `0x5555555555555555555555555555555555555555`;
export const DEFAULT_HTS_TOKEN = mockData.token;
export const DEPLOYED_BYTECODE =
'0x608060405234801561001057600080fd5b5060405161078938038061078983398181016040528101906100329190';
export const MIRROR_NODE_DEPLOYED_BYTECODE =
'0x608060405234801561001057600080fd5b5060405161078938038061078983398181016040528101906100321234';
export const EXAMPLE_CONTRACT_BYTECODE =
'0x6080604052348015600f57600080fd5b50609e8061001e6000396000f3fe608060405260043610602a5760003560e01c80635c36b18614603557806383197ef014605557600080fd5b36603057005b600080fd5b348015604057600080fd5b50600160405190815260200160405180910390f35b348015606057600080fd5b50606633ff5b00fea2646970667358221220886a6d6d6c88bcfc0063129ca2391a3d98aee75ad7fe3e870ec6679215456a3964736f6c63430008090033';
export const TINYBAR_TO_WEIBAR_COEF_BIGINT = BigInt(constants.TINYBAR_TO_WEIBAR_COEF);
export const ONE_TINYBAR_IN_WEI_HEX = toHex(TINYBAR_TO_WEIBAR_COEF_BIGINT);

Expand Down
17 changes: 15 additions & 2 deletions packages/relay/tests/lib/eth/eth_call.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';

import { EthImpl } from '../../../src/lib/eth';
import constants from '../../../src/lib/constants';
import { SDKClient } from '../../../src/lib/clients';
import {
ACCOUNT_ADDRESS_1,
BLOCK_HASH_TRIMMED,
CONTRACT_ADDRESS_1,
CONTRACT_ADDRESS_2,
CONTRACT_CALL_DATA,
Expand All @@ -43,6 +41,7 @@ import {
NON_EXISTENT_CONTRACT_ADDRESS,
WRONG_CONTRACT_ADDRESS,
ONE_TINYBAR_IN_WEI_HEX,
EXAMPLE_CONTRACT_BYTECODE,
} from './eth-config';
import { JsonRpcError, predefined } from '../../../src/lib/errors/JsonRpcError';
import RelayAssertions from '../../assertions';
Expand Down Expand Up @@ -777,6 +776,20 @@ describe('@ethCall Eth Call spec', async function () {
expect(result).to.be.not.null;
expect(result).to.equal('0x');
});

it('eth_call to simulate deploying a smart contract (empty/null `to` field)', async function () {
const callData = {
data: EXAMPLE_CONTRACT_BYTECODE,
to: null,
from: ACCOUNT_ADDRESS_1,
};

web3Mock
.onPost('contracts/call', { ...callData, estimate: false, block: 'latest' })
.reply(200, { result: EXAMPLE_CONTRACT_BYTECODE });
const result = await ethImpl.call(callData, 'latest');
expect(result).to.eq(EXAMPLE_CONTRACT_BYTECODE);
});
});

describe('contractCallFormat', () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/server/tests/acceptance/rpc_batch3.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () {
expect(res).to.eq(BASIC_CONTRACT_PING_RESULT);
});

it('@release should execute "eth_call" request to simulate deploying a contract (empty/null `to` field)', async function () {
const callData = {
from: accounts[0].address,
to: null,
data: basicContractJson.bytecode,
};
const res = await relay.call(RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId);
console.log(res);

expect(res).to.eq(basicContractJson.deployedBytecode);
});

it('should fail "eth_call" request without data field', async function () {
const callData = {
from: accounts[0].address,
Expand Down

0 comments on commit d669260

Please sign in to comment.