diff --git a/packages/ic-management/README.md b/packages/ic-management/README.md index efa5cbe8..7b3a14dd 100644 --- a/packages/ic-management/README.md +++ b/packages/ic-management/README.md @@ -94,7 +94,7 @@ Create a new canister | ---------------- | ------------------------------------------------------------------------------------- | | `createCanister` | `({ settings, senderCanisterVersion, }?: CreateCanisterParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L82) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L67) ##### :gear: updateSettings @@ -104,7 +104,7 @@ Update canister settings | ---------------- | ------------------------------------------------------------------------------------------- | | `updateSettings` | `({ canisterId, senderCanisterVersion, settings, }: UpdateSettingsParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L105) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L90) ##### :gear: installCode @@ -114,7 +114,7 @@ Install code to a canister | ------------- | -------------------------------------------------------------------------------------------------- | | `installCode` | `({ canisterId, wasmModule, senderCanisterVersion, ...rest }: InstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L130) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L115) ##### :gear: uploadChunk @@ -129,7 +129,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks will be stored. - `params.chunk`: A chunk of Wasm module. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L156) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L141) ##### :gear: clearChunkStore @@ -143,7 +143,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L176) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L161) ##### :gear: storedChunks @@ -157,7 +157,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L195) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L180) ##### :gear: installChunkedCode @@ -177,7 +177,7 @@ Parameters: - `params.storeCanisterId`: Specifies the canister in whose chunk storage the chunks are stored (this parameter defaults to target_canister if not specified). - `params.wasmModuleHash`: The Wasm module hash as hex string. Used to check that the SHA-256 hash of wasm_module is equal to the wasm_module_hash parameter and can calls install_code with parameters. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L220) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L205) ##### :gear: uninstallCode @@ -187,7 +187,7 @@ Uninstall code from a canister | --------------- | -------------------------------------------------------------------------------- | | `uninstallCode` | `({ canisterId, senderCanisterVersion, }: UninstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L251) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L236) ##### :gear: startCanister @@ -197,7 +197,7 @@ Start a canister | --------------- | ------------------------------------------ | | `startCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L269) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L254) ##### :gear: stopCanister @@ -207,7 +207,7 @@ Stop a canister | -------------- | ------------------------------------------ | | `stopCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L281) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L266) ##### :gear: canisterStatus @@ -217,7 +217,7 @@ Get canister details (memory size, status, etc.) | ---------------- | ------------------------------------------------------------ | | `canisterStatus` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L292) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L277) ##### :gear: deleteCanister @@ -227,7 +227,7 @@ Deletes a canister | ---------------- | ------------------------------------------ | | `deleteCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L306) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L291) ##### :gear: provisionalCreateCanisterWithCycles @@ -237,7 +237,7 @@ Creates a canister. Only available on development instances. | ------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `provisionalCreateCanisterWithCycles` | `({ settings, amount, canisterId, }?: ProvisionalCreateCanisterWithCyclesParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L321) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L306) ##### :gear: fetchCanisterLogs @@ -247,7 +247,7 @@ Given a canister ID as input, this method returns a vector of logs of that canis | ------------------- | ---------------------------------------------------------------- | | `fetchCanisterLogs` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L344) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L329) ##### :gear: takeCanisterSnapshot @@ -265,7 +265,7 @@ Parameters: Can be provided as a `string` or a `Uint8Array`. If not provided, a new snapshot will be created. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L370) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L355) ##### :gear: listCanisterSnapshots @@ -280,7 +280,7 @@ Parameters: - `params`: - Parameters for the listing operation. - `params.canisterId`: - The ID of the canister for which snapshots will be listed. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L399) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L384) ##### :gear: loadCanisterSnapshot @@ -297,7 +297,7 @@ Parameters: - `params.snapshotId`: - The ID of the snapshot to load. - `params.senderCanisterVersion`: - The optional sender canister version. If provided, its value must be equal to ic0.canister_version. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L425) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L410) ##### :gear: deleteCanisterSnapshot @@ -313,7 +313,7 @@ Parameters: - `params.canisterId`: - The ID of the canister for which the snapshot will be deleted. - `params.snapshotId`: - The ID of the snapshot to delete. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L456) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L441) diff --git a/packages/ic-management/src/ic-management.canister.ts b/packages/ic-management/src/ic-management.canister.ts index 8bef5ba0..ce08a013 100644 --- a/packages/ic-management/src/ic-management.canister.ts +++ b/packages/ic-management/src/ic-management.canister.ts @@ -1,4 +1,3 @@ -import type { CallConfig } from "@dfinity/agent"; import { Principal } from "@dfinity/principal"; import { createServices, @@ -34,6 +33,7 @@ import type { FetchCanisterLogsResponse, } from "./types/ic-management.responses"; import { mapSnapshotId } from "./utils/ic-management.utils"; +import { transform } from "./utils/transform.utils"; export class ICManagementCanister { private constructor(private readonly service: IcManagementService) { @@ -41,21 +41,6 @@ export class ICManagementCanister { } public static create(options: ICManagementCanisterOptions) { - // Source getManagementCanister in agent-js. - // Allow usage of the ICManagementCanister wrapper locally. - const transform = ( - _methodName: string, - args: unknown[], - _callConfig: CallConfig, - ) => { - const first = args[0] as { canister_id: string }; - let effectiveCanisterId = Principal.fromHex(""); - if (first && typeof first === "object" && first.canister_id) { - effectiveCanisterId = Principal.from(first.canister_id as unknown); - } - return { effectiveCanisterId }; - }; - const { service } = createServices({ options: { ...options, diff --git a/packages/ic-management/src/utils/transform.utils.spec.ts b/packages/ic-management/src/utils/transform.utils.spec.ts new file mode 100644 index 00000000..53ebc703 --- /dev/null +++ b/packages/ic-management/src/utils/transform.utils.spec.ts @@ -0,0 +1,74 @@ +import type { CallConfig } from "@dfinity/agent"; +import { Principal } from "@dfinity/principal"; +import { mockCanisterId } from "../ic-management.mock"; +import { transform } from "./transform.utils"; + +describe("transform", () => { + it("should map the effectiveCanisterId when a valid canister_id is provided as principal in the request", () => { + const methodName = "someMethod"; + const args = [{ canister_id: mockCanisterId }]; + const callConfig: CallConfig = {}; + + const result = transform(methodName, args, callConfig); + + expect(result).toEqual({ + effectiveCanisterId: mockCanisterId, + }); + }); + + it("should map the effectiveCanisterId when a valid canister_id is provided as string in the request", () => { + const methodName = "someMethod"; + const args = [{ canister_id: mockCanisterId.toText() }]; + const callConfig: CallConfig = {}; + + const result = transform(methodName, args, callConfig); + + expect(result).toEqual({ + effectiveCanisterId: mockCanisterId, + }); + }); + + it("should return effectiveCanisterId aaaaa-aa when args is empty", () => { + const methodName = "someMethod"; + const args: unknown[] = []; + const callConfig: CallConfig = {}; + + const result = transform(methodName, args, callConfig); + + expect(result).toEqual({ + effectiveCanisterId: Principal.fromHex(""), + }); + }); + + it("should return effectiveCanisterId aaaaa-aa when canister_id is missing in the first argument", () => { + const methodName = "someMethod"; + const args = [{}]; + const callConfig: CallConfig = {}; + + const result = transform(methodName, args, callConfig); + + expect(result).toEqual({ + effectiveCanisterId: Principal.fromHex(""), + }); + }); + + it("should return effectiveCanisterId aaaaa-aa when the first argument is not an object", () => { + const methodName = "someMethod"; + const args = [42]; + const callConfig: CallConfig = {}; + + const result = transform(methodName, args, callConfig); + + expect(result).toEqual({ + effectiveCanisterId: Principal.fromHex(""), + }); + }); + + it("should throw an error if canister_id is provided in the request but is not a valid principal or representation", () => { + const methodName = "someMethod"; + const args = [{ canister_id: 12345 }]; + const callConfig: CallConfig = {}; + + expect(() => transform(methodName, args, callConfig)).toThrow(); + }); +}); diff --git a/packages/ic-management/src/utils/transform.utils.ts b/packages/ic-management/src/utils/transform.utils.ts new file mode 100644 index 00000000..4037f3cd --- /dev/null +++ b/packages/ic-management/src/utils/transform.utils.ts @@ -0,0 +1,30 @@ +import type { ActorConfig, CallConfig } from "@dfinity/agent"; +import { Principal } from "@dfinity/principal"; + +type CallTransform = Required["callTransform"]; + +type QueryTransform = Required["queryTransform"]; + +/** + * Transformer function for service creation with `callTransform` or `queryTransform`. + * + * This function maps the `effective_canister_id` for calls to the Management Canister (`aaaaa-aa`). + * + * Original source `getManagementCanister` in agent-js. + * + * Providing a transformer is required to determine the effective_canister_id when the request is an update call to the Management Canister (aaaaa-aa). + * + * @link https://internetcomputer.org/docs/current/references/ic-interface-spec/#http-effective-canister-id + **/ +export const transform: CallTransform | QueryTransform = ( + _methodName: string, + args: unknown[], + _callConfig: CallConfig, +): { effectiveCanisterId: Principal } => { + const first = args[0] as { canister_id: string }; + let effectiveCanisterId = Principal.fromHex(""); + if (first && typeof first === "object" && first.canister_id) { + effectiveCanisterId = Principal.from(first.canister_id as unknown); + } + return { effectiveCanisterId }; +};