use super::live_query_context;
use crate::candid_call::{query_canister, query_no_args};
use crate::icrc::{
ledger::{fetch_icrc1_token_metadata, nat_text, principal_from_text},
model::{
IcrcAllowanceData, IcrcAllowanceRequest, IcrcBalanceData, IcrcBalanceRequest, IcrcError,
IcrcLedgerRequest, IcrcTokenData, subaccount_bytes_from_hex,
},
};
use ic_platform::agent::Agent;
use ic_platform::candid::contract::{Nat, Principal};
use ic_platform::icrc_ledger_types::{Account as LedgerAccount, Allowance, AllowanceArgs};
const ICRC1_SYMBOL_METHOD: &str = "icrc1_symbol";
const ICRC1_DECIMALS_METHOD: &str = "icrc1_decimals";
const ICRC1_BALANCE_OF_METHOD: &str = "icrc1_balance_of";
const ICRC2_ALLOWANCE_METHOD: &str = "icrc2_allowance";
pub(in crate::icrc::live) async fn fetch_token_async(
source: &crate::icrc::LiveIcrcSource,
request: &IcrcLedgerRequest,
) -> Result<IcrcTokenData, IcrcError> {
let (agent, ledger_canister) = live_query_context(
source,
&request.source_endpoint,
&request.ledger_canister_id,
)?;
Box::pin(fetch_icrc1_token_metadata::<IcrcError>(
&agent,
&ledger_canister,
))
.await
}
pub(in crate::icrc::live) async fn fetch_balance_async(
source: &crate::icrc::LiveIcrcSource,
request: &IcrcBalanceRequest,
) -> Result<IcrcBalanceData, IcrcError> {
let (agent, ledger_canister) = live_query_context(
source,
&request.ledger.source_endpoint,
&request.ledger.ledger_canister_id,
)?;
let account = account_from_parts(
&request.account_owner,
request.subaccount_hex.as_deref(),
"account_owner",
)?;
let (token_symbol, decimals) = query_token_display_fields(&agent, &ledger_canister).await?;
let balance: Nat = query_canister::<LedgerAccount, Nat, IcrcError>(
&agent,
&ledger_canister,
ICRC1_BALANCE_OF_METHOD,
ICRC1_BALANCE_OF_METHOD,
ICRC1_BALANCE_OF_METHOD,
&account,
)
.await?;
Ok(IcrcBalanceData {
token_symbol,
decimals,
balance: nat_text(&balance),
})
}
pub(in crate::icrc::live) async fn fetch_allowance_async(
source: &crate::icrc::LiveIcrcSource,
request: &IcrcAllowanceRequest,
) -> Result<IcrcAllowanceData, IcrcError> {
let (agent, ledger_canister) = live_query_context(
source,
&request.ledger.source_endpoint,
&request.ledger.ledger_canister_id,
)?;
let allowance_args = AllowanceArgs {
account: account_from_parts(
&request.account_owner,
request.account_subaccount_hex.as_deref(),
"account_owner",
)?,
spender: account_from_parts(
&request.spender_owner,
request.spender_subaccount_hex.as_deref(),
"spender_owner",
)?,
};
let (token_symbol, decimals) = query_token_display_fields(&agent, &ledger_canister).await?;
let allowance = query_canister::<AllowanceArgs, Allowance, IcrcError>(
&agent,
&ledger_canister,
ICRC2_ALLOWANCE_METHOD,
ICRC2_ALLOWANCE_METHOD,
ICRC2_ALLOWANCE_METHOD,
&allowance_args,
)
.await?;
Ok(IcrcAllowanceData {
token_symbol,
decimals,
allowance: nat_text(&allowance.allowance),
expires_at_unix_nanos: allowance
.expires_at
.map(|expires_at| expires_at.to_string()),
})
}
pub(in crate::icrc::live) async fn query_token_display_fields(
agent: &Agent,
ledger_canister: &Principal,
) -> Result<(String, u8), IcrcError> {
let (token_symbol, decimals) = futures::try_join!(
query_no_args::<String, IcrcError>(agent, ledger_canister, ICRC1_SYMBOL_METHOD),
query_no_args::<u8, IcrcError>(agent, ledger_canister, ICRC1_DECIMALS_METHOD),
)?;
Ok((token_symbol, decimals))
}
pub(in crate::icrc::live) fn account_from_parts(
owner: &str,
subaccount_hex: Option<&str>,
owner_field: &'static str,
) -> Result<LedgerAccount, IcrcError> {
Ok(LedgerAccount {
owner: principal_from_text(owner, owner_field)?,
subaccount: subaccount_hex.map(subaccount_bytes_from_hex).transpose()?,
})
}