From 47b3522117d18b21be44c07ae5c8e60c27a14cb5 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 07:22:11 +0200 Subject: [PATCH 01/11] feat: ledger icrc2 --- .../candid/icrc1_ledger.certified.idl.js | 168 ++++++++++++++---- packages/ledger/candid/icrc1_ledger.d.ts | 158 ++++++++++++---- packages/ledger/candid/icrc1_ledger.did | 162 +++++++++++++---- packages/ledger/candid/icrc1_ledger.idl.js | 164 +++++++++++++---- 4 files changed, 516 insertions(+), 136 deletions(-) diff --git a/packages/ledger/candid/icrc1_ledger.certified.idl.js b/packages/ledger/candid/icrc1_ledger.certified.idl.js index ec90529a6..6a4de0c3a 100644 --- a/packages/ledger/candid/icrc1_ledger.certified.idl.js +++ b/packages/ledger/candid/icrc1_ledger.certified.idl.js @@ -16,30 +16,40 @@ export const idlFactory = ({ IDL }) => { 'SetTo' : Account, 'Unset' : IDL.Null, }); + const FeatureFlags = IDL.Record({ 'icrc2' : IDL.Bool }); const UpgradeArgs = IDL.Record({ 'token_symbol' : IDL.Opt(IDL.Text), - 'transfer_fee' : IDL.Opt(IDL.Nat64), + 'transfer_fee' : IDL.Opt(IDL.Nat), 'metadata' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue))), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'change_fee_collector' : IDL.Opt(ChangeFeeCollector), 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Opt(IDL.Text), + 'feature_flags' : IDL.Opt(FeatureFlags), }); const InitArgs = IDL.Record({ + 'decimals' : IDL.Opt(IDL.Nat8), 'token_symbol' : IDL.Text, - 'transfer_fee' : IDL.Nat64, + 'transfer_fee' : IDL.Nat, 'metadata' : IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue)), 'minting_account' : Account, - 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)), + 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'fee_collector_account' : IDL.Opt(Account), 'archive_options' : IDL.Record({ 'num_blocks_to_archive' : IDL.Nat64, + 'max_transactions_per_response' : IDL.Opt(IDL.Nat64), 'trigger_threshold' : IDL.Nat64, 'max_message_size_bytes' : IDL.Opt(IDL.Nat64), 'cycles_for_archive_creation' : IDL.Opt(IDL.Nat64), 'node_max_memory_size_bytes' : IDL.Opt(IDL.Nat64), 'controller_id' : IDL.Principal, }), + 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Text, + 'feature_flags' : IDL.Opt(FeatureFlags), }); const LedgerArg = IDL.Variant({ 'Upgrade' : IDL.Opt(UpgradeArgs), @@ -87,35 +97,45 @@ export const idlFactory = ({ IDL }) => { 'start' : TxIndex, 'length' : IDL.Nat, }); + const Burn = IDL.Record({ + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'spender' : IDL.Opt(Account), + }); + const Mint = IDL.Record({ + 'to' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + }); + const Approve = IDL.Record({ + 'fee' : IDL.Opt(IDL.Nat), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'expected_allowance' : IDL.Opt(IDL.Nat), + 'expires_at' : IDL.Opt(IDL.Nat64), + 'spender' : Account, + }); + const Transfer = IDL.Record({ + 'to' : Account, + 'fee' : IDL.Opt(IDL.Nat), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'spender' : IDL.Opt(Account), + }); const Transaction = IDL.Record({ - 'burn' : IDL.Opt( - IDL.Record({ - 'from' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'burn' : IDL.Opt(Burn), 'kind' : IDL.Text, - 'mint' : IDL.Opt( - IDL.Record({ - 'to' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'mint' : IDL.Opt(Mint), + 'approve' : IDL.Opt(Approve), 'timestamp' : IDL.Nat64, - 'transfer' : IDL.Opt( - IDL.Record({ - 'to' : Account, - 'fee' : IDL.Opt(IDL.Nat), - 'from' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'transfer' : IDL.Opt(Transfer), }); const TransactionRange = IDL.Record({ 'transactions' : IDL.Vec(Transaction), @@ -138,6 +158,7 @@ export const idlFactory = ({ IDL }) => { ), }); const Tokens = IDL.Nat; + const StandardRecord = IDL.Record({ 'url' : IDL.Text, 'name' : IDL.Text }); const Timestamp = IDL.Nat64; const TransferArg = IDL.Record({ 'to' : Account, @@ -164,6 +185,66 @@ export const idlFactory = ({ IDL }) => { 'Ok' : BlockIndex, 'Err' : TransferError, }); + const AllowanceArgs = IDL.Record({ + 'account' : Account, + 'spender' : Account, + }); + const Allowance = IDL.Record({ + 'allowance' : IDL.Nat, + 'expires_at' : IDL.Opt(IDL.Nat64), + }); + const ApproveArgs = IDL.Record({ + 'fee' : IDL.Opt(IDL.Nat), + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'from_subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'expected_allowance' : IDL.Opt(IDL.Nat), + 'expires_at' : IDL.Opt(IDL.Nat64), + 'spender' : Account, + }); + const ApproveError = IDL.Variant({ + 'GenericError' : IDL.Record({ + 'message' : IDL.Text, + 'error_code' : IDL.Nat, + }), + 'TemporarilyUnavailable' : IDL.Null, + 'Duplicate' : IDL.Record({ 'duplicate_of' : IDL.Nat }), + 'BadFee' : IDL.Record({ 'expected_fee' : IDL.Nat }), + 'AllowanceChanged' : IDL.Record({ 'current_allowance' : IDL.Nat }), + 'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'TooOld' : IDL.Null, + 'Expired' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'InsufficientFunds' : IDL.Record({ 'balance' : IDL.Nat }), + }); + const ApproveResult = IDL.Variant({ 'Ok' : IDL.Nat, 'Err' : ApproveError }); + const TransferFromArgs = IDL.Record({ + 'to' : Account, + 'fee' : IDL.Opt(Tokens), + 'spender_subaccount' : IDL.Opt(Subaccount), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(Timestamp), + 'amount' : Tokens, + }); + const TransferFromError = IDL.Variant({ + 'GenericError' : IDL.Record({ + 'message' : IDL.Text, + 'error_code' : IDL.Nat, + }), + 'TemporarilyUnavailable' : IDL.Null, + 'InsufficientAllowance' : IDL.Record({ 'allowance' : Tokens }), + 'BadBurn' : IDL.Record({ 'min_burn_amount' : Tokens }), + 'Duplicate' : IDL.Record({ 'duplicate_of' : BlockIndex }), + 'BadFee' : IDL.Record({ 'expected_fee' : Tokens }), + 'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'TooOld' : IDL.Null, + 'InsufficientFunds' : IDL.Record({ 'balance' : Tokens }), + }); + const TransferFromResult = IDL.Variant({ + 'Ok' : BlockIndex, + 'Err' : TransferFromError, + }); return IDL.Service({ 'get_blocks' : IDL.Func([GetBlocksArgs], [GetBlocksResponse], []), 'get_data_certificate' : IDL.Func([], [DataCertificate], []), @@ -182,14 +263,17 @@ export const idlFactory = ({ IDL }) => { ), 'icrc1_minting_account' : IDL.Func([], [IDL.Opt(Account)], []), 'icrc1_name' : IDL.Func([], [IDL.Text], []), - 'icrc1_supported_standards' : IDL.Func( - [], - [IDL.Vec(IDL.Record({ 'url' : IDL.Text, 'name' : IDL.Text }))], - [], - ), + 'icrc1_supported_standards' : IDL.Func([], [IDL.Vec(StandardRecord)], []), 'icrc1_symbol' : IDL.Func([], [IDL.Text], []), 'icrc1_total_supply' : IDL.Func([], [Tokens], []), 'icrc1_transfer' : IDL.Func([TransferArg], [TransferResult], []), + 'icrc2_allowance' : IDL.Func([AllowanceArgs], [Allowance], []), + 'icrc2_approve' : IDL.Func([ApproveArgs], [ApproveResult], []), + 'icrc2_transfer_from' : IDL.Func( + [TransferFromArgs], + [TransferFromResult], + [], + ), }); }; export const init = ({ IDL }) => { @@ -208,30 +292,40 @@ export const init = ({ IDL }) => { 'SetTo' : Account, 'Unset' : IDL.Null, }); + const FeatureFlags = IDL.Record({ 'icrc2' : IDL.Bool }); const UpgradeArgs = IDL.Record({ 'token_symbol' : IDL.Opt(IDL.Text), - 'transfer_fee' : IDL.Opt(IDL.Nat64), + 'transfer_fee' : IDL.Opt(IDL.Nat), 'metadata' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue))), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'change_fee_collector' : IDL.Opt(ChangeFeeCollector), 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Opt(IDL.Text), + 'feature_flags' : IDL.Opt(FeatureFlags), }); const InitArgs = IDL.Record({ + 'decimals' : IDL.Opt(IDL.Nat8), 'token_symbol' : IDL.Text, - 'transfer_fee' : IDL.Nat64, + 'transfer_fee' : IDL.Nat, 'metadata' : IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue)), 'minting_account' : Account, - 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)), + 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'fee_collector_account' : IDL.Opt(Account), 'archive_options' : IDL.Record({ 'num_blocks_to_archive' : IDL.Nat64, + 'max_transactions_per_response' : IDL.Opt(IDL.Nat64), 'trigger_threshold' : IDL.Nat64, 'max_message_size_bytes' : IDL.Opt(IDL.Nat64), 'cycles_for_archive_creation' : IDL.Opt(IDL.Nat64), 'node_max_memory_size_bytes' : IDL.Opt(IDL.Nat64), 'controller_id' : IDL.Principal, }), + 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Text, + 'feature_flags' : IDL.Opt(FeatureFlags), }); const LedgerArg = IDL.Variant({ 'Upgrade' : IDL.Opt(UpgradeArgs), diff --git a/packages/ledger/candid/icrc1_ledger.d.ts b/packages/ledger/candid/icrc1_ledger.d.ts index 9f3a009d5..e9736a9e5 100644 --- a/packages/ledger/candid/icrc1_ledger.d.ts +++ b/packages/ledger/candid/icrc1_ledger.d.ts @@ -5,17 +5,68 @@ export interface Account { owner: Principal; subaccount: [] | [Subaccount]; } +export interface Allowance { + allowance: bigint; + expires_at: [] | [bigint]; +} +export interface AllowanceArgs { + account: Account; + spender: Account; +} +export interface Approve { + fee: [] | [bigint]; + from: Account; + memo: [] | [Uint8Array]; + created_at_time: [] | [bigint]; + amount: bigint; + expected_allowance: [] | [bigint]; + expires_at: [] | [bigint]; + spender: Account; +} +export interface ApproveArgs { + fee: [] | [bigint]; + memo: [] | [Uint8Array]; + from_subaccount: [] | [Uint8Array]; + created_at_time: [] | [bigint]; + amount: bigint; + expected_allowance: [] | [bigint]; + expires_at: [] | [bigint]; + spender: Account; +} +export type ApproveError = + | { + GenericError: { message: string; error_code: bigint }; + } + | { TemporarilyUnavailable: null } + | { Duplicate: { duplicate_of: bigint } } + | { BadFee: { expected_fee: bigint } } + | { AllowanceChanged: { current_allowance: bigint } } + | { CreatedInFuture: { ledger_time: bigint } } + | { TooOld: null } + | { Expired: { ledger_time: bigint } } + | { InsufficientFunds: { balance: bigint } }; +export type ApproveResult = { Ok: bigint } | { Err: ApproveError }; export type Block = Value; export type BlockIndex = bigint; export interface BlockRange { blocks: Array; } +export interface Burn { + from: Account; + memo: [] | [Uint8Array]; + created_at_time: [] | [bigint]; + amount: bigint; + spender: [] | [Account]; +} export type ChangeFeeCollector = { SetTo: Account } | { Unset: null }; export interface DataCertificate { certificate: [] | [Uint8Array]; hash_tree: Uint8Array; } export type Duration = bigint; +export interface FeatureFlags { + icrc2: boolean; +} export interface GetBlocksArgs { start: BlockIndex; length: bigint; @@ -45,22 +96,39 @@ export interface GetTransactionsResponse { length: bigint; }>; } +export interface HttpRequest { + url: string; + method: string; + body: Uint8Array; + headers: Array<[string, string]>; +} +export interface HttpResponse { + body: Uint8Array; + headers: Array<[string, string]>; + status_code: number; +} export interface InitArgs { + decimals: [] | [number]; token_symbol: string; transfer_fee: bigint; metadata: Array<[string, MetadataValue]>; minting_account: Account; initial_balances: Array<[Account, bigint]>; + maximum_number_of_accounts: [] | [bigint]; + accounts_overflow_trim_quantity: [] | [bigint]; fee_collector_account: [] | [Account]; archive_options: { num_blocks_to_archive: bigint; + max_transactions_per_response: [] | [bigint]; trigger_threshold: bigint; max_message_size_bytes: [] | [bigint]; cycles_for_archive_creation: [] | [bigint]; node_max_memory_size_bytes: [] | [bigint]; controller_id: Principal; }; + max_memo_length: [] | [number]; token_name: string; + feature_flags: [] | [FeatureFlags]; } export type LedgerArg = { Upgrade: [] | [UpgradeArgs] } | { Init: InitArgs }; export type Map = Array<[string, Value]>; @@ -69,53 +137,44 @@ export type MetadataValue = | { Nat: bigint } | { Blob: Uint8Array } | { Text: string }; +export interface Mint { + to: Account; + memo: [] | [Uint8Array]; + created_at_time: [] | [bigint]; + amount: bigint; +} export type QueryArchiveFn = ActorMethod< [GetTransactionsRequest], TransactionRange >; export type QueryBlockArchiveFn = ActorMethod<[GetBlocksArgs], BlockRange>; +export interface StandardRecord { + url: string; + name: string; +} export type Subaccount = Uint8Array; export type Timestamp = bigint; export type Tokens = bigint; export interface Transaction { - burn: - | [] - | [ - { - from: Account; - memo: [] | [Uint8Array]; - created_at_time: [] | [bigint]; - amount: bigint; - }, - ]; + burn: [] | [Burn]; kind: string; - mint: - | [] - | [ - { - to: Account; - memo: [] | [Uint8Array]; - created_at_time: [] | [bigint]; - amount: bigint; - }, - ]; + mint: [] | [Mint]; + approve: [] | [Approve]; timestamp: bigint; - transfer: - | [] - | [ - { - to: Account; - fee: [] | [bigint]; - from: Account; - memo: [] | [Uint8Array]; - created_at_time: [] | [bigint]; - amount: bigint; - }, - ]; + transfer: [] | [Transfer]; } export interface TransactionRange { transactions: Array; } +export interface Transfer { + to: Account; + fee: [] | [bigint]; + from: Account; + memo: [] | [Uint8Array]; + created_at_time: [] | [bigint]; + amount: bigint; + spender: [] | [Account]; +} export interface TransferArg { to: Account; fee: [] | [Tokens]; @@ -135,15 +194,42 @@ export type TransferError = | { CreatedInFuture: { ledger_time: bigint } } | { TooOld: null } | { InsufficientFunds: { balance: Tokens } }; +export interface TransferFromArgs { + to: Account; + fee: [] | [Tokens]; + spender_subaccount: [] | [Subaccount]; + from: Account; + memo: [] | [Uint8Array]; + created_at_time: [] | [Timestamp]; + amount: Tokens; +} +export type TransferFromError = + | { + GenericError: { message: string; error_code: bigint }; + } + | { TemporarilyUnavailable: null } + | { InsufficientAllowance: { allowance: Tokens } } + | { BadBurn: { min_burn_amount: Tokens } } + | { Duplicate: { duplicate_of: BlockIndex } } + | { BadFee: { expected_fee: Tokens } } + | { CreatedInFuture: { ledger_time: bigint } } + | { TooOld: null } + | { InsufficientFunds: { balance: Tokens } }; +export type TransferFromResult = + | { Ok: BlockIndex } + | { Err: TransferFromError }; export type TransferResult = { Ok: BlockIndex } | { Err: TransferError }; export type TxIndex = bigint; export interface UpgradeArgs { token_symbol: [] | [string]; transfer_fee: [] | [bigint]; metadata: [] | [Array<[string, MetadataValue]>]; + maximum_number_of_accounts: [] | [bigint]; + accounts_overflow_trim_quantity: [] | [bigint]; change_fee_collector: [] | [ChangeFeeCollector]; max_memo_length: [] | [number]; token_name: [] | [string]; + feature_flags: [] | [FeatureFlags]; } export type Value = | { Int: bigint } @@ -166,11 +252,11 @@ export interface _SERVICE { icrc1_metadata: ActorMethod<[], Array<[string, MetadataValue]>>; icrc1_minting_account: ActorMethod<[], [] | [Account]>; icrc1_name: ActorMethod<[], string>; - icrc1_supported_standards: ActorMethod< - [], - Array<{ url: string; name: string }> - >; + icrc1_supported_standards: ActorMethod<[], Array>; icrc1_symbol: ActorMethod<[], string>; icrc1_total_supply: ActorMethod<[], Tokens>; icrc1_transfer: ActorMethod<[TransferArg], TransferResult>; + icrc2_allowance: ActorMethod<[AllowanceArgs], Allowance>; + icrc2_approve: ActorMethod<[ApproveArgs], ApproveResult>; + icrc2_transfer_from: ActorMethod<[TransferFromArgs], TransferFromResult>; } diff --git a/packages/ledger/candid/icrc1_ledger.did b/packages/ledger/candid/icrc1_ledger.did index d6868525c..cda3995b1 100644 --- a/packages/ledger/candid/icrc1_ledger.did +++ b/packages/ledger/candid/icrc1_ledger.did @@ -1,4 +1,4 @@ -// Generated from IC repo commit d855f0e20477f41218b21ef572058271db24df9b 'rs/rosetta-api/icrc1/ledger/ledger.did' by import-candid +// Generated from IC repo commit d2331ec4b3c60f408b876427d7238ec15fb16ad5 'rs/rosetta-api/icrc1/ledger/ledger.did' by import-candid type BlockIndex = nat; type Subaccount = blob; // Number of nanoseconds since the UNIX epoch in UTC timezone. @@ -7,6 +7,52 @@ type Timestamp = nat64; type Duration = nat64; type Tokens = nat; type TxIndex = nat; +type Allowance = record { allowance : nat; expires_at : opt nat64 }; +type AllowanceArgs = record { account : Account; spender : Account }; +type Approve = record { + fee : opt nat; + from : Account; + memo : opt vec nat8; + created_at_time : opt nat64; + amount : nat; + expected_allowance : opt nat; + expires_at : opt nat64; + spender : Account; +}; +type ApproveArgs = record { + fee : opt nat; + memo : opt vec nat8; + from_subaccount : opt vec nat8; + created_at_time : opt nat64; + amount : nat; + expected_allowance : opt nat; + expires_at : opt nat64; + spender : Account; +}; +type ApproveError = variant { + GenericError : record { message : text; error_code : nat }; + TemporarilyUnavailable; + Duplicate : record { duplicate_of : nat }; + BadFee : record { expected_fee : nat }; + AllowanceChanged : record { current_allowance : nat }; + CreatedInFuture : record { ledger_time : nat64 }; + TooOld; + Expired : record { ledger_time : nat64 }; + InsufficientFunds : record { balance : nat }; +}; +type ApproveResult = variant { Ok : nat; Err : ApproveError }; + +type HttpRequest = record { + url : text; + method : text; + body : vec nat8; + headers : vec record { text; text }; +}; +type HttpResponse = record { + body : vec nat8; + headers : vec record { text; text }; + status_code : nat16; +}; type Account = record { owner : principal; @@ -46,17 +92,27 @@ type MetadataValue = variant { Blob : blob; }; +type FeatureFlags = record { + icrc2 : bool; +}; + // The initialization parameters of the Ledger type InitArgs = record { minting_account : Account; fee_collector_account : opt Account; - transfer_fee : nat64; + transfer_fee : nat; + decimals : opt nat8; + max_memo_length : opt nat16; token_symbol : text; token_name : text; metadata : vec record { text; MetadataValue }; - initial_balances : vec record { Account; nat64 }; + initial_balances : vec record { Account; nat }; + feature_flags : opt FeatureFlags; + maximum_number_of_accounts : opt nat64; + accounts_overflow_trim_quantity : opt nat64; archive_options : record { num_blocks_to_archive : nat64; + max_transactions_per_response : opt nat64; trigger_threshold : nat64; max_message_size_bytes : opt nat64; cycles_for_archive_creation : opt nat64; @@ -73,9 +129,12 @@ type UpgradeArgs = record { metadata : opt vec record { text; MetadataValue }; token_symbol : opt text; token_name : opt text; - transfer_fee : opt nat64; + transfer_fee : opt nat; change_fee_collector : opt ChangeFeeCollector; max_memo_length : opt nat16; + feature_flags : opt FeatureFlags; + maximum_number_of_accounts: opt nat64; + accounts_overflow_trim_quantity: opt nat64; }; type LedgerArg = variant { @@ -151,28 +210,37 @@ type TransactionRange = record { type QueryArchiveFn = func (GetTransactionsRequest) -> (TransactionRange) query; type Transaction = record { - kind : text; - mint : opt record { - amount : nat; - to : Account; - memo : opt blob; - created_at_time : opt nat64; - }; - burn : opt record { - amount : nat; - from : Account; - memo : opt blob; - created_at_time : opt nat64; - }; - transfer : opt record { - amount : nat; - from : Account; - to : Account; - memo : opt blob; - created_at_time : opt nat64; - fee : opt nat; - }; - timestamp : nat64; + burn : opt Burn; + kind : text; + mint : opt Mint; + approve : opt Approve; + timestamp : nat64; + transfer : opt Transfer; +}; + +type Burn = record { + from : Account; + memo : opt vec nat8; + created_at_time : opt nat64; + amount : nat; + spender : opt Account; +}; + +type Mint = record { + to : Account; + memo : opt vec nat8; + created_at_time : opt nat64; + amount : nat; +}; + +type Transfer = record { + to : Account; + fee : opt nat; + from : Account; + memo : opt vec nat8; + created_at_time : opt nat64; + amount : nat; + spender : opt Account; }; type Value = variant { @@ -261,7 +329,40 @@ type DataCertificate = record { hash_tree : blob; }; +type StandardRecord = record { url : text; name : text }; + +type TransferFromArgs = record { + spender_subaccount : opt Subaccount; + from : Account; + to : Account; + amount : Tokens; + fee : opt Tokens; + memo : opt blob; + created_at_time: opt Timestamp; +}; + +type TransferFromResult = variant { + Ok : BlockIndex; + Err : TransferFromError; +}; + +type TransferFromError = variant { + BadFee : record { expected_fee : Tokens }; + BadBurn : record { min_burn_amount : Tokens }; + InsufficientFunds : record { balance : Tokens }; + InsufficientAllowance : record { allowance : Tokens }; + TooOld; + CreatedInFuture : record { ledger_time : nat64 }; + Duplicate : record { duplicate_of : BlockIndex }; + TemporarilyUnavailable; + GenericError : record { error_code : nat; message : text }; +}; + service : (ledger_arg : LedgerArg) -> { + get_transactions : (GetTransactionsRequest) -> (GetTransactionsResponse) query; + get_blocks : (GetBlocksArgs) -> (GetBlocksResponse) query; + get_data_certificate : () -> (DataCertificate) query; + icrc1_name : () -> (text) query; icrc1_symbol : () -> (text) query; icrc1_decimals : () -> (nat8) query; @@ -271,8 +372,9 @@ service : (ledger_arg : LedgerArg) -> { icrc1_minting_account : () -> (opt Account) query; icrc1_balance_of : (Account) -> (Tokens) query; icrc1_transfer : (TransferArg) -> (TransferResult); - icrc1_supported_standards : () -> (vec record { name : text; url : text }) query; - get_transactions : (GetTransactionsRequest) -> (GetTransactionsResponse) query; - get_blocks : (GetBlocksArgs) -> (GetBlocksResponse) query; - get_data_certificate : () -> (DataCertificate) query; + icrc1_supported_standards : () -> (vec StandardRecord) query; + + icrc2_approve : (ApproveArgs) -> (ApproveResult); + icrc2_allowance : (AllowanceArgs) -> (Allowance) query; + icrc2_transfer_from : (TransferFromArgs) -> (TransferFromResult); } diff --git a/packages/ledger/candid/icrc1_ledger.idl.js b/packages/ledger/candid/icrc1_ledger.idl.js index 5efbb5541..f112535dd 100644 --- a/packages/ledger/candid/icrc1_ledger.idl.js +++ b/packages/ledger/candid/icrc1_ledger.idl.js @@ -16,30 +16,40 @@ export const idlFactory = ({ IDL }) => { 'SetTo' : Account, 'Unset' : IDL.Null, }); + const FeatureFlags = IDL.Record({ 'icrc2' : IDL.Bool }); const UpgradeArgs = IDL.Record({ 'token_symbol' : IDL.Opt(IDL.Text), - 'transfer_fee' : IDL.Opt(IDL.Nat64), + 'transfer_fee' : IDL.Opt(IDL.Nat), 'metadata' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue))), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'change_fee_collector' : IDL.Opt(ChangeFeeCollector), 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Opt(IDL.Text), + 'feature_flags' : IDL.Opt(FeatureFlags), }); const InitArgs = IDL.Record({ + 'decimals' : IDL.Opt(IDL.Nat8), 'token_symbol' : IDL.Text, - 'transfer_fee' : IDL.Nat64, + 'transfer_fee' : IDL.Nat, 'metadata' : IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue)), 'minting_account' : Account, - 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)), + 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'fee_collector_account' : IDL.Opt(Account), 'archive_options' : IDL.Record({ 'num_blocks_to_archive' : IDL.Nat64, + 'max_transactions_per_response' : IDL.Opt(IDL.Nat64), 'trigger_threshold' : IDL.Nat64, 'max_message_size_bytes' : IDL.Opt(IDL.Nat64), 'cycles_for_archive_creation' : IDL.Opt(IDL.Nat64), 'node_max_memory_size_bytes' : IDL.Opt(IDL.Nat64), 'controller_id' : IDL.Principal, }), + 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Text, + 'feature_flags' : IDL.Opt(FeatureFlags), }); const LedgerArg = IDL.Variant({ 'Upgrade' : IDL.Opt(UpgradeArgs), @@ -91,35 +101,45 @@ export const idlFactory = ({ IDL }) => { 'start' : TxIndex, 'length' : IDL.Nat, }); + const Burn = IDL.Record({ + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'spender' : IDL.Opt(Account), + }); + const Mint = IDL.Record({ + 'to' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + }); + const Approve = IDL.Record({ + 'fee' : IDL.Opt(IDL.Nat), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'expected_allowance' : IDL.Opt(IDL.Nat), + 'expires_at' : IDL.Opt(IDL.Nat64), + 'spender' : Account, + }); + const Transfer = IDL.Record({ + 'to' : Account, + 'fee' : IDL.Opt(IDL.Nat), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'spender' : IDL.Opt(Account), + }); const Transaction = IDL.Record({ - 'burn' : IDL.Opt( - IDL.Record({ - 'from' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'burn' : IDL.Opt(Burn), 'kind' : IDL.Text, - 'mint' : IDL.Opt( - IDL.Record({ - 'to' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'mint' : IDL.Opt(Mint), + 'approve' : IDL.Opt(Approve), 'timestamp' : IDL.Nat64, - 'transfer' : IDL.Opt( - IDL.Record({ - 'to' : Account, - 'fee' : IDL.Opt(IDL.Nat), - 'from' : Account, - 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'created_at_time' : IDL.Opt(IDL.Nat64), - 'amount' : IDL.Nat, - }) - ), + 'transfer' : IDL.Opt(Transfer), }); const TransactionRange = IDL.Record({ 'transactions' : IDL.Vec(Transaction), @@ -142,6 +162,7 @@ export const idlFactory = ({ IDL }) => { ), }); const Tokens = IDL.Nat; + const StandardRecord = IDL.Record({ 'url' : IDL.Text, 'name' : IDL.Text }); const Timestamp = IDL.Nat64; const TransferArg = IDL.Record({ 'to' : Account, @@ -168,6 +189,66 @@ export const idlFactory = ({ IDL }) => { 'Ok' : BlockIndex, 'Err' : TransferError, }); + const AllowanceArgs = IDL.Record({ + 'account' : Account, + 'spender' : Account, + }); + const Allowance = IDL.Record({ + 'allowance' : IDL.Nat, + 'expires_at' : IDL.Opt(IDL.Nat64), + }); + const ApproveArgs = IDL.Record({ + 'fee' : IDL.Opt(IDL.Nat), + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'from_subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(IDL.Nat64), + 'amount' : IDL.Nat, + 'expected_allowance' : IDL.Opt(IDL.Nat), + 'expires_at' : IDL.Opt(IDL.Nat64), + 'spender' : Account, + }); + const ApproveError = IDL.Variant({ + 'GenericError' : IDL.Record({ + 'message' : IDL.Text, + 'error_code' : IDL.Nat, + }), + 'TemporarilyUnavailable' : IDL.Null, + 'Duplicate' : IDL.Record({ 'duplicate_of' : IDL.Nat }), + 'BadFee' : IDL.Record({ 'expected_fee' : IDL.Nat }), + 'AllowanceChanged' : IDL.Record({ 'current_allowance' : IDL.Nat }), + 'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'TooOld' : IDL.Null, + 'Expired' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'InsufficientFunds' : IDL.Record({ 'balance' : IDL.Nat }), + }); + const ApproveResult = IDL.Variant({ 'Ok' : IDL.Nat, 'Err' : ApproveError }); + const TransferFromArgs = IDL.Record({ + 'to' : Account, + 'fee' : IDL.Opt(Tokens), + 'spender_subaccount' : IDL.Opt(Subaccount), + 'from' : Account, + 'memo' : IDL.Opt(IDL.Vec(IDL.Nat8)), + 'created_at_time' : IDL.Opt(Timestamp), + 'amount' : Tokens, + }); + const TransferFromError = IDL.Variant({ + 'GenericError' : IDL.Record({ + 'message' : IDL.Text, + 'error_code' : IDL.Nat, + }), + 'TemporarilyUnavailable' : IDL.Null, + 'InsufficientAllowance' : IDL.Record({ 'allowance' : Tokens }), + 'BadBurn' : IDL.Record({ 'min_burn_amount' : Tokens }), + 'Duplicate' : IDL.Record({ 'duplicate_of' : BlockIndex }), + 'BadFee' : IDL.Record({ 'expected_fee' : Tokens }), + 'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }), + 'TooOld' : IDL.Null, + 'InsufficientFunds' : IDL.Record({ 'balance' : Tokens }), + }); + const TransferFromResult = IDL.Variant({ + 'Ok' : BlockIndex, + 'Err' : TransferFromError, + }); return IDL.Service({ 'get_blocks' : IDL.Func([GetBlocksArgs], [GetBlocksResponse], ['query']), 'get_data_certificate' : IDL.Func([], [DataCertificate], ['query']), @@ -188,12 +269,19 @@ export const idlFactory = ({ IDL }) => { 'icrc1_name' : IDL.Func([], [IDL.Text], ['query']), 'icrc1_supported_standards' : IDL.Func( [], - [IDL.Vec(IDL.Record({ 'url' : IDL.Text, 'name' : IDL.Text }))], + [IDL.Vec(StandardRecord)], ['query'], ), 'icrc1_symbol' : IDL.Func([], [IDL.Text], ['query']), 'icrc1_total_supply' : IDL.Func([], [Tokens], ['query']), 'icrc1_transfer' : IDL.Func([TransferArg], [TransferResult], []), + 'icrc2_allowance' : IDL.Func([AllowanceArgs], [Allowance], ['query']), + 'icrc2_approve' : IDL.Func([ApproveArgs], [ApproveResult], []), + 'icrc2_transfer_from' : IDL.Func( + [TransferFromArgs], + [TransferFromResult], + [], + ), }); }; export const init = ({ IDL }) => { @@ -212,30 +300,40 @@ export const init = ({ IDL }) => { 'SetTo' : Account, 'Unset' : IDL.Null, }); + const FeatureFlags = IDL.Record({ 'icrc2' : IDL.Bool }); const UpgradeArgs = IDL.Record({ 'token_symbol' : IDL.Opt(IDL.Text), - 'transfer_fee' : IDL.Opt(IDL.Nat64), + 'transfer_fee' : IDL.Opt(IDL.Nat), 'metadata' : IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue))), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'change_fee_collector' : IDL.Opt(ChangeFeeCollector), 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Opt(IDL.Text), + 'feature_flags' : IDL.Opt(FeatureFlags), }); const InitArgs = IDL.Record({ + 'decimals' : IDL.Opt(IDL.Nat8), 'token_symbol' : IDL.Text, - 'transfer_fee' : IDL.Nat64, + 'transfer_fee' : IDL.Nat, 'metadata' : IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue)), 'minting_account' : Account, - 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)), + 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), + 'maximum_number_of_accounts' : IDL.Opt(IDL.Nat64), + 'accounts_overflow_trim_quantity' : IDL.Opt(IDL.Nat64), 'fee_collector_account' : IDL.Opt(Account), 'archive_options' : IDL.Record({ 'num_blocks_to_archive' : IDL.Nat64, + 'max_transactions_per_response' : IDL.Opt(IDL.Nat64), 'trigger_threshold' : IDL.Nat64, 'max_message_size_bytes' : IDL.Opt(IDL.Nat64), 'cycles_for_archive_creation' : IDL.Opt(IDL.Nat64), 'node_max_memory_size_bytes' : IDL.Opt(IDL.Nat64), 'controller_id' : IDL.Principal, }), + 'max_memo_length' : IDL.Opt(IDL.Nat16), 'token_name' : IDL.Text, + 'feature_flags' : IDL.Opt(FeatureFlags), }); const LedgerArg = IDL.Variant({ 'Upgrade' : IDL.Opt(UpgradeArgs), From 9a6c52c5293bd87419b94478e2d9b1fa6d9f88ef Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 07:55:55 +0200 Subject: [PATCH 02/11] feat: ledger icrc2 transfer from --- .../src/converters/ledger.converters.ts | 25 ++++++- packages/ledger/src/errors/ledger.errors.ts | 8 +-- packages/ledger/src/ledger.canister.spec.ts | 67 ++++++++++++++++++- packages/ledger/src/ledger.canister.ts | 33 ++++++++- packages/ledger/src/types/ledger.params.ts | 16 +++++ 5 files changed, 139 insertions(+), 10 deletions(-) diff --git a/packages/ledger/src/converters/ledger.converters.ts b/packages/ledger/src/converters/ledger.converters.ts index 85f6f8a1d..c90ad64c8 100644 --- a/packages/ledger/src/converters/ledger.converters.ts +++ b/packages/ledger/src/converters/ledger.converters.ts @@ -1,6 +1,9 @@ import { toNullable } from "@dfinity/utils"; -import type { TransferArg } from "../../candid/icrc1_ledger"; -import type { TransferParams } from "../types/ledger.params"; +import type { TransferArg, TransferFromArgs } from "../../candid/icrc1_ledger"; +import type { + TransferFromParams, + TransferParams, +} from "../types/ledger.params"; export const toTransferArg = ({ to, @@ -17,3 +20,21 @@ export const toTransferArg = ({ from_subaccount: toNullable(from_subaccount), created_at_time: toNullable(created_at_time), }); + +export const toTransferFromArgs = ({ + to, + from, + spender_subaccount, + fee, + amount, + created_at_time, + memo, +}: TransferFromParams): TransferFromArgs => ({ + to, + from, + amount, + fee: toNullable(fee), + memo: toNullable(memo), + spender_subaccount: toNullable(spender_subaccount), + created_at_time: toNullable(created_at_time), +}); diff --git a/packages/ledger/src/errors/ledger.errors.ts b/packages/ledger/src/errors/ledger.errors.ts index be42abfd9..468789160 100644 --- a/packages/ledger/src/errors/ledger.errors.ts +++ b/packages/ledger/src/errors/ledger.errors.ts @@ -1,8 +1,6 @@ -import type { TransferError } from "../../candid/icrc1_ledger"; - -export class IcrcTransferError extends Error { - public errorType: TransferError; - constructor({ msg, errorType }: { msg?: string; errorType: TransferError }) { +export class IcrcTransferError extends Error { + public errorType: T; + constructor({ msg, errorType }: { msg?: string; errorType: T }) { super(msg); this.errorType = errorType; } diff --git a/packages/ledger/src/ledger.canister.spec.ts b/packages/ledger/src/ledger.canister.spec.ts index 60af2b8f9..513736ac5 100644 --- a/packages/ledger/src/ledger.canister.spec.ts +++ b/packages/ledger/src/ledger.canister.spec.ts @@ -4,15 +4,17 @@ import { arrayOfNumberToUint8Array } from "@dfinity/utils"; import { mock } from "jest-mock-extended"; import type { TransferArg, + TransferFromArgs, _SERVICE as IcrcLedgerService, } from "../candid/icrc1_ledger"; import { IcrcTransferError } from "./errors/ledger.errors"; import { IcrcLedgerCanister } from "./ledger.canister"; import { ledgerCanisterIdMock, + mockPrincipal, tokeMetadataResponseMock, } from "./mocks/ledger.mock"; -import { TransferParams } from "./types/ledger.params"; +import { TransferFromParams, TransferParams } from "./types/ledger.params"; describe("Ledger canister", () => { it("should return the token metadata", async () => { @@ -149,4 +151,67 @@ describe("Ledger canister", () => { expect(call).rejects.toThrow(IcrcTransferError); }); }); + + describe("transfer-from", () => { + const transferParams: TransferFromParams = { + from: { + owner: mockPrincipal, + subaccount: [], + }, + to: { + owner: Principal.fromText("aaaaa-aa"), + subaccount: [], + }, + amount: BigInt(100_000_000), + }; + const transferArg: TransferFromArgs = { + from: { + owner: mockPrincipal, + subaccount: [], + }, + to: { + owner: Principal.fromText("aaaaa-aa"), + subaccount: [], + }, + fee: [], + memo: [], + spender_subaccount: [], + created_at_time: [], + amount: BigInt(100_000_000), + }; + it("should return the block height successfully", async () => { + const service = mock>(); + const blockHeight = BigInt(100); + service.icrc2_transfer_from.mockResolvedValue({ Ok: blockHeight }); + + const canister = IcrcLedgerCanister.create({ + canisterId: ledgerCanisterIdMock, + certifiedServiceOverride: service, + }); + + const res = await canister.transferFrom(transferParams); + expect(res).toEqual(blockHeight); + expect(service.icrc2_transfer_from).toBeCalledWith(transferArg); + }); + + it("should raise IcrcTransferError error", async () => { + const service = mock>(); + const errorResponse = { + Err: { + InsufficientFunds: { + balance: BigInt(0), + }, + }, + }; + service.icrc2_transfer_from.mockResolvedValue(errorResponse); + + const canister = IcrcLedgerCanister.create({ + canisterId: ledgerCanisterIdMock, + certifiedServiceOverride: service, + }); + + const call = () => canister.transferFrom(transferParams); + expect(call).rejects.toThrow(IcrcTransferError); + }); + }); }); diff --git a/packages/ledger/src/ledger.canister.ts b/packages/ledger/src/ledger.canister.ts index daa66f380..0d2c9974a 100644 --- a/packages/ledger/src/ledger.canister.ts +++ b/packages/ledger/src/ledger.canister.ts @@ -7,10 +7,17 @@ import type { } from "../candid/icrc1_ledger"; import { idlFactory as certifiedIdlFactory } from "../candid/icrc1_ledger.certified.idl"; import { idlFactory } from "../candid/icrc1_ledger.idl"; -import { toTransferArg } from "./converters/ledger.converters"; +import { + toTransferArg, + toTransferFromArgs, +} from "./converters/ledger.converters"; import { IcrcTransferError } from "./errors/ledger.errors"; import type { IcrcLedgerCanisterOptions } from "./types/canister.options"; -import type { BalanceParams, TransferParams } from "./types/ledger.params"; +import type { + BalanceParams, + TransferFromParams, + TransferParams, +} from "./types/ledger.params"; import type { IcrcTokenMetadataResponse } from "./types/ledger.responses"; export class IcrcLedgerCanister extends Canister { @@ -77,4 +84,26 @@ export class IcrcLedgerCanister extends Canister { totalTokensSupply = (params: QueryParams): Promise => { return this.caller(params).icrc1_total_supply(); }; + + /** + * Transfers a token amount from the `from` account to the `to` account using the allowance of the spender's account (`SpenderAccount = { owner = caller; subaccount = spender_subaccount }`). The ledger draws the fees from the `from` account. + * + * Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_transfer_from + * + * @param {TransferFromParams} params The parameters to transfer tokens from to. + * + * @throws {IcrcTransferError} If the transfer fails. + */ + transferFrom = async (params: TransferFromParams): Promise => { + const response = await this.caller({ certified: true }).icrc2_transfer_from( + toTransferFromArgs(params), + ); + if ("Err" in response) { + throw new IcrcTransferError({ + errorType: response.Err, + msg: "Failed to transfer from", + }); + } + return response.Ok; + }; } diff --git a/packages/ledger/src/types/ledger.params.ts b/packages/ledger/src/types/ledger.params.ts index 7d59b8e66..3ab60a3e8 100644 --- a/packages/ledger/src/types/ledger.params.ts +++ b/packages/ledger/src/types/ledger.params.ts @@ -32,3 +32,19 @@ export interface TransferParams { created_at_time?: Timestamp; amount: Tokens; } + +/** + * Params for an icrc2_transfer_from. + * + * @param {Account} to The account to transfer tokens to. + * @param {Account} from The account to transfer tokens from. + * @param {Subaccount?} spender_subaccount A spender subaccount. + * @param {Tokens} amount The Amount of tokens to transfer. + * @param {Uint8Array?} memo Transfer memo. + * @param {Timestamp?} created_at_time nanoseconds since unix epoc to trigger deduplication and avoid other issues + * @param {Tokens?} fee The fee of the transfer when it's not the default fee. + */ +export type TransferFromParams = Omit & { + spender_subaccount?: Subaccount; + from: Account; +}; From 9e5b25814f724857cea6cfb86e72bb97b89f50d3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 05:57:35 +0000 Subject: [PATCH 03/11] =?UTF-8?q?=F0=9F=A4=96=20Documentation=20auto-updat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ledger/README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/ledger/README.md b/packages/ledger/README.md index 545cfbd8b..9fe530931 100644 --- a/packages/ledger/README.md +++ b/packages/ledger/README.md @@ -123,7 +123,7 @@ Parameters: ### :factory: IcrcLedgerCanister -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L16) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L23) #### Methods @@ -133,6 +133,7 @@ Parameters: - [balance](#gear-balance) - [transfer](#gear-transfer) - [totalTokensSupply](#gear-totaltokenssupply) +- [transferFrom](#gear-transferfrom) ##### :gear: create @@ -140,7 +141,7 @@ Parameters: | -------- | ---------------------------------------------------------------------- | | `create` | `(options: IcrcLedgerCanisterOptions<_SERVICE>) => IcrcLedgerCanister` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L17) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L24) ##### :gear: metadata @@ -150,7 +151,7 @@ The token metadata (name, symbol, etc.). | ---------- | ------------------------------------------------------------- | | `metadata` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L31) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L38) ##### :gear: transactionFee @@ -160,7 +161,7 @@ The ledger transaction fees. | ---------------- | ------------------------------------------ | | `transactionFee` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L39) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L46) ##### :gear: balance @@ -174,7 +175,7 @@ Parameters: - `params`: The parameters to get the balance of an account. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L48) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L55) ##### :gear: transfer @@ -188,7 +189,7 @@ Parameters: - `params`: The parameters to transfer tokens. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L61) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L68) ##### :gear: totalTokensSupply @@ -198,7 +199,23 @@ Returns the total supply of tokens. | ------------------- | ------------------------------------------ | | `totalTokensSupply` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L77) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L84) + +##### :gear: transferFrom + +Transfers a token amount from the `from` account to the `to` account using the allowance of the spender's account (`SpenderAccount = { owner = caller; subaccount = spender_subaccount }`). The ledger draws the fees from the `from` account. + +Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_transfer_from + +| Method | Type | +| -------------- | ------------------------------------------------- | +| `transferFrom` | `(params: TransferFromParams) => Promise` | + +Parameters: + +- `params`: The parameters to transfer tokens from to. + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L97) ### :factory: IcrcIndexCanister From 29bdd45853237d629369a52a321670bd9ac69c36 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 08:40:14 +0200 Subject: [PATCH 04/11] feat: ledger icrc2 approve --- .../src/converters/ledger.converters.ts | 34 +++++++--- packages/ledger/src/ledger.canister.spec.ts | 65 ++++++++++++++++++- packages/ledger/src/ledger.canister.ts | 26 +++++++- packages/ledger/src/types/ledger.params.ts | 6 ++ 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/packages/ledger/src/converters/ledger.converters.ts b/packages/ledger/src/converters/ledger.converters.ts index c90ad64c8..69c494014 100644 --- a/packages/ledger/src/converters/ledger.converters.ts +++ b/packages/ledger/src/converters/ledger.converters.ts @@ -1,20 +1,20 @@ import { toNullable } from "@dfinity/utils"; import type { TransferArg, TransferFromArgs } from "../../candid/icrc1_ledger"; +import { ApproveArgs } from "../../candid/icrc1_ledger"; import type { TransferFromParams, TransferParams, } from "../types/ledger.params"; +import { ApproveParams } from "../types/ledger.params"; export const toTransferArg = ({ - to, from_subaccount, fee, - amount, created_at_time, memo, + ...rest }: TransferParams): TransferArg => ({ - to, - amount, + ...rest, fee: toNullable(fee), memo: toNullable(memo), from_subaccount: toNullable(from_subaccount), @@ -22,19 +22,33 @@ export const toTransferArg = ({ }); export const toTransferFromArgs = ({ - to, - from, spender_subaccount, fee, - amount, created_at_time, memo, + ...rest }: TransferFromParams): TransferFromArgs => ({ - to, - from, - amount, + ...rest, fee: toNullable(fee), memo: toNullable(memo), spender_subaccount: toNullable(spender_subaccount), created_at_time: toNullable(created_at_time), }); + +export const toApproveArgs = ({ + fee, + created_at_time, + memo, + from_subaccount, + expected_allowance, + expires_at, + ...rest +}: ApproveParams): ApproveArgs => ({ + ...rest, + fee: toNullable(fee), + memo: toNullable(memo), + from_subaccount: toNullable(from_subaccount), + created_at_time: toNullable(created_at_time), + expected_allowance: toNullable(expected_allowance), + expires_at: toNullable(expires_at), +}); diff --git a/packages/ledger/src/ledger.canister.spec.ts b/packages/ledger/src/ledger.canister.spec.ts index 513736ac5..626fa46ba 100644 --- a/packages/ledger/src/ledger.canister.spec.ts +++ b/packages/ledger/src/ledger.canister.spec.ts @@ -3,6 +3,7 @@ import { Principal } from "@dfinity/principal"; import { arrayOfNumberToUint8Array } from "@dfinity/utils"; import { mock } from "jest-mock-extended"; import type { + ApproveArgs, TransferArg, TransferFromArgs, _SERVICE as IcrcLedgerService, @@ -14,7 +15,11 @@ import { mockPrincipal, tokeMetadataResponseMock, } from "./mocks/ledger.mock"; -import { TransferFromParams, TransferParams } from "./types/ledger.params"; +import { + ApproveParams, + TransferFromParams, + TransferParams, +} from "./types/ledger.params"; describe("Ledger canister", () => { it("should return the token metadata", async () => { @@ -214,4 +219,62 @@ describe("Ledger canister", () => { expect(call).rejects.toThrow(IcrcTransferError); }); }); + + describe("approve", () => { + const approveParams: ApproveParams = { + spender: { + owner: mockPrincipal, + subaccount: [], + }, + amount: BigInt(100_000_000), + expires_at: 123n, + }; + const approveArg: ApproveArgs = { + expected_allowance: [], + expires_at: [123n], + from_subaccount: [], + spender: { + owner: mockPrincipal, + subaccount: [], + }, + fee: [], + memo: [], + created_at_time: [], + amount: BigInt(100_000_000), + }; + it("should return the block height successfully", async () => { + const service = mock>(); + const blockHeight = BigInt(100); + service.icrc2_approve.mockResolvedValue({ Ok: blockHeight }); + + const canister = IcrcLedgerCanister.create({ + canisterId: ledgerCanisterIdMock, + certifiedServiceOverride: service, + }); + + const res = await canister.approve(approveParams); + expect(res).toEqual(blockHeight); + expect(service.icrc2_approve).toBeCalledWith(approveArg); + }); + + it("should raise IcrcTransferError error", async () => { + const service = mock>(); + const errorResponse = { + Err: { + InsufficientFunds: { + balance: BigInt(0), + }, + }, + }; + service.icrc2_approve.mockResolvedValue(errorResponse); + + const canister = IcrcLedgerCanister.create({ + canisterId: ledgerCanisterIdMock, + certifiedServiceOverride: service, + }); + + const call = () => canister.approve(approveParams); + expect(call).rejects.toThrow(IcrcTransferError); + }); + }); }); diff --git a/packages/ledger/src/ledger.canister.ts b/packages/ledger/src/ledger.canister.ts index 0d2c9974a..d6ed2d9a3 100644 --- a/packages/ledger/src/ledger.canister.ts +++ b/packages/ledger/src/ledger.canister.ts @@ -8,6 +8,7 @@ import type { import { idlFactory as certifiedIdlFactory } from "../candid/icrc1_ledger.certified.idl"; import { idlFactory } from "../candid/icrc1_ledger.idl"; import { + toApproveArgs, toTransferArg, toTransferFromArgs, } from "./converters/ledger.converters"; @@ -18,6 +19,7 @@ import type { TransferFromParams, TransferParams, } from "./types/ledger.params"; +import { ApproveParams } from "./types/ledger.params"; import type { IcrcTokenMetadataResponse } from "./types/ledger.responses"; export class IcrcLedgerCanister extends Canister { @@ -92,7 +94,7 @@ export class IcrcLedgerCanister extends Canister { * * @param {TransferFromParams} params The parameters to transfer tokens from to. * - * @throws {IcrcTransferError} If the transfer fails. + * @throws {IcrcTransferError} If the transfer from fails. */ transferFrom = async (params: TransferFromParams): Promise => { const response = await this.caller({ certified: true }).icrc2_transfer_from( @@ -106,4 +108,26 @@ export class IcrcLedgerCanister extends Canister { } return response.Ok; }; + + /** + * This method entitles the `spender` to transfer token `amount` on behalf of the caller from account `{ owner = caller; subaccount = from_subaccount }`. + * + * Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_approve + * + * @param {ApproveParams} params The parameters to approve. + * + * @throws {IcrcTransferError} If the approval fails. + */ + approve = async (params: ApproveParams): Promise => { + const response = await this.caller({ certified: true }).icrc2_approve( + toApproveArgs(params), + ); + if ("Err" in response) { + throw new IcrcTransferError({ + errorType: response.Err, + msg: "Failed to entitle the spender to transfer the amount", + }); + } + return response.Ok; + }; } diff --git a/packages/ledger/src/types/ledger.params.ts b/packages/ledger/src/types/ledger.params.ts index 3ab60a3e8..b95633a73 100644 --- a/packages/ledger/src/types/ledger.params.ts +++ b/packages/ledger/src/types/ledger.params.ts @@ -48,3 +48,9 @@ export type TransferFromParams = Omit & { spender_subaccount?: Subaccount; from: Account; }; + +export type ApproveParams = Omit & { + expected_allowance?: bigint; + expires_at?: bigint; + spender: Account; +}; From abeebcf35c2f23ba38a34c0e401e38f5164d5b63 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 06:41:59 +0000 Subject: [PATCH 05/11] =?UTF-8?q?=F0=9F=A4=96=20Documentation=20auto-updat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ledger/README.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/ledger/README.md b/packages/ledger/README.md index 9fe530931..04160c79f 100644 --- a/packages/ledger/README.md +++ b/packages/ledger/README.md @@ -123,7 +123,7 @@ Parameters: ### :factory: IcrcLedgerCanister -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L23) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L25) #### Methods @@ -134,6 +134,7 @@ Parameters: - [transfer](#gear-transfer) - [totalTokensSupply](#gear-totaltokenssupply) - [transferFrom](#gear-transferfrom) +- [approve](#gear-approve) ##### :gear: create @@ -141,7 +142,7 @@ Parameters: | -------- | ---------------------------------------------------------------------- | | `create` | `(options: IcrcLedgerCanisterOptions<_SERVICE>) => IcrcLedgerCanister` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L24) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L26) ##### :gear: metadata @@ -151,7 +152,7 @@ The token metadata (name, symbol, etc.). | ---------- | ------------------------------------------------------------- | | `metadata` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L38) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L40) ##### :gear: transactionFee @@ -161,7 +162,7 @@ The ledger transaction fees. | ---------------- | ------------------------------------------ | | `transactionFee` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L46) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L48) ##### :gear: balance @@ -175,7 +176,7 @@ Parameters: - `params`: The parameters to get the balance of an account. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L55) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L57) ##### :gear: transfer @@ -189,7 +190,7 @@ Parameters: - `params`: The parameters to transfer tokens. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L68) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L70) ##### :gear: totalTokensSupply @@ -199,7 +200,7 @@ Returns the total supply of tokens. | ------------------- | ------------------------------------------ | | `totalTokensSupply` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L84) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L86) ##### :gear: transferFrom @@ -215,7 +216,23 @@ Parameters: - `params`: The parameters to transfer tokens from to. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L97) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L99) + +##### :gear: approve + +This method entitles the `spender` to transfer token `amount` on behalf of the caller from account `{ owner = caller; subaccount = from_subaccount }`. + +Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_approve + +| Method | Type | +| --------- | -------------------------------------------- | +| `approve` | `(params: ApproveParams) => Promise` | + +Parameters: + +- `params`: The parameters to approve. + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L121) ### :factory: IcrcIndexCanister From 6e4491220b878814f558030c2835b3a4dccd0bc9 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 09:24:57 +0200 Subject: [PATCH 06/11] feat: ledger icrc2 allowance --- packages/ledger/src/ledger.canister.spec.ts | 38 +++++++++++++++++++++ packages/ledger/src/ledger.canister.ts | 18 +++++++++- packages/ledger/src/types/ledger.params.ts | 18 ++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/packages/ledger/src/ledger.canister.spec.ts b/packages/ledger/src/ledger.canister.spec.ts index 626fa46ba..3ea3090a9 100644 --- a/packages/ledger/src/ledger.canister.spec.ts +++ b/packages/ledger/src/ledger.canister.spec.ts @@ -3,6 +3,7 @@ import { Principal } from "@dfinity/principal"; import { arrayOfNumberToUint8Array } from "@dfinity/utils"; import { mock } from "jest-mock-extended"; import type { + Allowance, ApproveArgs, TransferArg, TransferFromArgs, @@ -16,6 +17,7 @@ import { tokeMetadataResponseMock, } from "./mocks/ledger.mock"; import { + AllowanceParams, ApproveParams, TransferFromParams, TransferParams, @@ -277,4 +279,40 @@ describe("Ledger canister", () => { expect(call).rejects.toThrow(IcrcTransferError); }); }); + + describe("allowance", () => { + const allowance: Allowance = { + allowance: 123n, + expires_at: [456n], + }; + + const allowanceParams: Omit = { + spender: { + owner: mockPrincipal, + subaccount: [], + }, + account: { + owner: Principal.fromText("aaaaa-aa"), + subaccount: [], + }, + }; + + it("should return the allowance", async () => { + const service = mock>(); + service.icrc2_allowance.mockResolvedValue(allowance); + + const canister = IcrcLedgerCanister.create({ + canisterId: ledgerCanisterIdMock, + certifiedServiceOverride: service, + }); + + const owner = Principal.fromText("aaaaa-aa"); + const res = await canister.allowance({ + ...allowanceParams, + certified: true, + }); + expect(service.icrc2_allowance).toBeCalledWith(allowanceParams); + expect(res).toEqual(allowance); + }); + }); }); diff --git a/packages/ledger/src/ledger.canister.ts b/packages/ledger/src/ledger.canister.ts index d6ed2d9a3..57152f318 100644 --- a/packages/ledger/src/ledger.canister.ts +++ b/packages/ledger/src/ledger.canister.ts @@ -1,6 +1,7 @@ import type { QueryParams } from "@dfinity/utils"; import { Canister, createServices, toNullable } from "@dfinity/utils"; import type { + Allowance, BlockIndex, Tokens, _SERVICE as IcrcLedgerService, @@ -15,11 +16,12 @@ import { import { IcrcTransferError } from "./errors/ledger.errors"; import type { IcrcLedgerCanisterOptions } from "./types/canister.options"; import type { + AllowanceParams, + ApproveParams, BalanceParams, TransferFromParams, TransferParams, } from "./types/ledger.params"; -import { ApproveParams } from "./types/ledger.params"; import type { IcrcTokenMetadataResponse } from "./types/ledger.responses"; export class IcrcLedgerCanister extends Canister { @@ -130,4 +132,18 @@ export class IcrcLedgerCanister extends Canister { } return response.Ok; }; + + /** + * Returns the token allowance that the `spender` account can transfer from the specified `account`, and the expiration time for that allowance, if any. + * + * Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_allowance + * + * @param {AllowanceParams} params The parameters to call the allowance. + * + * @returns {Allowance} The token allowance. If there is no active approval, the ledger MUST return `{ allowance = 0; expires_at = null }`. + */ + allowance = async (params: AllowanceParams): Promise => { + const { certified, ...rest } = params; + return this.caller({ certified }).icrc2_allowance({ ...rest }); + }; } diff --git a/packages/ledger/src/types/ledger.params.ts b/packages/ledger/src/types/ledger.params.ts index b95633a73..f047f98a1 100644 --- a/packages/ledger/src/types/ledger.params.ts +++ b/packages/ledger/src/types/ledger.params.ts @@ -5,6 +5,7 @@ import type { Timestamp, Tokens, } from "../../candid/icrc1_ledger"; +import { AllowanceArgs } from "../../candid/icrc1_ledger"; import type { IcrcAccount } from "./ledger.responses"; /** @@ -49,8 +50,25 @@ export type TransferFromParams = Omit & { from: Account; }; +/** + * Params for an icrc2_approve. + * + * @param {Account} spender The account of the spender. + * @param {Tokens} amount The Amount of tokens to approve. + * @param {Subaccount?} from_subaccount The subaccount to transfer tokens from. + * @param {Uint8Array?} memo Transfer memo. + * @param {Timestamp?} created_at_time nanoseconds since unix epoc to trigger deduplication and avoid other issues + * @param {Tokens?} fee The fee of the transfer when it's not the default fee. + * @param {bigint?} expected_allowance The optional allowance expected. If the expected_allowance field is set, the ledger MUST ensure that the current allowance for the spender from the caller's account is equal to the given value and return the AllowanceChanged error otherwise. + * @param {bigint?} expires_at When the approval expires. If the field is set, it's greater than the current ledger time. + */ export type ApproveParams = Omit & { expected_allowance?: bigint; expires_at?: bigint; spender: Account; }; + +/** + * Params to get the token allowance that the spender account can transfer from the specified account + */ +export type AllowanceParams = AllowanceArgs & QueryParams; From 0084f23e4780244e691488fe3f9ecae474792255 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 09:26:23 +0200 Subject: [PATCH 07/11] chore: lint --- packages/ledger/src/converters/ledger.converters.ts | 9 ++++++--- packages/ledger/src/types/ledger.params.ts | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/ledger/src/converters/ledger.converters.ts b/packages/ledger/src/converters/ledger.converters.ts index 69c494014..eb4f0bc25 100644 --- a/packages/ledger/src/converters/ledger.converters.ts +++ b/packages/ledger/src/converters/ledger.converters.ts @@ -1,11 +1,14 @@ import { toNullable } from "@dfinity/utils"; -import type { TransferArg, TransferFromArgs } from "../../candid/icrc1_ledger"; -import { ApproveArgs } from "../../candid/icrc1_ledger"; import type { + ApproveArgs, + TransferArg, + TransferFromArgs, +} from "../../candid/icrc1_ledger"; +import type { + ApproveParams, TransferFromParams, TransferParams, } from "../types/ledger.params"; -import { ApproveParams } from "../types/ledger.params"; export const toTransferArg = ({ from_subaccount, diff --git a/packages/ledger/src/types/ledger.params.ts b/packages/ledger/src/types/ledger.params.ts index f047f98a1..5db9c76ea 100644 --- a/packages/ledger/src/types/ledger.params.ts +++ b/packages/ledger/src/types/ledger.params.ts @@ -1,11 +1,11 @@ import type { QueryParams } from "@dfinity/utils"; import type { Account, + AllowanceArgs, Subaccount, Timestamp, Tokens, } from "../../candid/icrc1_ledger"; -import { AllowanceArgs } from "../../candid/icrc1_ledger"; import type { IcrcAccount } from "./ledger.responses"; /** From eb00a83bf8dec6f75a27451ce2c46621bfac5011 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 09:27:14 +0200 Subject: [PATCH 08/11] build: bump ledger size limit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3eb8e3f3c..d7b33bc8d 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ { "name": "@dfinity/ledger", "path": "./packages/ledger/dist/index.js", - "limit": "3 kB", + "limit": "4 kB", "ignore": [ "@dfinity/agent", "@dfinity/candid", From 21d27cb29029967ccc9eab715497c10cc689f60d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:29:30 +0000 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=A4=96=20Documentation=20auto-updat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ledger/README.md | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/ledger/README.md b/packages/ledger/README.md index 04160c79f..0c3219fd5 100644 --- a/packages/ledger/README.md +++ b/packages/ledger/README.md @@ -123,7 +123,7 @@ Parameters: ### :factory: IcrcLedgerCanister -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L25) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L27) #### Methods @@ -135,6 +135,7 @@ Parameters: - [totalTokensSupply](#gear-totaltokenssupply) - [transferFrom](#gear-transferfrom) - [approve](#gear-approve) +- [allowance](#gear-allowance) ##### :gear: create @@ -142,7 +143,7 @@ Parameters: | -------- | ---------------------------------------------------------------------- | | `create` | `(options: IcrcLedgerCanisterOptions<_SERVICE>) => IcrcLedgerCanister` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L26) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L28) ##### :gear: metadata @@ -152,7 +153,7 @@ The token metadata (name, symbol, etc.). | ---------- | ------------------------------------------------------------- | | `metadata` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L40) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L42) ##### :gear: transactionFee @@ -162,7 +163,7 @@ The ledger transaction fees. | ---------------- | ------------------------------------------ | | `transactionFee` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L48) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L50) ##### :gear: balance @@ -176,7 +177,7 @@ Parameters: - `params`: The parameters to get the balance of an account. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L57) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L59) ##### :gear: transfer @@ -190,7 +191,7 @@ Parameters: - `params`: The parameters to transfer tokens. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L70) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L72) ##### :gear: totalTokensSupply @@ -200,7 +201,7 @@ Returns the total supply of tokens. | ------------------- | ------------------------------------------ | | `totalTokensSupply` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L86) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L88) ##### :gear: transferFrom @@ -216,7 +217,7 @@ Parameters: - `params`: The parameters to transfer tokens from to. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L99) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L101) ##### :gear: approve @@ -232,7 +233,23 @@ Parameters: - `params`: The parameters to approve. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L121) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L123) + +##### :gear: allowance + +Returns the token allowance that the `spender` account can transfer from the specified `account`, and the expiration time for that allowance, if any. + +Reference: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_allowance + +| Method | Type | +| ----------- | ------------------------------------------------- | +| `allowance` | `(params: AllowanceParams) => Promise` | + +Parameters: + +- `params`: The parameters to call the allowance. + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger/src/ledger.canister.ts#L145) ### :factory: IcrcIndexCanister From 93818acc2dcbc518243faf8f25bda398be6ed7f6 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 09:29:59 +0200 Subject: [PATCH 10/11] docs: add changes to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 584e1bcc3..eb1f70d6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ## Features +- add support for `icrc2_transfer_from`, `icrc2_approve` and `icrc2_allowance` in `@dfinity/ledger` - update index did definitions in ledger which provides more information in the transactions ## Build From 92e93380f0477dbe96e9ffc58001428c97c3ddeb Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 13 Sep 2023 10:56:34 +0200 Subject: [PATCH 11/11] feat: use types --- packages/ledger/src/ledger.canister.ts | 2 +- packages/ledger/src/types/ledger.params.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ledger/src/ledger.canister.ts b/packages/ledger/src/ledger.canister.ts index 57152f318..3e27f094c 100644 --- a/packages/ledger/src/ledger.canister.ts +++ b/packages/ledger/src/ledger.canister.ts @@ -120,7 +120,7 @@ export class IcrcLedgerCanister extends Canister { * * @throws {IcrcTransferError} If the approval fails. */ - approve = async (params: ApproveParams): Promise => { + approve = async (params: ApproveParams): Promise => { const response = await this.caller({ certified: true }).icrc2_approve( toApproveArgs(params), ); diff --git a/packages/ledger/src/types/ledger.params.ts b/packages/ledger/src/types/ledger.params.ts index 5db9c76ea..095624a83 100644 --- a/packages/ledger/src/types/ledger.params.ts +++ b/packages/ledger/src/types/ledger.params.ts @@ -59,12 +59,12 @@ export type TransferFromParams = Omit & { * @param {Uint8Array?} memo Transfer memo. * @param {Timestamp?} created_at_time nanoseconds since unix epoc to trigger deduplication and avoid other issues * @param {Tokens?} fee The fee of the transfer when it's not the default fee. - * @param {bigint?} expected_allowance The optional allowance expected. If the expected_allowance field is set, the ledger MUST ensure that the current allowance for the spender from the caller's account is equal to the given value and return the AllowanceChanged error otherwise. - * @param {bigint?} expires_at When the approval expires. If the field is set, it's greater than the current ledger time. + * @param {Tokens?} expected_allowance The optional allowance expected. If the expected_allowance field is set, the ledger MUST ensure that the current allowance for the spender from the caller's account is equal to the given value and return the AllowanceChanged error otherwise. + * @param {Timestamp?} expires_at When the approval expires. If the field is set, it's greater than the current ledger time. */ export type ApproveParams = Omit & { - expected_allowance?: bigint; - expires_at?: bigint; + expected_allowance?: Tokens; + expires_at?: Timestamp; spender: Account; };