From 6187a6922ba65fda881dd0c084990eec466edbc0 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 30 Sep 2024 12:53:28 +0200 Subject: [PATCH] feat: expose icrc ledger.converters (#724) # Motivation Similarly to #716 for ICP, we also need to exposes the ICRC `ledger.converters` for the signer standards library. # Changes - Expose module in `index.ts` --------- Signed-off-by: David Dal Busco --- CHANGELOG.md | 1 + .../src/converters/ledger.converters.spec.ts | 168 ++++++++++++++++++ packages/ledger-icrc/src/index.ts | 1 + 3 files changed, 170 insertions(+) create mode 100644 packages/ledger-icrc/src/converters/ledger.converters.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6636903..9a67171ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Add support for `icrc21_canister_call_consent_message` to `@dfinity/ledger-icp` and `@dfinity/ledger-icrc`. - Add support for `"regtest"` in `BitcoinNetwork`. +- Expose `ledger-converters` utility modules in `@dfinity/ledger-icp` and `@dfinity/ledger-icrc`. ## Build diff --git a/packages/ledger-icrc/src/converters/ledger.converters.spec.ts b/packages/ledger-icrc/src/converters/ledger.converters.spec.ts new file mode 100644 index 000000000..00c145688 --- /dev/null +++ b/packages/ledger-icrc/src/converters/ledger.converters.spec.ts @@ -0,0 +1,168 @@ +import { Principal } from "@dfinity/principal"; +import { toNullable } from "@dfinity/utils"; +import { mockPrincipalText } from "../mocks/ledger.mock"; +import type { + ApproveParams, + Icrc21ConsentMessageParams, + TransferFromParams, + TransferParams, +} from "../types/ledger.params"; +import { + toApproveArgs, + toIcrc21ConsentMessageArgs, + toTransferArg, + toTransferFromArgs, +} from "./ledger.converters"; + +describe("ledger.converters", () => { + const owner = Principal.fromText(mockPrincipalText); + + describe("toTransferArg", () => { + it("should transform TransferParams to TransferArg correctly", () => { + const params: TransferParams = { + to: { owner, subaccount: [[7, 8, 9]] }, + amount: BigInt(1000), + fee: BigInt(10), + memo: new Uint8Array([1, 2, 3]), + from_subaccount: new Uint8Array([4, 5, 6]), + created_at_time: BigInt(100000), + }; + + const result = toTransferArg(params); + + expect(result).toEqual({ + to: params.to, + amount: params.amount, + fee: toNullable(params.fee), + memo: toNullable(params.memo), + from_subaccount: toNullable(params.from_subaccount), + created_at_time: toNullable(params.created_at_time), + }); + }); + }); + + describe("toTransferFromArgs", () => { + it("should transform TransferFromParams to TransferFromArgs correctly", () => { + const params: TransferFromParams = { + from: { owner, subaccount: [[4, 5, 6]] }, + to: { owner, subaccount: [[1, 2, 3]] }, + amount: BigInt(500), + fee: BigInt(5), + memo: new Uint8Array([7, 8, 9]), + spender_subaccount: new Uint8Array([10, 11, 12]), + created_at_time: BigInt(12345), + }; + + const result = toTransferFromArgs(params); + + expect(result).toEqual({ + from: params.from, + to: params.to, + amount: params.amount, + fee: toNullable(params.fee), + memo: toNullable(params.memo), + spender_subaccount: toNullable(params.spender_subaccount), + created_at_time: toNullable(params.created_at_time), + }); + }); + }); + + describe("toApproveArgs", () => { + it("should transform ApproveParams to ApproveArgs correctly", () => { + const params: ApproveParams = { + spender: { owner, subaccount: [[7, 8, 9]] }, + amount: BigInt(200), + fee: BigInt(2), + memo: new Uint8Array([13, 14, 15]), + from_subaccount: new Uint8Array([16, 17, 18]), + created_at_time: BigInt(56789), + expected_allowance: BigInt(100), + expires_at: BigInt(98765), + }; + + const result = toApproveArgs(params); + + expect(result).toEqual({ + spender: params.spender, + amount: params.amount, + fee: toNullable(params.fee), + memo: toNullable(params.memo), + from_subaccount: toNullable(params.from_subaccount), + created_at_time: toNullable(params.created_at_time), + expected_allowance: toNullable(params.expected_allowance), + expires_at: toNullable(params.expires_at), + }); + }); + }); + + describe("toIcrc21ConsentMessageArgs", () => { + it("should transform Icrc21ConsentMessageParams to ConsentMessageArgs correctly", () => { + const params: Icrc21ConsentMessageParams = { + method: "someMethod", + arg: new Uint8Array([21, 22, 23]), + userPreferences: { + metadata: { + language: "en", + utcOffsetMinutes: 120, + }, + deriveSpec: { + LineDisplay: { + charactersPerLine: 40, + linesPerPage: 10, + }, + }, + }, + }; + + const result = toIcrc21ConsentMessageArgs(params); + + expect(result).toEqual({ + method: params.method, + arg: params.arg, + user_preferences: { + metadata: { + language: params.userPreferences.metadata.language, + utc_offset_minutes: toNullable( + params.userPreferences.metadata.utcOffsetMinutes, + ), + }, + device_spec: toNullable({ + LineDisplay: { + characters_per_line: 40, + lines_per_page: 10, + }, + }), + }, + }); + }); + + it("should handle undefined deriveSpec in userPreferences", () => { + const params: Icrc21ConsentMessageParams = { + method: "someMethod", + arg: new Uint8Array([21, 22, 23]), + userPreferences: { + metadata: { + language: "en", + utcOffsetMinutes: 120, + }, + }, + }; + + const result = toIcrc21ConsentMessageArgs(params); + + expect(result).toEqual({ + method: params.method, + arg: params.arg, + user_preferences: { + metadata: { + language: params.userPreferences.metadata.language, + utc_offset_minutes: toNullable( + params.userPreferences.metadata.utcOffsetMinutes, + ), + }, + device_spec: toNullable(), + }, + }); + }); + }); +}); diff --git a/packages/ledger-icrc/src/index.ts b/packages/ledger-icrc/src/index.ts index faf02d4e4..b9105d6b2 100644 --- a/packages/ledger-icrc/src/index.ts +++ b/packages/ledger-icrc/src/index.ts @@ -6,6 +6,7 @@ export type { TransferError as IcrcTransferVariatError, Value as IcrcValue, } from "../candid/icrc_ledger"; +export * from "./converters/ledger.converters"; export * from "./errors/index.errors"; export * from "./errors/ledger.errors"; export { IcrcIndexNgCanister } from "./index-ng.canister";