How to convert USDC into CKUSDC?

In our project, we already have a CKUSDC deposit functionality on our wall recognize the CKUSDC

The problem is for the user experience. It’s very annoying to convert before they deposit so want to do the conversion with our platform itself instead of using a plug wallet or Oisy.

How to achieve that is there is a swipe in the Icp ledger canister or somewhere

1 Like

the flow and all required operations are described here in detail:

right now, it requires the user to perform two contract calls on Ethereum:

  • approve(helper_contract, amount)
    • on the USDC ERC20 contract
  • depositErc20(token_id, amount, principal, subaccount)
    • on the Helper Contract

in order to provide a “seamless” experience, you’d need your users to connect their Ethereum Wallet and propose those transactions for them to approve.

the conversion takes ~20mins.

it will still not be the perfect user experience, but this way you can allow your users to convert within your application.

1 Like

So, i am new to this but in found this in the repo of erc20

USDC Handler (usdcHandler.js)

class USDCHandler {
  constructor(web3, userAddress) {
    this.web3 = web3;
    this.userAddress = userAddress;
    this.usdcContract = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
    this.helperContract = '0x18901044688D3756C35Ed2b36D93e6a5B8e00E68';
    this.usdcABI = [
      {"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"},
      {"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"}
    ];
    this.helperABI = [
      {"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"principal","type":"bytes32"},{"name":"subaccount","type":"bytes32"}],"name":"depositErc20","outputs":[],"type":"function"}
    ];
  }

  async approveUSDC(amount) {
    const contract = new this.web3.eth.Contract(this.usdcABI, this.usdcContract);
    return await contract.methods.approve(this.helperContract, amount).send({from: this.userAddress});
  }

  async depositUSDC(amount, icPrincipal, subaccount = '0x0000000000000000000000000000000000000000000000000000000000000000') {
    const contract = new this.web3.eth.Contract(this.helperABI, this.helperContract);
    return await contract.methods.depositErc20(
      this.usdcContract, 
      amount, 
      icPrincipal, 
      subaccount
    ).send({from: this.userAddress});
  }

  async getUSDCBalance() {
    const contract = new this.web3.eth.Contract(this.usdcABI, this.usdcContract);
    return await contract.methods.balanceOf(this.userAddress).call();
  }

  encodePrincipal(principalText) {
    const principal = Principal.fromText(principalText);
    const bytes = principal.toUint8Array();
    const padded = new Uint8Array(32);
    padded.set(bytes, 32 - bytes.length);
    return '0x' + Array.from(padded).map(b => b.toString(16).padStart(2, '0')).join('');
  }
}

USDC to ckUSDC Converter Function

export const usdcTockUSDC = async (web3, userAddress, amount, icPrincipal) => {
  try {
    const handler = new USDCHandler(web3, userAddress);
    const encodedPrincipal = handler.encodePrincipal(icPrincipal);
    const usdcAmount = (amount * 1_000_000).toString(); // USDC has 6 decimals
    
    // Step 1: Approve USDC spending
    const approveResult = await handler.approveUSDC(usdcAmount);
    console.log('Approval tx:', approveResult.transactionHash);
    
    // Step 2: Deposit USDC to get ckUSDC
    const depositResult = await handler.depositUSDC(usdcAmount, encodedPrincipal);
    console.log('Deposit tx:', depositResult.transactionHash);
    
    return {
      success: true,
      approveHash: approveResult.transactionHash,
      depositHash: depositResult.transactionHash
    };
  } catch (error) {
    console.error('Conversion failed:', error);
    return { success: false, error: error.message };
  }
};

This handles the two-step Ethereum process: approve the helper contract to spend USDC, then call depositErc20 to trigger the conversion. The ckUSDC will appear in your IC account after ~20 minutes.

Is this fine now? i am testing it now but still tring to test with hardHat

@AliSci yes this seems correct from a quick look.

for testing this flow you might want to consider changing the contract ids on Ethereum those deployed on Sepolia Testnet. you would need to change both addresses, the one of the USDC contract and the one of the Helper Contract.

see ic/rs/ethereum/cketh/docs/ckerc20.adoc at master · dfinity/ic · GitHub

and here the overview for all canisters related to ckSepoliaUSDC on ICP:

1 Like

But the docs does not show how to generate USDC address. It shows how to connect with chrome extenuation wallet to deposit ?
My goal is

  1. generate USDC address
  2. send USDC to it from Binance or coin base without the need for extension or connect wallet.

ckUSDC and other ckETH assets don’t work the same way as ckBTC - users don’t get dedicated addresses where they can send USDC/USDT/ETH/etc

Instead, you need to interact with smart contract on Ethereum

Here are detailed steps:
https://internetcomputer.org/docs/defi/chain-key-tokens/ckerc20/making-transactions#converting-erc-20-tokens-to-ckerc20-tokens

USDC address is just EVM address. Some examples:

https://internetcomputer.org/docs/building-apps/chain-fusion/ethereum/using-eth/generating-addresses

You take ECDSA public key from API do Ethereum address algo generation and get an address:

1 Like