1- use bigdecimal:: { BigDecimal , ToPrimitive } ;
2- use candid:: { Decode , Encode } ;
1+ use bigdecimal:: BigDecimal ;
32use clap:: Args ;
4- use ic_agent:: AgentError ;
5- use ic_ledger_types:: {
6- AccountIdentifier , Memo , Subaccount , Tokens , TransferArgs , TransferError , TransferResult ,
7- } ;
83use icp:: { agent, context:: GetAgentError , identity, network} ;
9- use icp_canister_interfaces:: {
10- cycles_ledger:: CYCLES_LEDGER_BLOCK_FEE ,
11- cycles_minting_canister:: {
12- CYCLES_MINTING_CANISTER_PRINCIPAL , ConversionRateResponse , MEMO_MINT_CYCLES ,
13- NotifyMintArgs , NotifyMintErr , NotifyMintResponse ,
14- } ,
15- icp_ledger:: { ICP_LEDGER_BLOCK_FEE_E8S , ICP_LEDGER_PRINCIPAL } ,
16- } ;
174
185use icp:: context:: Context ;
196
207use crate :: commands:: args:: TokenCommandArgs ;
8+ use crate :: operations:: token:: mint:: { MintCyclesError , mint_cycles} ;
219
2210#[ derive( Debug , Args ) ]
2311pub ( crate ) struct MintArgs {
@@ -47,38 +35,22 @@ pub(crate) enum CommandError {
4735 #[ error( transparent) ]
4836 Agent ( #[ from] agent:: CreateAgentError ) ,
4937
50- #[ error( "Failed to get identity principal: {message}" ) ]
51- Principal { message : String } ,
52-
53- #[ error( "Failed to talk to {canister} canister: {source}" ) ]
54- CanisterError {
55- canister : String ,
56- source : AgentError ,
57- } ,
58-
59- #[ error( "ICP amount overflow. Specify less tokens." ) ]
60- IcpAmountOverflow ,
61-
62- #[ error( "Failed ICP ledger transfer: {src:?}" ) ]
63- TransferError { src : TransferError } ,
38+ #[ error( transparent) ]
39+ GetAgent ( #[ from] GetAgentError ) ,
6440
65- #[ error( "Insufficient funds: {required} ICP required, {available} ICP available." ) ]
66- InsufficientFunds {
67- required : BigDecimal ,
68- available : BigDecimal ,
69- } ,
41+ #[ error( transparent) ]
42+ MintCycles ( #[ from] MintCyclesError ) ,
7043
7144 #[ error( "No amount specified. Use --icp or --cycles." ) ]
7245 NoAmountSpecified ,
73-
74- #[ error( "Failed to notify mint cycles: {src:?}" ) ]
75- NotifyMintError { src : NotifyMintErr } ,
76-
77- #[ error( transparent) ]
78- GetAgent ( #[ from] GetAgentError ) ,
7946}
8047
8148pub ( crate ) async fn exec ( ctx : & Context , args : & MintArgs ) -> Result < ( ) , CommandError > {
49+ // Validate args
50+ if args. icp . is_none ( ) && args. cycles . is_none ( ) {
51+ return Err ( CommandError :: NoAmountSpecified ) ;
52+ }
53+
8254 let selections = args. token_command_args . selections ( ) ;
8355
8456 // Agent
@@ -90,116 +62,13 @@ pub(crate) async fn exec(ctx: &Context, args: &MintArgs) -> Result<(), CommandEr
9062 )
9163 . await ?;
9264
93- // Prepare deposit
94- let user_principal = agent
95- . get_principal ( )
96- . map_err ( |e| CommandError :: Principal { message : e } ) ?;
97-
98- let icp_e8s_to_deposit = if let Some ( icp_amount) = & args. icp {
99- ( icp_amount * 100_000_000_u64 )
100- . to_u64 ( )
101- . ok_or ( CommandError :: IcpAmountOverflow ) ?
102- } else if let Some ( cycles_amount) = args. cycles {
103- let cmc_response = agent
104- . query (
105- & CYCLES_MINTING_CANISTER_PRINCIPAL ,
106- "get_icp_xdr_conversion_rate" ,
107- )
108- . with_arg ( Encode ! ( & ( ) ) . expect ( "Failed to encode get ICP XDR conversion rate args" ) )
109- . call ( )
110- . await
111- . map_err ( |e| CommandError :: CanisterError {
112- canister : "cmc" . to_string ( ) ,
113- source : e,
114- } ) ?;
115-
116- let cmc_response =
117- Decode ! ( & cmc_response, ConversionRateResponse ) . expect ( "CMC response type changed" ) ;
118- let cycles_per_e8s = cmc_response. data . xdr_permyriad_per_icp as u128 ;
119- let cycles_plus_fees = cycles_amount + CYCLES_LEDGER_BLOCK_FEE ;
120- let e8s_to_deposit = cycles_plus_fees. div_ceil ( cycles_per_e8s) ;
121-
122- e8s_to_deposit
123- . to_u64 ( )
124- . ok_or ( CommandError :: IcpAmountOverflow ) ?
125- } else {
126- return Err ( CommandError :: NoAmountSpecified ) ;
127- } ;
128-
129- let account_id = AccountIdentifier :: new (
130- & CYCLES_MINTING_CANISTER_PRINCIPAL ,
131- & Subaccount :: from ( user_principal) ,
132- ) ;
133- let memo = Memo ( MEMO_MINT_CYCLES ) ;
134- let transfer_args = TransferArgs {
135- memo,
136- amount : Tokens :: from_e8s ( icp_e8s_to_deposit) ,
137- fee : Tokens :: from_e8s ( ICP_LEDGER_BLOCK_FEE_E8S ) ,
138- from_subaccount : None ,
139- to : account_id,
140- created_at_time : None ,
141- } ;
142-
143- let transfer_result = agent
144- . update ( & ICP_LEDGER_PRINCIPAL , "transfer" )
145- . with_arg ( Encode ! ( & transfer_args) . expect ( "Failed to encode transfer args" ) )
146- . call_and_wait ( )
147- . await
148- . map_err ( |e| CommandError :: CanisterError {
149- canister : "ICP ledger" . to_string ( ) ,
150- source : e,
151- } ) ?;
152- let transfer_response =
153- Decode ! ( & transfer_result, TransferResult ) . expect ( "ICP ledger transfer result type changed" ) ;
154- let block_index = match transfer_response {
155- Ok ( block_index) => block_index,
156- Err ( err) => match err {
157- TransferError :: TxDuplicate { duplicate_of } => duplicate_of,
158- TransferError :: InsufficientFunds { balance } => {
159- let required =
160- BigDecimal :: new ( ( icp_e8s_to_deposit + ICP_LEDGER_BLOCK_FEE_E8S ) . into ( ) , 8 ) ;
161- let available = BigDecimal :: new ( balance. e8s ( ) . into ( ) , 8 ) ;
162- return Err ( CommandError :: InsufficientFunds {
163- required,
164- available,
165- } ) ;
166- }
167- err => {
168- return Err ( CommandError :: TransferError { src : err } ) ;
169- }
170- } ,
171- } ;
172-
173- let notify_response = agent
174- . update ( & CYCLES_MINTING_CANISTER_PRINCIPAL , "notify_mint_cycles" )
175- . with_arg (
176- Encode ! ( & NotifyMintArgs {
177- block_index,
178- deposit_memo: None ,
179- to_subaccount: None ,
180- } )
181- . expect ( "Failed to encode notify mint cycles args" ) ,
182- )
183- . call_and_wait ( )
184- . await
185- . map_err ( |e| CommandError :: CanisterError {
186- canister : "cmc" . to_string ( ) ,
187- source : e,
188- } ) ?;
189- let notify_response = Decode ! ( & notify_response, NotifyMintResponse )
190- . expect ( "Notify mint cycles response type changed" ) ;
191- let minted = match notify_response {
192- NotifyMintResponse :: Ok ( ok) => ok,
193- NotifyMintResponse :: Err ( err) => {
194- return Err ( CommandError :: NotifyMintError { src : err } ) ;
195- }
196- } ;
197-
198- // display
199- let deposited = BigDecimal :: new ( ( minted. minted - CYCLES_LEDGER_BLOCK_FEE ) . into ( ) , 12 ) ;
200- let new_balance = BigDecimal :: new ( minted. balance . into ( ) , 12 ) ;
65+ // Execute mint operation
66+ let mint_info = mint_cycles ( & agent, args. icp . as_ref ( ) , args. cycles ) . await ?;
67+
68+ // Display results
20169 let _ = ctx. term . write_line ( & format ! (
202- "Minted {deposited} TCYCLES to your account, new balance: {new_balance} TCYCLES."
70+ "Minted {} TCYCLES to your account, new balance: {} TCYCLES." ,
71+ mint_info. deposited, mint_info. new_balance
20372 ) ) ;
20473
20574 Ok ( ( ) )
0 commit comments