Skip to content

Commit

Permalink
Adding support for EVM view and function calls (#517)
Browse files Browse the repository at this point in the history
* Adding support for EVM view and function calls.

* Move things around a bit in eth-call

* Fix linter

* evm-call renaming: evmAccounts » nearEvmAccountId, contractName » evmContractName

* specify gas as "NEAR gas" in evm-call

* correct search/replace mistake key evmAccountId for NearProvider added back

* standardize arg names, log args better, unique names for scheduleEVMFunctionCall

* package.json: latest version for near-api-js and near-web3-provider

* update yarn.lock file

* readme: add evm-view/evm-call

Co-authored-by: Mike Purvis <mikedotexe@gmail.com>
  • Loading branch information
ilblackdragon and mikedotexe authored Nov 17, 2020
1 parent 890c8bb commit d55a359
Show file tree
Hide file tree
Showing 7 changed files with 2,584 additions and 115 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ For a list of up-to-date commands, run `near` in your terminal with no arguments
near delete-key [accessKey] # delete access key
```

#### For smart contract:
#### For native smart contracts:
```bash
near deploy [accountId] [wasmFile] [initFunction] [initArgs] [initGas] [initDeposit] # deploy your smart contract
near dev-deploy [wasmFile] # deploy your smart contract using temporary account (TestNet only)
Expand All @@ -47,6 +47,12 @@ For a list of up-to-date commands, run `near` in your terminal with no arguments
near clean # clean the smart contract build locally (remove ./out )
```

#### For NEAR EVM smart contracts:
```bash
near evm-view <evmAccount> <contractName> <methodName> [args] # make an EVM smart contract call which can view state
near evm-call <evmAccount> <contractName> <methodName> [args] # schedule an EVM smart contract call which can modify state
```

#### For transactions:
```bash
near tx-status <hash> # lookup transaction status by hash
Expand Down
3 changes: 3 additions & 0 deletions bin/near-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ yargs // eslint-disable-line
.middleware(require('../middleware/print-options'))
.middleware(require('../middleware/key-store'))
.middleware(require('../middleware/ledger'))
.middleware(require('../middleware/abi'))
.middleware(require('../middleware/seed-phrase'))
.command(require('../commands/create-account').createAccountCommand)
.command(require('../commands/create-account').createAccountCommandDeprecated)
Expand All @@ -244,6 +245,8 @@ yargs // eslint-disable-line
.command(require('../commands/delete-key'))
.command(require('../commands/validators'))
.command(require('../commands/proposals'))
.command(require('../commands/evm-call'))
.command(require('../commands/evm-view'))
.config(config)
.alias({
'accountId': ['account_id'],
Expand Down
56 changes: 56 additions & 0 deletions commands/evm-call.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const exitOnError = require('../utils/exit-on-error');
const web3 = require('web3');
const { NearProvider, utils } = require('near-web3-provider');
const assert = require('assert');

module.exports = {
command: 'evm-call <evmAccount> <contractName> <methodName> [args]',
desc: 'Schedule call inside EVM machine',
builder: (yargs) => yargs
.option('gas', {
desc: 'Max amount of NEAR gas this call can use',
type: 'string',
default: '100000000000000'
})
.option('amount', {
desc: 'Number of tokens to attach',
type: 'string',
default: '0'
})
.option('args', {
desc: 'Arguments to the contract call, in JSON format (e.g. \'[1, "str"]\') based on contract ABI',
type: 'string',
default: null
})
.option('accountId', {
required: true,
desc: 'Unique identifier for the account that will be used to sign this call',
type: 'string',
})
.option('abi', {
required: true,
desc: 'Path to ABI for given contract',
type: 'string',
}),
handler: exitOnError(scheduleEVMFunctionCall)
};

async function scheduleEVMFunctionCall(options) {
const args = JSON.parse(options.args || '[]');
console.log(`Scheduling a call inside ${options.evmAccount} EVM:`);
console.log(`${options.contractName}.${options.methodName}()` +
(options.amount && options.amount !== '0' ? ` with attached ${options.amount} NEAR` : ''));
console.log(' with args', args);
const web = new web3();
web.setProvider(new NearProvider({
nodeUrl: options.nodeUrl,
// TODO: make sure near-api-js has the same version between near-web3-provider.
// keyStore: options.keyStore,
masterAccountId: options.accountId,
networkId: options.networkId,
evmAccountId: options.evmAccount,
}));
const contract = new web.eth.Contract(options.abi, options.contractName);
assert(options.methodName in contract.methods, `${options.methodName} is not present in ABI`);
await contract.methods[options.methodName](...args).send({ from: utils.nearAccountToEvmAddress(options.accountId) });
}
42 changes: 42 additions & 0 deletions commands/evm-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const exitOnError = require('../utils/exit-on-error');
const web3 = require('web3');
const { NearProvider, utils } = require('near-web3-provider');
const assert = require('assert');

module.exports = {
command: 'evm-view <evmAccount> <contractName> <methodName> [args]',
desc: 'View call inside EVM machine',
builder: (yargs) => yargs
.option('args', {
desc: 'Arguments to the contract call, in JSON format (e.g. \'[1, "str"]\') based on contract ABI',
type: 'string',
default: null
})
.option('accountId', {
required: true,
desc: 'Unique identifier for the account that will be used to sign this call',
type: 'string',
})
.option('abi', {
desc: 'Path to ABI for given contract',
type: 'string',
}),
handler: exitOnError(scheduleEVMFunctionView)
};

async function scheduleEVMFunctionView(options) {
const web = new web3();
web.setProvider(new NearProvider({
nodeUrl: options.nodeUrl,
// TODO: make sure near-api-js has the same version between near-web3-provider.
// keyStore: options.keyStore,
masterAccountId: options.accountId,
networkId: options.networkId,
evmAccountId: options.evmAccount,
}));
const contract = new web.eth.Contract(options.abi, options.contractName);
const args = JSON.parse(options.args || '[]');
assert(options.methodName in contract.methods, `${options.methodName} is not present in ABI`);
const result = await contract.methods[options.methodName](...args).call({ from: utils.nearAccountToEvmAddress(options.accountId) });
console.log(result);
}
11 changes: 11 additions & 0 deletions middleware/abi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs = require('fs');

async function loadAbi(abiPath) {
return JSON.parse(fs.readFileSync(abiPath)).abi;
}

module.exports = async function parseAbi(options) {
if (options.abi) {
options.abi = await loadAbi(options.abi);
}
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"jest-environment-node": "^26.0.0",
"mixpanel": "^0.13.0",
"ncp": "^2.0.0",
"near-api-js": "^0.31.0",
"near-web3-provider": "^1.0.0",
"near-api-js": "^0.34.0",
"near-seed-phrase": "^0.1.0",
"open": "^7.0.1",
"rimraf": "^3.0.0",
Expand All @@ -51,6 +52,7 @@
"update-notifier": "^5.0.0",
"uuid": "^8.0.0",
"v8flags": "^3.1.3",
"web3": "^1.2.11",
"yargs": "^16.0.3"
},
"optionalDependencies": {
Expand Down
Loading

0 comments on commit d55a359

Please sign in to comment.