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 };
681
682 self.update("wallet_create_canister")
683 .with_arg(In { cycles, settings })
684 .build()
685 .map(|result: (Result<CreateResult, String>,)| (result.0,))
686 }
687
688 pub fn wallet_create_canister128(
690 &self,
691 cycles: u128,
692 controllers: Option<Vec<Principal>>,
693 compute_allocation: Option<ComputeAllocation>,
694 memory_allocation: Option<MemoryAllocation>,
695 freezing_threshold: Option<FreezingThreshold>,
696 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
697 #[derive(CandidType)]
698 struct In {
699 cycles: u128,
700 settings: CanisterSettings,
701 }
702
703 let settings = CanisterSettings {
704 controllers,
705 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
706 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
707 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
708 reserved_cycles_limit: None,
709 wasm_memory_limit: None,
710 wasm_memory_threshold: None,
711 log_visibility: None,
712 };
713
714 self.update("wallet_create_canister128")
715 .with_arg(In { cycles, settings })
716 .build()
717 .map(|result: (Result<CreateResult, String>,)| (result.0,))
718 }
719
720 pub async fn wallet_create_canister(
730 &self,
731 cycles: u128,
732 controllers: Option<Vec<Principal>>,
733 compute_allocation: Option<ComputeAllocation>,
734 memory_allocation: Option<MemoryAllocation>,
735 freezing_threshold: Option<FreezingThreshold>,
736 ) -> Result<CreateResult, AgentError> {
737 if self.version_supports_u128_cycles() {
738 self.wallet_create_canister128(
739 cycles,
740 controllers,
741 compute_allocation,
742 memory_allocation,
743 freezing_threshold,
744 )
745 .call_and_wait()
746 .await?
747 } else {
748 let cycles = u64::try_from(cycles).map_err(|_| {
749 AgentError::WalletUpgradeRequired(
750 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
751 )
752 })?;
753 if self.version_supports_multiple_controllers() {
754 self.wallet_create_canister64_v2(
755 cycles,
756 controllers,
757 compute_allocation,
758 memory_allocation,
759 freezing_threshold,
760 )
761 .call_and_wait()
762 .await?
763 } else {
764 let controller: Option<Principal> = match &controllers {
765 Some(c) if c.len() == 1 => {
766 let first: Option<&Principal> = c.first();
767 let first: Principal = *first.unwrap();
768 Ok(Some(first))
769 }
770 Some(_) => Err(AgentError::WalletUpgradeRequired(
771 "The installed wallet does not support multiple controllers.".to_string(),
772 )),
773 None => Ok(None),
774 }?;
775 self.wallet_create_canister64_v1(
776 cycles,
777 controller,
778 compute_allocation,
779 memory_allocation,
780 freezing_threshold,
781 )
782 .call_and_wait()
783 .await?
784 }
785 }
786 .0
787 .map_err(AgentError::WalletError)
788 }
789
790 pub fn wallet_create_wallet64_v1(
792 &self,
793 cycles: u64,
794 controller: Option<Principal>,
795 compute_allocation: Option<ComputeAllocation>,
796 memory_allocation: Option<MemoryAllocation>,
797 freezing_threshold: Option<FreezingThreshold>,
798 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
799 #[derive(CandidType)]
800 struct In {
801 cycles: u64,
802 settings: CanisterSettingsV1,
803 }
804
805 let settings = CanisterSettingsV1 {
806 controller,
807 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
808 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
809 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
810 };
811
812 self.update("wallet_create_wallet")
813 .with_arg(In { cycles, settings })
814 .build()
815 .map(|result: (Result<CreateResult, String>,)| (result.0,))
816 }
817
818 pub fn wallet_create_wallet64_v2(
820 &self,
821 cycles: u64,
822 controllers: Option<Vec<Principal>>,
823 compute_allocation: Option<ComputeAllocation>,
824 memory_allocation: Option<MemoryAllocation>,
825 freezing_threshold: Option<FreezingThreshold>,
826 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
827 #[derive(CandidType)]
828 struct In {
829 cycles: u64,
830 settings: CanisterSettings,
831 }
832
833 let settings = CanisterSettings {
834 controllers,
835 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
836 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
837 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
838 reserved_cycles_limit: None,
839 wasm_memory_limit: None,
840 wasm_memory_threshold: None,
841 log_visibility: None,
842 };
843
844 self.update("wallet_create_wallet")
845 .with_arg(In { cycles, settings })
846 .build()
847 .map(|result: (Result<CreateResult, String>,)| (result.0,))
848 }
849
850 pub fn wallet_create_wallet128(
852 &self,
853 cycles: u128,
854 controllers: Option<Vec<Principal>>,
855 compute_allocation: Option<ComputeAllocation>,
856 memory_allocation: Option<MemoryAllocation>,
857 freezing_threshold: Option<FreezingThreshold>,
858 ) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
859 #[derive(CandidType)]
860 struct In {
861 cycles: u128,
862 settings: CanisterSettings,
863 }
864
865 let settings = CanisterSettings {
866 controllers,
867 compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
868 memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
869 freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
870 reserved_cycles_limit: None,
871 wasm_memory_limit: None,
872 wasm_memory_threshold: None,
873 log_visibility: None,
874 };
875
876 self.update("wallet_create_wallet128")
877 .with_arg(In { cycles, settings })
878 .build()
879 .map(|result: (Result<CreateResult, String>,)| (result.0,))
880 }
881
882 pub async fn wallet_create_wallet(
884 &self,
885 cycles: u128,
886 controllers: Option<Vec<Principal>>,
887 compute_allocation: Option<ComputeAllocation>,
888 memory_allocation: Option<MemoryAllocation>,
889 freezing_threshold: Option<FreezingThreshold>,
890 ) -> Result<CreateResult, AgentError> {
891 if self.version_supports_u128_cycles() {
892 self.wallet_create_wallet128(
893 cycles,
894 controllers,
895 compute_allocation,
896 memory_allocation,
897 freezing_threshold,
898 )
899 .call_and_wait()
900 .await?
901 } else {
902 let cycles = u64::try_from(cycles).map_err(|_| {
903 AgentError::WalletUpgradeRequired(
904 "The installed wallet does not support cycle counts >2^64-1.".to_string(),
905 )
906 })?;
907 if self.version_supports_multiple_controllers() {
908 self.wallet_create_wallet64_v2(
909 cycles,
910 controllers,
911 compute_allocation,
912 memory_allocation,
913 freezing_threshold,
914 )
915 .call_and_wait()
916 .await?
917 } else {
918 let controller: Option<Principal> = match &controllers {
919 Some(c) if c.len() == 1 => Ok(Some(c[0])),
920 Some(_) => Err(AgentError::WalletUpgradeRequired(
921 "The installed wallet does not support multiple controllers.".to_string(),
922 )),
923 None => Ok(None),
924 }?;
925 self.wallet_create_wallet64_v1(
926 cycles,
927 controller,
928 compute_allocation,
929 memory_allocation,
930 freezing_threshold,
931 )
932 .call_and_wait()
933 .await?
934 }
935 }
936 .0
937 .map_err(AgentError::WalletError)
938 }
939
940 pub fn wallet_store_wallet_wasm(
943 &self,
944 wasm_module: Vec<u8>,
945 ) -> impl 'agent + AsyncCall<Value = ()> {
946 #[derive(CandidType, Deserialize)]
947 struct In {
948 #[serde(with = "serde_bytes")]
949 wasm_module: Vec<u8>,
950 }
951 self.update("wallet_store_wallet_wasm")
952 .with_arg(In { wasm_module })
953 .build()
954 }
955
956 pub fn add_address(&self, address: AddressEntry) -> impl 'agent + AsyncCall<Value = ()> {
958 self.update("add_address").with_arg(address).build()
959 }
960
961 pub fn list_addresses(&self) -> impl 'agent + SyncCall<Value = (Vec<AddressEntry>,)> {
963 self.query("list_addresses").build()
964 }
965
966 pub fn remove_address(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
968 self.update("remove_address").with_arg(principal).build()
969 }
970
971 pub fn get_events64(
973 &self,
974 from: Option<u32>,
975 to: Option<u32>,
976 ) -> impl 'agent + SyncCall<Value = (Vec<Event<u64>>,)> {
977 #[derive(CandidType)]
978 struct In {
979 from: Option<u32>,
980 to: Option<u32>,
981 }
982
983 let arg = if from.is_none() && to.is_none() {
984 None
985 } else {
986 Some(In { from, to })
987 };
988
989 self.query("get_events").with_arg(arg).build()
990 }
991
992 pub fn get_events128(
994 &self,
995 from: Option<u32>,
996 to: Option<u32>,
997 ) -> impl 'agent + SyncCall<Value = (Vec<Event>,)> {
998 #[derive(CandidType)]
999 struct In {
1000 from: Option<u32>,
1001 to: Option<u32>,
1002 }
1003 let arg = if from.is_none() && to.is_none() {
1004 None
1005 } else {
1006 Some(In { from, to })
1007 };
1008 self.query("get_events128").with_arg(arg).build()
1009 }
1010
1011 pub async fn get_events(
1013 &self,
1014 from: Option<u32>,
1015 to: Option<u32>,
1016 ) -> Result<Vec<Event>, AgentError> {
1017 if self.version_supports_u128_cycles() {
1018 self.get_events128(from, to)
1019 .call()
1020 .await
1021 .map(|(events,)| events)
1022 } else {
1023 self.get_events64(from, to)
1024 .call()
1025 .await
1026 .map(|(events,)| events.into_iter().map(Event::into).collect())
1027 }
1028 }
1029
1030 pub fn call64<'canister, Out, M: Into<String>>(
1033 &'canister self,
1034 destination: Principal,
1035 method_name: M,
1036 arg: Argument,
1037 amount: u64,
1038 ) -> CallForwarder<'agent, 'canister, Out>
1039 where
1040 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1041 {
1042 CallForwarder {
1043 wallet: self,
1044 destination,
1045 method_name: method_name.into(),
1046 amount: amount.into(),
1047 arg,
1048 phantom_out: std::marker::PhantomData,
1049 u128: false,
1050 }
1051 }
1052
1053 pub fn call128<'canister, Out, M: Into<String>>(
1056 &'canister self,
1057 destination: Principal,
1058 method_name: M,
1059 arg: Argument,
1060 amount: u128,
1061 ) -> CallForwarder<'agent, 'canister, Out>
1062 where
1063 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1064 {
1065 CallForwarder {
1066 wallet: self,
1067 destination,
1068 method_name: method_name.into(),
1069 amount,
1070 arg,
1071 phantom_out: std::marker::PhantomData,
1072 u128: true,
1073 }
1074 }
1075
1076 pub fn call<'canister, Out, M: Into<String>>(
1079 &'canister self,
1080 destination: Principal,
1081 method_name: M,
1082 arg: Argument,
1083 amount: u128,
1084 ) -> CallForwarder<'agent, 'canister, Out>
1085 where
1086 Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
1087 {
1088 CallForwarder {
1089 wallet: self,
1090 destination,
1091 method_name: method_name.into(),
1092 amount,
1093 arg,
1094 phantom_out: std::marker::PhantomData,
1095 u128: self.version_supports_u128_cycles(),
1096 }
1097 }
1098
1099 pub fn list_managed_canisters(
1101 &self,
1102 from: Option<u32>,
1103 to: Option<u32>,
1104 ) -> impl 'agent + SyncCall<Value = (Vec<ManagedCanisterInfo>, u32)> {
1105 #[derive(CandidType)]
1106 struct In {
1107 from: Option<u32>,
1108 to: Option<u32>,
1109 }
1110 self.query("list_managed_canisters")
1111 .with_arg((In { from, to },))
1112 .build()
1113 }
1114
1115 pub fn get_managed_canister_events64(
1117 &self,
1118 canister: Principal,
1119 from: Option<u32>,
1120 to: Option<u32>,
1121 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent<u64>>>,)> {
1122 #[derive(CandidType)]
1123 struct In {
1124 canister: Principal,
1125 from: Option<u32>,
1126 to: Option<u32>,
1127 }
1128 self.query("get_managed_canister_events")
1129 .with_arg((In { canister, from, to },))
1130 .build()
1131 }
1132
1133 pub fn get_managed_canister_events128(
1135 &self,
1136 canister: Principal,
1137 from: Option<u32>,
1138 to: Option<u32>,
1139 ) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent>>,)> {
1140 #[derive(CandidType)]
1141 struct In {
1142 canister: Principal,
1143 from: Option<u32>,
1144 to: Option<u32>,
1145 }
1146 self.query("get_managed_canister_events128")
1147 .with_arg((In { canister, from, to },))
1148 .build()
1149 }
1150
1151 pub async fn get_managed_canister_events(
1153 &self,
1154 canister: Principal,
1155 from: Option<u32>,
1156 to: Option<u32>,
1157 ) -> Result<Option<Vec<ManagedCanisterEvent>>, AgentError> {
1158 if self.version_supports_u128_cycles() {
1159 self.get_managed_canister_events128(canister, from, to)
1160 .call()
1161 .await
1162 .map(|(events,)| events)
1163 } else {
1164 self.get_managed_canister_events64(canister, from, to)
1165 .call()
1166 .await
1167 .map(|(events,)| {
1168 events
1169 .map(|events| events.into_iter().map(ManagedCanisterEvent::into).collect())
1170 })
1171 }
1172 }
1173
1174 pub fn version_supports_multiple_controllers(&self) -> bool {
1176 static CONTROLLERS: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.2.0").unwrap());
1177 CONTROLLERS.matches(&self.version)
1178 }
1179
1180 pub fn version_supports_u128_cycles(&self) -> bool {
1182 static U128_CYCLES: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.3.0").unwrap());
1183 U128_CYCLES.matches(&self.version)
1184 }
1185}