diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a29d08b..9a003acd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - For consistency, the `CMCCanister.create` function now requires the `canisterId` option to be provided exclusively as a `Principal`. +## Features + +- Add support for `get_default_subnets` to `@dfinity/cmc`. + # 2024.10.09-1140Z ## Overview diff --git a/packages/cmc/README.md b/packages/cmc/README.md index e06541f7d..d1bc2d572 100644 --- a/packages/cmc/README.md +++ b/packages/cmc/README.md @@ -63,6 +63,7 @@ const rate = await getIcpToCyclesConversionRate(); - [getIcpToCyclesConversionRate](#gear-geticptocyclesconversionrate) - [notifyCreateCanister](#gear-notifycreatecanister) - [notifyTopUp](#gear-notifytopup) +- [getDefaultSubnets](#gear-getdefaultsubnets) ##### :gear: create @@ -104,4 +105,21 @@ It returns the new Cycles of the canister. [:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/cmc/src/cmc.canister.ts#L76) +##### :gear: getDefaultSubnets + +This function calls the `get_default_subnets` method of the CMC canister, which returns a list of +default subnets as `Principal` objects. It can be called as query or update. + +| Method | Type | +| ------------------- | ------------------------------------------------------- | +| `getDefaultSubnets` | `({ certified }?: QueryParams) => Promise` | + +Parameters: + +- `params`: - The query parameters for the call. +- `params.certified`: - Determines whether the response should be certified + (default: non-certified if not specified). + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/cmc/src/cmc.canister.ts#L101) + diff --git a/packages/cmc/src/cmc.canister.spec.ts b/packages/cmc/src/cmc.canister.spec.ts index 701848d2b..0bcd84552 100644 --- a/packages/cmc/src/cmc.canister.spec.ts +++ b/packages/cmc/src/cmc.canister.spec.ts @@ -1,6 +1,7 @@ import { ActorSubclass, HttpAgent } from "@dfinity/agent"; import { Principal } from "@dfinity/principal"; import { arrayOfNumberToUint8Array } from "@dfinity/utils"; +import type { QueryParams } from "@dfinity/utils/src"; import { mock } from "jest-mock-extended"; import type { _SERVICE as CMCService, @@ -16,6 +17,7 @@ import { RefundedError, TransactionTooOldError, } from "./cmc.errors"; +import { mockPrincipalText } from "./cmc.mock"; describe("CyclesMintingCanister", () => { const mockAgent: HttpAgent = mock(); @@ -291,4 +293,65 @@ describe("CyclesMintingCanister", () => { expect(call).rejects.toThrowError(CMCError); }); }); + + describe("CMCCanister.getDefaultSubnets", () => { + const expectedSubnets = [ + Principal.fromText(mockPrincipalText), + Principal.fromText("aaaaa-aa"), + ]; + + it("should return a list of default subnets for a query", async () => { + const service = mock(); + service.get_default_subnets.mockResolvedValue(expectedSubnets); + + const cmc = await createCMC(service); + + const callerSpy = jest.spyOn( + cmc as unknown as { + caller: (params: QueryParams) => Promise; + }, + "caller", + ); + + const result = await cmc.getDefaultSubnets({ certified: false }); + + expect(result).toEqual(expectedSubnets); + expect(service.get_default_subnets).toHaveBeenCalledTimes(1); + + expect(callerSpy).toHaveBeenCalledWith({ certified: false }); + }); + + it("should return a list of default subnets for an update", async () => { + const service = mock(); + service.get_default_subnets.mockResolvedValue(expectedSubnets); + + const cmc = await createCMC(service); + + const callerSpy = jest.spyOn( + cmc as unknown as { + caller: (params: QueryParams) => Promise; + }, + "caller", + ); + + const result = await cmc.getDefaultSubnets({ certified: true }); + + expect(result).toEqual(expectedSubnets); + expect(service.get_default_subnets).toHaveBeenCalledTimes(1); + + expect(callerSpy).toHaveBeenCalledWith({ certified: true }); + }); + + it("should throw an error if the canister call ends in error", async () => { + const service = mock(); + service.get_default_subnets.mockRejectedValue(new Error("Test")); + + const cmc = await createCMC(service); + + await expect(cmc.getDefaultSubnets({ certified: true })).rejects.toThrow( + "Test", + ); + expect(service.get_default_subnets).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/packages/cmc/src/cmc.canister.ts b/packages/cmc/src/cmc.canister.ts index b366aac67..778385061 100644 --- a/packages/cmc/src/cmc.canister.ts +++ b/packages/cmc/src/cmc.canister.ts @@ -1,5 +1,5 @@ import type { Principal } from "@dfinity/principal"; -import { Canister, createServices } from "@dfinity/utils"; +import { Canister, createServices, type QueryParams } from "@dfinity/utils"; import type { _SERVICE as CMCCanisterService, Cycles, @@ -86,4 +86,22 @@ export class CMCCanister extends Canister { `Unsupported response type in notifyTopUp ${JSON.stringify(response)}`, ); }; + + /** + * This function calls the `get_default_subnets` method of the CMC canister, which returns a list of + * default subnets as `Principal` objects. It can be called as query or update. + * + * @param {Object} [params] - The query parameters for the call. + * @param {boolean} [params.certified] - Determines whether the response should be certified + * (default: non-certified if not specified). + * + * @returns {Promise} - A promise that resolves to an array of `Principal` objects + * representing the default subnets. + */ + public getDefaultSubnets = async ({ certified }: QueryParams = {}): Promise< + Principal[] + > => { + const { get_default_subnets } = this.caller({ certified }); + return get_default_subnets(); + }; }