Deploying a canister interacting with the ledger canister on mainnet

I’ve been developing a dapp interacting with the ledger canister like the official invoice-canister.

I would like to know how to deploy an app that is dependent on the ledger on the mainnet without deploying my local ledger canister.
I tried to deploy it with the following settings, but my local ledger canister also deployed with.

More specifically, I get the following result, with dfx deploy --network ic myapp.

Deploying: ledger myapp
Creating canisters...
Creating canister "ledger"...
"ledger" canister created on network "ic" with canister id: "xxxxx-xxxxx-xxxxx-xxxxx-cai"
Creating canister "myapp"...

As the ledger canister was deployed by the dfinity team already, what I would like to do is to deploy my “myapp” only.

The settings are as follows:

canister_ids.json

{
  "ledger": {
    "ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
  }
}

dfx.json

  "canisters": {
    "myapp": {
      "type": "motoko",
      "main": "myapp/main.mo",
      "dependencies": [
        "ledger"
      ]
    },
    "ledger": {
      "type": "custom",
      "wasm": "ledger/ledger.wasm",
      "candid": "ledger/ledger.public.did"
    }
  },
1 Like

Hey man. I think you could try this
You can remove the

"ledger": {
      "type": "custom",
      "wasm": "ledger/ledger.wasm",
      "candid": "ledger/ledger.public.did"
    }

from your dfx.json config file completely and just run the dfx deploy --network ic

Now your initial command SHOULD have initially deployed only the myapp canister that you specified in the command itself and I don’t know why it didn’t only deploy that one (could it be because ledger is specified as custom type ? Maybe someone else with more knowledge on this could explain this part ?

2 Likes

I tied it, however “myapp” has a dependency with ledger.

    "myapp": {
      "type": "motoko",
      "main": "myapp/main.mo",
      "dependencies": [
        "ledger"
      ]
    },

Unfortunately, I was not able to deploy it when I remove the one you mentioned throwing an error of Error: Cannot find canister 'ledger'.

2 Likes

@AnonymousCoder Forgot to mention to you.

Right, I think you can safely remove that dependency because you will use the main ledger when you deploy it to mainnet, not your own version of that (that you deployed locally) :slight_smile: Remove the dependency and try again :slight_smile: You can have the myapp block like this:

"myapp": {
      "type": "motoko",
      "main": "myapp/main.mo"
    },
1 Like

@AnonymousCoder I’ve tried it, and then I got another error.

import error [M0011], canister alias "ledger" not defined

for this line :sweat_smile:

import Ledger     "canister:ledger";

That’s because that import statement also tries to fetch that canister locally and expects the imported canister to have been deployed along with your myapp canister. If you want to use a “live” (ie: mainnet canister), you can’t actually “import” it into your canister this way, because you actually need to reference canisters living on the mainnet a different way. For example, you would have to define a variable that would be of type actor and actually provide the principal address of that already deployed canister on the mainnet (in this case the official ledger canister). Now, I believe to get to know all of the possible methods you can call on the ledger canister (as you don’t have it’s actual code), you can use the ledger.private.did file and see the service block of it, which tells you available methods in the ledger canister, ie this:

service: (LedgerCanisterInitPayload) -> {
  send_dfx : (SendArgs) -> (BlockHeight);
  notify_dfx: (NotifyCanisterArgs) -> ();
  account_balance_dfx : (AccountBalanceArgs) -> (ICPTs) query;
}

In that file you will also find all of the types that are used by this service.
So, the way you would for example make a variable in your smart contract that needs to use the ledger canister from mainnet, would be something like:

let LEDGER = actor "ryjl3-tyaaa-aaaaa-aaaba-cai" : actor { 
    send_dfx : shared SendArgs -> async Nat64;
    account_balance_dfx : shared query AccountBalanceArgs -> async ICPTs; 
  };

That way, in your further code, where you need to call either send_dfx or account_balance_dfx, you can just do LEDGER.send_dfx(provide your arguments that it expects here)

I think I am correct with this (also still in process of learning) :smiley: If not, I would be glad if someone corrects me and provides explanation to where I was wrong and how to fix it

I see. I thought there is another way to import different canisters without an actor reference, but there isn’t.

But thank you! I will change the script as you suggested :slightly_smiling_face: