Skip to main content

Ledger Local Setup

If you are working in a local development environment, i.e with a local replica instead of the public Internet Computer, you can't access the ICP ledger. In order to test your application that integrates with the ICP ledger locally, you need to deploy a local ledger canister. However, this local ledger canister won't have the history and balances of the live ICP ledger. Follow the steps below to deploy your copy of the ledger canister to a local replica.

  1. Get a pre-built Ledger canister module and Candid interface files.

    export IC_VERSION=dd3a710b03bd3ae10368a91b255571d012d1ec2f
    curl -o ledger.wasm.gz "https://download.dfinity.systems/ic/$IC_VERSION/canisters/ledger-canister_notify-method.wasm.gz"
    gunzip ledger.wasm.gz
    curl -o ledger.private.did "https://raw.githubusercontent.com/dfinity/ic/$IC_VERSION/rs/rosetta-api/ledger.did"
    # on Linux:
    dfx canister --network ic call ryjl3-tyaaa-aaaaa-aaaba-cai __get_candid_interface_tmp_hack '()' --query | sed -i 's/\\n/\n/g'
    # on MacOS:
    dfx canister --network ic call ryjl3-tyaaa-aaaaa-aaaba-cai __get_candid_interface_tmp_hack '()' --query | sed 's/\\n/\n/g'
    note

    The IC_VERSION variable is a commit hash from the http://github.com/dfinity/ic repository. To get the latest version, take the commit hash from the last blessed version from the releases dashboard.

  2. Make sure you use a recent version of dfx. If you don’t have dfx installed, follow instructions on the Installing the SDK section to install it.

  3. If you don’t have a dfx project yet, follow these instructions to create a new dfx project: dfx-new

  4. Copy the file you obtained at the first step (ledger.wasm, ledger.private.did, ledger.public.did) into the root of your project.

  5. Add the following canister definition to the dfx.json file in your project:

    {
    "canisters": {
    "ledger": {
    "type": "custom",
    "wasm": "ledger.wasm",
    "candid": "ledger.private.did"
    }
    }
    }
  6. Configure your replica to run a System subnet. Modify dfx.json to include:

    {
    "defaults":{
    "replica": {
    "subnet_type":"system"
    }
    }
    }
  7. Start a local replica.

    dfx start --background
  8. Create a new identity that will work as a minting account:

    dfx identity new minter
    dfx identity use minter
    export MINT_ACC=$(dfx ledger account-id)

    Transfers from the minting account will create Mint transactions. Transfers to the minting account will create Burn transactions.

  9. Switch back to your default identity and record its ledger account identifier.

    dfx identity use default
    export LEDGER_ACC=$(dfx ledger account-id)
  10. Deploy the ledger canister to your network.

    dfx deploy ledger --argument '(record {minting_account = "'${MINT_ACC}'"; initial_values = vec { record { "'${LEDGER_ACC}'"; record { e8s=100_000_000_000 } }; }; send_whitelist = vec {}})'

    If you want to setup the ledger in a way that matches the production deployment, you should deploy it with archiving enabled. In this setup, the ledger canister dynamically creates new canisters to store old blocks. We recommend using this setup if you are planning to exercise the interface for fetching blocks.

    Obtain the principal of the identity you use for development. This principal will be the controller of archive canisters.

    dfx identity use default
    export ARCHIVE_CONTROLLER=$(dfx identity get-principal)

    Deploy the ledger canister with archiving options:

    dfx deploy ledger --argument '(record {minting_account = "'${MINT_ACC}'"; initial_values = vec { record { "'${LEDGER_ACC}'"; record { e8s=100_000_000_000 } }; }; send_whitelist = vec {}; archive_options = opt record { trigger_threshold = 2000; num_blocks_to_archive = 1000; controller_id = principal "'${ARCHIVE_CONTROLLER}'" }})'

    You may want to set trigger_threshold and num_blocks_to_archive options to low values (e.g., 10 and 5) to trigger archivation after only a few blocks.

  11. Update the canister definition in the dfx.json file to use the public Candid interface:

     {
    "canisters": {
    "ledger": {
    "type": "custom",
    "wasm": "ledger.wasm",
    - "candid": "ledger.private.did"
    + "candid": "ledger.public.did"
    }
    }
    }
  12. Update the canister definition in the dfx.json file to specify a remote id for the ledger. This will prevent dfx from deploying your own ledger in case you decide to deploy your project to the Internet Computer:

    "ledger": {
    "type": "custom",
    "candid": "ledger.public.did",
    "wasm": "ledger.wasm",
    "remote": {
    "candid": "ledger.public.did",
    "id": {
    "ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
    }
    }
    }
  13. Check that the Ledger canister is healthy. Execute the following command:

    dfx canister call ledger account_balance '(record { account = '$(python3 -c 'print("vec{" + ";".join([str(b) for b in bytes.fromhex("'$LEDGER_ACC'")]) + "}")')' })'

    The output should look like the following:

    (record { e8s = 100_000_000_000 : nat64 })

Your local ICP ledger canister is up and running now. You can now deploy other canisters that need to communicate with the ledger canister.