Create ICP Account from within a smart contract for each user

Hi guys. Your help would be very much appreciated. I am looking for a way my Rust code to connect to the Ledger canister of the IC and create an account for each of my users. Let’s say i authenticate my users in a different way and I don’t want to use the internet identity.

Could you kindly help me with some code on how to create the identifier (unique ICP address/account to receive funds) for each user? Then how to sign to transfer ICP?

https://k7gat-daaaa-aaaae-qaahq-cai.ic0.app/listing/nns-ledger-10244

I see in the above URL the methods and the canister being ryjl3-tyaaa-aaaaa-aaaba-cai

Is that correct? I see how to use them in Rust… But i can’t see how i can implement the above example as I want a separate account for each user… I will save that identifier in an array corresponding to the user and i want when the user has funds to be able to transfer…

Could you kindly help? :sweat_smile: :pray:

2 Likes

Hi there, let me ping some of the folks working with the Ledger Canister

@sparebytes This application that you are writing is a canister or an application off-chain? If it’s a canister then consider using ic-ledger-types. Otherwise you can use ic-agent to connect to the Ledger and make transfers.

Also I’m not sure to understand what you mean by one account for each user. If you want to create an account owned by your principal then it’s easy. An account is created from a Principal and a Subaccount. You can use your Principal as the Principal of the account and then use the user id as subaccount. The subaccount itself is 32 bytes so any user id should fit.

3 Likes

Hi @mariop. It is for a canister smart contract I want to write. Yes it is for account owned by the canister principal. The canister in Rust will have a method let’s say registerUser()

and inside that method I want to create the user, give him an auto increment ID and also an ICP address to be able to give to others to receive funds.

Then another method checkBalance() which will check the account balance of that user, so i will look for the address i assigned to the user on registration and use the Ledger canister balance method. And last another method to transfer ICP from his balance to another ICP address.

Could you help me how to do that first thing with a line of code in Rust that I should put inside my user registration method to create that address where he can receive funds? this will be done for EACH user so they all have of course different addresses.

I’d suggest using a sub-account for each user, then you can make your smart contract so that your canister’s sub-accounts can only send funds to the owned user from those sub accounts. It will help keep you from mixing funds and you won’t have a problem with a node provider looking at private keys in your canister. This code is motoko but shows how an account id is created using a sub account. You have 32 bytes so you can have up to 4.2 billion or so subaccount for you canister. While you could just auto increment, you may want to use Hash.Hash(msg.caller) to get some randomization. principal.mo/AccountIdentifier.mo at main · aviate-labs/principal.mo · GitHub

2 Likes

First have a way to create new users, e.g. with:

thread_local! {
    static NEXT_ID: std::cell::RefCell<u64> = std::cell::RefCell::new(0);
}

fn next_user_id() -> u64 {
    NEXT_ID.with(|cell| {
        let next_id = *cell.borrow();
        cell.replace(next_id+1);
        next_id
    })
}

then you can create the account of a user like this

fn id_to_account(id: u64) -> AccountIdentifier {
    let mut subaccount = [0u8;32];
    subaccount.split_at_mut(8).0.copy_from_slice(id.to_le_bytes().as_slice());
    let subaccount = Subaccount(subaccount);
    let account_id = AccountIdentifier::new(&caller(), &subaccount);
    account_id
}

I didn’t test this code so take it as an example to adapt :slight_smile: .

3 Likes

You are a star. I appreciate it a lot.

FWIW this is how the NNS keeps funds separate when staking a neuron.