Skip to main content

Canisters

The following functions can be used to interact with well-known Internet Computer canisters from your serverless functions.

๐Ÿ“ฆ Library

The Canisters API is provided by the @junobuild/functions library.

To add it to your project:

npm i @junobuild/functions

You have to follow the pace of the Juno release to ensure compatibility. Refer to the maintenance guide for instructions.


Overviewโ€‹

The @junobuild/functions/canisters module provides a unified interface for interacting with core Internet Computer canisters from within your Juno serverless functions. It offers:

  • โš™๏ธ Type-safe interfaces for well-known IC canisters
  • ๐Ÿงฉ Modular structure with independent sub-packages
  • ๐Ÿ”„ Up-to-date Candid declarations and types
  • ๐Ÿงช Battle-tested through production applications

Available Canistersโ€‹

Import Structureโ€‹

Each canister is available as a sub-entry. Import the desired module directly from its entry point:

Canister(s)ImportStatus
ICP Ledger@junobuild/functions/canisters/ledger/icpโœ… Canister + Declarations
ICRC Ledger@junobuild/functions/canisters/ledger/icrcโœ… Canister + Declarations
CMC@junobuild/functions/canisters/cmcโœ… Canister + Declarations
ckBTC@junobuild/functions/canisters/ckbtc๐Ÿ“ฆ Declarations Only
ckETH@junobuild/functions/canisters/cketh๐Ÿ“ฆ Declarations Only
IC Management@junobuild/functions/canisters/ic-management๐Ÿ“ฆ Declarations Only
NNS@junobuild/functions/canisters/nns๐Ÿ“ฆ Declarations Only
SNS@junobuild/functions/canisters/sns๐Ÿ“ฆ Declarations Only

ICP Ledgerโ€‹

You can interact with the ICP Ledger through the class IcpLedgerCanister.

transferโ€‹

Sends ICP using the Ledger canister transfer method.

transfer(params: {
args: IcpLedgerDid.TransferArgs
}): Promise<IcpLedgerDid.TransferResult>

Parameters:โ€‹

  • args: The ledger transfer arguments
  • to: Destination account identifier (AccountIdentifier)
  • amount: Transfer amount object with e8s (bigint)
  • fee: Fee object with e8s (bigint)
  • memo: Optional transfer memo (bigint)
  • from_subaccount: Optional subaccount (Uint8Array | number[])
  • created_at_time: Optional timestamp object with timestamp_nanos (bigint)

Returns:โ€‹

  • Promise<IcpLedgerDid.TransferResult>: The result of the ICP transfer
  • On success: { Ok: bigint } (block height)
  • On error: { Err: TransferError }

Example:โ€‹

import { IcpLedgerCanister } from "@junobuild/functions/canisters/ledger/icp";

export const onExecute = async () => {
const ledger = new IcpLedgerCanister();

const result = await ledger.transfer({
args: {
to: "destination-account-identifier",
amount: { e8s: 100_000_000n }, // 1 ICP
fee: { e8s: 10_000n },
memo: 0n
}
});

if ("Ok" in result) {
console.log("Transfer successful, block height:", result.Ok);
} else {
console.error("Transfer failed:", result.Err);
}
};

ICRC Ledgerโ€‹

Interact with ICRC compatible ledgers through the class IcrcLedgerCanister.

icrc1BalanceOfโ€‹

Returns the balance of an ICRC account.

icrc1BalanceOf(params: {
account: IcrcLedgerDid.Account
}): Promise<IcrcLedgerDid.Tokens>

Parameters:โ€‹

  • account: The account to query
  • owner: Principal of the account owner
  • subaccount: Optional subaccount (Uint8Array | number[])

Returns:โ€‹

  • Promise<IcrcLedgerDid.Tokens>: The token balance (bigint)

Example:โ€‹

import { IcrcLedgerCanister } from "@junobuild/functions/canisters/ledger/icrc";
import { Principal } from "@dfinity/principal";

export const onExecute = async () => {
const ledger = new IcrcLedgerCanister({
canisterId: "your-icrc-ledger-canister-id"
});

const balance = await ledger.icrc1BalanceOf({
account: {
owner: Principal.fromText("user-principal"),
subaccount: []
}
});

console.log("Balance:", balance);
};

icrc1Transferโ€‹

Transfers tokens using the ICRC-1 icrc1_transfer method.

icrc1Transfer(params: {
args: IcrcLedgerDid.TransferArg
}): Promise<IcrcLedgerDid.TransferResult>

Parameters:โ€‹

  • args: Transfer arguments
  • to: Destination account (Account object)
  • amount: Transfer amount (bigint)
  • fee: Optional fee (bigint)
  • memo: Optional memo (Uint8Array | number[])
  • from_subaccount: Optional subaccount (Uint8Array | number[])
  • created_at_time: Optional timestamp (bigint)

Returns:โ€‹

  • Promise<IcrcLedgerDid.TransferResult>: The result of the transfer
  • On success: { Ok: bigint } (block index)
  • On error: { Err: TransferError }

Example:โ€‹

import { IcrcLedgerCanister } from "@junobuild/functions/canisters/ledger/icrc";
import { Principal } from "@dfinity/principal";

export const onExecute = async () => {
const ledger = new IcrcLedgerCanister({
canisterId: "your-icrc-ledger-canister-id"
});

const result = await ledger.icrc1Transfer({
args: {
to: {
owner: Principal.fromText("recipient-principal"),
subaccount: []
},
amount: 1_000_000n,
fee: 10_000n
}
});

if ("Ok" in result) {
console.log("Transfer successful, block index:", result.Ok);
} else {
console.error("Transfer failed:", result.Err);
}
};

icrc2TransferFromโ€‹

Transfers tokens using the ICRC-2 icrc2_transfer_from method.

Allows transferring tokens from another user's account when an approval has previously been granted via icrc2_approve.

icrc2TransferFrom(params: {
args: IcrcLedgerDid.TransferFromArgs
}): Promise<IcrcLedgerDid.TransferFromResult>

Parameters:โ€‹

  • args: Transfer-from arguments
  • from: Source account (Account object)
  • to: Destination account (Account object)
  • amount: Transfer amount (bigint)
  • fee: Optional fee (bigint)
  • memo: Optional memo (Uint8Array | number[])
  • spender_subaccount: Optional spender subaccount (Uint8Array | number[])
  • created_at_time: Optional timestamp (bigint)

Returns:โ€‹

  • Promise<IcrcLedgerDid.TransferFromResult>: The result of the transfer-from operation
  • On success: { Ok: bigint } (block index)
  • On error: { Err: TransferFromError }

Example:โ€‹

import { IcrcLedgerCanister } from "@junobuild/functions/canisters/ledger/icrc";
import { Principal } from "@dfinity/principal";

export const onExecute = async () => {
const ledger = new IcrcLedgerCanister({
canisterId: "your-icrc-ledger-canister-id"
});

const result = await ledger.icrc2TransferFrom({
args: {
from: {
owner: Principal.fromText("source-principal"),
subaccount: []
},
to: {
owner: Principal.fromText("destination-principal"),
subaccount: []
},
amount: 500_000n
}
});

if ("Ok" in result) {
console.log("Transfer from successful, block index:", result.Ok);
} else {
console.error("Transfer from failed:", result.Err);
}
};

Cycle Minting Canister (CMC)โ€‹

Interact with the Cycle Minting Canister to convert ICP into cycles using the class CMCCanister.

notifyTopUpโ€‹

Notifies the Cycle Minting Canister that a top-up transfer has been completed.

After sending ICP to the CMC top-up account for a canister, the transfer is recorded on the ledger. The CMC does not automatically convert that ICP into cycles โ€” you must call this function to let the CMC know which transaction to process.

The CMC will then convert the ICP from the given ledger block into cycles and add them to the specified canister.

notifyTopUp(params: {
args: CmcDid.NotifyTopUpArg
}): Promise<CmcDid.NotifyTopUpResult>

Parameters:โ€‹

  • args: Arguments containing:
  • block_index: The ledger block index of the ICP transfer (bigint)
  • canister_id: The canister ID that should receive the cycles (Principal)

Returns:โ€‹

  • Promise<CmcDid.NotifyTopUpResult>: The result of the CMC conversion and deposit
  • On success: { Ok: bigint } (cycles deposited)
  • On error: { Err: NotifyError }

Example:โ€‹

import { CMCCanister } from "@junobuild/functions/canisters/cmc";
import { IcpLedgerCanister } from "@junobuild/functions/canisters/ledger/icp";
import { Principal } from "@dfinity/principal";

export const onExecute = async () => {
// Step 1: Send ICP to the CMC top-up account
const ledger = new IcpLedgerCanister();
const transferResult = await ledger.transfer({
args: {
to: "cmc-top-up-account-identifier",
amount: { e8s: 100_000_000n }, // 1 ICP
fee: { e8s: 10_000n },
memo: 0n
}
});

if ("Err" in transferResult) {
console.error("Transfer failed:", transferResult.Err);
return;
}

const blockIndex = transferResult.Ok;

// Step 2: Notify the CMC to convert the ICP to cycles
const cmc = new CMCCanister();
const notifyResult = await cmc.notifyTopUp({
args: {
block_index: blockIndex,
canister_id: Principal.fromText("your-canister-id")
}
});

if ("Ok" in notifyResult) {
console.log("Cycles deposited:", notifyResult.Ok);
} else {
console.error("Notify failed:", notifyResult.Err);
}
};

Declarations-Only Exportsโ€‹

The following canisters currently provide Candid type definitions and IDL declarations only. You can use these with the call function for custom interactions.

ckBTCโ€‹

import {
CkBTCBitcoinIdl,
CkBTCMinterIdl,
type CkBTCBitcoinDid,
type CkBTCMinterDid
} from "@junobuild/functions/canisters/ckbtc";

ckETHโ€‹

import {
CkETHMinterIdl,
CkETHOrchestratorIdl,
type CkETHMinterDid,
type CkETHOrchestratorDid
} from "@junobuild/functions/canisters/cketh";

IC Managementโ€‹

import {
IcManagementIdl,
type IcManagementDid
} from "@junobuild/functions/canisters/ic-management";

NNSโ€‹

import {
NnsGovernanceIdl,
NnsSnsWasmIdl,
type NnsGovernanceDid,
type NnsSnsWasmDid
} from "@junobuild/functions/canisters/nns";

SNSโ€‹

import {
NnsSnsWasmIdl,
SnsGovernanceIdl,
SnsRootIdl,
SnsSwapIdl,
type SnsGovernanceDid,
type SnsRootDid,
type SnsSwapDid
} from "@junobuild/functions/canisters/sns";

Usage with call()โ€‹

You can use these type definitions with the call function to interact with any canister:

import { call } from "@junobuild/functions/ic-cdk";
import {
IcManagementIdl,
type IcManagementDid
} from "@junobuild/functions/canisters/ic-management";

export const onExecute = async () => {
const status = await call<IcManagementDid.CanisterStatusResult>({
canisterId: "aaaaa-aa", // Management canister
method: "canister_status",
args: [
[IcManagementIdl.CanisterIdRecord, { canister_id: "your-canister-id" }]
],
result: IcManagementIdl.CanisterStatusResult
});

console.log("Canister status:", status);
};

Notesโ€‹

  • All canister classes use the call function internally to communicate with the Internet Computer.
  • The caller's identity is automatically determined by the serverless function execution context.
  • For admin operations, you can use the id function to get the Satellite's principal, which has admin privileges.
  • Error handling follows the Result pattern: check for Ok or Err properties in the response.