From bd009a1146be2594c0e9c9c87d65527efbceb007 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 17 Apr 2026 13:40:09 +0200 Subject: [PATCH 1/2] docs: update call canister without candid mumbo jumbo in TypeScript Signed-off-by: David Dal Busco --- .../functions/typescript/canister-calls.mdx | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/docs/examples/functions/typescript/canister-calls.mdx b/docs/examples/functions/typescript/canister-calls.mdx index 8d8310b4..07f5649f 100644 --- a/docs/examples/functions/typescript/canister-calls.mdx +++ b/docs/examples/functions/typescript/canister-calls.mdx @@ -86,7 +86,10 @@ import { defineHook, type OnSetDoc } from "@junobuild/functions"; -import { IcrcLedgerDid } from "@junobuild/functions/canisters/ledger/icrc"; +import { + type Account, + encodeIcrcAccount +} from "@junobuild/functions/canisters/ledger/icrc"; import { id } from "@junobuild/functions/ic-cdk"; import { decodeDocData } from "@junobuild/functions/sdk"; import { COLLECTION_REQUEST, ICP_LEDGER_ID } from "../constants/app.constants"; @@ -129,9 +132,8 @@ export const onSetDoc = defineHook({ const ledgerId = ICP_LEDGER_ID; - const fromAccount: IcrcLedgerDid.Account = { - owner: Principal.fromUint8Array(context.caller), - subaccount: [] + const fromAccount: Account = { + owner: Principal.fromUint8Array(context.caller) }; // ############### @@ -161,9 +163,8 @@ export const onSetDoc = defineHook({ // Transfer from wallet to satellite. // ############### - const toAccount: IcrcLedgerDid.Account = { - owner: id(), - subaccount: [] + const toAccount: Account = { + owner: id() }; await transferIcpFromWallet({ @@ -181,6 +182,14 @@ export const onSetDoc = defineHook({ ```ts // src/satellite/services.ts +import { Principal } from "@icp-sdk/core/principal"; +import { + IcrcLedgerCanister, + type Account, + type TransferFromArgs, + type Tokens +} from "@junobuild/functions/canisters/ledger/icrc"; + export const assertWalletBalance = async ({ ledgerId, fromAccount, @@ -188,23 +197,18 @@ export const assertWalletBalance = async ({ fee }: { ledgerId: Principal; - fromAccount: IcrcLedgerDid.Account; + fromAccount: Account; amount: bigint; fee: bigint | undefined; }) => { const { icrc1BalanceOf } = new IcrcLedgerCanister({ canisterId: ledgerId }); - const balance = await icrc1BalanceOf({ - account: fromAccount - }); + const balance = await icrc1BalanceOf({ account: fromAccount }); - const total = amount + (fee ?? IC_TRANSACTION_FEE_ICP); + const total = amount + (fee ?? 10_000n); if (balance < total) { - const encodedAccountText = encodeIcrcAccount({ - owner: fromAccount.owner, - subaccount: fromNullable(fromAccount.subaccount) - }); + const encodedAccountText = encodeIcrcAccount(fromAccount); throw new Error( `Balance ${balance} is smaller than ${total} for account ${encodedAccountText}.` @@ -215,24 +219,21 @@ export const assertWalletBalance = async ({ export const transferIcpFromWallet = async ({ ledgerId, fromAccount, + toAccount, amount, - fee, - toAccount + fee }: { ledgerId: Principal; - fromAccount: IcrcLedgerDid.Account; - toAccount: IcrcLedgerDid.Account; + fromAccount: Account; + toAccount: Account; amount: bigint; fee: bigint | undefined; -}): Promise => { - const args: IcrcLedgerDid.TransferFromArgs = { +}): Promise => { + const args: TransferFromArgs = { amount, from: fromAccount, to: toAccount, - created_at_time: toNullable(), - fee: toNullable(fee), - memo: toNullable(), - spender_subaccount: toNullable() + fee }; const { icrc2TransferFrom } = new IcrcLedgerCanister({ From 2be14652541cc06e2becbc18f230107ba8a2c1f2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:42:18 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=84=20Update=20LLMs.txt=20snapshot?= =?UTF-8?q?=20for=20PR=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .llms-snapshots/llms-full.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.llms-snapshots/llms-full.txt b/.llms-snapshots/llms-full.txt index c936e5ca..ca5556f5 100644 --- a/.llms-snapshots/llms-full.txt +++ b/.llms-snapshots/llms-full.txt @@ -6387,11 +6387,11 @@ typescript/calls/├── src/│ ├── satellite/ # TypeScript Here’s the actual TypeScript logic from `index.ts` and `services.ts`: ``` -// src/satellite/index.tsimport { Principal } from "@icp-sdk/core/principal";import { type AssertSetDoc, defineAssert, defineHook, type OnSetDoc} from "@junobuild/functions";import { IcrcLedgerDid } from "@junobuild/functions/canisters/ledger/icrc";import { id } from "@junobuild/functions/ic-cdk";import { decodeDocData } from "@junobuild/functions/sdk";import { COLLECTION_REQUEST, ICP_LEDGER_ID } from "../constants/app.constants";import { RequestData, RequestDataSchema } from "../types/request";import { assertWalletBalance, setRequestProcessed, transferIcpFromWallet} from "./services";export const assertSetDoc = defineAssert({ collections: [COLLECTION_REQUEST], assert: (context) => { // We validate that the data submitted for create or update matches the expected schema. const person = decodeDocData(context.data.data.proposed.data); RequestDataSchema.parse(person); }});export const onSetDoc = defineHook({ collections: [COLLECTION_REQUEST], run: async (context) => { // ############### // Init data // ############### const { data: { key, data: { after: { version } } } } = context; const data = decodeDocData(context.data.data.after.data); const { amount: requestAmount, fee } = data; const ledgerId = ICP_LEDGER_ID; const fromAccount: IcrcLedgerDid.Account = { owner: Principal.fromUint8Array(context.caller), subaccount: [] }; // ############### // Check current account balance. This way the process can stop early on // ############### await assertWalletBalance({ ledgerId, fromAccount, amount: requestAmount, fee }); // ############### // The request is about to be processed by transferring the amount via the ICP ledger. // We update the status beforehand. Since the function is atomic, a failed transfer reverts everything. // This avoids a case where the transfer succeeds but the status isn't updated — even if unlikely. // This is for demo only. In production, proper error handling and bookkeeping would be required. // ############### setRequestProcessed({ key, version, data }); // ############### // Transfer from wallet to satellite. // ############### const toAccount: IcrcLedgerDid.Account = { owner: id(), subaccount: [] }; await transferIcpFromWallet({ ledgerId, fromAccount, toAccount, amount: requestAmount, fee }); console.log(`${requestAmount} ICP transferred to Satellite 🥳`); }}); +// src/satellite/index.tsimport { Principal } from "@icp-sdk/core/principal";import { type AssertSetDoc, defineAssert, defineHook, type OnSetDoc} from "@junobuild/functions";import { type Account, encodeIcrcAccount} from "@junobuild/functions/canisters/ledger/icrc";import { id } from "@junobuild/functions/ic-cdk";import { decodeDocData } from "@junobuild/functions/sdk";import { COLLECTION_REQUEST, ICP_LEDGER_ID } from "../constants/app.constants";import { RequestData, RequestDataSchema } from "../types/request";import { assertWalletBalance, setRequestProcessed, transferIcpFromWallet} from "./services";export const assertSetDoc = defineAssert({ collections: [COLLECTION_REQUEST], assert: (context) => { // We validate that the data submitted for create or update matches the expected schema. const person = decodeDocData(context.data.data.proposed.data); RequestDataSchema.parse(person); }});export const onSetDoc = defineHook({ collections: [COLLECTION_REQUEST], run: async (context) => { // ############### // Init data // ############### const { data: { key, data: { after: { version } } } } = context; const data = decodeDocData(context.data.data.after.data); const { amount: requestAmount, fee } = data; const ledgerId = ICP_LEDGER_ID; const fromAccount: Account = { owner: Principal.fromUint8Array(context.caller) }; // ############### // Check current account balance. This way the process can stop early on // ############### await assertWalletBalance({ ledgerId, fromAccount, amount: requestAmount, fee }); // ############### // The request is about to be processed by transferring the amount via the ICP ledger. // We update the status beforehand. Since the function is atomic, a failed transfer reverts everything. // This avoids a case where the transfer succeeds but the status isn't updated — even if unlikely. // This is for demo only. In production, proper error handling and bookkeeping would be required. // ############### setRequestProcessed({ key, version, data }); // ############### // Transfer from wallet to satellite. // ############### const toAccount: Account = { owner: id() }; await transferIcpFromWallet({ ledgerId, fromAccount, toAccount, amount: requestAmount, fee }); console.log(`${requestAmount} ICP transferred to Satellite 🥳`); }}); ``` ``` -// src/satellite/services.tsexport const assertWalletBalance = async ({ ledgerId, fromAccount, amount, fee}: { ledgerId: Principal; fromAccount: IcrcLedgerDid.Account; amount: bigint; fee: bigint | undefined;}) => { const { icrc1BalanceOf } = new IcrcLedgerCanister({ canisterId: ledgerId }); const balance = await icrc1BalanceOf({ account: fromAccount }); const total = amount + (fee ?? IC_TRANSACTION_FEE_ICP); if (balance < total) { const encodedAccountText = encodeIcrcAccount({ owner: fromAccount.owner, subaccount: fromNullable(fromAccount.subaccount) }); throw new Error( `Balance ${balance} is smaller than ${total} for account ${encodedAccountText}.` ); }};export const transferIcpFromWallet = async ({ ledgerId, fromAccount, amount, fee, toAccount}: { ledgerId: Principal; fromAccount: IcrcLedgerDid.Account; toAccount: IcrcLedgerDid.Account; amount: bigint; fee: bigint | undefined;}): Promise => { const args: IcrcLedgerDid.TransferFromArgs = { amount, from: fromAccount, to: toAccount, created_at_time: toNullable(), fee: toNullable(fee), memo: toNullable(), spender_subaccount: toNullable() }; const { icrc2TransferFrom } = new IcrcLedgerCanister({ canisterId: ledgerId }); const result = await icrc2TransferFrom({ args }); if ("Err" in result) { throw new Error( `Failed to transfer ICP from wallet: ${JSON.stringify(result, jsonReplacer)}` ); } return result.Ok;};export const setRequestProcessed = ({ key, data: currentData, version: originalVersion}: { key: string; data: RequestData; version: bigint | undefined;}) => { const updateData: RequestData = { ...currentData, status: "processed" }; const data = encodeDocData(updateData); const doc: SetDoc = { data, version: originalVersion }; setDocStore({ caller: id(), collection: COLLECTION_REQUEST, doc, key });}; +// src/satellite/services.tsimport { Principal } from "@icp-sdk/core/principal";import { IcrcLedgerCanister, type Account, type TransferFromArgs, type Tokens} from "@junobuild/functions/canisters/ledger/icrc";export const assertWalletBalance = async ({ ledgerId, fromAccount, amount, fee}: { ledgerId: Principal; fromAccount: Account; amount: bigint; fee: bigint | undefined;}) => { const { icrc1BalanceOf } = new IcrcLedgerCanister({ canisterId: ledgerId }); const balance = await icrc1BalanceOf({ account: fromAccount }); const total = amount + (fee ?? 10_000n); if (balance < total) { const encodedAccountText = encodeIcrcAccount(fromAccount); throw new Error( `Balance ${balance} is smaller than ${total} for account ${encodedAccountText}.` ); }};export const transferIcpFromWallet = async ({ ledgerId, fromAccount, toAccount, amount, fee}: { ledgerId: Principal; fromAccount: Account; toAccount: Account; amount: bigint; fee: bigint | undefined;}): Promise => { const args: TransferFromArgs = { amount, from: fromAccount, to: toAccount, fee }; const { icrc2TransferFrom } = new IcrcLedgerCanister({ canisterId: ledgerId }); const result = await icrc2TransferFrom({ args }); if ("Err" in result) { throw new Error( `Failed to transfer ICP from wallet: ${JSON.stringify(result, jsonReplacer)}` ); } return result.Ok;};export const setRequestProcessed = ({ key, data: currentData, version: originalVersion}: { key: string; data: RequestData; version: bigint | undefined;}) => { const updateData: RequestData = { ...currentData, status: "processed" }; const data = encodeDocData(updateData); const doc: SetDoc = { data, version: originalVersion }; setDocStore({ caller: id(), collection: COLLECTION_REQUEST, doc, key });}; ``` **Explanation:**