Access EVM accounts

This tutorial illustrates how to use crates from the Frontier project to build an Ethereum-compatible blockchain that can access Ethereum-based accounts and execute Solidity-based smart contracts. The two main goals of the Frontier project are to enable you to do the following:

  • Run Ethereum decentralized apps unmodified using local Substrate nodes.
  • Import state from the Ethereum main network.

This tutorial uses a predefined node template to provide a working environment. The template was generated using the instructions in the Frontier release guide.

If you want to generate a standalone template for yourself, you can use the node-template-release.sh template generation script. If you build your own node using from the frontier repository or using the template generation script, be aware that Frontier uses its own versions of Substrate crates and you might need to update the dependencies in your Cargo files to match the dependencies in your project.

Before you begin

You should have completed the following Substrate tutorials before attempting this tutorial:

From the tutorials, you should be familiar with how to perform the following tasks:

  • Launch a Substrate blockchain node.
  • Add, remove, and configure pallets in a runtime.
  • Submit transactions by connecting to a node using Polkadot-JS or another front-end.

Before starting this tutorial, you should also be familiar with the following:

  • Ethereum core concepts and terminology
  • Ethereum Virtual Machine (EVM) basics
  • Decentralized applications and smart contracts
  • Pallet design principles

Genesis configuration

The development chain specification in the frontier-node-template defines a genesis block that is been pre-configured with an EVM account for the alice account. When you start this node in development mode, the EVM account for alice is funded with a default amount of Ether. You'll be using this account to view EVM account details and to call Ethereum smart contracts. After you start the node, you'll be able to use the Polkadot-JS application to see the details of the EVM account for alice.

Compile a Frontier node

The Frontier node template provides a working development environment so that you can start building on Substrate right away.

To compile the Frontier node template:

  1. Open a terminal shell on your computer.
  2. Clone the node template repository by running the following command:

    git clone https://github.com/substrate-developer-hub/frontier-node-template.git
  3. Change to the root of the node template directory by running the following command:

    cd frontier-node-template
  4. Compile the node template by running the following command:

    cargo build --release

Connect to the node

After your node compiles, you must start the node to begin exploring the preconfigured EVM accounts.

To start the local Substrate node:

  1. Open a terminal shell on your local computer, if needed.
  2. Change to the root directory where you compiled the frontier-node-template.
  3. Start the node in development mode by running the following command:

    ./target/release/frontier-template-node --dev

    The --dev command-line option specifies that the node runs using the predefined development chain specification that includes the predefined EVM account for alice and other accounts for testing.

  4. Verify your node is up and running successfully by reviewing the output displayed in the terminal.

    The terminal should display output similar to this:

    2022-07-08 10:06:42 Frontier Node
    2022-07-08 10:06:42 โœŒ๏ธ  version 0.0.0-1b6bff4-x86_64-macos
    2022-07-08 10:06:42 โค๏ธ  by Substrate DevHub <https://github.com/substrate-developer-hub>, 2021-2022
    2022-07-08 10:06:42 ๐Ÿ“‹ Chain specification: Development
    2022-07-08 10:06:42 ๐Ÿท  Node name: flippant-boat-0444
    2022-07-08 10:06:42 ๐Ÿ‘ค Role: AUTHORITY
    ...
  5. Connect to the local node using the Polkadot-JS application.
  6. Click Settings, then click Developer.

    Developer settings
  7. Define the following account information to create an EVM Account type and enable the account to send transactions and to inspect blocks.

    To send transactions, you must define Address and LookupSource settings.
    To inspect blocks, you must define Transaction and Signature settings.

    {
      "Address": "MultiAddress",
      "LookupSource": "MultiAddress",
      "Account": {
         "nonce": "U256",
         "balance": "U256"
      },
      "Transaction": {
         "nonce": "U256",
         "action": "String",
         "gas_price": "u64",
         "gas_limit": "u64",
         "value": "U256",
         "input": "Vec<u8>",
         "signature": "Signature"
      },
      "Signature": {
         "v": "u64",
         "r": "H256",
         "s": "H256"
      }
    }
  8. Click Save.

Query balances using RPC

After you have configured settings for the EVM account, you can use the Polkadot-JS application to view information about the EVM account for alice.

  1. Verify that your node is still running and the Polkadot-JS application is connected to the node.
  2. Click Developer, then select RPC calls.
  3. On the Submission tab, select eth as the endpoint to call.
  4. Select getBalance(address, number) from the list of functions to call.
  5. Specify the EVM account identifier for the alice account for the address.

    The predefined account address is 0xd43593c715fdd31c61141abd04a99fd6822c8558. The address for the account was calculated from the public key for the alice account using Substrate EVM utilities.

  6. Click Submit RPC call.

    The call should return output similar to the following:

    2: eth.getBalance: U256
    340,282,366,920,938,463,463,374,607,431,768,210,955

Deploy a smart contract

Now that you'e seen how to query the balance for an Ethereum address, you might want to explore how you can deploy and call Ethereum smart contracts and test the related functionality. This tutorial uses a Truffle sample contract that defines an ERC-20 token. You can also create an ERC-20 token contract using Polkadot JS SDK and Typescript.

  1. Create the ERC-20 contract.

    For convenience, you can use the compiled bytecode from the token contract in MyToken.json to deploy the contract on the Substrate blockchain.

  2. Verify that your node is still running and the Polkadot-JS application is connected to the node.
  3. Click Developer, then select Extrinsics.
  4. Select the ALICE development account as the account used to submit the transaction.
  5. Select evm.
  6. Select a create function.
  7. Configure the parameters for the function.

    For thisSpecify this
    source0xd43593c715fdd31c61141abd04a99fd6822c8558
    initraw bytecode hex value from MyToken.json
    value0
    gasLimit4294967295
    maxFeePerGas100000000

    You can leave optional parameters empty. The value for the nonce will increment the known nonce for the source account, starting from 0x0. Depending on the function you selected, you might need to remove unused parameters.

  8. Click Submit Transaction.
  9. Click Sign and Submit to authorize the transaction.

View the smart contract

After you submit the transaction, the contract is deployed on the network and you can view information about it using the Polkadot-JS application.

  1. Verify that your node is still running and the Polkadot-JS application is connected to the node.
  2. Click Network, then select Explorer.
  3. Click the evm.Created event to verify the address of the newly-created contract is 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f.

    Successful contract created event

    You can also view details about the transaction using the Console in the Developer Tools for your browser.

    Because EVM contract addresses are determined by the account identifier and nonce of the contract creator, the address where the contract is deployed is calculated using the well-known account identifier 0xd43593c715fdd31c61141abd04a99fd6822c8558 and nonce 0x0 for the alice account.

  4. Click Developer, then select Chain State.
  5. Select evm as the state to query and accountCodes.
  6. Specify the account identifier 0xd43593c715fdd31c61141abd04a99fd6822c8558 for the alice account and notice that the account code is empty (0x)
  7. Specify the contract address 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f for the contract you deployed using the alice development account and notice that contract account code is the bytecode from the Solidity contract.

View account storage

The ERC-20 contract you deployed is based on the OpenZeppelin ERC-20 implementation. This contract includes a constructor that mints a maximum number of tokens and stores them in the account associated with the contract creator.

To query the account storage associated with the smart contract:

  1. In Chain State with evm as the state to query, select accountStorages.
  2. Specify the ERC-20 contract address 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f as the first parameter.
  3. Specify the storage slot to read as the second parameter 0x045c0350b9cf0df39c4b40400c965118df2dca5ce0fbcf0de4aafc099aea4a14.

    The storage slot for the address was calculated using Substrate EVM utilities based on slot 0 and the account identifier 0xd43593c715fdd31c61141abd04a99fd6822c8558.

    The value that is returned should be 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.

    If you check the balance for the alice account after deploying the contract, you see that a fee was withdrawn from the account and the getBalance(address, number) call returns a value similar to the following:

    340,282,366,920,938,463,463,374,603,530,233,757,803

Transfer tokens

So far, you have worked exclusively with the alice development account. The next step is to use the deployed contract to transfer tokens to another account.

  1. Verify that your node is still running and the Polkadot-JS application is connected to the node.
  2. Click Developer, then select Extrinsics.
  3. Select the ALICE development account as the account used to submit the transaction.
  4. Select evm.
  5. Select call to invoke the transfer(address, uint256) function on the ERC-20 contract.
  6. Configure the parameters for the function.

    For thisSpecify this
    source0xd43593c715fdd31c61141abd04a99fd6822c8558
    target0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f
    input0xa9059cbb0000000000000000000000008eaf04151687736326c9fea17e25fc528761369300000000000000000000000000000000000000000000000000000000000000dd
    value0
    gasLimit4294967295
    maxFeePerGas100000000

    The source represents the account holding the tokens. In this case, the source is the EVM account for alice, the contract creator. The target is the contract address for the transfer of tokens from alice to bob.

    The input parameter is an EVM ABI-encoded function call that specifies the function call to perform a transfer (0xa9059cbb) and the arguments the function requires. For this function, the arguments are the bob EVM account identifier (0x8eaf04151687736326c9fea17e25fc5287613693) and the number of tokens to be transferred (221 or 0xdd in hex).

    The input value in this tutorial was calculated using the Remix web IDE.

  7. Click Submit Transaction.
  8. Click Sign and Submit to authorize the transaction.

Verify the token transfer

After you submit the transaction, the contract is deployed on the network and you can view information about it using the Polkadot-JS application.

  1. Verify that your node is still running and the Polkadot-JS application is connected to the node.
  2. Click Network, then select Explorer.
  3. Click the evm.Executed event to verify the address of the executed contract is 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f.

    Successful execution event
  4. Click Developer, then select Chain State.
  5. Select evm as the state to query and accountStorages.
  6. Check the storage for the contract address 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f and storage slot 0x045c0350b9cf0df39c4b40400c965118df2dca5ce0fbcf0de4aafc099aea4a14.

    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff22

    If you check the balance for the alice account after deploying the contract, you see that a fee was withdrawn from the account and the getBalance(address, number) call returns a value similar to the following:

    340,282,366,920,938,463,463,374,603,530,233,366,411

Where to go next