Basic Ethereum
This tutorial will walk you through how to deploy a sample canister smart contract that can send and receive Ether (ETH) on the Internet Computer.
Architecture
This example internally leverages the threshold ECDSA and HTTPs outcalls features of the Internet Computer.
For a deeper understanding of the ICP < > ETH integration, see the Ethereum integration overview.
Prerequisites
- Install the IC SDK.
Step 1: Building and deploying sample code
Clone the smart contract
To clone and build the smart contract in Rust:
git clone https://github.com/dfinity/examples
cd examples/rust/basic_ethereum
git submodule update --init --recursive
If you are using MacOS, you'll need to install Homebrew and run brew install llvm
to be able to compile the example.
Acquire cycles to deploy
Deploying to the Internet Computer requires cycles (the equivalent of "gas" on other blockchains).
Deploy the smart contract to the Internet Computer
dfx deploy --ic basic_ethereum --argument '(opt record {ethereum_network = opt variant {Sepolia}; ecdsa_key_name = opt variant {TestKey1}})'
What this does
dfx deploy
tells the command line interface todeploy
the smart contract--ic
tells the command line to deploy the smart contract to the mainnet ICP blockchain--argument (opt record {ethereum_network = opt variant {Sepolia}; ecdsa_key_name = opt variant {TestKey1}})
initializes the smart contract with the provided arguments:ethereum_network = opt variant {Sepolia}
: the canister uses the Ethereum Testnet Sepolia network.ecdsa_key_name = opt variant {TestKey1}
: the canister uses a test key for signing via threshold ECDSA that is available on the ICP mainnet. See signing messages for more details.
If successful, you should see an output that looks like this:
Deploying: basic_ethereum
Building canisters...
...
Deployed canisters.
URLs:
Candid:
basic_ethereum: https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=<YOUR-CANISTER-ID>
Your canister is live and ready to use! You can interact with it using either the command line or using the Candid UI, which is the link you see in the output above.
In the output above, to see the Candid Web UI for your ethereum canister, you would use the
URL https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=<YOUR-CANISTER-ID>
. You should see the methods specified in the Candid file basic_ethereum.did
.
Step 2: Generating an Ethereum address
An Ethereum address can be derived from an ECDSA public key. To derive a user's specific address, identified on the IC
by a principal, the canister uses its own threshold ECDSA public key to derive a new public key deterministically for
each requested principal. To retrieve your Ethereum address, you can call the ethereum_address
method on the
previously deployed canister:
dfx canister --ic call basic_ethereum ethereum_address
This will return an Ethereum address such as ("0x378a452B20d1f06008C06c581b1656BdC5313c0C")
that is tied to your
principal. Your address will be different. You can view such addresses on any Ethereum block explorer such as Etherscan.
If you want to send some ETH to someone else, you can also use the above method to enquire about their Ethereum address given their IC principal:
dfx canister --ic call basic_ethereum ethereum_address '(opt principal "hkroy-sm7vs-yyjs7-ekppe-qqnwx-hm4zf-n7ybs-titsi-k6e3k-ucuiu-uqe")'
This will return a different Ethereum address as the one above, such
as ("0x8d68f7B3cdb40A2E77071077658b01A9EA4B040F")
.
Step 3: Receiving ETH
Now that you have your Ethereum address, let us send some (Sepolia) ETH to it:
- Get some Sepolia ETH if you don't have any. You can for example use this faucet.
- Send some Sepolia ETH to the address you obtained in the previous step. You can use any Ethereum wallet (e.g., Metamask) to do so.
Once the transaction has at least one confirmation, which can take a few seconds, you'll be able to see it in your Ethereum address's balance, which should be visible in an Ethereum block explorer, e.g., https://sepolia.etherscan.io/address/0x378a452b20d1f06008c06c581b1656bdc5313c0c.
Step 4: Sending ETH
You can send ETH using the send_eth
endpoint on your canister, specifying an Ethereum destination address and an
amount in the smallest unit (Wei). For example, to send 1 Wei to 0xdd2851Cdd40aE6536831558DD46db62fAc7A844d
, run the following command:
dfx canister --ic call basic_ethereum send_eth '("0xdd2851Cdd40aE6536831558DD46db62fAc7A844d", 1)'
The send_eth
endpoint sends ETH by executing the following steps:
- Retrieving the transaction count for the sender's address at
Latest
block height. This is necessary because Ethereum transactions for a given sender's address are ordered by anonce
, which is a monotonically incrementally increasing non-negative counter. - Estimating the current transaction fees. For simplicity, the current gas fees are hard-coded with a generous limit. A
real world application would dynamically fetch the latest transaction fees, for example using
the
eth_feeHistory
method in the EVM-RPC canister. - Building an Ethereum transaction (EIP-1559) to send the specified amount to the given receiver's address.
- Signing the Ethereum transaction using the sign_with_ecdsa API.
- Sending the signed transaction to the Ethereum network using
the
eth_sendRawTransaction
method in the EVM-RPC canister.
The send_eth
endpoint returns the hash of the transaction sent to the Ethereum network, which can for example be used
to track the transaction on an Ethereum blockchain explorer.
Conclusion
In this tutorial, you were able to:
- Deploy a canister smart contract on the ICP blockchain that can receive and send ETH.
- Acquire cycles to deploy the canister to the ICP mainnet.
- Connect the canister to the Ethereum Sepolia testnet.
- Send the canister some Sepolia ETH.
- Use the canister to send ETH to another Ethereum address.
Additional examples regarding the ICP < > ETH integration can be found here.
Security considerations and best practices
If you base your application on this example, we recommend you familiarize yourself with and adhere to the security best practices for developing on the Internet Computer. This example may not implement all the best practices.
For example, the following aspects are particularly relevant for this app:
- Certify query responses if they are relevant for security, since the app offers a method to read balances, for example.
- Use a decentralized governance system like SNS to make a canister have a decentralized controller, since decentralized control may be essential for canisters holding ETH on behalf of users.