Skip to content

Commit

Permalink
Add support for optional data field on eth_call flow (#317)
Browse files Browse the repository at this point in the history
* Fix schema mismatch for nums in eth_getTransactionReceipt response

Add support for optional data field on eth_call flow

- Add check to populate functionParams only on valid data field
- Update servicesClient transaction with memo to help differentiate
- Add caller name to `sdkClient` to help track
- Update Basic contract with `receive` function to support no data param test

Signed-off-by: Nana-EC <[email protected]>
  • Loading branch information
Nana-EC committed Jul 15, 2022
1 parent 637d3af commit bbc34c6
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 26 deletions.
19 changes: 14 additions & 5 deletions packages/relay/src/lib/clients/sdkClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,23 +189,26 @@ export class SDKClient {
return transactionResponse.getRecord(this.clientMain);
}

async submitEthereumTransaction(transactionBuffer: Uint8Array): Promise<TransactionResponse> {
async submitEthereumTransaction(transactionBuffer: Uint8Array, callerName: string): Promise<TransactionResponse> {
return this.executeTransaction(new EthereumFlow()
.setEthereumData(transactionBuffer));
.setEthereumData(transactionBuffer), callerName);
}

async submitContractCallQuery(to: string, data: string, gas: number, callerName: string): Promise<ContractFunctionResult> {
const contract = SDKClient.prune0x(to);
const callData = SDKClient.prune0x(data);
const contractId = contract.startsWith("00000000000")
? ContractId.fromSolidityAddress(contract)
: ContractId.fromEvmAddress(0, 0, contract);

const contractCallQuery = new ContractCallQuery()
.setContractId(contractId)
.setFunctionParameters(Buffer.from(callData, 'hex'))
.setGas(gas);

// data is optional and can be omitted in which case fallback function will be employed
if (data) {
contractCallQuery.setFunctionParameters(Buffer.from(SDKClient.prune0x(data), 'hex'));
}

if (this.clientMain.operatorAccountId !== null) {
contractCallQuery
.setPaymentTransactionId(TransactionId.generate(this.clientMain.operatorAccountId));
Expand Down Expand Up @@ -262,7 +265,7 @@ export class SDKClient {
}
};

private executeTransaction = async (transaction: Transaction | EthereumFlow): Promise<TransactionResponse> => {
private executeTransaction = async (transaction: Transaction | EthereumFlow, callerName: string): Promise<TransactionResponse> => {
const transactionType = transaction.constructor.name;
try {
this.logger.info(`Execute ${transactionType} transaction`);
Expand All @@ -273,6 +276,12 @@ export class SDKClient {
catch (e: any) {
const statusCode = e.status ? e.status._code : Status.Unknown._code;
this.logger.info(`Consensus Node ${transactionType} transaction response: ${statusCode}`);
this.captureMetrics(
SDKClient.transactionMode,
transactionType,
statusCode,
0,
callerName);

// capture sdk transaction response errors and shorten familiar stack trace
if (e.status && e.status._code) {
Expand Down
2 changes: 1 addition & 1 deletion packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ export class EthImpl implements Eth {
const transactionBuffer = Buffer.from(EthImpl.prune0x(transaction), 'hex');

try {
const contractExecuteResponse = await this.sdkClient.submitEthereumTransaction(transactionBuffer);
const contractExecuteResponse = await this.sdkClient.submitEthereumTransaction(transactionBuffer, EthImpl.ethSendRawTransaction);

try {
// Wait for the record from the execution.
Expand Down
22 changes: 11 additions & 11 deletions packages/server/tests/acceptance/rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { AccountBalanceQuery, ContractFunctionParameters } from '@hashgraph/sdk'
// local resources
import parentContractJson from '../contracts/Parent.json';
import basicContractJson from '../contracts/Basic.json';
import {JsonRpcError} from "@hashgraph/json-rpc-relay";
import logsContractJson from '../contracts/Logs.json';
import { predefined } from '../../../relay/src/lib/errors';

Expand Down Expand Up @@ -994,6 +993,17 @@ describe('RPC Server Acceptance Tests', function () {
expect(res).to.eq(BASIC_CONTRACT_PING_RESULT);
});

it('should fail "eth_call" request without data field', async function () {
const callData = {
from: accounts[2].address,
to: evmAddress,
gas: 30000
};

const res = await relay.call('eth_call', [callData]);
expect(res).to.eq('0x'); // confirm no error
});

it('should fail "eth_call" for non-existing contract address', async function () {
const callData = {
from: accounts[2].address,
Expand Down Expand Up @@ -1026,16 +1036,6 @@ describe('RPC Server Acceptance Tests', function () {
const res = await relay.call('eth_call', [callData]);
expect(res).to.eq(BASIC_CONTRACT_PING_RESULT);
});

it('should fail "eth_call" request without data field', async function () {
const callData = {
from: accounts[2].address,
to: evmAddress,
gas: 30000
};

await relay.callFailing('eth_call', [callData]);
});
});
});

Expand Down
22 changes: 15 additions & 7 deletions packages/server/tests/clients/servicesClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export default class ServicesClient {
.setTokenSymbol(symbol)
.setDecimals(3)
.setInitialSupply(new Hbar(initialSupply).toTinybars())
.setTreasuryAccountId(this._thisAccountId()));
.setTreasuryAccountId(this._thisAccountId())
.setTransactionMemo('Relay test token create'));

this.logger.trace(`get token id from receipt`);
const tokenId = resp?.tokenId;
Expand All @@ -126,7 +127,8 @@ export default class ServicesClient {
await this.executeAndGetTransactionReceipt(
await new TokenAssociateTransaction()
.setAccountId(this._thisAccountId())
.setTokenIds([tokenId]));
.setTokenIds([tokenId])
.setTransactionMemo('Relay test token association'));

this.logger.debug(
`Associated account ${this._thisAccountId()} with token ${tokenId.toString()}`
Expand All @@ -136,7 +138,8 @@ export default class ServicesClient {
async transferToken(tokenId, recipient: AccountId, amount = 10) {
await this.executeAndGetTransactionReceipt(new TransferTransaction()
.addTokenTransfer(tokenId, this._thisAccountId(), -amount)
.addTokenTransfer(tokenId, recipient, amount));
.addTokenTransfer(tokenId, recipient, amount)
.setTransactionMemo('Relay test token transfer'));

this.logger.debug(
`Sent 10 tokens from account ${this._thisAccountId()} to account ${recipient.toString()} on token ${tokenId.toString()}`
Expand All @@ -157,7 +160,8 @@ export default class ServicesClient {

const fileReceipt = await this.executeAndGetTransactionReceipt(new FileCreateTransaction()
.setKeys([this.client.operatorPublicKey || this.DEFAULT_KEY])
.setContents(contractByteCode));
.setContents(contractByteCode)
.setTransactionMemo('Relay test file create'));

// Fetch the receipt for transaction that created the file
// The file ID is located on the transaction receipt
Expand All @@ -172,7 +176,8 @@ export default class ServicesClient {
.setGas(75000)
.setInitialBalance(1)
.setBytecodeFileId(fileId || '')
.setAdminKey(this.client.operatorPublicKey || this.DEFAULT_KEY));
.setAdminKey(this.client.operatorPublicKey || this.DEFAULT_KEY)
.setTransactionMemo('Relay test contract create'));

// The contract ID is located on the transaction receipt
const contractId = contractReceipt?.contractId;
Expand All @@ -192,7 +197,8 @@ export default class ServicesClient {
.setFunction(
functionName,
params
));
)
.setTransactionMemo('Relay test contract execution'));

// @ts-ignore
const resp = await this.getRecordResponseDetails(contractExecTransactionResponse);
Expand All @@ -213,7 +219,8 @@ export default class ServicesClient {

const aliasCreationResponse = await this.executeTransaction(new TransferTransaction()
.addHbarTransfer(this._thisAccountId(), new Hbar(initialBalance).negated())
.addHbarTransfer(aliasAccountId, new Hbar(initialBalance)));
.addHbarTransfer(aliasAccountId, new Hbar(initialBalance))
.setTransactionMemo('Relay test crypto transfer'));

this.logger.debug(`Get ${aliasAccountId.toString()} receipt`);
await aliasCreationResponse?.getReceipt(this.client);
Expand Down Expand Up @@ -275,6 +282,7 @@ export default class ServicesClient {
const response = await new FileUpdateTransaction()
.setFileId(fileId)
.setContents(Buffer.from(content, 'hex'))
.setTransactionMemo('Relay test update')
.execute(this.client);

const receipt = await response.getReceipt(this.client);
Expand Down
8 changes: 6 additions & 2 deletions packages/server/tests/contracts/Basic.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@
],
"stateMutability": "pure",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
],
"bytecode": "0x608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80635c36b18614602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212206ad62fc0dbb8a1f3b6389e460d953f0f7fb0ce2eb71250d4057992f62dfc56d264736f6c63430008040033",
"deployedBytecode": "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80635c36b18614602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212206ad62fc0dbb8a1f3b6389e460d953f0f7fb0ce2eb71250d4057992f62dfc56d264736f6c63430008040033",
"bytecode": "0x608060405234801561001057600080fd5b5060bf8061001f6000396000f3fe608060405260043610601f5760003560e01c80635c36b18614602a576025565b36602557005b600080fd5b348015603557600080fd5b50603c6050565b604051604791906070565b60405180910390f35b60006001905090565b6000819050919050565b606a816059565b82525050565b6000602082019050608360008301846063565b9291505056fea2646970667358221220defb1b1fca12d2d0cdcb178a4117d75fe5c8500fae15807c82c8c3db9b32e5b764736f6c63430008090033",
"deployedBytecode": "0x608060405260043610601f5760003560e01c80635c36b18614602a576025565b36602557005b600080fd5b348015603557600080fd5b50603c6050565b604051604791906070565b60405180910390f35b60006001905090565b6000819050919050565b606a816059565b82525050565b6000602082019050608360008301846063565b9291505056fea2646970667358221220defb1b1fca12d2d0cdcb178a4117d75fe5c8500fae15807c82c8c3db9b32e5b764736f6c63430008090033",
"linkReferences": {},
"deployedLinkReferences": {}
}
3 changes: 3 additions & 0 deletions packages/server/tests/contracts/Basic.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract Basic {

constructor() {}

function ping() public pure returns (int) {
return 1;
}

receive() external payable { }
}

0 comments on commit bbc34c6

Please sign in to comment.