use crate::{
InternalError, InternalErrorOrigin,
ops::ic::{
IcOps,
ledger::{LedgerMeta, LedgerOps},
},
workflow::prelude::*,
};
pub struct LedgerWorkflow;
impl LedgerWorkflow {
pub async fn transfer_with_allowance_check(
ledger_id: Principal,
payer: Principal,
spender: Account,
to: Account,
amount: u64,
memo: Option<Vec<u8>>,
) -> Result<u64, InternalError> {
LedgerRules::validate_allowance(ledger_id, payer, spender, amount).await?;
let block_index = LedgerOps::transfer_from(ledger_id, payer, to, amount, memo).await?;
Ok(block_index)
}
}
pub struct LedgerRules;
impl LedgerRules {
#[must_use]
pub fn ledger_meta(ledger_id: Principal) -> LedgerMeta {
LedgerOps::ledger_meta(ledger_id)
}
pub async fn validate_allowance(
ledger_id: Principal,
payer: Principal,
spender: Account,
required_amount: u64,
) -> Result<(), InternalError> {
let meta = Self::ledger_meta(ledger_id);
let symbol = meta.symbol;
let payer_account = Account {
owner: payer,
subaccount: None,
};
let allowance_record = LedgerOps::allowance(ledger_id, payer_account, spender).await?;
let approved_amount = allowance_record.allowance;
if approved_amount < required_amount {
return Err(InternalError::domain(
InternalErrorOrigin::Workflow,
format!(
"insufficient {symbol} allowance: has {approved_amount}, needs {required_amount}"
),
));
}
if let Some(expires_at_nanos) = allowance_record.expires_at {
let now_nanos = IcOps::now_nanos();
if expires_at_nanos <= now_nanos {
return Err(InternalError::domain(
InternalErrorOrigin::Workflow,
format!("{symbol} allowance expired at {expires_at_nanos} (now {now_nanos})"),
));
}
}
Ok(())
}
}