Skip to content

Commit

Permalink
Support 'from' in 'eth_call' (#375)
Browse files Browse the repository at this point in the history
* Support from in eth_call

When a from value is in the eth_call params, set it to the senderId.

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon committed Jul 25, 2022
1 parent 9288e0c commit 5fd35e3
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 12 deletions.
6 changes: 5 additions & 1 deletion packages/relay/src/lib/clients/sdkClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export class SDKClient {
.setEthereumData(transactionBuffer), callerName);
}

async submitContractCallQuery(to: string, data: string, gas: number, callerName: string): Promise<ContractFunctionResult> {
async submitContractCallQuery(to: string, data: string, gas: number, from: string, callerName: string): Promise<ContractFunctionResult> {
const contract = SDKClient.prune0x(to);
const contractId = contract.startsWith("00000000000")
? ContractId.fromSolidityAddress(contract)
Expand All @@ -209,6 +209,10 @@ export class SDKClient {
contractCallQuery.setFunctionParameters(Buffer.from(SDKClient.prune0x(data), 'hex'));
}

if (from) {
contractCallQuery.setSenderAccountId(AccountId.fromEvmAddress(0,0, from))
}

if (this.clientMain.operatorAccountId !== null) {
contractCallQuery
.setPaymentTransactionId(TransactionId.generate(this.clientMain.operatorAccountId));
Expand Down
6 changes: 3 additions & 3 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,10 @@ export class EthImpl implements Eth {
gas = call.gas;
}
}

// Execute the call and get the response
this.logger.debug('Making eth_call on contract %o with gas %d and call data "%s"', call.to, gas, call.data);
const contractCallResponse = await this.sdkClient.submitContractCallQuery(call.to, call.data, gas, EthImpl.ethCall);
this.logger.debug('Making eth_call on contract %o with gas %d and call data "%s" from "%s"', call.to, gas, call.data, call.from);
const contractCallResponse = await this.sdkClient.submitContractCallQuery(call.to, call.data, gas, call.from, EthImpl.ethCall);

// FIXME Is this right? Maybe so?
return EthImpl.prepend0x(Buffer.from(contractCallResponse.asBytes()).toString('hex'));
Expand Down
77 changes: 76 additions & 1 deletion packages/relay/tests/lib/eth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import pino from 'pino';
import { Block, Transaction } from '../../src/lib/model';
import constants from '../../src/lib/constants';
import { SDKClient } from '../../src/lib/clients';
import { TextEncoder } from 'util';
const logger = pino();
const registry = new Registry();
const Relay = new RelayImpl(logger, registry);
Expand Down Expand Up @@ -114,6 +113,7 @@ describe('Eth calls using MirrorNode', async function () {
const gasUsed2 = 800000;
const maxGasLimit = 250000;
const maxGasLimitHex = EthImpl.numberTo0x(maxGasLimit);
const contractCallData = "0xef641f44"
const firstTransactionTimestampSeconds = '1653077547';
const firstTransactionTimestampSecondsHex = EthImpl.numberTo0x(Number(firstTransactionTimestampSeconds));
const contractAddress1 = '0x000000000000000000000000000000000000055f';
Expand Down Expand Up @@ -1317,6 +1317,81 @@ describe('Eth calls using MirrorNode', async function () {
expect(error.message).to.equal('Error encountered estimating the gas price');
}
});

describe('eth_call', async function () {
it('eth_call with no gas', async function () {
sdkClientStub.submitContractCallQuery.returns({
asBytes: function () {
return Uint8Array.of(0)
}
}
);

const result = await ethImpl.call({
"from": contractAddress1,
"to": contractAddress2,
"data": contractCallData,
}, 'latest')

sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, 400_000, contractAddress1, 'eth_call');
expect(result).to.equal("0x00")
});

it('eth_call with no data', async function () {
sdkClientStub.submitContractCallQuery.returns({
asBytes: function () {
return Uint8Array.of(0)
}
}
);

var result = await ethImpl.call({
"from": contractAddress1,
"to": contractAddress2,
"gas": maxGasLimitHex
}, 'latest')

sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, undefined, maxGasLimit, contractAddress1, 'eth_call');
expect(result).to.equal("0x00")
});

it('eth_call with no from address', async function () {
sdkClientStub.submitContractCallQuery.returns({
asBytes: function () {
return Uint8Array.of(0)
}
}
);

const result = await ethImpl.call({
"to": contractAddress2,
"data": contractCallData,
"gas": maxGasLimitHex
}, 'latest')

sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, maxGasLimit, undefined, 'eth_call');
expect(result).to.equal("0x00")
});

it('eth_call with all fields', async function () {
sdkClientStub.submitContractCallQuery.returns({
asBytes: function () {
return Uint8Array.of(0)
}
}
);

const result = await ethImpl.call({
"from": contractAddress1,
"to": contractAddress2,
"data": contractCallData,
"gas": maxGasLimitHex
}, 'latest')

sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, maxGasLimit, contractAddress1, 'eth_call');
expect(result).to.equal("0x00")
});
});
});

describe('Eth', async function () {
Expand Down
14 changes: 7 additions & 7 deletions packages/server/tests/acceptance/erc20.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {Utils} from '../helpers/utils';

describe('ERC20 Acceptance Tests', async function () {
this.timeout(240 * 1000); // 240 seconds
const {servicesNode, relay, logger} = global;
const {servicesNode, relay} = global;

// cached entities
const accounts: AliasAccount[] = [];
Expand Down Expand Up @@ -175,7 +175,8 @@ describe('ERC20 Acceptance Tests', async function () {
if (testTitles[i].testName !== HTS) {
describe('when the spender has enough allowance', function () {
before(async function () {
await contract.connect(tokenOwnerWallet).approve(spender, initialSupply);
const tx = await contract.connect(tokenOwnerWallet).approve(spender, initialSupply);
await tx.wait();
});

describe('when the token owner has enough balance', function () {
Expand All @@ -190,6 +191,7 @@ describe('ERC20 Acceptance Tests', async function () {

it('transfers the requested amount', async function () {
tx = await contract.connect(spenderWallet).transferFrom(tokenOwner, to, initialSupply);
await tx.wait();
const ownerBalance = await contract.balanceOf(tokenOwner);
const toBalance = await contract.balanceOf(to);
expect(ownerBalance.toString()).to.be.equal('0');
Expand Down Expand Up @@ -336,7 +338,7 @@ describe('ERC20 Acceptance Tests', async function () {
const receipt = await relay.provider.getTransactionReceipt(contract.deployTransaction.hash);
contract = new ethers.Contract(receipt.to, contractJson.abi, accounts[0].wallet);
return contract;
};
}

const createHTS = async(tokenName, symbol, adminAccount, initialSupply, abi, associatedAccounts) => {
const htsResult = await servicesNode.createHTS({
Expand All @@ -356,8 +358,6 @@ describe('ERC20 Acceptance Tests', async function () {
// Setup initial balance of token owner account
await servicesNode.transferHTSToken(accounts[0].accountId, htsResult.receipt.tokenId, initialSupply, htsResult.client);
const evmAddress = Utils.idToEvmAddress(htsResult.receipt.tokenId.toString());
const contract = new ethers.Contract(evmAddress, abi, accounts[0].wallet);

return contract;
return new ethers.Contract(evmAddress, abi, accounts[0].wallet);
};
});
});

0 comments on commit 5fd35e3

Please sign in to comment.