letmejustputthishere / iluvatar

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The ckETH Minter Canister

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.

Testing with Sepolia

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

Ethereum Mainnet

0x7574eB42cA208A4f6960ECCAfDF186D627dCC175

ckETH minter

ckETH ledger

ic/rs/ethereum/cketh/mainnet

Ethereum Testnet Sepolia

0xb44B5e756A894775FC32EDdf3314Bb1B1944dC34

ckSepoliaETH minter

ckSepoliaETH ledger

ic/rs/ethereum/cketh/testnet

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.

Feedback

We would greatly appreciate any feedback you may have. Please feel free to reach out by leaving a comment in this forum post.

Deposit: ETH to ckETH

 ┌────┐               ┌───────────────┐             ┌──────┐
 │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 array

With Cargo

cargo run --bin cketh-principal-to-hex $(dfx identity get-principal)

With Bazel

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
  • It’s critical that the encoded IC principal is correct otherwise the funds will be lost.

  • The helper smart contracts for Ethereum and for Sepolia have different addresses (refer to the above table).

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)"'" }'

Withdrawal: ckETH to ETH

 ┌────┐                       ┌──────┐        ┌──────┐                                           ┌────────────────┐
 │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 1 ETH = 1_000_000_000_000_000_000 WEI (Ethereum uses 18 decimals). You can use this converter to convert ETH to wei.

The first time a user wants to withdraw some ckETH, two steps are needed:

  1. 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 })"
  2. 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.

Example of a withdrawal

Example 1. Approve the minter to spend 1 ETH (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 })"
Example 2. Withdraw 0.15 ETH (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\"})"

Cost of a withdrawal

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

  1. Burn withdraw_amount on the ckETH ledger for the IC principal (the caller of withdraw_eth).

  2. Estimate the maximum current cost of a transaction on Ethereum, say max_tx_fee_estimate. This max_tx_fee_estimate is expected to be large enough to be valid for the few next blocks.

  3. Issue an Ethereum transaction (via threshold ECDSA) with the value withdraw_amount - max_tx_fee_estimate. This requires of course that withdraw_amount >= max_tx_fee_estimate and that’s why we currently have a conservative minimum value for withdrawals of 30_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).

  4. 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 construction max_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

  1. Total effective transaction fees: the sum of all actual_tx_fee for all withdrawals.

  2. Total unspent transaction fees: the sum of all max_tx_fee_estimate - actual_tx_fee for all withdrawals. This represents an overestimate of the actual transaction fees that were charged to the user but in retrospect not needed to mine the sent transaction.

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:

  1. Initial withdrawal amount: withdraw_amount:= 39_998_000_000_000_000 wei

  2. Gas limit: 21_000

  3. Max fee per gas: 0x14369c3348 == 86_815_552_328 wei

  4. Maximum estimated transaction fees: max_tx_fee_estimate:= 21_000 * 86_815_552_328 == 1_823_126_598_888_000 wei

  5. Amount received at destination: 39_998_000_000_000_000 - max_tx_fee_estimate == 38_174_873_401_112_000

  6. Effective gas price: 0x9f8c76bc8 == 42_828_524_488 wei

  7. Actual transaction fee: actual_tx_fee:= 21_000 * 42_828_524_488 == 899_399_014_248_000 wei

  8. Unspent transaction fee: max_tx_fee_estimate - actual_tx_fee == 923_727_584_640_000 wei

  9. Amount charged at minter’s address withdrawal_amount - (max_tx_fee_estimate - actual_tx_fee) == 39_074_272_415_360_000 wei

Cost of all ckETH Transactions

Operation Canister Cost Example

Deposit ETH → ckETH

Minter

Variable, depends on the Ethereum transaction fees.

Transaction 0xa1e10…​ needed 33_288 gas and cost roughly 0.0021 ETH

Withdrawal ckETH → ETH

(Minimum amount 0.03 ckETH)

Minter

Variable, depends on the Ethereum transaction fees.

Transaction 0x5ab62…​ detailed above needed 21_000 gas and cost roughly 0.0018 ETH

Transfer ckETH → ckETH

  1. icrc1_transfer

  2. icrc2_transfer_from

Ledger

Fix 0.000002 ckETH.

Set by transfer_fee decided in proposal 126309.

Transfer with ledger index 12

Approval

  1. icrc2_approve

Ledger

Fix 0.000002 ckETH.

Set by transfer_fee decided in proposal 126309.

Approval with ledger index 3

History of Proposals

  1. 126171: install minter canister

  2. 126173: install index canister

  3. 126309: install ledger canister

  4. 126314: upgrade minter canister

About


Languages

Language:Rust 95.8%Language:HTML 1.8%Language:Starlark 1.2%Language:JavaScript 0.7%Language:Solidity 0.2%Language:Python 0.2%