# Ckusdc custodial wallet

**URL:** <https://forum.dfinity.org/t/ckusdc-custodial-wallet/37225>\
**Category:** Developers\
**Created:** [November 12, 2024, 10:14am UTC](https://forum.dfinity.org/t/ckusdc-custodial-wallet/37225 "2024-11-12T10:14:25Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![AliSci](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alisci/32/33799_2.png) [@AliSci](https://forum.dfinity.org/u/AliSci)\
**Post date:** [November 12, 2024, 10:14am UTC](https://forum.dfinity.org/t/ckusdc-custodial-wallet/37225/1 "2024-11-12T10:14:25Z")

</div>

### **Title:**

How to Enable `ckUSDC` Deposit and Withdrawal in a Custodial Wallet Using Internet Computer (IC)?

* * *

### **Question Body:**

I’m building a custodial wallet on the Internet Computer (IC) where users can deposit and withdraw `ckUSDC`. The system allows users to deposit `USDC` into their account, make off-chain transactions with other users, and withdraw their earnings to an external wallet like Coinbase or MetaMask.

To achieve this, I’m following the [icp-pay repo](https://github.com/MugglePay/icp-pay) as a reference. Here’s my current approach for implementing the **deposit** and **withdraw** functionality:

### **Key points**

1. **Error Simplification** : Use consistent and clear error handling with informative messages for developers and end-users.
2. **Abstraction** : Create helper functions to encapsulate repetitive tasks like decoding responses and making canister calls.
3. **Comments** : Add comments to explain the purpose of each function and its parameters.
4. **Type Validation** : Ensure types are correctly mapped during canister calls to avoid decoding errors.
5. **Clear Interfaces** : Provide a function `get_ckusdc_deposit_address` that gives developers a clear and simple API to interact with.
6. 

```rs
use crate::wallet::error::{Error, USDCResult};
use crate::websocket::{NoteContent, Notification};
use crate::workspaces::{
    estimate_transaction_fees, nat_to_u256, nat_to_u64, validate_caller_not_anonymous,
    EthereumNetwork, EVM_RPC,
};
use crate::{assert_token_symbol_length, parse_eth_address, wallet, UserToken};
use crate::{CPayment, ExchangeType, PaymentStatus, Wallet};
use alloy_consensus::{SignableTransaction, TxEip1559, TxEnvelope};
use alloy_primitives::{address, hex, Signature, TxKind, U256};
use candid::CandidType;
use candid::{func, Nat, Principal};
use ethers_core::types::Res;
use evm_rpc_canister_types::{
    BlockTag, EthMainnetService, EthSepoliaService, EvmRpcCanister, GetTransactionCountArgs,
    GetTransactionCountResult, MultiGetTransactionCountResult, RequestResult, RpcService,
};
use ic_cdk::caller;
use ic_cdk_macros::update;
use icrc_ledger_types::icrc1::account::Account;
use icrc_ledger_types::icrc1::transfer::BlockIndex;
use icrc_ledger_types::icrc2::transfer_from::TransferFromArgs;
use num::Num;
use serde_json::{self};
use std::time;

async fn get_user_balance() -> USDCResult<Nat> {
    let args = Account {
        owner: caller(),
        subaccount: None,
    };
    ic_cdk::call::<(Account,), (USDCResult<Nat>,)>(
        Principal::from_text("xevnm-gaaaa-aaaar-qafnq-cai").unwrap(),
        "icrc1_balance_of",
        (args,),
    )
    .await
    .map_err(|(_, message)| Error::IcCdkError { message })?
    .0
    .map_err(|e| Error::IcCdkError {
        message: format!("{:?}", e),
    })
}

async fn transfer_from(amount: Nat, from: Principal, to: Principal) -> USDCResult<BlockIndex> {
    let args = TransferFromArgs {
        spender_subaccount: None,
        from: Account {
            owner: from,
            subaccount: None,
        },
        to: Account {
            owner: to,
            subaccount: None,
        },
        amount,
        fee: None,
        memo: None,
        created_at_time: None,
    };
    ic_cdk::call::<(TransferFromArgs,), (USDCResult<BlockIndex>,)>(
        Principal::from_text("xevnm-gaaaa-aaaar-qafnq-cai").unwrap(),
        "icrc2_transfer_from",
        (args,),
    )
    .await
    .map_err(|(_, message)| Error::IcCdkError { message })?
    .0
    .map_err(|e| Error::IcCdkError {
        message: format!("{:?}", e),
    })
}

#[update]
async fn deposit_ckusdt() -> Result<Nat, Error> {
    let balance = get_user_balance().await?;
    if balance > Nat::from(0 as u64) {
        transfer_from(balance.clone(), caller(), ic_cdk::id()).await?;
        let mut wallet = Wallet::get(caller());
        wallet
            .deposit(
                nat_to_u64(balance.clone()) as f64,
                "ExternalWallet".to_string(),
                ExchangeType::Deposit,
            )
            .map_err(|e| Error::IcCdkError {
                message: format!("{:?}", e),
            })?;
        let message = format!("You received {} CKUSD", balance);
        Notification::new(
            format!("{:?}", time::SystemTime::now()),
            caller(),
            NoteContent::ReceivedDeposit(message),
        )
        .save();
    }
    Ok(balance)
}

#[update]
async fn withdraw_ckusdt(amount: u64, address: String) -> Result<Nat, Error> {
    let balance = get_user_balance().await?;
    if Nat::from(amount.clone()) >= balance {
        transfer_from(
            Nat::from(amount.clone() as u64),
            ic_cdk::id(),
            Principal::from_text(address.clone()).unwrap(),
        )
        .await?;
        let mut wallet = Wallet::get(caller());
        wallet
            .withdraw(amount as f64, address, ExchangeType::Withdraw)
            .map_err(|e| Error::IcCdkError { message: e })?;
    }
    Ok(balance)
}

```

Issue:  
When I call get\_user\_balance, I get the following error:

css  
Copy code  
Err(IcCdkError { message: “failed to decode canister response as (core::result::Result\<candid::types::number::Nat, alloc::string::String\>,): Fail to decode argument 0 from nat to variant { Ok : nat; Err : text }” })

* * *

also i got error : “Canister xevnm-gaaaa-aaaar-qafnq-cai not found”

---

<div class="post-metadata">

**Author:** ![marc0olo](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/marc0olo/32/17290_2.png) [@marc0olo](https://forum.dfinity.org/u/marc0olo)\
**Post date:** [November 12, 2024, 11:32am UTC](https://forum.dfinity.org/t/ckusdc-custodial-wallet/37225/2 "2024-11-12T11:32:44Z")

</div>

can you quickly confirm that you aim to achieve following things:

- let users deposit ckUSDC to the dapp specific user principal
- fetch & display ckUSDC balances for each user
- let users do something in your dapp to lock ckUSDC in the container backend and pay/release ckUSDC to other users (who earn the ckUSDC) within the dapp
  - this is out of scope of the question here, right?

- let users withdraw the ckUSDC from their dapp principal to any principal that they provide

is that correct?

---

<div class="post-metadata">

**Author:** ![AliSci](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alisci/32/33799_2.png) [@AliSci](https://forum.dfinity.org/u/AliSci)\
**Post date:** [November 12, 2024, 11:55am UTC](https://forum.dfinity.org/t/ckusdc-custodial-wallet/37225/3 "2024-11-12T11:55:43Z")

</div>

Yes that is correct, in general it is a custodial wallet .
