use std::{
future::{Future, IntoFuture},
ops::Deref,
};
use crate::{
call::{AsyncCall, CallFuture, SyncCall},
canister::Argument,
interfaces::management_canister::{
attributes::{ComputeAllocation, FreezingThreshold, MemoryAllocation},
builders::CanisterSettings,
},
Canister,
};
use async_trait::async_trait;
use candid::{decode_args, utils::ArgumentDecoder, CandidType, Deserialize, Nat};
use ic_agent::{agent::CallResponse, export::Principal, Agent, AgentError};
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
const REPLICA_ERROR_NO_SUCH_QUERY_METHOD: &str = "has no query method 'wallet_api_version'";
const IC_REF_ERROR_NO_SUCH_QUERY_METHOD: &str = "query method does not exist";
#[derive(Debug)]
pub struct CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
{
wallet: &'canister WalletCanister<'agent>,
destination: Principal,
method_name: String,
amount: u128,
u128: bool,
arg: Argument,
phantom_out: std::marker::PhantomData<Out>,
}
#[derive(Debug, Clone, CandidType, Deserialize)]
pub struct CanisterSettingsV1 {
pub controller: Option<Principal>,
pub compute_allocation: Option<Nat>,
pub memory_allocation: Option<Nat>,
pub freezing_threshold: Option<Nat>,
}
impl<'agent: 'canister, 'canister, Out> CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
{
pub fn with_arg<Argument>(mut self, arg: Argument) -> Self
where
Argument: CandidType + Sync + Send,
{
self.arg.set_idl_arg(arg);
self
}
pub fn with_args(mut self, tuple: impl candid::utils::ArgumentEncoder) -> Self {
if self.arg.0.is_some() {
panic!("argument is being set more than once");
}
self.arg = Argument::from_candid(tuple);
self
}
pub fn with_arg_raw(mut self, arg: Vec<u8>) -> Self {
self.arg.set_raw_arg(arg);
self
}
pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = Out>, AgentError> {
#[derive(CandidType, Deserialize)]
struct In<TCycles> {
canister: Principal,
method_name: String,
#[serde(with = "serde_bytes")]
args: Vec<u8>,
cycles: TCycles,
}
Ok(if self.u128 {
self.wallet.update("wallet_call128").with_arg(In {
canister: self.destination,
method_name: self.method_name,
args: self.arg.serialize()?.to_vec(),
cycles: self.amount,
})
} else {
self.wallet.update("wallet_call").with_arg(In {
canister: self.destination,
method_name: self.method_name,
args: self.arg.serialize()?.to_vec(),
cycles: u64::try_from(self.amount).map_err(|_| {
AgentError::WalletUpgradeRequired(
"The installed wallet does not support cycle counts >2^64-1".to_string(),
)
})?,
})
}
.build()
.and_then(|(result,): (Result<CallResult, String>,)| async move {
let result = result.map_err(AgentError::WalletCallFailed)?;
decode_args::<Out>(result.r#return.as_slice())
.map_err(|e| AgentError::CandidError(Box::new(e)))
}))
}
pub fn call(self) -> impl Future<Output = Result<CallResponse<Out>, AgentError>> + 'agent {
let call = self.build();
async { call?.call().await }
}
pub fn call_and_wait(self) -> impl Future<Output = Result<Out, AgentError>> + 'agent {
let call = self.build();
async { call?.call_and_wait().await }
}
}
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
#[cfg_attr(not(target_family = "wasm"), async_trait)]
impl<'agent: 'canister, 'canister, Out> AsyncCall for CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
{
type Value = Out;
async fn call(self) -> Result<CallResponse<Out>, AgentError> {
self.call().await
}
async fn call_and_wait(self) -> Result<Out, AgentError> {
self.call_and_wait().await
}
}
impl<'agent: 'canister, 'canister, Out> IntoFuture for CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync + 'agent,
{
type IntoFuture = CallFuture<'agent, Out>;
type Output = Result<Out, AgentError>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(self.call_and_wait())
}
}
#[derive(Debug, Clone)]
pub struct WalletCanister<'agent> {
canister: Canister<'agent>,
version: Version,
}
impl<'agent> Deref for WalletCanister<'agent> {
type Target = Canister<'agent>;
fn deref(&self) -> &Self::Target {
&self.canister
}
}
#[derive(CandidType, Debug, Deserialize)]
pub enum EventKind<TCycles = u128> {
CyclesSent {
to: Principal,
amount: TCycles,
refund: TCycles,
},
CyclesReceived {
from: Principal,
amount: TCycles,
memo: Option<String>,
},
AddressAdded {
id: Principal,
name: Option<String>,
role: Role,
},
AddressRemoved {
id: Principal,
},
CanisterCreated {
canister: Principal,
cycles: TCycles,
},
CanisterCalled {
canister: Principal,
method_name: String,
cycles: TCycles,
},
}
impl From<EventKind<u64>> for EventKind {
fn from(kind: EventKind<u64>) -> Self {
use EventKind::*;
match kind {
AddressAdded { id, name, role } => AddressAdded { id, name, role },
AddressRemoved { id } => AddressRemoved { id },
CanisterCalled {
canister,
cycles,
method_name,
} => CanisterCalled {
canister,
cycles: cycles as u128,
method_name,
},
CanisterCreated { canister, cycles } => CanisterCreated {
canister,
cycles: cycles as u128,
},
CyclesReceived { amount, from, memo } => CyclesReceived {
amount: amount as u128,
from,
memo,
},
CyclesSent { amount, refund, to } => CyclesSent {
amount: amount as u128,
refund: refund as u128,
to,
},
}
}
}
#[derive(CandidType, Debug, Deserialize)]
pub struct Event<TCycles = u128> {
pub id: u32,
pub timestamp: u64,
pub kind: EventKind<TCycles>,
}
impl From<Event<u64>> for Event {
fn from(
Event {
id,
timestamp,
kind,
}: Event<u64>,
) -> Self {
Self {
id,
timestamp,
kind: kind.into(),
}
}
}
#[derive(CandidType, Debug, Deserialize)]
pub enum Role {
Contact,
Custodian,
Controller,
}
#[derive(CandidType, Debug, Deserialize)]
pub enum Kind {
Unknown,
User,
Canister,
}
#[derive(CandidType, Debug, Deserialize)]
pub struct AddressEntry {
pub id: Principal,
pub name: Option<String>,
pub kind: Kind,
pub role: Role,
}
#[derive(CandidType, Debug, Deserialize)]
pub struct ManagedCanisterInfo {
pub id: Principal,
pub name: Option<String>,
pub created_at: u64,
}
#[derive(CandidType, Debug, Deserialize)]
pub enum ManagedCanisterEventKind<TCycles = u128> {
CyclesSent {
amount: TCycles,
refund: TCycles,
},
Called {
method_name: String,
cycles: TCycles,
},
Created {
cycles: TCycles,
},
}
impl From<ManagedCanisterEventKind<u64>> for ManagedCanisterEventKind {
fn from(event: ManagedCanisterEventKind<u64>) -> Self {
use ManagedCanisterEventKind::*;
match event {
Called {
cycles,
method_name,
} => Called {
cycles: cycles as u128,
method_name,
},
Created { cycles } => Created {
cycles: cycles as u128,
},
CyclesSent { amount, refund } => CyclesSent {
amount: amount as u128,
refund: refund as u128,
},
}
}
}
#[derive(CandidType, Deserialize, Debug)]
pub struct ManagedCanisterEvent<TCycles = u128> {
pub id: u32,
pub timestamp: u64,
pub kind: ManagedCanisterEventKind<TCycles>,
}
impl From<ManagedCanisterEvent<u64>> for ManagedCanisterEvent {
fn from(
ManagedCanisterEvent {
id,
timestamp,
kind,
}: ManagedCanisterEvent<u64>,
) -> Self {
Self {
id,
timestamp,
kind: kind.into(),
}
}
}
#[derive(Debug, Copy, Clone, CandidType, Deserialize)]
pub struct BalanceResult<TCycles = u128> {
pub amount: TCycles,
}
#[derive(Debug, Copy, Clone, CandidType, Deserialize)]
pub struct CreateResult {
pub canister_id: Principal,
}
#[derive(Debug, Clone, CandidType, Deserialize)]
pub struct CallResult {
#[serde(with = "serde_bytes")]
pub r#return: Vec<u8>,
}
impl<'agent> WalletCanister<'agent> {
pub async fn create(
agent: &'agent Agent,
canister_id: Principal,
) -> Result<WalletCanister<'agent>, AgentError> {
let canister = Canister::builder()
.with_agent(agent)
.with_canister_id(canister_id)
.build()
.unwrap();
Self::from_canister(canister).await
}
pub async fn from_canister(
canister: Canister<'agent>,
) -> Result<WalletCanister<'agent>, AgentError> {
static DEFAULT_VERSION: Lazy<Version> = Lazy::new(|| Version::parse("0.1.0").unwrap());
let version: Result<(String,), _> =
canister.query("wallet_api_version").build().call().await;
let version = match version {
Err(AgentError::UncertifiedReject(replica_error))
if replica_error
.reject_message
.contains(REPLICA_ERROR_NO_SUCH_QUERY_METHOD)
|| replica_error
.reject_message
.contains(IC_REF_ERROR_NO_SUCH_QUERY_METHOD) =>
{
DEFAULT_VERSION.clone()
}
version => Version::parse(&version?.0).unwrap_or_else(|_| DEFAULT_VERSION.clone()),
};
Ok(Self { canister, version })
}
pub fn from_canister_with_version(canister: Canister<'agent>, version: Version) -> Self {
Self { canister, version }
}
}
impl<'agent> WalletCanister<'agent> {
pub fn fetch_wallet_api_version(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
self.query("wallet_api_version").build()
}
pub fn wallet_api_version(&self) -> &Version {
&self.version
}
pub fn name(&self) -> impl 'agent + SyncCall<Value = (Option<String>,)> {
self.query("name").build()
}
pub fn set_name(&self, name: String) -> impl 'agent + AsyncCall<Value = ()> {
self.update("set_name").with_arg(name).build()
}
pub fn get_controllers(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
self.query("get_controllers").build()
}
pub fn add_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
self.update("add_controller").with_arg(principal).build()
}
pub fn remove_controller(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
self.update("remove_controller").with_arg(principal).build()
}
pub fn get_custodians(&self) -> impl 'agent + SyncCall<Value = (Vec<Principal>,)> {
self.query("get_custodians").build()
}
pub fn authorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
self.update("authorize").with_arg(custodian).build()
}
pub fn deauthorize(&self, custodian: Principal) -> impl 'agent + AsyncCall<Value = ()> {
self.update("deauthorize").with_arg(custodian).build()
}
pub fn wallet_balance64(&self) -> impl 'agent + SyncCall<Value = (BalanceResult<u64>,)> {
self.query("wallet_balance").build()
}
pub fn wallet_balance128(&self) -> impl 'agent + SyncCall<Value = (BalanceResult,)> {
self.query("wallet_balance128").build()
}
pub async fn wallet_balance(&self) -> Result<BalanceResult, AgentError> {
if self.version_supports_u128_cycles() {
self.wallet_balance128().call().await.map(|(r,)| r)
} else {
self.wallet_balance64()
.call()
.await
.map(|(r,)| BalanceResult {
amount: r.amount as u128,
})
}
}
pub fn wallet_send64(
&self,
destination: Principal,
amount: u64,
) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
#[derive(CandidType)]
struct In {
canister: Principal,
amount: u64,
}
self.update("wallet_send")
.with_arg(In {
canister: destination,
amount,
})
.build()
}
pub fn wallet_send128<'canister: 'agent>(
&'canister self,
destination: Principal,
amount: u128,
) -> impl 'agent + AsyncCall<Value = (Result<(), String>,)> {
#[derive(CandidType)]
struct In {
canister: Principal,
amount: u128,
}
self.update("wallet_send128")
.with_arg(In {
canister: destination,
amount,
})
.build()
}
pub async fn wallet_send(
&self,
destination: Principal,
amount: u128,
) -> Result<(), AgentError> {
if self.version_supports_u128_cycles() {
self.wallet_send128(destination, amount)
.call_and_wait()
.await?
} else {
let amount = u64::try_from(amount).map_err(|_| {
AgentError::WalletUpgradeRequired(
"The installed wallet does not support cycle counts >2^64-1.".to_string(),
)
})?;
self.wallet_send64(destination, amount)
.call_and_wait()
.await?
}
.0
.map_err(AgentError::WalletError)
}
pub fn wallet_receive(&self, memo: Option<String>) -> impl 'agent + AsyncCall<Value = ((),)> {
#[derive(CandidType)]
struct In {
memo: Option<String>,
}
self.update("wallet_receive")
.with_arg(memo.map(|memo| In { memo: Some(memo) }))
.build()
}
pub fn wallet_create_canister64_v1(
&self,
cycles: u64,
controller: Option<Principal>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u64,
settings: CanisterSettingsV1,
}
let settings = CanisterSettingsV1 {
controller,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
};
self.update("wallet_create_canister")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub fn wallet_create_canister64_v2(
&self,
cycles: u64,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u64,
settings: CanisterSettings,
}
let settings = CanisterSettings {
controllers,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
reserved_cycles_limit: None,
wasm_memory_limit: None,
log_visibility: None,
};
self.update("wallet_create_canister")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub fn wallet_create_canister128(
&self,
cycles: u128,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u128,
settings: CanisterSettings,
}
let settings = CanisterSettings {
controllers,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
reserved_cycles_limit: None,
wasm_memory_limit: None,
log_visibility: None,
};
self.update("wallet_create_canister128")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub async fn wallet_create_canister(
&self,
cycles: u128,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> Result<CreateResult, AgentError> {
if self.version_supports_u128_cycles() {
self.wallet_create_canister128(
cycles,
controllers,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
} else {
let cycles = u64::try_from(cycles).map_err(|_| {
AgentError::WalletUpgradeRequired(
"The installed wallet does not support cycle counts >2^64-1.".to_string(),
)
})?;
if self.version_supports_multiple_controllers() {
self.wallet_create_canister64_v2(
cycles,
controllers,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
} else {
let controller: Option<Principal> = match &controllers {
Some(c) if c.len() == 1 => {
let first: Option<&Principal> = c.first();
let first: Principal = *first.unwrap();
Ok(Some(first))
}
Some(_) => Err(AgentError::WalletUpgradeRequired(
"The installed wallet does not support multiple controllers.".to_string(),
)),
None => Ok(None),
}?;
self.wallet_create_canister64_v1(
cycles,
controller,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
}
}
.0
.map_err(AgentError::WalletError)
}
pub fn wallet_create_wallet64_v1(
&self,
cycles: u64,
controller: Option<Principal>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u64,
settings: CanisterSettingsV1,
}
let settings = CanisterSettingsV1 {
controller,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
};
self.update("wallet_create_wallet")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub fn wallet_create_wallet64_v2(
&self,
cycles: u64,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u64,
settings: CanisterSettings,
}
let settings = CanisterSettings {
controllers,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
reserved_cycles_limit: None,
wasm_memory_limit: None,
log_visibility: None,
};
self.update("wallet_create_wallet")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub fn wallet_create_wallet128(
&self,
cycles: u128,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> impl 'agent + AsyncCall<Value = (Result<CreateResult, String>,)> {
#[derive(CandidType)]
struct In {
cycles: u128,
settings: CanisterSettings,
}
let settings = CanisterSettings {
controllers,
compute_allocation: compute_allocation.map(u8::from).map(Nat::from),
memory_allocation: memory_allocation.map(u64::from).map(Nat::from),
freezing_threshold: freezing_threshold.map(u64::from).map(Nat::from),
reserved_cycles_limit: None,
wasm_memory_limit: None,
log_visibility: None,
};
self.update("wallet_create_wallet128")
.with_arg(In { cycles, settings })
.build()
.map(|result: (Result<CreateResult, String>,)| (result.0,))
}
pub async fn wallet_create_wallet(
&self,
cycles: u128,
controllers: Option<Vec<Principal>>,
compute_allocation: Option<ComputeAllocation>,
memory_allocation: Option<MemoryAllocation>,
freezing_threshold: Option<FreezingThreshold>,
) -> Result<CreateResult, AgentError> {
if self.version_supports_u128_cycles() {
self.wallet_create_wallet128(
cycles,
controllers,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
} else {
let cycles = u64::try_from(cycles).map_err(|_| {
AgentError::WalletUpgradeRequired(
"The installed wallet does not support cycle counts >2^64-1.".to_string(),
)
})?;
if self.version_supports_multiple_controllers() {
self.wallet_create_wallet64_v2(
cycles,
controllers,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
} else {
let controller: Option<Principal> = match &controllers {
Some(c) if c.len() == 1 => Ok(Some(c[0])),
Some(_) => Err(AgentError::WalletUpgradeRequired(
"The installed wallet does not support multiple controllers.".to_string(),
)),
None => Ok(None),
}?;
self.wallet_create_wallet64_v1(
cycles,
controller,
compute_allocation,
memory_allocation,
freezing_threshold,
)
.call_and_wait()
.await?
}
}
.0
.map_err(AgentError::WalletError)
}
pub fn wallet_store_wallet_wasm(
&self,
wasm_module: Vec<u8>,
) -> impl 'agent + AsyncCall<Value = ()> {
#[derive(CandidType, Deserialize)]
struct In {
#[serde(with = "serde_bytes")]
wasm_module: Vec<u8>,
}
self.update("wallet_store_wallet_wasm")
.with_arg(In { wasm_module })
.build()
}
pub fn add_address(&self, address: AddressEntry) -> impl 'agent + AsyncCall<Value = ()> {
self.update("add_address").with_arg(address).build()
}
pub fn list_addresses(&self) -> impl 'agent + SyncCall<Value = (Vec<AddressEntry>,)> {
self.query("list_addresses").build()
}
pub fn remove_address(&self, principal: Principal) -> impl 'agent + AsyncCall<Value = ()> {
self.update("remove_address").with_arg(principal).build()
}
pub fn get_events64(
&self,
from: Option<u32>,
to: Option<u32>,
) -> impl 'agent + SyncCall<Value = (Vec<Event<u64>>,)> {
#[derive(CandidType)]
struct In {
from: Option<u32>,
to: Option<u32>,
}
let arg = if from.is_none() && to.is_none() {
None
} else {
Some(In { from, to })
};
self.query("get_events").with_arg(arg).build()
}
pub fn get_events128(
&self,
from: Option<u32>,
to: Option<u32>,
) -> impl 'agent + SyncCall<Value = (Vec<Event>,)> {
#[derive(CandidType)]
struct In {
from: Option<u32>,
to: Option<u32>,
}
let arg = if from.is_none() && to.is_none() {
None
} else {
Some(In { from, to })
};
self.query("get_events128").with_arg(arg).build()
}
pub async fn get_events(
&self,
from: Option<u32>,
to: Option<u32>,
) -> Result<Vec<Event>, AgentError> {
if self.version_supports_u128_cycles() {
self.get_events128(from, to)
.call()
.await
.map(|(events,)| events)
} else {
self.get_events64(from, to)
.call()
.await
.map(|(events,)| events.into_iter().map(|event| event.into()).collect())
}
}
pub fn call64<'canister, Out, M: Into<String>>(
&'canister self,
destination: Principal,
method_name: M,
arg: Argument,
amount: u64,
) -> CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
{
CallForwarder {
wallet: self,
destination,
method_name: method_name.into(),
amount: amount as u128,
arg,
phantom_out: std::marker::PhantomData,
u128: false,
}
}
pub fn call128<'canister, Out, M: Into<String>>(
&'canister self,
destination: Principal,
method_name: M,
arg: Argument,
amount: u128,
) -> CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
{
CallForwarder {
wallet: self,
destination,
method_name: method_name.into(),
amount,
arg,
phantom_out: std::marker::PhantomData,
u128: true,
}
}
pub fn call<'canister, Out, M: Into<String>>(
&'canister self,
destination: Principal,
method_name: M,
arg: Argument,
amount: u128,
) -> CallForwarder<'agent, 'canister, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
{
CallForwarder {
wallet: self,
destination,
method_name: method_name.into(),
amount,
arg,
phantom_out: std::marker::PhantomData,
u128: self.version_supports_u128_cycles(),
}
}
pub fn list_managed_canisters(
&self,
from: Option<u32>,
to: Option<u32>,
) -> impl 'agent + SyncCall<Value = (Vec<ManagedCanisterInfo>, u32)> {
#[derive(CandidType)]
struct In {
from: Option<u32>,
to: Option<u32>,
}
self.query("list_managed_canisters")
.with_arg((In { from, to },))
.build()
}
pub fn get_managed_canister_events64(
&self,
canister: Principal,
from: Option<u32>,
to: Option<u32>,
) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent<u64>>>,)> {
#[derive(CandidType)]
struct In {
canister: Principal,
from: Option<u32>,
to: Option<u32>,
}
self.query("get_managed_canister_events")
.with_arg((In { canister, from, to },))
.build()
}
pub fn get_managed_canister_events128(
&self,
canister: Principal,
from: Option<u32>,
to: Option<u32>,
) -> impl 'agent + SyncCall<Value = (Option<Vec<ManagedCanisterEvent>>,)> {
#[derive(CandidType)]
struct In {
canister: Principal,
from: Option<u32>,
to: Option<u32>,
}
self.query("get_managed_canister_events128")
.with_arg((In { canister, from, to },))
.build()
}
pub async fn get_managed_canister_events(
&self,
canister: Principal,
from: Option<u32>,
to: Option<u32>,
) -> Result<Option<Vec<ManagedCanisterEvent>>, AgentError> {
if self.version_supports_u128_cycles() {
self.get_managed_canister_events128(canister, from, to)
.call()
.await
.map(|(events,)| events)
} else {
self.get_managed_canister_events64(canister, from, to)
.call()
.await
.map(|(events,)| {
events.map(|events| events.into_iter().map(|event| event.into()).collect())
})
}
}
pub fn version_supports_multiple_controllers(&self) -> bool {
static CONTROLLERS: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.2.0").unwrap());
CONTROLLERS.matches(&self.version)
}
pub fn version_supports_u128_cycles(&self) -> bool {
static U128_CYCLES: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse(">=0.3.0").unwrap());
U128_CYCLES.matches(&self.version)
}
}