use async_trait::async_trait;
use candid::types::number::Nat;
use candid::utils::{ArgumentDecoder, ArgumentEncoder};
use candid::Principal;
use icrc_ledger_types::icrc::generic_metadata_value::MetadataValue as Value;
use icrc_ledger_types::icrc1::account::Account;
use icrc_ledger_types::icrc1::transfer::{BlockIndex, TransferArg, TransferError};
use icrc_ledger_types::icrc2::approve::{ApproveArgs, ApproveError};
use icrc_ledger_types::icrc2::transfer_from::{TransferFromArgs, TransferFromError};
#[async_trait]
pub trait Runtime {
async fn call<In, Out>(
&self,
id: Principal,
method: &str,
args: In,
) -> Result<Out, (i32, String)>
where
In: ArgumentEncoder + Send,
Out: for<'a> ArgumentDecoder<'a>;
}
pub struct ICRC1Client<R: Runtime> {
pub runtime: R,
pub ledger_canister_id: Principal,
}
impl<R: Runtime> ICRC1Client<R> {
pub async fn balance_of(&self, account: Account) -> Result<Nat, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_balance_of", (account,))
.await
.map(untuple)
}
pub async fn decimals(&self) -> Result<u8, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_decimals", ())
.await
.map(untuple)
}
pub async fn name(&self) -> Result<String, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_name", ())
.await
.map(untuple)
}
pub async fn metadata(&self) -> Result<Vec<(String, Value)>, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_metadata", ())
.await
.map(untuple)
}
pub async fn symbol(&self) -> Result<String, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_symbol", ())
.await
.map(untuple)
}
pub async fn total_supply(&self) -> Result<Nat, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_total_supply", ())
.await
.map(untuple)
}
pub async fn fee(&self) -> Result<Nat, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_fee", ())
.await
.map(untuple)
}
pub async fn minting_account(&self) -> Result<Option<Account>, (i32, String)> {
self.runtime
.call(self.ledger_canister_id, "icrc1_minting_account", ())
.await
.map(untuple)
}
pub async fn transfer(
&self,
args: TransferArg,
) -> Result<Result<BlockIndex, TransferError>, (i32, String)> {
let result: Result<Nat, TransferError> = self
.runtime
.call(self.ledger_canister_id, "icrc1_transfer", (args,))
.await
.map(untuple)?;
Ok(result)
}
pub async fn transfer_from(
&self,
args: TransferFromArgs,
) -> Result<Result<BlockIndex, TransferFromError>, (i32, String)> {
let result: Result<Nat, TransferFromError> = self
.runtime
.call(self.ledger_canister_id, "icrc2_transfer_from", (args,))
.await
.map(untuple)?;
Ok(result)
}
pub async fn approve(
&self,
args: ApproveArgs,
) -> Result<Result<BlockIndex, ApproveError>, (i32, String)> {
let result: Result<Nat, ApproveError> = self
.runtime
.call(self.ledger_canister_id, "icrc2_approve", (args,))
.await
.map(untuple)?;
Ok(result)
}
}
fn untuple<T>(t: (T,)) -> T {
t.0
}