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(replica_error))
454 if replica_error
455 .reject_message
456 .contains(REPLICA_ERROR_NO_SUCH_QUERY_METHOD)
457 || replica_error
458 .reject_message
459 .contains(IC_REF_ERROR_NO_SUCH_QUERY_METHOD) =>
460 {
461 DEFAULT_VERSION.clone()
462 }
463 version => Version::parse(&version?.0).unwrap_or_else(|_| DEFAULT_VERSION.clone()),
464 };
465 Ok(Self { canister, version })
466 }
467
468 pub fn from_canister_with_version(canister: Canister<'agent>, version: Version) -> Self {
472 Self { canister, version }
473 }
474}
475
476impl<'agent> WalletCanister<'agent> {
477 pub fn fetch_wallet_api_version(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
479 self.query("wallet_api_version").build()
480 }
481
482 pub fn wallet_api_version(&self) -> &Version {
484 &self.version
485 }
486
487 pub fn name(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
489 self.query("name").build()
490 }
491
492 pub fn set_name(&self, name: String) -> impl 'agent + AsyncCall<Value = ()> {
494 self.update("set_name").with_arg(name).build()
495 }
496
497 pub fn get_controllers(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
499 self.query("get_controllers").build()
500 }
501
502 pub fn add_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
504 self.update("add_controller").with_arg(principal).build()
505 }
506
507 pub fn remove_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
509 self.update("remove_controller").with_arg(principal).build()
510 }
511
512 pub fn get_custodians(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
514 self.query("get_custodians").build()
515 }
516
517 pub fn authorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
519 self.update("authorize").with_arg(custodian).build()
520 }
521
522 pub fn deauthorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
524 self.update("deauthorize").with_arg(custodian).build()
525 }
526
527 pub fn wallet_balance64(&self) -> impl 'agent + SyncCall<Value = (BalanceResult<u64>,)> {
529 self.query("wallet_balance").build()
530 }
531
532 pub fn wallet_balance128(&self) -> impl 'agent + SyncCall<Value = (BalanceResult,)> {
534 self.query("wallet_balance128").build()
535 }
536
537 pub async fn wallet_balance(&self) -> Result<BalanceResult, AgentError> {
539 if self.version_supports_u128_cycles() {
540 self.wallet_balance128().call().await.map(|(r,)| r)
541 } else {
542 self.wallet_balance64()
543 .call()
544 .await
545 .map(|(r,)| BalanceResult {
546 amount: r.amount.into(),
547 })
548 }
549 }
550
551 pub fn wallet_send64(
553 &self,
554 destination: Principal,
555 amount: u64,
556 ) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
557 #[derive(CandidType)]
558 struct In {
559 canister: Principal,
560 amount: u64,
561 }
562
563 self.update("wallet_send")
564 .with_arg(In {
565 canister: destination,
566 amount,
567 })
568 .build()
569 }
570
571 pub fn wallet_send128<'canister: 'agent>(
573 &'canister self,
574 destination: Principal,
575 amount: u128,
576 ) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
577 #[derive(CandidType)]
578 struct In {
579 canister: Principal,
580 amount: u128,
581 }
582
583 self.update("wallet_send128")
584 .with_arg(In {
585 canister: destination,
586 amount,
587 })
588 .build()
589 }
590
591 pub async fn wallet_send(
593 &self,
594 destination: Principal,
595 amount: u128,
596 ) -> Result<(), AgentError> {
597 if self.version_supports_u128_cycles() {
598 self.wallet_send128(destination, amount)
599 .call_and_wait()
600 .await?
601 } else {
602 let amount = u64::try_from(amount).map_err(|_| {
603 AgentError::WalletUpgradeRequired(
604 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
605 )
606 })?;
607 self.wallet_send64(destination, amount)
608 .call_and_wait()
609 .await?
610 }
611 .0
612 .map_err(AgentError::WalletError)
613 }
614
615 pub fn wallet_receive(&self, memo: Option<String>) -> impl 'agent + AsyncCall<Value = ((),)> {
617 #[derive(CandidType)]
618 struct In {
619 memo: Option<String>,
620 }
621 self.update("wallet_receive")
622 .with_arg(memo.map(|memo| In { memo: Some(memo) }))
623 .build()
624 }
625
626 pub fn wallet_create_canister64_v1(
628 &self,
629 cycles: u64,
630 controller: Option<Principal>,
631 compute_allocation: Option<ComputeAllocation>,
632 memory_allocation: Option<MemoryAllocation>,
633 freezing_threshold: Option<FreezingThreshold>,
634 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
635 #[derive(CandidType)]
636 struct In {
637 cycles: u64,
638 settings: CanisterSettingsV1,
639 }
640
641 let settings = CanisterSettingsV1 {
642 controller,
643 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
644 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
645 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
646 };
647
648 self.update("wallet_create_canister")
649 .with_arg(In { cycles, settings })
650 .build()
651 .map(|result: (Result<CreateResult, String>,)| (result.0,))
652 }
653
654 pub fn wallet_create_canister64_v2(
656 &self,
657 cycles: u64,
658 controllers: Option<Vec<Principal>>,
659 compute_allocation: Option<ComputeAllocation>,
660 memory_allocation: Option<MemoryAllocation>,
661 freezing_threshold: Option<FreezingThreshold>,
662 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
663 #[derive(CandidType)]
664 struct In {
665 cycles: u64,
666 settings: CanisterSettings,
667 }
668
669 let settings = CanisterSettings {
670 controllers,
671 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
672 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
673 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
674 reserved_cycles_limit: None,
675 wasm_memory_limit: None,
676 wasm_memory_threshold: None,
677 log_visibility: None,
678 };
679
680 self.update("wallet_create_canister")
681 .with_arg(In { cycles, settings })
682 .build()
683 .map(|result: (Result<CreateResult, String>,)| (result.0,))
684 }
685
686 pub fn wallet_create_canister128(
688 &self,
689 cycles: u128,
690 controllers: Option<Vec<Principal>>,
691 compute_allocation: Option<ComputeAllocation>,
692 memory_allocation: Option<MemoryAllocation>,
693 freezing_threshold: Option<FreezingThreshold>,
694 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
695 #[derive(CandidType)]
696 struct In {
697 cycles: u128,
698 settings: CanisterSettings,
699 }
700
701 let settings = CanisterSettings {
702 controllers,
703 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
704 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
705 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
706 reserved_cycles_limit: None,
707 wasm_memory_limit: None,
708 wasm_memory_threshold: None,
709 log_visibility: None,
710 };
711
712 self.update("wallet_create_canister128")
713 .with_arg(In { cycles, settings })
714 .build()
715 .map(|result: (Result<CreateResult, String>,)| (result.0,))
716 }
717
718 pub async fn wallet_create_canister(
728 &self,
729 cycles: u128,
730 controllers: Option<Vec<Principal>>,
731 compute_allocation: Option<ComputeAllocation>,
732 memory_allocation: Option<MemoryAllocation>,
733 freezing_threshold: Option<FreezingThreshold>,
734 ) -> Result<CreateResult, AgentError> {
735 if self.version_supports_u128_cycles() {
736 self.wallet_create_canister128(
737 cycles,
738 controllers,
739 compute_allocation,
740 memory_allocation,
741 freezing_threshold,
742 )
743 .call_and_wait()
744 .await?
745 } else {
746 let cycles = u64::try_from(cycles).map_err(|_| {
747 AgentError::WalletUpgradeRequired(
748 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
749 )
750 })?;
751 if self.version_supports_multiple_controllers() {
752 self.wallet_create_canister64_v2(
753 cycles,
754 controllers,
755 compute_allocation,
756 memory_allocation,
757 freezing_threshold,
758 )
759 .call_and_wait()
760 .await?
761 } else {
762 let controller: Option<Principal> = match &controllers {
763 Some(c) if c.len() == 1 => {
764 let first: Option<&Principal> = c.first();
765 let first: Principal = *first.unwrap();
766 Ok(Some(first))
767 }
768 Some(_) => Err(AgentError::WalletUpgradeRequired(
769 "The installed wallet does not support multiple controllers.".to_string(),
770 )),
771 None => Ok(None),
772 }?;
773 self.wallet_create_canister64_v1(
774 cycles,
775 controller,
776 compute_allocation,
777 memory_allocation,
778 freezing_threshold,
779 )
780 .call_and_wait()
781 .await?
782 }
783 }
784 .0
785 .map_err(AgentError::WalletError)
786 }
787
788 pub fn wallet_create_wallet64_v1(
790 &self,
791 cycles: u64,
792 controller: Option<Principal>,
793 compute_allocation: Option<ComputeAllocation>,
794 memory_allocation: Option<MemoryAllocation>,
795 freezing_threshold: Option<FreezingThreshold>,
796 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
797 #[derive(CandidType)]
798 struct In {
799 cycles: u64,
800 settings: CanisterSettingsV1,
801 }
802
803 let settings = CanisterSettingsV1 {
804 controller,
805 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
806 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
807 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
808 };
809
810 self.update("wallet_create_wallet")
811 .with_arg(In { cycles, settings })
812 .build()
813 .map(|result: (Result<CreateResult, String>,)| (result.0,))
814 }
815
816 pub fn wallet_create_wallet64_v2(
818 &self,
819 cycles: u64,
820 controllers: Option<Vec<Principal>>,
821 compute_allocation: Option<ComputeAllocation>,
822 memory_allocation: Option<MemoryAllocation>,
823 freezing_threshold: Option<FreezingThreshold>,
824 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
825 #[derive(CandidType)]
826 struct In {
827 cycles: u64,
828 settings: CanisterSettings,
829 }
830
831 let settings = CanisterSettings {
832 controllers,
833 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
834 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
835 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
836 reserved_cycles_limit: None,
837 wasm_memory_limit: None,
838 wasm_memory_threshold: None,
839 log_visibility: None,
840 };
841
842 self.update("wallet_create_wallet")
843 .with_arg(In { cycles, settings })
844 .build()
845 .map(|result: (Result<CreateResult, String>,)| (result.0,))
846 }
847
848 pub fn wallet_create_wallet128(
850 &self,
851 cycles: u128,
852 controllers: Option<Vec<Principal>>,
853 compute_allocation: Option<ComputeAllocation>,
854 memory_allocation: Option<MemoryAllocation>,
855 freezing_threshold: Option<FreezingThreshold>,
856 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
857 #[derive(CandidType)]
858 struct In {
859 cycles: u128,
860 settings: CanisterSettings,
861 }
862
863 let settings = CanisterSettings {
864 controllers,
865 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
866 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
867 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
868 reserved_cycles_limit: None,
869 wasm_memory_limit: None,
870 wasm_memory_threshold: None,
871 log_visibility: None,
872 };
873
874 self.update("wallet_create_wallet128")
875 .with_arg(In { cycles, settings })
876 .build()
877 .map(|result: (Result<CreateResult, String>,)| (result.0,))
878 }
879
880 pub async fn wallet_create_wallet(
882 &self,
883 cycles: u128,
884 controllers: Option<Vec<Principal>>,
885 compute_allocation: Option<ComputeAllocation>,
886 memory_allocation: Option<MemoryAllocation>,
887 freezing_threshold: Option<FreezingThreshold>,
888 ) -> Result<CreateResult, AgentError> {
889 if self.version_supports_u128_cycles() {
890 self.wallet_create_wallet128(
891 cycles,
892 controllers,
893 compute_allocation,
894 memory_allocation,
895 freezing_threshold,
896 )
897 .call_and_wait()
898 .await?
899 } else {
900 let cycles = u64::try_from(cycles).map_err(|_| {
901 AgentError::WalletUpgradeRequired(
902 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
903 )
904 })?;
905 if self.version_supports_multiple_controllers() {
906 self.wallet_create_wallet64_v2(
907 cycles,
908 controllers,
909 compute_allocation,
910 memory_allocation,
911 freezing_threshold,
912 )
913 .call_and_wait()
914 .await?
915 } else {
916 let controller: Option<Principal> = match &controllers {
917 Some(c) if c.len() == 1 => Ok(Some(c[0])),
918 Some(_) => Err(AgentError::WalletUpgradeRequired(
919 "The installed wallet does not support multiple controllers.".to_string(),
920 )),
921 None => Ok(None),
922 }?;
923 self.wallet_create_wallet64_v1(
924 cycles,
925 controller,
926 compute_allocation,
927 memory_allocation,
928 freezing_threshold,
929 )
930 .call_and_wait()
931 .await?
932 }
933 }
934 .0
935 .map_err(AgentError::WalletError)
936 }
937
938 pub fn wallet_store_wallet_wasm(
941 &self,
942 wasm_module: Vec<u8>,
943 ) -> impl 'agent + AsyncCall<Value = ()> {
944 #[derive(CandidType, Deserialize)]
945 struct In {
946 #[serde(with = "serde_bytes")]
947 wasm_module: Vec<u8>,
948 }
949 self.update("wallet_store_wallet_wasm")
950 .with_arg(In { wasm_module })
951 .build()
952 }
953
954 pub fn add_address(&self, address: AddressEntry) -> impl 'agent + AsyncCall<Value = ()> {
956 self.update("add_address").with_arg(address).build()
957 }
958
959 pub fn list_addresses(&self) -> impl 'agent + SyncCall<Value = (Vec<AddressEntry>,)> {
961 self.query("list_addresses").build()
962 }
963
964 pub fn remove_address(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
966 self.update("remove_address").with_arg(principal).build()
967 }
968
969 pub fn get_events64(
971 &self,
972 from: Option<u32>,
973 to: Option<u32>,
974 ) -> impl 'agent + SyncCall<Value = (Vec<Event<u64>>,)> {
975 #[derive(CandidType)]
976 struct In {
977 from: Option<u32>,
978 to: Option<u32>,
979 }
980
981 let arg = if from.is_none() && to.is_none() {
982 None
983 } else {
984 Some(In { from, to })
985 };
986
987 self.query("get_events").with_arg(arg).build()
988 }
989
990 pub fn get_events128(
992 &self,
993 from: Option<u32>,
994 to: Option<u32>,
995 ) -> impl 'agent + SyncCall<Value = (Vec<Event>,)> {
996 #[derive(CandidType)]
997 struct In {
998 from: Option<u32>,
999 to: Option<u32>,
1000 }
1001 let arg = if from.is_none() && to.is_none() {
1002 None
1003 } else {
1004 Some(In { from, to })
1005 };
1006 self.query("get_events128").with_arg(arg).build()
1007 }
1008
1009 pub async fn get_events(
1011 &self,
1012 from: Option<u32>,
1013 to: Option<u32>,
1014 ) -> Result<Vec<Event>, AgentError> {
1015 if self.version_supports_u128_cycles() {
1016 self.get_events128(from, to)
1017 .call()
1018 .await
1019 .map(|(events,)| events)
1020 } else {
1021 self.get_events64(from, to)
1022 .call()
1023 .await
1024 .map(|(events,)| events.into_iter().map(Event::into).collect())
1025 }
1026 }
1027
1028 pub fn call64<'canister, Out, M: Into<String>>(
1031 &'canister self,
1032 destination: Principal,
1033 method_name: M,
1034 arg: Argument,
1035 amount: u64,
1036 ) -> CallForwarder<'agent, 'canister, Out>
1037 where
1038 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1039 {
1040 CallForwarder {
1041 wallet: self,
1042 destination,
1043 method_name: method_name.into(),
1044 amount: amount.into(),
1045 arg,
1046 phantom_out: std::marker::PhantomData,
1047 u128: false,
1048 }
1049 }
1050
1051 pub fn call128<'canister, Out, M: Into<String>>(
1054 &'canister self,
1055 destination: Principal,
1056 method_name: M,
1057 arg: Argument,
1058 amount: u128,
1059 ) -> CallForwarder<'agent, 'canister, Out>
1060 where
1061 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1062 {
1063 CallForwarder {
1064 wallet: self,
1065 destination,
1066 method_name: method_name.into(),
1067 amount,
1068 arg,
1069 phantom_out: std::marker::PhantomData,
1070 u128: true,
1071 }
1072 }
1073
1074 pub fn call<'canister, Out, M: Into<String>>(
1077 &'canister self,
1078 destination: Principal,
1079 method_name: M,
1080 arg: Argument,
1081 amount: u128,
1082 ) -> CallForwarder<'agent, 'canister, Out>
1083 where
1084 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1085 {
1086 CallForwarder {
1087 wallet: self,
1088 destination,
1089 method_name: method_name.into(),
1090 amount,
1091 arg,
1092 phantom_out: std::marker::PhantomData,
1093 u128: self.version_supports_u128_cycles(),
1094 }
1095 }
1096
1097 pub fn list_managed_canisters(
1099 &self,
1100 from: Option<u32>,
1101 to: Option<u32>,
1102 ) -> impl 'agent + SyncCall<Value = (Vec<ManagedCanisterInfo>, u32)> {
1103 #[derive(CandidType)]
1104 struct In {
1105 from: Option<u32>,
1106 to: Option<u32>,
1107 }
1108 self.query("list_managed_canisters")
1109 .with_arg((In { from, to },))
1110 .build()
1111 }
1112
1113 pub fn get_managed_canister_events64(
1115 &self,
1116 canister: Principal,
1117 from: Option<u32>,
1118 to: Option<u32>,
1119 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent<u64>>>,)> {
1120 #[derive(CandidType)]
1121 struct In {
1122 canister: Principal,
1123 from: Option<u32>,
1124 to: Option<u32>,
1125 }
1126 self.query("get_managed_canister_events")
1127 .with_arg((In { canister, from, to },))
1128 .build()
1129 }
1130
1131 pub fn get_managed_canister_events128(
1133 &self,
1134 canister: Principal,
1135 from: Option<u32>,
1136 to: Option<u32>,
1137 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent>>,)> {
1138 #[derive(CandidType)]
1139 struct In {
1140 canister: Principal,
1141 from: Option<u32>,
1142 to: Option<u32>,
1143 }
1144 self.query("get_managed_canister_events128")
1145 .with_arg((In { canister, from, to },))
1146 .build()
1147 }
1148
1149 pub async fn get_managed_canister_events(
1151 &self,
1152 canister: Principal,
1153 from: Option<u32>,
1154 to: Option<u32>,
1155 ) -> Result<Option<Vec<ManagedCanisterEvent>>, AgentError> {
1156 if self.version_supports_u128_cycles() {
1157 self.get_managed_canister_events128(canister, from, to)
1158 .call()
1159 .await
1160 .map(|(events,)| events)
1161 } else {
1162 self.get_managed_canister_events64(canister, from, to)
1163 .call()
1164 .await
1165 .map(|(events,)| {
1166 events
1167 .map(|events| events.into_iter().map(ManagedCanisterEvent::into).collect())
1168 })
1169 }
1170 }
1171
1172 pub fn version_supports_multiple_controllers(&self) -> bool {
1174 static CONTROLLERS: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.2.0").unwrap());
1175 CONTROLLERS.matches(&self.version)
1176 }
1177
1178 pub fn version_supports_u128_cycles(&self) -> bool {
1180 static U128_CYCLES: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.3.0").unwrap());
1181 U128_CYCLES.matches(&self.version)
1182 }
1183}