1use std::{
6 future::{Future, IntoFuture},
7 ops::Deref,
8};
9
10use crate::{
11 call::{AsyncCall, CallFuture, SyncCall},
12 canister::Argument,
13 interfaces::management_canister::{
14 attributes::{ComputeAllocation, FreezingThreshold, MemoryAllocation},
15 builders::CanisterSettings,
16 },
17 Canister,
18};
19use async_trait::async_trait;
20use candid::{decode_args, utils::ArgumentDecoder, CandidType, Deserialize, Nat};
21use ic_agent::{agent::CallResponse, export::Principal, Agent, AgentError};
22use once_cell::sync::Lazy;
23use semver::{Version, VersionReq};
24
25const REPLICA_ERROR_NO_SUCH_QUERY_METHOD: &str = "has no query method 'wallet_api_version'";
26const IC_REF_ERROR_NO_SUCH_QUERY_METHOD: &str = "query method does not exist";
27
28#[derive(Debug)]
30pub struct CallForwarder<'agent, 'canister, Out>
31where
32 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
33{
34 wallet: &'canister WalletCanister<'agent>,
35 destination: Principal,
36 method_name: String,
37 amount: u128,
38 u128: bool,
39 arg: Argument,
40 phantom_out: std::marker::PhantomData<Out>,
41}
42
43#[derive(Debug, Clone, CandidType, Deserialize)]
46pub struct CanisterSettingsV1 {
47 pub controller: Option<Principal>,
49 pub compute_allocation: Option<Nat>,
51 pub memory_allocation: Option<Nat>,
53 pub freezing_threshold: Option<Nat>,
55}
56
57impl<'agent: 'canister, 'canister, Out> CallForwarder<'agent, 'canister, Out>
58where
59 Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
60{
61 pub fn with_arg<Argument>(mut self, arg: Argument) -> Self
63 where
64 Argument: CandidType + Sync + Send,
65 {
66 self.arg.set_idl_arg(arg);
67 self
68 }
69 pub fn with_args(mut self, tuple: impl candid::utils::ArgumentEncoder) -> Self {
71 if self.arg.0.is_some() {
72 panic!("argument is being set more than once");
73 }
74 self.arg = Argument::from_candid(tuple);
75 self
76 }
77
78 pub fn with_arg_raw(mut self, arg: Vec<u8>) -> Self {
80 self.arg.set_raw_arg(arg);
81 self
82 }
83
84 pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = Out>, AgentError> {
86 #[derive(CandidType, Deserialize)]
87 struct In<TCycles> {
88 canister: Principal,
89 method_name: String,
90 #[serde(with = "serde_bytes")]
91 args: Vec<u8>,
92 cycles: TCycles,
93 }
94 Ok(if self.u128 {
95 self.wallet.update("wallet_call128").with_arg(In {
96 canister: self.destination,
97 method_name: self.method_name,
98 args: self.arg.serialize()?,
99 cycles: self.amount,
100 })
101 } else {
102 self.wallet.update("wallet_call").with_arg(In {
103 canister: self.destination,
104 method_name: self.method_name,
105 args: self.arg.serialize()?,
106 cycles: u64::try_from(self.amount).map_err(|_| {
107 AgentError::WalletUpgradeRequired(
108 "The installed wallet does not support cycle counts >2^64-1".to_string(),
109 )
110 })?,
111 })
112 }
113 .build()
114 .and_then(|(result,): (Result<CallResult, String>,)| async move {
115 let result = result.map_err(AgentError::WalletCallFailed)?;
116 decode_args::<Out>(result.r#return.as_slice())
117 .map_err(|e| AgentError::CandidError(Box::new(e)))
118 }))
119 }
120
121 pub fn call(self) -> impl Future<Output = Result<CallResponse<Out>, AgentError>> + 'agent {
123 let call = self.build();
124 async { call?.call().await }
125 }
126
127 pub fn call_and_wait(self) -> impl Future<Output = Result<Out, AgentError>> + 'agent {
129 let call = self.build();
130 async { call?.call_and_wait().await }
131 }
132}
133
134#[cfg_attr(target_family = "wasm", async_trait(?Send))]
135#[cfg_attr(not(target_family = "wasm"), async_trait)]
136impl<'agent: 'canister, 'canister, Out> AsyncCall for CallForwarder<'agent, 'canister, Out>
137where
138 Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
139{
140 type Value = Out;
141
142 async fn call(self) -> Result<CallResponse<Out>, AgentError> {
143 self.call().await
144 }
145
146 async fn call_and_wait(self) -> Result<Out, AgentError> {
147 self.call_and_wait().await
148 }
149}
150
151impl<'agent: 'canister, 'canister, Out> IntoFuture for CallForwarder<'agent, 'canister, Out>
152where
153 Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
154{
155 type IntoFuture = CallFuture<'agent, Out>;
156 type Output = Result<Out, AgentError>;
157 fn into_future(self) -> Self::IntoFuture {
158 Box::pin(self.call_and_wait())
159 }
160}
161
162#[derive(Debug, Clone)]
165pub struct WalletCanister<'agent> {
166 canister: Canister<'agent>,
167 version: Version,
168}
169
170impl<'agent> Deref for WalletCanister<'agent> {
171 type Target = Canister<'agent>;
172 fn deref(&self) -> &Self::Target {
173 &self.canister
174 }
175}
176
177#[derive(CandidType, Debug, Deserialize)]
179pub enum EventKind<TCycles = u128> {
180 CyclesSent {
182 to: Principal,
184 amount: TCycles,
186 refund: TCycles,
188 },
189 CyclesReceived {
191 from: Principal,
193 amount: TCycles,
195 memo: Option<String>,
197 },
198 AddressAdded {
200 id: Principal,
202 name: Option<String>,
204 role: Role,
206 },
207 AddressRemoved {
209 id: Principal,
211 },
212 CanisterCreated {
214 canister: Principal,
216 cycles: TCycles,
218 },
219 CanisterCalled {
221 canister: Principal,
223 method_name: String,
225 cycles: TCycles,
227 },
228}
229
230impl From<EventKind<u64>> for EventKind {
231 fn from(kind: EventKind<u64>) -> Self {
232 use EventKind::*;
233 match kind {
234 AddressAdded { id, name, role } => AddressAdded { id, name, role },
235 AddressRemoved { id } => AddressRemoved { id },
236 CanisterCalled {
237 canister,
238 cycles,
239 method_name,
240 } => CanisterCalled {
241 canister,
242 cycles: cycles.into(),
243 method_name,
244 },
245 CanisterCreated { canister, cycles } => CanisterCreated {
246 canister,
247 cycles: cycles.into(),
248 },
249 CyclesReceived { amount, from, memo } => CyclesReceived {
250 amount: amount.into(),
251 from,
252 memo,
253 },
254 CyclesSent { amount, refund, to } => CyclesSent {
255 amount: amount.into(),
256 refund: refund.into(),
257 to,
258 },
259 }
260 }
261}
262
263#[derive(CandidType, Debug, Deserialize)]
265pub struct Event<TCycles = u128> {
266 pub id: u32,
268 pub timestamp: u64,
270 pub kind: EventKind<TCycles>,
272}
273
274impl From<Event<u64>> for Event {
275 fn from(
276 Event {
277 id,
278 timestamp,
279 kind,
280 }: Event<u64>,
281 ) -> Self {
282 Self {
283 id,
284 timestamp,
285 kind: kind.into(),
286 }
287 }
288}
289
290#[derive(CandidType, Debug, Deserialize)]
292pub enum Role {
293 Contact,
295 Custodian,
297 Controller,
299}
300
301#[derive(CandidType, Debug, Deserialize)]
303pub enum Kind {
304 Unknown,
306 User,
308 Canister,
310}
311
312#[derive(CandidType, Debug, Deserialize)]
314pub struct AddressEntry {
315 pub id: Principal,
317 pub name: Option<String>,
319 pub kind: Kind,
321 pub role: Role,
323}
324
325#[derive(CandidType, Debug, Deserialize)]
327pub struct ManagedCanisterInfo {
328 pub id: Principal,
330 pub name: Option<String>,
332 pub created_at: u64,
334}
335
336#[derive(CandidType, Debug, Deserialize)]
338pub enum ManagedCanisterEventKind<TCycles = u128> {
339 CyclesSent {
341 amount: TCycles,
343 refund: TCycles,
345 },
346 Called {
348 method_name: String,
350 cycles: TCycles,
352 },
353 Created {
355 cycles: TCycles,
357 },
358}
359
360impl From<ManagedCanisterEventKind<u64>> for ManagedCanisterEventKind {
361 fn from(event: ManagedCanisterEventKind<u64>) -> Self {
362 use ManagedCanisterEventKind::*;
363 match event {
364 Called {
365 cycles,
366 method_name,
367 } => Called {
368 cycles: cycles.into(),
369 method_name,
370 },
371 Created { cycles } => Created {
372 cycles: cycles.into(),
373 },
374 CyclesSent { amount, refund } => CyclesSent {
375 amount: amount.into(),
376 refund: refund.into(),
377 },
378 }
379 }
380}
381
382#[derive(CandidType, Deserialize, Debug)]
384pub struct ManagedCanisterEvent<TCycles = u128> {
385 pub id: u32,
387 pub timestamp: u64,
389 pub kind: ManagedCanisterEventKind<TCycles>,
391}
392
393impl From<ManagedCanisterEvent<u64>> for ManagedCanisterEvent {
394 fn from(
395 ManagedCanisterEvent {
396 id,
397 timestamp,
398 kind,
399 }: ManagedCanisterEvent<u64>,
400 ) -> Self {
401 Self {
402 id,
403 timestamp,
404 kind: kind.into(),
405 }
406 }
407}
408
409#[derive(Debug, Copy, Clone, CandidType, Deserialize)]
411pub struct BalanceResult<TCycles = u128> {
412 pub amount: TCycles,
414}
415
416#[derive(Debug, Copy, Clone, CandidType, Deserialize)]
418pub struct CreateResult {
419 pub canister_id: Principal,
421}
422
423#[derive(Debug, Clone, CandidType, Deserialize)]
425pub struct CallResult {
426 #[serde(with = "serde_bytes")]
428 pub r#return: Vec<u8>,
429}
430
431impl<'agent> WalletCanister<'agent> {
432 pub async fn create(
434 agent: &'agent Agent,
435 canister_id: Principal,
436 ) -> Result<WalletCanister<'agent>, AgentError> {
437 let canister = Canister::builder()
438 .with_agent(agent)
439 .with_canister_id(canister_id)
440 .build()
441 .unwrap();
442 Self::from_canister(canister).await
443 }
444
445 pub async fn from_canister(
447 canister: Canister<'agent>,
448 ) -> Result<WalletCanister<'agent>, AgentError> {
449 static DEFAULT_VERSION: Lazy<Version> = Lazy::new(|| Version::parse("0.1.0").unwrap());
450 let version: Result<(String,), _> =
451 canister.query("wallet_api_version").build().call().await;
452 let version = match version {
453 Err(AgentError::UncertifiedReject {
454 reject: replica_error,
455 ..
456 }) if replica_error
457 .reject_message
458 .contains(REPLICA_ERROR_NO_SUCH_QUERY_METHOD)
459 || replica_error
460 .reject_message
461 .contains(IC_REF_ERROR_NO_SUCH_QUERY_METHOD) =>
462 {
463 DEFAULT_VERSION.clone()
464 }
465 version => Version::parse(&version?.0).unwrap_or_else(|_| DEFAULT_VERSION.clone()),
466 };
467 Ok(Self { canister, version })
468 }
469
470 pub fn from_canister_with_version(canister: Canister<'agent>, version: Version) -> Self {
474 Self { canister, version }
475 }
476}
477
478impl<'agent> WalletCanister<'agent> {
479 pub fn fetch_wallet_api_version(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
481 self.query("wallet_api_version").build()
482 }
483
484 pub fn wallet_api_version(&self) -> &Version {
486 &self.version
487 }
488
489 pub fn name(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
491 self.query("name").build()
492 }
493
494 pub fn set_name(&self, name: String) -> impl 'agent + AsyncCall<Value = ()> {
496 self.update("set_name").with_arg(name).build()
497 }
498
499 pub fn get_controllers(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
501 self.query("get_controllers").build()
502 }
503
504 pub fn add_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
506 self.update("add_controller").with_arg(principal).build()
507 }
508
509 pub fn remove_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
511 self.update("remove_controller").with_arg(principal).build()
512 }
513
514 pub fn get_custodians(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
516 self.query("get_custodians").build()
517 }
518
519 pub fn authorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
521 self.update("authorize").with_arg(custodian).build()
522 }
523
524 pub fn deauthorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
526 self.update("deauthorize").with_arg(custodian).build()
527 }
528
529 pub fn wallet_balance64(&self) -> impl 'agent + SyncCall<Value = (BalanceResult<u64>,)> {
531 self.query("wallet_balance").build()
532 }
533
534 pub fn wallet_balance128(&self) -> impl 'agent + SyncCall<Value = (BalanceResult,)> {
536 self.query("wallet_balance128").build()
537 }
538
539 pub async fn wallet_balance(&self) -> Result<BalanceResult, AgentError> {
541 if self.version_supports_u128_cycles() {
542 self.wallet_balance128().call().await.map(|(r,)| r)
543 } else {
544 self.wallet_balance64()
545 .call()
546 .await
547 .map(|(r,)| BalanceResult {
548 amount: r.amount.into(),
549 })
550 }
551 }
552
553 pub fn wallet_send64(
555 &self,
556 destination: Principal,
557 amount: u64,
558 ) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
559 #[derive(CandidType)]
560 struct In {
561 canister: Principal,
562 amount: u64,
563 }
564
565 self.update("wallet_send")
566 .with_arg(In {
567 canister: destination,
568 amount,
569 })
570 .build()
571 }
572
573 pub fn wallet_send128<'canister: 'agent>(
575 &'canister self,
576 destination: Principal,
577 amount: u128,
578 ) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
579 #[derive(CandidType)]
580 struct In {
581 canister: Principal,
582 amount: u128,
583 }
584
585 self.update("wallet_send128")
586 .with_arg(In {
587 canister: destination,
588 amount,
589 })
590 .build()
591 }
592
593 pub async fn wallet_send(
595 &self,
596 destination: Principal,
597 amount: u128,
598 ) -> Result<(), AgentError> {
599 if self.version_supports_u128_cycles() {
600 self.wallet_send128(destination, amount)
601 .call_and_wait()
602 .await?
603 } else {
604 let amount = u64::try_from(amount).map_err(|_| {
605 AgentError::WalletUpgradeRequired(
606 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
607 )
608 })?;
609 self.wallet_send64(destination, amount)
610 .call_and_wait()
611 .await?
612 }
613 .0
614 .map_err(AgentError::WalletError)
615 }
616
617 pub fn wallet_receive(&self, memo: Option<String>) -> impl 'agent + AsyncCall<Value = ((),)> {
619 #[derive(CandidType)]
620 struct In {
621 memo: Option<String>,
622 }
623 self.update("wallet_receive")
624 .with_arg(memo.map(|memo| In { memo: Some(memo) }))
625 .build()
626 }
627
628 pub fn wallet_create_canister64_v1(
630 &self,
631 cycles: u64,
632 controller: Option<Principal>,
633 compute_allocation: Option<ComputeAllocation>,
634 memory_allocation: Option<MemoryAllocation>,
635 freezing_threshold: Option<FreezingThreshold>,
636 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
637 #[derive(CandidType)]
638 struct In {
639 cycles: u64,
640 settings: CanisterSettingsV1,
641 }
642
643 let settings = CanisterSettingsV1 {
644 controller,
645 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
646 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
647 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
648 };
649
650 self.update("wallet_create_canister")
651 .with_arg(In { cycles, settings })
652 .build()
653 .map(|result: (Result<CreateResult, String>,)| (result.0,))
654 }
655
656 pub fn wallet_create_canister64_v2(
658 &self,
659 cycles: u64,
660 controllers: Option<Vec<Principal>>,
661 compute_allocation: Option<ComputeAllocation>,
662 memory_allocation: Option<MemoryAllocation>,
663 freezing_threshold: Option<FreezingThreshold>,
664 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
665 #[derive(CandidType)]
666 struct In {
667 cycles: u64,
668 settings: CanisterSettings,
669 }
670
671 let settings = CanisterSettings {
672 controllers,
673 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
674 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
675 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
676 reserved_cycles_limit: None,
677 wasm_memory_limit: None,
678 wasm_memory_threshold: None,
679 log_visibility: None,
680 log_memory_limit: None,
681 environment_variables: None,
682 snapshot_visibility: None,
683 };
684
685 self.update("wallet_create_canister")
686 .with_arg(In { cycles, settings })
687 .build()
688 .map(|result: (Result<CreateResult, String>,)| (result.0,))
689 }
690
691 pub fn wallet_create_canister128(
693 &self,
694 cycles: u128,
695 controllers: Option<Vec<Principal>>,
696 compute_allocation: Option<ComputeAllocation>,
697 memory_allocation: Option<MemoryAllocation>,
698 freezing_threshold: Option<FreezingThreshold>,
699 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
700 #[derive(CandidType)]
701 struct In {
702 cycles: u128,
703 settings: CanisterSettings,
704 }
705
706 let settings = CanisterSettings {
707 controllers,
708 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
709 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
710 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
711 reserved_cycles_limit: None,
712 wasm_memory_limit: None,
713 wasm_memory_threshold: None,
714 log_visibility: None,
715 log_memory_limit: None,
716 environment_variables: None,
717 snapshot_visibility: None,
718 };
719
720 self.update("wallet_create_canister128")
721 .with_arg(In { cycles, settings })
722 .build()
723 .map(|result: (Result<CreateResult, String>,)| (result.0,))
724 }
725
726 pub async fn wallet_create_canister(
736 &self,
737 cycles: u128,
738 controllers: Option<Vec<Principal>>,
739 compute_allocation: Option<ComputeAllocation>,
740 memory_allocation: Option<MemoryAllocation>,
741 freezing_threshold: Option<FreezingThreshold>,
742 ) -> Result<CreateResult, AgentError> {
743 if self.version_supports_u128_cycles() {
744 self.wallet_create_canister128(
745 cycles,
746 controllers,
747 compute_allocation,
748 memory_allocation,
749 freezing_threshold,
750 )
751 .call_and_wait()
752 .await?
753 } else {
754 let cycles = u64::try_from(cycles).map_err(|_| {
755 AgentError::WalletUpgradeRequired(
756 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
757 )
758 })?;
759 if self.version_supports_multiple_controllers() {
760 self.wallet_create_canister64_v2(
761 cycles,
762 controllers,
763 compute_allocation,
764 memory_allocation,
765 freezing_threshold,
766 )
767 .call_and_wait()
768 .await?
769 } else {
770 let controller: Option<Principal> = match &controllers {
771 Some(c) if c.len() == 1 => {
772 let first: Option<&Principal> = c.first();
773 let first: Principal = *first.unwrap();
774 Ok(Some(first))
775 }
776 Some(_) => Err(AgentError::WalletUpgradeRequired(
777 "The installed wallet does not support multiple controllers.".to_string(),
778 )),
779 None => Ok(None),
780 }?;
781 self.wallet_create_canister64_v1(
782 cycles,
783 controller,
784 compute_allocation,
785 memory_allocation,
786 freezing_threshold,
787 )
788 .call_and_wait()
789 .await?
790 }
791 }
792 .0
793 .map_err(AgentError::WalletError)
794 }
795
796 pub fn wallet_create_wallet64_v1(
798 &self,
799 cycles: u64,
800 controller: Option<Principal>,
801 compute_allocation: Option<ComputeAllocation>,
802 memory_allocation: Option<MemoryAllocation>,
803 freezing_threshold: Option<FreezingThreshold>,
804 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
805 #[derive(CandidType)]
806 struct In {
807 cycles: u64,
808 settings: CanisterSettingsV1,
809 }
810
811 let settings = CanisterSettingsV1 {
812 controller,
813 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
814 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
815 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
816 };
817
818 self.update("wallet_create_wallet")
819 .with_arg(In { cycles, settings })
820 .build()
821 .map(|result: (Result<CreateResult, String>,)| (result.0,))
822 }
823
824 pub fn wallet_create_wallet64_v2(
826 &self,
827 cycles: u64,
828 controllers: Option<Vec<Principal>>,
829 compute_allocation: Option<ComputeAllocation>,
830 memory_allocation: Option<MemoryAllocation>,
831 freezing_threshold: Option<FreezingThreshold>,
832 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
833 #[derive(CandidType)]
834 struct In {
835 cycles: u64,
836 settings: CanisterSettings,
837 }
838
839 let settings = CanisterSettings {
840 controllers,
841 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
842 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
843 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
844 reserved_cycles_limit: None,
845 wasm_memory_limit: None,
846 wasm_memory_threshold: None,
847 log_visibility: None,
848 log_memory_limit: None,
849 environment_variables: None,
850 snapshot_visibility: None,
851 };
852
853 self.update("wallet_create_wallet")
854 .with_arg(In { cycles, settings })
855 .build()
856 .map(|result: (Result<CreateResult, String>,)| (result.0,))
857 }
858
859 pub fn wallet_create_wallet128(
861 &self,
862 cycles: u128,
863 controllers: Option<Vec<Principal>>,
864 compute_allocation: Option<ComputeAllocation>,
865 memory_allocation: Option<MemoryAllocation>,
866 freezing_threshold: Option<FreezingThreshold>,
867 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
868 #[derive(CandidType)]
869 struct In {
870 cycles: u128,
871 settings: CanisterSettings,
872 }
873
874 let settings = CanisterSettings {
875 controllers,
876 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
877 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
878 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
879 reserved_cycles_limit: None,
880 wasm_memory_limit: None,
881 wasm_memory_threshold: None,
882 log_visibility: None,
883 log_memory_limit: None,
884 environment_variables: None,
885 snapshot_visibility: None,
886 };
887
888 self.update("wallet_create_wallet128")
889 .with_arg(In { cycles, settings })
890 .build()
891 .map(|result: (Result<CreateResult, String>,)| (result.0,))
892 }
893
894 pub async fn wallet_create_wallet(
896 &self,
897 cycles: u128,
898 controllers: Option<Vec<Principal>>,
899 compute_allocation: Option<ComputeAllocation>,
900 memory_allocation: Option<MemoryAllocation>,
901 freezing_threshold: Option<FreezingThreshold>,
902 ) -> Result<CreateResult, AgentError> {
903 if self.version_supports_u128_cycles() {
904 self.wallet_create_wallet128(
905 cycles,
906 controllers,
907 compute_allocation,
908 memory_allocation,
909 freezing_threshold,
910 )
911 .call_and_wait()
912 .await?
913 } else {
914 let cycles = u64::try_from(cycles).map_err(|_| {
915 AgentError::WalletUpgradeRequired(
916 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
917 )
918 })?;
919 if self.version_supports_multiple_controllers() {
920 self.wallet_create_wallet64_v2(
921 cycles,
922 controllers,
923 compute_allocation,
924 memory_allocation,
925 freezing_threshold,
926 )
927 .call_and_wait()
928 .await?
929 } else {
930 let controller: Option<Principal> = match &controllers {
931 Some(c) if c.len() == 1 => Ok(Some(c[0])),
932 Some(_) => Err(AgentError::WalletUpgradeRequired(
933 "The installed wallet does not support multiple controllers.".to_string(),
934 )),
935 None => Ok(None),
936 }?;
937 self.wallet_create_wallet64_v1(
938 cycles,
939 controller,
940 compute_allocation,
941 memory_allocation,
942 freezing_threshold,
943 )
944 .call_and_wait()
945 .await?
946 }
947 }
948 .0
949 .map_err(AgentError::WalletError)
950 }
951
952 pub fn wallet_store_wallet_wasm(
955 &self,
956 wasm_module: Vec<u8>,
957 ) -> impl 'agent + AsyncCall<Value = ()> {
958 #[derive(CandidType, Deserialize)]
959 struct In {
960 #[serde(with = "serde_bytes")]
961 wasm_module: Vec<u8>,
962 }
963 self.update("wallet_store_wallet_wasm")
964 .with_arg(In { wasm_module })
965 .build()
966 }
967
968 pub fn add_address(&self, address: AddressEntry) -> impl 'agent + AsyncCall<Value = ()> {
970 self.update("add_address").with_arg(address).build()
971 }
972
973 pub fn list_addresses(&self) -> impl 'agent + SyncCall<Value = (Vec<AddressEntry>,)> {
975 self.query("list_addresses").build()
976 }
977
978 pub fn remove_address(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
980 self.update("remove_address").with_arg(principal).build()
981 }
982
983 pub fn get_events64(
985 &self,
986 from: Option<u32>,
987 to: Option<u32>,
988 ) -> impl 'agent + SyncCall<Value = (Vec<Event<u64>>,)> {
989 #[derive(CandidType)]
990 struct In {
991 from: Option<u32>,
992 to: Option<u32>,
993 }
994
995 let arg = if from.is_none() && to.is_none() {
996 None
997 } else {
998 Some(In { from, to })
999 };
1000
1001 self.query("get_events").with_arg(arg).build()
1002 }
1003
1004 pub fn get_events128(
1006 &self,
1007 from: Option<u32>,
1008 to: Option<u32>,
1009 ) -> impl 'agent + SyncCall<Value = (Vec<Event>,)> {
1010 #[derive(CandidType)]
1011 struct In {
1012 from: Option<u32>,
1013 to: Option<u32>,
1014 }
1015 let arg = if from.is_none() && to.is_none() {
1016 None
1017 } else {
1018 Some(In { from, to })
1019 };
1020 self.query("get_events128").with_arg(arg).build()
1021 }
1022
1023 pub async fn get_events(
1025 &self,
1026 from: Option<u32>,
1027 to: Option<u32>,
1028 ) -> Result<Vec<Event>, AgentError> {
1029 if self.version_supports_u128_cycles() {
1030 self.get_events128(from, to)
1031 .call()
1032 .await
1033 .map(|(events,)| events)
1034 } else {
1035 self.get_events64(from, to)
1036 .call()
1037 .await
1038 .map(|(events,)| events.into_iter().map(Event::into).collect())
1039 }
1040 }
1041
1042 pub fn call64<'canister, Out, M: Into<String>>(
1045 &'canister self,
1046 destination: Principal,
1047 method_name: M,
1048 arg: Argument,
1049 amount: u64,
1050 ) -> CallForwarder<'agent, 'canister, Out>
1051 where
1052 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1053 {
1054 CallForwarder {
1055 wallet: self,
1056 destination,
1057 method_name: method_name.into(),
1058 amount: amount.into(),
1059 arg,
1060 phantom_out: std::marker::PhantomData,
1061 u128: false,
1062 }
1063 }
1064
1065 pub fn call128<'canister, Out, M: Into<String>>(
1068 &'canister self,
1069 destination: Principal,
1070 method_name: M,
1071 arg: Argument,
1072 amount: u128,
1073 ) -> CallForwarder<'agent, 'canister, Out>
1074 where
1075 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1076 {
1077 CallForwarder {
1078 wallet: self,
1079 destination,
1080 method_name: method_name.into(),
1081 amount,
1082 arg,
1083 phantom_out: std::marker::PhantomData,
1084 u128: true,
1085 }
1086 }
1087
1088 pub fn call<'canister, Out, M: Into<String>>(
1091 &'canister self,
1092 destination: Principal,
1093 method_name: M,
1094 arg: Argument,
1095 amount: u128,
1096 ) -> CallForwarder<'agent, 'canister, Out>
1097 where
1098 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1099 {
1100 CallForwarder {
1101 wallet: self,
1102 destination,
1103 method_name: method_name.into(),
1104 amount,
1105 arg,
1106 phantom_out: std::marker::PhantomData,
1107 u128: self.version_supports_u128_cycles(),
1108 }
1109 }
1110
1111 pub fn list_managed_canisters(
1113 &self,
1114 from: Option<u32>,
1115 to: Option<u32>,
1116 ) -> impl 'agent + SyncCall<Value = (Vec<ManagedCanisterInfo>, u32)> {
1117 #[derive(CandidType)]
1118 struct In {
1119 from: Option<u32>,
1120 to: Option<u32>,
1121 }
1122 self.query("list_managed_canisters")
1123 .with_arg((In { from, to },))
1124 .build()
1125 }
1126
1127 pub fn get_managed_canister_events64(
1129 &self,
1130 canister: Principal,
1131 from: Option<u32>,
1132 to: Option<u32>,
1133 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent<u64>>>,)> {
1134 #[derive(CandidType)]
1135 struct In {
1136 canister: Principal,
1137 from: Option<u32>,
1138 to: Option<u32>,
1139 }
1140 self.query("get_managed_canister_events")
1141 .with_arg((In { canister, from, to },))
1142 .build()
1143 }
1144
1145 pub fn get_managed_canister_events128(
1147 &self,
1148 canister: Principal,
1149 from: Option<u32>,
1150 to: Option<u32>,
1151 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent>>,)> {
1152 #[derive(CandidType)]
1153 struct In {
1154 canister: Principal,
1155 from: Option<u32>,
1156 to: Option<u32>,
1157 }
1158 self.query("get_managed_canister_events128")
1159 .with_arg((In { canister, from, to },))
1160 .build()
1161 }
1162
1163 pub async fn get_managed_canister_events(
1165 &self,
1166 canister: Principal,
1167 from: Option<u32>,
1168 to: Option<u32>,
1169 ) -> Result<Option<Vec<ManagedCanisterEvent>>, AgentError> {
1170 if self.version_supports_u128_cycles() {
1171 self.get_managed_canister_events128(canister, from, to)
1172 .call()
1173 .await
1174 .map(|(events,)| events)
1175 } else {
1176 self.get_managed_canister_events64(canister, from, to)
1177 .call()
1178 .await
1179 .map(|(events,)| {
1180 events
1181 .map(|events| events.into_iter().map(ManagedCanisterEvent::into).collect())
1182 })
1183 }
1184 }
1185
1186 pub fn version_supports_multiple_controllers(&self) -> bool {
1188 static CONTROLLERS: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.2.0").unwrap());
1189 CONTROLLERS.matches(&self.version)
1190 }
1191
1192 pub fn version_supports_u128_cycles(&self) -> bool {
1194 static U128_CYCLES: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.3.0").unwrap());
1195 U128_CYCLES.matches(&self.version)
1196 }
1197}