icrc 0.0.1

Collection of ICRC Packages
Documentation
use std::cell::RefCell;
use ic_cdk_macros::{init, query, update};
use candid::{candid_method, Nat};
use ic_cdk::caller;
use icrc_ledger_types::icrc1::account::Account;

use icrc::icrc7::consts::*;
use icrc::icrc7::ledger::{Ledger, Value};
use icrc::icrc7::types::{TransferArgs, TransferResult};

struct Access;
impl Access {
    fn with_ledger<R>(f: impl FnOnce(&Ledger) -> R) -> R {
        LEDGER.with(|cell| {
            f(cell
                .borrow()
                .as_ref()
                .expect("ledger state not initialized"))
        })
    }

    fn with_ledger_mut<R>(f: impl FnOnce(&mut Ledger) -> R) -> R {
        LEDGER.with(|cell| {
            f(cell
                .borrow_mut()
                .as_mut()
                .expect("ledger state not initialized"))
        })
    }
}

thread_local! {
    static LEDGER: RefCell<Option<Ledger>> = RefCell::default();
}

#[init]
#[candid_method(init)]
fn init() {
    LEDGER.with(|ledger| {
        let mut ledger = ledger.borrow_mut();
        *ledger = Some(Ledger::new(
            "ICRC7".to_string(),
            "ICRC7".to_string(),
        ));
    });
}

#[query(name = "icrc7_collection_metadata")]
#[candid_method(query, rename = "icrc7_collection_metadata")]
fn collection_metadata() -> Vec<(String, Value)> {
    Access::with_ledger(|ledger| ledger.collection_metadata())
}

#[query(name = "icrc7_symbol")]
#[candid_method(query, rename = "icrc7_symbol")]
fn symbol() -> String {
    Access::with_ledger(|ledger| ledger.symbol.clone())
}

#[query(name = "icrc7_name")]
#[candid_method(query, rename = "icrc7_name")]
fn name() -> String {
    Access::with_ledger(|ledger| ledger.name.clone())
}

#[query(name = "icrc7_description")]
#[candid_method(query, rename = "icrc7_description")]
fn description() -> Option<String> {
    Access::with_ledger(|ledger| ledger.description.clone())
}

#[query(name = "icrc7_logo")]
#[candid_method(query, rename = "icrc7_logo")]
fn logo() -> Option<String> {
    Access::with_ledger(|ledger| ledger.logo.clone())
}

#[query(name = "icrc7_total_supply")]
#[candid_method(query, rename = "icrc7_total_supply")]
fn total_supply() -> Nat {
    Access::with_ledger(|ledger| ledger.tokens.len().into())
}

#[query(name = "icrc7_supply_cap")]
#[candid_method(query, rename = "icrc7_supply_cap")]
fn supply_cap() -> Option<Nat> {
    Access::with_ledger(|ledger| ledger.supply_cap.clone())
}

#[query(name = "icrc7_max_query_batch_size")]
#[candid_method(query, rename = "icrc7_max_query_batch_size")]
fn max_query_batch_size() -> Option<Nat> {
    Some(Nat::from(MAX_QUERY_BATCH_SIZE))
}

#[query(name = "icrc7_max_update_batch_size")]
#[candid_method(query, rename = "icrc7_max_update_batch_size")]
fn max_update_batch_size() -> Option<Nat> {
    Some(Nat::from(MAX_UPDATE_BATCH_SIZE))
}

#[query(name = "icrc7_default_take_value")]
#[candid_method(query, rename = "icrc7_default_take_value")]
fn default_take_value() -> Option<Nat> {
    Some(Nat::from(DEFAULT_TAKE_VALUE))
}

#[query(name = "icrc7_max_take_value")]
#[candid_method(query, rename = "icrc7_max_take_value")]
fn max_take_value() -> Option<Nat> {
    Some(Nat::from(MAX_TAKE_VALUE))
}

#[query(name = "icrc7_max_memo_size")]
#[candid_method(query, rename = "icrc7_max_memo_size")]
fn max_memo_size() -> Option<Nat> {
    Some(Nat::from(MAX_MEMO_SIZE))
}

#[query(name = "icrc7_atomic_batch_transfers")]
#[candid_method(query, rename = "icrc7_atomic_batch_transfers")]
fn atomic_batch_transfers() -> bool {
    ATOMIC_BATCH_TRANSFERS
}

#[query(name = "icrc7_tx_window")]
#[candid_method(query, rename = "icrc7_tx_window")]
fn tx_window() -> Option<Nat> {
    Some(Nat::from(TX_WINDOW))
}

#[query(name = "icrc7_permitted_drift")]
#[candid_method(query, rename = "icrc7_permitted_drift")]
fn permitted_drift() -> Option<Nat> {
    Some(Nat::from(PERMITTED_DRIFT))
}

#[query(name = "icrc7_token_metadata")]
#[candid_method(query, rename = "icrc7_token_metadata")]
fn token_metadata(token_ids: Vec<Nat>) -> Vec<Option<Vec<(String, Value)>>> {
    Access::with_ledger(|ledger| ledger.token_metadata(token_ids))
}

#[query(name = "icrc7_owner_of")]
#[candid_method(query, rename = "icrc7_owner_of")]
fn owner_of(token_ids: Vec<Nat>) -> Vec<Option<Account>> {
    Access::with_ledger(|ledger| ledger.owner_of(token_ids))
}

#[query(name = "icrc7_balance_of")]
#[candid_method(query, rename = "icrc7_balance_of")]
fn balance_of(owners: Vec<Account>) -> Vec<Nat> {
    Access::with_ledger(|ledger| ledger.balance_of(owners))
}

#[query(name = "icrc7_tokens")]
#[candid_method(query, rename = "icrc7_tokens")]
fn tokens(prev: Option<Nat>, take: Option<Nat>) -> Vec<Nat> {
    Access::with_ledger(|ledger| ledger.tokens(prev, take))
}

#[query(name = "icrc7_tokens_of")]
#[candid_method(query, rename = "icrc7_tokens_of")]
fn tokens_of(owner: Account, prev: Option<Nat>, take: Option<Nat>) -> Vec<Nat> {
    Access::with_ledger(|ledger| ledger.tokens_of(owner, prev, take))
}

#[update(name = "icrc7_transfer")]
#[candid_method(update, rename = "icrc7_transfer")]
fn transfer(args: Vec<TransferArgs>) -> Vec<Option<TransferResult>> {
    Access::with_ledger_mut(|ledger| ledger.transfer(caller(), args, true))
}


candid::export_service!();

fn main() {}

#[test]
fn check_candid_interface() {
    use candid_parser::utils::{service_compatible, CandidSource};

    let new_interface = __export_service();
    let interface = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("icrc7.did");
    service_compatible(
        CandidSource::Text(&new_interface),
        CandidSource::File(interface.as_path()),
    )
        .unwrap_or_else(|e| {
            panic!(
                "the ledger interface is not compatible with {}: {:?}",
                interface.display(),
                e
            )
        });
}