PocketIC: How do I check the balance of an account id for the ICP ledger?

I need to check the balance of an account id in one of my tests. Is there a way to do this using Pocket IC?

 #[test]
    fn should_transfer_icp_native() {
        let receiver = generate_principal();

        let pic = PocketIc::new();
        let caller = generate_principal();

        let account_id = pic.create_canister_with_settings(Some(caller), None);

        pic.add_cycles(account_id, 2_000_000_000_000);

        let wasm_module = include_bytes!("../../../target/wasm32-unknown-unknown/release/account.wasm").to_vec();

        let receiver_account_id = ledger::to_subaccount_id_from_principal(receiver, to_subaccount(0));

        let signer = generate_principal();

        pic.install_canister(account_id, wasm_module, Vec::new(), Some(caller));

        let wasm_result = pic.update_call(account_id, caller, "transaction_1", encode_one(0).unwrap());

        if wasm_result.is_err() {
            panic!("Update call failed: {:?}", wasm_result);
        }

       // I should check if balance of receiver's account id is higher than zero.
        
    }
1 Like

Yes, you can make an update call to the corresponding ledger endpoint for fetching balance passing in the corresponding candid argument:

use candid::{CandidType, Principal};
use ic_ledger_types::{
    AccountBalanceArgs, AccountIdentifier, Memo, Subaccount, Tokens, TransferArgs, TransferError,
    DEFAULT_SUBACCOUNT,
};
use pocket_ic::{update_candid_as, PocketIc};

pub fn get_icp_balance(env: &PocketIc, user_id: Principal) -> u64 {
    let ledger_canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap();
    let account = AccountIdentifier::new(&user_id, &DEFAULT_SUBACCOUNT);
    let account_balance_args = AccountBalanceArgs { account };
    let res: (Tokens,) = update_candid_as(
        env,
        ledger_canister_id,
        user_id,
        "account_balance",
        (account_balance_args,),
    )
    .unwrap();
    res.0.e8s()
}

I’m getting this error when interacting with that principal id using PocketIC.

thread 'tests::intent_tests::should_transfer_icp' panicked at /Users/vincxntes/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pocket-ic-5.0.0/src/nonblocking.rs:1088:51:
BadIngressMessage("Canister ryjl3-tyaaa-aaaaa-aaaba-cai does not belong to any subnet.")

Do I need to deploy a mock ICP ledger before using this principal id?

1 Like

Indeed, you need to deploy a canister with that canister id first. I’d recommend to deploy the actual ICP ledger and set up some accounts with ICP balances during deployment:

    let controller = Principal::from_text("r7inp-6aaaa-aaaaa-aaabq-cai").unwrap(); // NNS root canister, does not need to be actually deployed 

    let specified_nns_ledger_canister_id =
        Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap();
    let nns_ledger_canister_id = env
        .create_canister_with_id(Some(controller), None, specified_nns_ledger_canister_id)
        .unwrap();
    assert_eq!(nns_ledger_canister_id, specified_nns_ledger_canister_id);

    let icp_ledger_canister_wasm: Vec<u8> = [...]; // get the ICP ledger wasm
    let minter = [...]; // some principal not used anywhere else
    let minting_account = AccountIdentifier::new(&minter, &DEFAULT_SUBACCOUNT);
    let icp_ledger_init_args = NnsLedgerCanisterPayload::Init(NnsLedgerCanisterInitPayload {
        minting_account: minting_account.to_string(),
        initial_values: HashMap::from([...]), // fill in some initial account balances
        send_whitelist: HashSet::new(),
        transfer_fee: Some(Tokens::from_e8s(10_000)),
        token_symbol: Some("ICP".to_string()),
        token_name: Some("Internet Computer".to_string()),
    });
    env.install_canister(
        nns_ledger_canister_id,
        icp_ledger_canister_wasm,
        Encode!(&icp_ledger_init_args).unwrap(),
        Some(controller),
    );
1 Like

I’m having some issues getting the cycle ledger setup with PocketIC (PicJS)

Using dfx nns install; dfx nns import I’m able to get the icp ledger and then use it in my tests directly via

const nnsLedgerCanisterId = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai");
const ledgerActor: await pic.createActor<LedgerService>(
    nnsLedgerIdlFactory,
    nnsLedgerCanisterId
),

However, when trying to do the same thing with the cycle ledger (via dfx deps, type pull) I receive this :point_down:

PocketIC server encountered an error BadIngressMessage("Canister um5iw-rqaaa-aaaaq-qaaba-cai does not belong to any subnet.")

Trying to set up the canister first, like this

const cyclesLedgerSetup = await pic.setupCanister<CyclesLedgerService>({
    idlFactory: cyclesLedgerIDLFactory,
    wasm: ".dfx/local/canisters/cycles_ledger/cycles_ledger.wasm.gz",
    sender: adminPrincipal,
    arg: IDL.encode(cyclesLedgerInit({ IDL }), [cycleLedgerArgs]),
    targetCanisterId: Principal.fromText("um5iw-rqaaa-aaaaq-qaaba-cai"),
    targetSubnetId: Principal.fromText(NNS_SUBNET_ID),
  });

I then receive this error:

Canister um5iw-rqaaa-aaaaq-qaaba-cai is already installed.
    This is an internal error on the IC. Please report it to IC devs on the forum: https://forum.dfinity.org

I then tried a variety of different options, including create canister, update settings, reinstall, etc. and still end up with this first error:

PocketIC server encountered an error BadIngressMessage("Canister um5iw-rqaaa-aaaaq-qaaba-cai does not belong to any subnet.")

Any suggestions on how to set this up?