The following example uses an existing smart contract deployed to the EVM on betanet, and makes simple read and write operations. Feel free to follow along with this repository. It uses two existing NEAR betanet accounts, so you don't have to create one. An upcoming section covers building and deploying a contract from Solidity and deploying from your own betanet account, but we'll keep it simple for now. For this
evm-simple example, we'll interact with a version of the NEAR Pet Shop, which is a dApp based on the Truffle starter project. In this application, there are two functions:
getAdopters()— returns an array of 16 Ethereum addresses corresponding to 16 pets that are either adopted or need to be adopted. For example, when a user adopts the first pet, we can expect the value returned from this function to have their Ethereum address as the first of 16 items in the array returned. If a pet has not been adopted, the zero address is used. This function is read-only.
adopt(uint petId)— this assigns the adoption of a pet to the Ethereum address that sent the transaction, mutating (modifying) state.
The entire file is 42 lines and newcomer-friendly. In the below screenshot, the function
callEvm is collapsed for readability. The last line of the file calls
start, which will send two NEAR betanet account names to
✋ Quick note: all code screenshots on this page link to GitHub projects at the latest commit when this was written.
Now let's look at the expanded
callEvm function and discuss the highlighted sections:
Breaking down each step:
- Instantiate the
NearProvider— passing it the:
networkIdassociated with the NEAR network you wish to interact with. (See a list of networks and reasonable defaults in NEAR Web3 Provider's
- For signed transactions, the
masterAccountId, which is the name of the NEAR account on that network that'll be signing the transaction. If the provider only needs to read state, a
masterAccountIdshould be omitted, adding
isReadOnly: trueinstead. (We cover an example of this in the Pet Shop documentation.) More on this in the docs.
keyStorefor the location of the private key. As per the comment above that line of code, NEAR's library near-api-js (used by the NEAR Web3 Provider) has different types of key stores. In this example there are keys located in the project's
private-keysdirectory. For more information on that, please see the corresponding README. (Note: in the NEAR Pet Shop documentation, the front-end will use a different type of key store for the browser.)
- Instantiate the smart contract and set the provider.
- Derive the Ethereum address associated with the NEAR account. This uses a small helper function
nearAccountToEvmAddresswhich simply takes 20 bytes of the hashed NEAR account name, shown here.
- Send a transaction using Web3 as one normally would. Here we're calling the function
adoptfrom the derived Ethereum address, indicating to adopt the first pet, hence the
0argument for that index in the array.
- Call the contract's read-only function
getAdopters. The provider instantiated in Step 1 can sign transactions as well as call read-only functions.
Finally, run this project following the instructions in the README. You'll see this simple NodeJS app setting the adopter of the first pet to be
mike.betanet, show the list of adopted pets, then have
josh.betanet adopt the first pet and show the results again.
NEAR CLI commands
Let's show how to interact with the same contract from before using only your Terminal or Command Prompt.
npm install -g near-cli
In the previous
evm-simple example, there was an artifact for the Adoption contract. This was created when the contract was compiled, and lives in the directory
build/contracts. It contains application binary interface (ABI) information necessary to call functions and handle their returned values, contract data, and other details including where this contract was deployed. Let's peek at the bottom of the artifact file, Adoption.json:
Two key things to note here:
- The chain ID is
1313161554. This has been registered for NEAR at https://chainid.network (Minor changes are currently needed to differentiate testnet and mainnet chain IDs.)
addresskey contains the Ethereum address of the deployed Adoption contract:
Let's use NEAR CLI to call the read-only function
NEAR_ENV=betanet near evm-view evm 0xAdf11a39283CEB00DEB90a5cE9220F89c6C27E67 getAdopters '' --abi /path/to/build/contracts/Adoption.json --accountId mike.betanet
If this command were translated to English it would say,
"Hey NEAR CLI, use the command
evm-view on the EVM contract deployed to the account
evm, calling the Ethereum smart contract located at address
0xAdf…E67 giving empty parameters to the function
getAdopters. And oh yeah, here's where you can find the artifact containing the ABI.”
After running the command you'll see something like this:
The item in the array contains the address
0x5d60a489b2f457cb351b0faabf5f9746d6bd4a8c. This is the Ethereum address derived from the NEAR account
josh.betanet, as demonstrated by a quick NodeJS REPL trick:
(A Quick reminder that Ethereum addresses are not case sensitive, so don't be confused by the two addresses ending in
…4a8c as shown here. Capitalization is only used as a nifty checksum, helping users make less mistakes.)
The second item in the array is all zeroes, meaning this "pet” hasn't been adopted yet, so let's use NEAR CLI to send a signed transaction from the NEAR account
mike.betanet and manually adopt this pet without a web interface. We can do this because the private key to
mike.betanet is a special, function-call access key, meaning NEAR tokens (Ⓝ) cannot be transferred, or the account deleted, etc.
NEAR_ENV=betanet near evm-call evm 0xAdf11a39283CEB00DEB90a5cE9220F89c6C27E67 adopt '["1"]' --abi /path/to/build/contracts/Adoption.json --accountId mike.betanet
After running that command, we'll rerun the previous
evm-view command for
getAdopters to see the second item in the array has updated to the Ethereum address of
We've demonstrated how to access and mutate state using NEAR CLI. The three new commands added related to the EVM are: