The ckETH minter canister converts ETH to ckETH and back, where ckETH is a token issued on the Internet Computer that is backed 1:1 by ETH. The minter works with a Ledger Canister, handling the ckETH token transfers and with HTTP outcalls, to query multiple JSON-RPC providers to interact with the Ethereum blockchain.
Tip
|
A demonstration of the interactions described in this file is available here. |
Another instance of the minter and of the ledger are deployed that target specifically the Sepolia Ethereum testnet. This allows you to test the minter without having to use real ETH. You can use one of the numerous Sepolia Faucet to obtain some Sepolia ETH and start testing the ckSepoliaETH minter!
Ethereum Chain | Helper smart contract | Minter dashboard | Ledger | dfx commands in |
---|---|---|---|---|
|
||||
|
Tip
|
To interact with the minter or ledger as exemplified below with dfx , make sure to locate yourself in the correct directory as listed in the table above.
|
We would greatly appreciate any feedback you may have. Please feel free to reach out by leaving a comment in this forum post.
┌────┐ ┌───────────────┐ ┌──────┐ │User│ │Helper Contract│ │Minter│ └─┬──┘ └───────┬───────┘ └──┬───┘ │ │ │ │deposit(amount, principal)│ │ │─────────────────────────>│ │ │ │ │ │ │ get_events │ │ │<───────────────────────│ │ │ │ │ │Events(amout, principal)│ │ │───────────────────────>│ │ │ │ │ mint(amout, principal) │ │<──────────────────────────────────────────────────│ ┌─┴──┐ ┌───────┴───────┐ ┌──┴───┐ │User│ │Helper Contract│ │Minter│ └────┘ └───────────────┘ └──────┘
Converting ETH into ckETH requires a call to a smart contract on Ethereum and passing your IC principal as argument, in the form of a bytes32
array.
Tip
|
Retrieve your IC principal
To know your principal, you can run dfx identity get-principal |
Warning
|
Address of the helper smart contract
The address of the helper smart contract may change in the future when the minter is upgraded. Please verify the address of the helper contract before any important transfer by querying the minter as follows dfx canister --network ic call minter smart_contract_address |
The simplest way to convert your IC principal to the smart contract argument is to use the minter dashboard.
Another way is to use the principal-to-hex
utility.
Tip
|
Convert your IC principal to a
bytes32 arrayWith cargo run --bin cketh-principal-to-hex $(dfx identity get-principal) With bazel run //rs/ethereum/cketh/minter:principal_to_hex -- $(dfx identity get-principal) |
Call the minter helper contract deposit
function with your principal encoded and the amount as parameters.
Warning
|
|
Tip
|
Check your ckETH balance
Once your transaction has been mined on Ethereum, the minter should pick it up and mint the corresponding amount of ckETH after roughly 20 minutes, so don’t panic if the following command currently returns 0. dfx canister --network ic call ledger icrc1_balance_of 'record {owner = principal "'"$(dfx identity get-principal)"'" }' |
┌────┐ ┌──────┐ ┌──────┐ ┌────────────────┐ │User│ │Ledger│ │Minter│ │Ethereum Network│ └─┬──┘ └──┬───┘ └──┬───┘ └───────┬────────┘ │ │ │ │ │icrc2_approve(minter, amount)│ │ │ │────────────────────────────>│ │ │ │ │ │ │ │withdraw_eth(destination_eth_address, amount)│ │ │────────────────────────────────────────────>│ │ │ │ │ │ │ │ │eth_sendRawTransaction(destination_eth_address, amount)│ │ │ │──────────────────────────────────────────────────────>│ ┌─┴──┐ ┌──┴───┐ ┌──┴───┐ ┌───────┴────────┐ │User│ │Ledger│ │Minter│ │Ethereum Network│ └────┘ └──────┘ └──────┘ └────────────────┘
Tip
|
Conversion ETH ←→ Wei
The amounts described below use the smallest denomination of ETH called wei, where
|
The first time a user wants to withdraw some ckETH, two steps are needed:
-
Approve the minter’s principal on the ledger for the desired amount.
dfx canister --network ic call ledger icrc2_approve "(record { spender = record { owner = principal \"$(dfx canister id minter --network ic)\" }; amount = LARGE_AMOUNT_WEI })"
-
Call the minter to make a withdrawal for the desired amount.
dfx canister --network ic call minter withdraw_eth '(SMALL_AMOUNT_WEI, "YOUR_ETH_ADDRESS")'
Additional withdrawals could be made as long as the allowance from step 1 was not exhausted or did not time out.
After calling withdraw_eth
, the minter will usually send a transaction to the Ethereum network within 6 minutes. Additional delays may occasionally occur due to reasons such as congestion on the Ethereum network or some Ethereum JSON-RPC providers being offline.
1_000_000_000_000_000_000
wei)dfx canister --network ic call ledger icrc2_approve "(record { spender = record { owner = principal \"$(dfx canister id minter --network ic)\" }; amount = 1_000_000_000_000_000_000 })"
150_000_000_000_000_000
wei) to 0xAB586458E47f3e9D350e476fB7E294a57825A3f4
dfx canister --network ic call minter withdraw_eth "(record {amount = 150_000_000_000_000_000; recipient = \"0xAB586458E47f3e9D350e476fB7E294a57825A3f4\"})"
Note that the transaction will be made at the cost of the beneficiary meaning that the resulting received amount will be less than the specified withdrawal amount. The exact fee deducted depends on the dynamic Ethereum transaction fees used at the time the transaction was created.
In more detail, assume that a user calls withdraw_eth
(after having approved the minter) to withdraw withdraw_amount
(e.g. 1ckETH) to some address.
Then the minter is going to do the following
-
Burn
withdraw_amount
on the ckETH ledger for the IC principal (the caller ofwithdraw_eth
). -
Estimate the maximum current cost of a transaction on Ethereum, say
max_tx_fee_estimate
. Thismax_tx_fee_estimate
is expected to be large enough to be valid for the few next blocks. -
Issue an Ethereum transaction (via threshold ECDSA) with the value
withdraw_amount - max_tx_fee_estimate
. This requires of course thatwithdraw_amount >= max_tx_fee_estimate
and that’s why we currently have a conservative minimum value for withdrawals of30_000_000_000_000_000
wei. This ensures that the minter can always send the transaction to Ethereum if one or several resubmissions are needed if the Ethereum network is congested and fees are increasing rapidly (each resubmission requires an increase of at least 10% of the transaction fee). -
When the transaction is mined, the destination of the transaction will receive
withdraw_amount - max_tx_fee_estimate
. Since on Ethereum transactions are paid by the sender, the minter’s account will be charged with(withdraw_amount - max_tx_fee_estimate) + actual_tx_fee == withdrawal_amount - (max_tx_fee_estimate - actual_tx_fee),
where
actual_tx_fee
represents the actual transaction fee (can be retrieved from the transaction receipt) and by constructionmax_tx_fee_estimate - actual_tx_fee > 0
.
Tip
|
Effective transaction fees vs unspent transaction fees
The minter dashboard displays in the metadata table the following fees
|
To make things more concrete, we break down the cost of a concrete withdrawal (ledger burn index 2
) that resulted in the Ethereum transaction 0x5ab62cfd3715c549fb4cd56fc511bc403f45c43b1e91ffdb83654201b0b5db39:
-
Initial withdrawal amount:
withdraw_amount:= 39_998_000_000_000_000
wei -
Gas limit:
21_000
-
Max fee per gas:
0x14369c3348 == 86_815_552_328
wei -
Maximum estimated transaction fees:
max_tx_fee_estimate:= 21_000 * 86_815_552_328 == 1_823_126_598_888_000
wei -
Amount received at destination:
39_998_000_000_000_000 - max_tx_fee_estimate == 38_174_873_401_112_000
-
Effective gas price:
0x9f8c76bc8 == 42_828_524_488
wei -
Actual transaction fee:
actual_tx_fee:= 21_000 * 42_828_524_488 == 899_399_014_248_000
wei -
Unspent transaction fee:
max_tx_fee_estimate - actual_tx_fee == 923_727_584_640_000
wei -
Amount charged at minter’s address
withdrawal_amount - (max_tx_fee_estimate - actual_tx_fee) == 39_074_272_415_360_000
wei
Operation | Canister | Cost | Example |
---|---|---|---|
Deposit ETH → ckETH |
Minter |
Variable, depends on the Ethereum transaction fees. |
Transaction 0xa1e10… needed |
Withdrawal ckETH → ETH (Minimum amount |
Minter |
Variable, depends on the Ethereum transaction fees. |
Transaction 0x5ab62… detailed above needed |
Transfer ckETH → ckETH
|
Ledger |
Fix Set by |
Transfer with ledger index 12 |
Approval
|
Ledger |
Fix Set by |
Approval with ledger index 3 |