diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 49c755d..fab1972 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -18,6 +18,7 @@ use pocket_ic::{PocketIc, WasmResult}; use serde::de::DeserializeOwned; use self::wasm::Canister; +use crate::wasm::Icrc2InitArgs; const DEFAULT_CYCLES: u128 = 2_000_000_000_000; @@ -26,6 +27,7 @@ pub struct TestEnv { pub pic: PocketIc, pub deferred_id: Principal, pub fly_id: Principal, + pub icp_ledger_id: Principal, pub marketplace_id: Principal, pub xrc_id: Principal, } @@ -83,26 +85,67 @@ impl TestEnv { let pic = PocketIc::new(); // create canisters + let icp_ledger_id = pic.create_canister(); + let ckbtc_id = pic.create_canister(); let xrc_id = pic.create_canister(); let deferred_id = pic.create_canister(); let fly_id = pic.create_canister(); let marketplace_id = pic.create_canister(); // install deferred canister + Self::install_icrc2(&pic, icp_ledger_id, "ICP", "Internet Computer", 8); + Self::install_icrc2(&pic, ckbtc_id, "ckBTC", "ckBTC", 8); Self::install_deferred(&pic, deferred_id, fly_id, marketplace_id); - Self::install_fly(&pic, fly_id, deferred_id, marketplace_id, xrc_id); - Self::install_marketplace(&pic, marketplace_id, deferred_id, fly_id, xrc_id); + Self::install_fly( + &pic, + fly_id, + deferred_id, + marketplace_id, + xrc_id, + icp_ledger_id, + ckbtc_id, + ); + Self::install_marketplace( + &pic, + marketplace_id, + deferred_id, + fly_id, + xrc_id, + icp_ledger_id, + ); Self::install_xrc(&pic, xrc_id); TestEnv { pic, deferred_id, + icp_ledger_id, fly_id, marketplace_id, xrc_id, } } + fn install_icrc2(pic: &PocketIc, id: Principal, symbol: &str, name: &str, decimals: u8) { + pic.add_cycles(id, DEFAULT_CYCLES); + let wasm_bytes = Self::load_wasm(Canister::Icrc2); + let init_arg = Encode!(&Icrc2InitArgs { + name: name.to_string(), + symbol: symbol.to_string(), + decimals, + fee: 10, + logo: "https://ic0.app/img/logo.png".to_string(), + minting_account: actor::minting_account(), + total_supply: Nat::from(21_268_400_889_u64), + accounts: vec![ + (actor::alice_account(), Nat::from(1_000_000_000)), + (actor::bob_account(), Nat::from(1_000_000_000)), + ], + }) + .unwrap(); + + pic.install_canister(id, wasm_bytes, init_arg, None); + } + fn install_deferred( pic: &PocketIc, deferred_id: Principal, @@ -128,6 +171,8 @@ impl TestEnv { deferred_id: Principal, marketplace_id: Principal, xrc_canister: Principal, + icp_ledger_canister: Principal, + ckbtc_canister: Principal, ) { pic.add_cycles(fly_id, DEFAULT_CYCLES); let wasm_bytes = Self::load_wasm(Canister::Fly); @@ -144,6 +189,8 @@ impl TestEnv { marketplace_canister: marketplace_id, swap_account: actor::swap_account(), xrc_canister, + icp_ledger_canister, + ckbtc_canister, }; let init_arg = Encode!(&init_arg).unwrap(); @@ -156,6 +203,7 @@ impl TestEnv { deferred_id: Principal, fly_id: Principal, xrc_canister: Principal, + icp_ledger_canister: Principal, ) { pic.add_cycles(marketplace_id, DEFAULT_CYCLES); let wasm_bytes = Self::load_wasm(Canister::Marketplace); @@ -165,6 +213,7 @@ impl TestEnv { deferred_canister: deferred_id, fly_canister: fly_id, xrc_canister, + icp_ledger_canister, }; let init_arg = Encode!(&init_arg).unwrap(); diff --git a/integration-tests/src/wasm.rs b/integration-tests/src/wasm.rs index 8133095..52321fa 100644 --- a/integration-tests/src/wasm.rs +++ b/integration-tests/src/wasm.rs @@ -1,8 +1,25 @@ use std::path::Path; +use candid::{CandidType, Nat}; +use icrc::icrc1::account::Account; +use serde::Deserialize; + +#[derive(Debug, Clone, CandidType, Deserialize)] +pub struct Icrc2InitArgs { + pub accounts: Vec<(Account, Nat)>, + pub decimals: u8, + pub fee: u64, + pub logo: String, + pub minting_account: Account, + pub name: String, + pub symbol: String, + pub total_supply: Nat, +} + pub enum Canister { Deferred, Fly, + Icrc2, Marketplace, Xrc, } @@ -14,6 +31,7 @@ impl Canister { Canister::Fly => Path::new("fly/fly.wasm"), Canister::Marketplace => Path::new("marketplace/marketplace.wasm"), Canister::Xrc => Path::new("test/xrc.wasm"), + Canister::Icrc2 => Path::new("test/icrc2-template-canister.wasm"), } } } diff --git a/scripts/deploy_functions.sh b/scripts/deploy_functions.sh index 9b94b9e..779c546 100644 --- a/scripts/deploy_functions.sh +++ b/scripts/deploy_functions.sh @@ -45,6 +45,8 @@ deploy_fly() { initial_balances = $INITIAL_BALANCES; minting_account = $MINTING_ACCOUNT; xrc_canister = principal \"uf6dk-hyaaa-aaaaq-qaaaq-cai\"; + ckbtc_canister = principal \"mxzaz-hqaaa-aaaar-qaada-cai\"; + icp_ledger_canister = principal \"ryjl3-tyaaa-aaaaa-aaaba-cai\"; })" dfx deploy --mode=$INSTALL_MODE --yes --network="$NETWORK" --argument="$fly_init_args" fly @@ -66,6 +68,7 @@ deploy_marketplace() { fly_canister = principal \"$FLY_PRINCIPAL\"; xrc_canister = principal \"uf6dk-hyaaa-aaaaq-qaaaq-cai\"; admins = vec { $(for admin in $ADMINS; do echo "principal \"$admin\";"; done) }; + icp_ledger_canister = principal \"ryjl3-tyaaa-aaaaa-aaaba-cai\"; })" dfx deploy --mode=$INSTALL_MODE --yes --network="$NETWORK" --argument="$marketplace_init_args" marketplace diff --git a/src/declarations/fly/fly.did.d.ts b/src/declarations/fly/fly.did.d.ts index 319301b..f24b6e3 100644 --- a/src/declarations/fly/fly.did.d.ts +++ b/src/declarations/fly/fly.did.d.ts @@ -53,7 +53,9 @@ export type FlyError = { 'Configuration' : ConfigurationError } | { 'Icrc2Transfer' : TransferFromError }; export interface FlyInitData { 'deferred_canister' : Principal, + 'icp_ledger_canister' : Principal, 'minting_account' : Account, + 'ckbtc_canister' : Principal, 'initial_balances' : Array<[Account, bigint]>, 'swap_account' : Account, 'xrc_canister' : Principal, @@ -145,6 +147,8 @@ export interface _SERVICE { 'admin_burn' : ActorMethod<[bigint], Result>, 'admin_cycles' : ActorMethod<[], bigint>, 'admin_remove_role' : ActorMethod<[Principal, Role], Result>, + 'admin_set_ckbtc_canister' : ActorMethod<[Principal], undefined>, + 'admin_set_icp_ledger_canister' : ActorMethod<[Principal], undefined>, 'admin_set_role' : ActorMethod<[Principal, Role], undefined>, 'admin_set_swap_account' : ActorMethod<[Account], undefined>, 'admin_set_xrc_canister' : ActorMethod<[Principal], undefined>, diff --git a/src/declarations/fly/fly.did.js b/src/declarations/fly/fly.did.js index b34ebf5..3609376 100644 --- a/src/declarations/fly/fly.did.js +++ b/src/declarations/fly/fly.did.js @@ -5,7 +5,9 @@ export const idlFactory = ({ IDL }) => { }); const FlyInitData = IDL.Record({ 'deferred_canister' : IDL.Principal, + 'icp_ledger_canister' : IDL.Principal, 'minting_account' : Account, + 'ckbtc_canister' : IDL.Principal, 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), 'swap_account' : Account, 'xrc_canister' : IDL.Principal, @@ -173,6 +175,8 @@ export const idlFactory = ({ IDL }) => { 'admin_burn' : IDL.Func([IDL.Nat], [Result], []), 'admin_cycles' : IDL.Func([], [IDL.Nat], ['query']), 'admin_remove_role' : IDL.Func([IDL.Principal, Role], [Result], []), + 'admin_set_ckbtc_canister' : IDL.Func([IDL.Principal], [], []), + 'admin_set_icp_ledger_canister' : IDL.Func([IDL.Principal], [], []), 'admin_set_role' : IDL.Func([IDL.Principal, Role], [], []), 'admin_set_swap_account' : IDL.Func([Account], [], []), 'admin_set_xrc_canister' : IDL.Func([IDL.Principal], [], []), @@ -215,7 +219,9 @@ export const init = ({ IDL }) => { }); const FlyInitData = IDL.Record({ 'deferred_canister' : IDL.Principal, + 'icp_ledger_canister' : IDL.Principal, 'minting_account' : Account, + 'ckbtc_canister' : IDL.Principal, 'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat)), 'swap_account' : Account, 'xrc_canister' : IDL.Principal, diff --git a/src/declarations/marketplace/marketplace.did.d.ts b/src/declarations/marketplace/marketplace.did.d.ts index fdd6847..c35df15 100644 --- a/src/declarations/marketplace/marketplace.did.d.ts +++ b/src/declarations/marketplace/marketplace.did.d.ts @@ -47,6 +47,7 @@ export type MarketplaceError = { 'Buy' : BuyError } | { 'Icrc2Transfer' : TransferFromError }; export interface MarketplaceInitData { 'deferred_canister' : Principal, + 'icp_ledger_canister' : Principal, 'fly_canister' : Principal, 'xrc_canister' : Principal, 'admins' : Array, @@ -118,6 +119,7 @@ export interface _SERVICE { 'admin_set_admins' : ActorMethod<[Array], Result>, 'admin_set_deferred_canister' : ActorMethod<[Principal], undefined>, 'admin_set_fly_canister' : ActorMethod<[Principal], Result>, + 'admin_set_icp_ledger_canister' : ActorMethod<[Principal], undefined>, 'admin_set_interest_rate_for_buyer' : ActorMethod<[number], undefined>, 'admin_set_xrc_canister' : ActorMethod<[Principal], undefined>, 'buy_token' : ActorMethod<[bigint, [] | [Uint8Array | number[]]], Result>, diff --git a/src/declarations/marketplace/marketplace.did.js b/src/declarations/marketplace/marketplace.did.js index e36db7c..a36a93a 100644 --- a/src/declarations/marketplace/marketplace.did.js +++ b/src/declarations/marketplace/marketplace.did.js @@ -1,6 +1,7 @@ export const idlFactory = ({ IDL }) => { const MarketplaceInitData = IDL.Record({ 'deferred_canister' : IDL.Principal, + 'icp_ledger_canister' : IDL.Principal, 'fly_canister' : IDL.Principal, 'xrc_canister' : IDL.Principal, 'admins' : IDL.Vec(IDL.Principal), @@ -144,6 +145,7 @@ export const idlFactory = ({ IDL }) => { 'admin_set_admins' : IDL.Func([IDL.Vec(IDL.Principal)], [Result], []), 'admin_set_deferred_canister' : IDL.Func([IDL.Principal], [], []), 'admin_set_fly_canister' : IDL.Func([IDL.Principal], [Result], []), + 'admin_set_icp_ledger_canister' : IDL.Func([IDL.Principal], [], []), 'admin_set_interest_rate_for_buyer' : IDL.Func([IDL.Float64], [], []), 'admin_set_xrc_canister' : IDL.Func([IDL.Principal], [], []), 'buy_token' : IDL.Func([IDL.Nat, IDL.Opt(IDL.Vec(IDL.Nat8))], [Result], []), @@ -153,6 +155,7 @@ export const idlFactory = ({ IDL }) => { export const init = ({ IDL }) => { const MarketplaceInitData = IDL.Record({ 'deferred_canister' : IDL.Principal, + 'icp_ledger_canister' : IDL.Principal, 'fly_canister' : IDL.Principal, 'xrc_canister' : IDL.Principal, 'admins' : IDL.Vec(IDL.Principal), diff --git a/src/did/src/fly.rs b/src/did/src/fly.rs index 51573d7..75c17c0 100644 --- a/src/did/src/fly.rs +++ b/src/did/src/fly.rs @@ -108,12 +108,16 @@ pub type PicoFly = Nat; #[derive(Debug, Clone, CandidType, Deserialize)] pub struct FlyInitData { pub admins: Vec, + /// The canister ID of the CKBTC canister + pub ckbtc_canister: Principal, /// Total supply of $picofly tokens pub total_supply: PicoFly, /// Initial balances (wallet subaccount -> picofly) pub initial_balances: Vec<(Account, PicoFly)>, /// Deferred canister pub deferred_canister: Principal, + /// ICP ledger canister + pub icp_ledger_canister: Principal, /// Marketplace canister pub marketplace_canister: Principal, /// Swap account diff --git a/src/did/src/marketplace.rs b/src/did/src/marketplace.rs index 4554e72..60b612a 100644 --- a/src/did/src/marketplace.rs +++ b/src/did/src/marketplace.rs @@ -85,6 +85,8 @@ pub struct MarketplaceInitData { pub deferred_canister: Principal, /// Fly canister pub fly_canister: Principal, + /// ICP ledger canister + pub icp_ledger_canister: Principal, /// XRC canister pub xrc_canister: Principal, } diff --git a/src/fly/fly.did b/src/fly/fly.did index e821be1..ced97f8 100644 --- a/src/fly/fly.did +++ b/src/fly/fly.did @@ -46,7 +46,9 @@ type FlyError = variant { }; type FlyInitData = record { deferred_canister : principal; + icp_ledger_canister : principal; minting_account : Account; + ckbtc_canister : principal; initial_balances : vec record { Account; nat }; swap_account : Account; xrc_canister : principal; @@ -132,6 +134,8 @@ service : (FlyInitData) -> { admin_burn : (nat) -> (Result); admin_cycles : () -> (nat) query; admin_remove_role : (principal, Role) -> (Result); + admin_set_ckbtc_canister : (principal) -> (); + admin_set_icp_ledger_canister : (principal) -> (); admin_set_role : (principal, Role) -> (); admin_set_swap_account : (Account) -> (); admin_set_xrc_canister : (principal) -> (); diff --git a/src/fly/src/app.rs b/src/fly/src/app.rs index ab0e318..44619ba 100644 --- a/src/fly/src/app.rs +++ b/src/fly/src/app.rs @@ -47,8 +47,10 @@ impl FlyCanister { Configuration::set_minting_account(data.minting_account); // set swap account Configuration::set_swap_account(data.swap_account); - // set xrc canister + // set canisters Configuration::set_xrc_canister(data.xrc_canister); + Configuration::set_ckbtc_canister(data.ckbtc_canister); + Configuration::set_icp_ledger_canister(data.icp_ledger_canister); // init liquidity pool LiquidityPool::init(); // set roles @@ -73,7 +75,7 @@ impl FlyCanister { fn set_timers() { #[cfg(target_family = "wasm")] async fn swap_icp_to_btc_timer() { - let xrc_principal = Configuration::set_xrc_canister(); + let xrc_principal = Configuration::get_xrc_canister(); let _ = LiquidityPool::swap_icp_to_btc(xrc_principal).await; } @@ -198,6 +200,22 @@ impl FlyCanister { } Configuration::set_xrc_canister(canister_id); } + + /// Set ckbtc canister + pub fn admin_set_ckbtc_canister(canister_id: Principal) { + if !Inspect::inspect_is_admin(utils::caller()) { + ic_cdk::trap("Unauthorized"); + } + Configuration::set_ckbtc_canister(canister_id); + } + + /// Set icp ledger canister + pub fn admin_set_icp_ledger_canister(canister_id: Principal) { + if !Inspect::inspect_is_admin(utils::caller()) { + ic_cdk::trap("Unauthorized"); + } + Configuration::set_icp_ledger_canister(canister_id); + } } impl Icrc1 for FlyCanister { @@ -498,6 +516,11 @@ mod test { // swap account assert_eq!(Configuration::get_swap_account(), bob_account()); + + // check canisters + assert_eq!(Configuration::get_xrc_canister(), caller()); + assert_eq!(Configuration::get_ckbtc_canister(), caller()); + assert_eq!(Configuration::get_icp_ledger_canister(), caller()); } #[tokio::test] @@ -732,6 +755,22 @@ mod test { assert_eq!(Configuration::get_xrc_canister(), canister_id); } + #[test] + fn test_should_set_ckbtc_canister() { + init_canister(); + let canister_id = Principal::from_str("aaaaa-aa").unwrap(); + FlyCanister::admin_set_ckbtc_canister(canister_id); + assert_eq!(Configuration::get_ckbtc_canister(), canister_id); + } + + #[test] + fn test_should_set_icp_ledger_canister() { + init_canister(); + let canister_id = Principal::from_str("aaaaa-aa").unwrap(); + FlyCanister::admin_set_icp_ledger_canister(canister_id); + assert_eq!(Configuration::get_icp_ledger_canister(), canister_id); + } + #[tokio::test] async fn test_should_get_balance_of() { init_canister(); @@ -1106,6 +1145,8 @@ mod test { (caller_account(), fly_to_picofly(100_000)), ], xrc_canister: caller(), + ckbtc_canister: caller(), + icp_ledger_canister: caller(), }; FlyCanister::init(data); } diff --git a/src/fly/src/app/configuration.rs b/src/fly/src/app/configuration.rs index 7a798f7..18fbcdb 100644 --- a/src/fly/src/app/configuration.rs +++ b/src/fly/src/app/configuration.rs @@ -11,7 +11,8 @@ use ic_stable_structures::{DefaultMemoryImpl, StableCell}; use icrc::icrc1::account::Account; use crate::app::memory::{ - MEMORY_MANAGER, MINTING_ACCOUNT_MEMORY_ID, SWAP_ACCOUNT_MEMORY_ID, XRC_CANISTER_MEMORY_ID, + CKBTC_CANISTER_MEMORY_ID, ICP_LEDGER_CANISTER_MEMORY_ID, MEMORY_MANAGER, + MINTING_ACCOUNT_MEMORY_ID, SWAP_ACCOUNT_MEMORY_ID, XRC_CANISTER_MEMORY_ID, }; thread_local! { @@ -33,11 +34,23 @@ thread_local! { }.into()).unwrap() ); - /// Swap account + /// Xrc canister static XRC_CANISTER: RefCell>> = RefCell::new(StableCell::new(MEMORY_MANAGER.with(|mm| mm.get(XRC_CANISTER_MEMORY_ID)), Principal::anonymous().into()).unwrap() ); + + /// Ckbtc canister + static CKBTC_CANISTER: RefCell>> = + RefCell::new(StableCell::new(MEMORY_MANAGER.with(|mm| mm.get(CKBTC_CANISTER_MEMORY_ID)), + Principal::anonymous().into()).unwrap() + ); + + /// ICP ledger canister + static ICP_LEDGER_CANISTER: RefCell>> = + RefCell::new(StableCell::new(MEMORY_MANAGER.with(|mm| mm.get(ICP_LEDGER_CANISTER_MEMORY_ID)), + Principal::anonymous().into()).unwrap() + ); } /// canister configuration @@ -80,6 +93,32 @@ impl Configuration { pub fn get_xrc_canister() -> Principal { XRC_CANISTER.with(|xrc| xrc.borrow().get().0) } + + /// Set ckbtc canister address + pub fn set_ckbtc_canister(canister_id: Principal) { + CKBTC_CANISTER.with_borrow_mut(|cell| { + cell.set(canister_id.into()).unwrap(); + }); + } + + /// Get ckbtc canister address + #[allow(dead_code)] + pub fn get_ckbtc_canister() -> Principal { + CKBTC_CANISTER.with(|ckbtc| ckbtc.borrow().get().0) + } + + /// Set icp ledger canister address + pub fn set_icp_ledger_canister(canister_id: Principal) { + ICP_LEDGER_CANISTER.with_borrow_mut(|cell| { + cell.set(canister_id.into()).unwrap(); + }); + } + + /// Get icp ledger canister address + #[allow(dead_code)] + pub fn get_icp_ledger_canister() -> Principal { + ICP_LEDGER_CANISTER.with(|icp| icp.borrow().get().0) + } } #[cfg(test)] @@ -114,4 +153,22 @@ mod test { Configuration::set_xrc_canister(principal); assert_eq!(Configuration::get_xrc_canister(), principal); } + + #[test] + fn test_should_set_icp_canister() { + let principal = + Principal::from_str("bs5l3-6b3zu-dpqyj-p2x4a-jyg4k-goneb-afof2-y5d62-skt67-3756q-dqe") + .unwrap(); + Configuration::set_icp_ledger_canister(principal); + assert_eq!(Configuration::get_icp_ledger_canister(), principal); + } + + #[test] + fn test_should_set_ckbtc_canister() { + let principal = + Principal::from_str("bs5l3-6b3zu-dpqyj-p2x4a-jyg4k-goneb-afof2-y5d62-skt67-3756q-dqe") + .unwrap(); + Configuration::set_ckbtc_canister(principal); + assert_eq!(Configuration::get_ckbtc_canister(), principal); + } } diff --git a/src/fly/src/app/liquidity_pool/ckbtc.rs b/src/fly/src/app/liquidity_pool/ckbtc.rs index 99aa86d..de6a313 100644 --- a/src/fly/src/app/liquidity_pool/ckbtc.rs +++ b/src/fly/src/app/liquidity_pool/ckbtc.rs @@ -93,6 +93,6 @@ impl CkBtc { #[inline] #[cfg(target_arch = "wasm32")] fn ckbtc_ledger_canister() -> Principal { - Principal::from_text(crate::constants::CKBTC_LEDGER_CANISTER).unwrap() + crate::app::Configuration::get_ckbtc_canister() } } diff --git a/src/fly/src/app/liquidity_pool/icp_ledger.rs b/src/fly/src/app/liquidity_pool/icp_ledger.rs index 5051e57..6fa03e5 100644 --- a/src/fly/src/app/liquidity_pool/icp_ledger.rs +++ b/src/fly/src/app/liquidity_pool/icp_ledger.rs @@ -60,7 +60,7 @@ impl IcpLedger { #[inline] #[cfg(target_arch = "wasm32")] fn icp_ledger_canister() -> Principal { - Principal::from_text(crate::constants::ICP_LEDGER_CANISTER).unwrap() + crate::app::Configuration::get_icp_ledger_canister() } } diff --git a/src/fly/src/app/memory.rs b/src/fly/src/app/memory.rs index 5aeb2c3..f16fd2a 100644 --- a/src/fly/src/app/memory.rs +++ b/src/fly/src/app/memory.rs @@ -12,6 +12,8 @@ pub const MINTING_ACCOUNT_MEMORY_ID: MemoryId = MemoryId::new(20); pub const ROLES_MEMORY_ID: MemoryId = MemoryId::new(21); pub const SWAP_ACCOUNT_MEMORY_ID: MemoryId = MemoryId::new(22); pub const XRC_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(23); +pub const CKBTC_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(24); +pub const ICP_LEDGER_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(25); // Rewards pub const RMC_MEMORY_ID: MemoryId = MemoryId::new(30); diff --git a/src/fly/src/constants.rs b/src/fly/src/constants.rs index 8870f9d..ae99725 100644 --- a/src/fly/src/constants.rs +++ b/src/fly/src/constants.rs @@ -18,13 +18,6 @@ pub const INITIAL_RMC: f64 = 0.0000042; /// Minimum reward pub const MIN_REWARD: u64 = ICRC1_FEE * 2; -/// The ledger canister id of the ICP token -#[cfg(target_arch = "wasm32")] -pub const ICP_LEDGER_CANISTER: &str = "ryjl3-tyaaa-aaaaa-aaaba-cai"; -/// The ledger canister id of the CKBTC token -#[cfg(target_arch = "wasm32")] -pub const CKBTC_LEDGER_CANISTER: &str = "mxzaz-hqaaa-aaaar-qaada-cai"; - #[cfg(target_family = "wasm")] pub const SPEND_ALLOWANCE_EXPIRED_ALLOWANCE_TIMER_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24 * 7); // 7 days diff --git a/src/fly/src/lib.rs b/src/fly/src/lib.rs index de40af9..9f771a2 100644 --- a/src/fly/src/lib.rs +++ b/src/fly/src/lib.rs @@ -102,6 +102,18 @@ pub fn admin_set_xrc_canister(canister_id: Principal) { FlyCanister::admin_set_xrc_canister(canister_id) } +#[update] +#[candid_method(update)] +pub fn admin_set_ckbtc_canister(canister_id: Principal) { + FlyCanister::admin_set_ckbtc_canister(canister_id) +} + +#[update] +#[candid_method(update)] +pub fn admin_set_icp_ledger_canister(canister_id: Principal) { + FlyCanister::admin_set_icp_ledger_canister(canister_id) +} + #[query] #[candid_method(query)] pub fn get_transaction(id: u64) -> FlyResult { diff --git a/src/marketplace/marketplace.did b/src/marketplace/marketplace.did index 956855d..9a826f1 100644 --- a/src/marketplace/marketplace.did +++ b/src/marketplace/marketplace.did @@ -54,6 +54,7 @@ type MarketplaceError = variant { }; type MarketplaceInitData = record { deferred_canister : principal; + icp_ledger_canister : principal; fly_canister : principal; xrc_canister : principal; admins : vec principal; @@ -128,6 +129,7 @@ service : (MarketplaceInitData) -> { admin_set_admins : (vec principal) -> (Result); admin_set_deferred_canister : (principal) -> (); admin_set_fly_canister : (principal) -> (Result); + admin_set_icp_ledger_canister : (principal) -> (); admin_set_interest_rate_for_buyer : (float64) -> (); admin_set_xrc_canister : (principal) -> (); buy_token : (nat, opt vec nat8) -> (Result); diff --git a/src/marketplace/src/app.rs b/src/marketplace/src/app.rs index 378b747..a2991a1 100644 --- a/src/marketplace/src/app.rs +++ b/src/marketplace/src/app.rs @@ -13,7 +13,7 @@ use dip721::TokenIdentifier; use icrc::icrc1::account::{Account, Subaccount}; use icrc::icrc2; -use self::configuration::Configuration; +pub use self::configuration::Configuration; pub use self::inspect::Inspect; use self::roles::RolesManager; use crate::app::exchange_rate::ExchangeRate; @@ -36,6 +36,7 @@ impl Marketplace { Configuration::set_deferred_canister(data.deferred_canister); Configuration::set_fly_canister(data.fly_canister); Configuration::set_xrc_canister(data.xrc_canister); + Configuration::set_icp_ledger_canister(data.icp_ledger_canister); RolesManager::set_admins(data.admins).unwrap(); } @@ -90,6 +91,14 @@ impl Marketplace { Configuration::set_xrc_canister(canister_id); } + /// Set icp ledger canister + pub fn admin_set_icp_ledger_canister(canister_id: Principal) { + if !Inspect::inspect_is_admin(caller()) { + ic_cdk::trap("Unauthorized"); + } + Configuration::set_icp_ledger_canister(canister_id); + } + /// Given a token id, returns the price of the token in ICP. pub async fn get_token_price_icp(token_id: TokenIdentifier) -> MarketplaceResult { // get token info @@ -289,6 +298,10 @@ mod test { assert_eq!(Configuration::get_deferred_canister(), deferred_canister()); assert_eq!(Configuration::get_fly_canister(), fly_canister()); assert_eq!(RolesManager::get_admins(), vec![caller()]); + + // check canisters + assert_eq!(Configuration::get_xrc_canister(), caller()); + assert_eq!(Configuration::get_icp_ledger_canister(), caller()); } #[tokio::test] @@ -466,10 +479,19 @@ mod test { .is_ok()); } + #[test] + fn test_should_set_icp_ledger_canister() { + init_canister(); + let canister_id = Principal::from_str("aaaaa-aa").unwrap(); + Marketplace::admin_set_icp_ledger_canister(canister_id); + assert_eq!(Configuration::get_icp_ledger_canister(), canister_id); + } + fn init_canister() { let data = MarketplaceInitData { deferred_canister: deferred_canister(), fly_canister: fly_canister(), + icp_ledger_canister: caller(), admins: vec![caller()], xrc_canister: caller(), }; diff --git a/src/marketplace/src/app/configuration.rs b/src/marketplace/src/app/configuration.rs index e4eecec..fa9eaff 100644 --- a/src/marketplace/src/app/configuration.rs +++ b/src/marketplace/src/app/configuration.rs @@ -13,7 +13,8 @@ use icrc::icrc1::account::Account; use crate::app::memory::{ DEFERRED_CANISTER_MEMORY_ID, FLY_CANISTER_MEMORY_ID, FLY_LIQUIDITY_POOL_ACCOUNT_MEMORY_ID, - INTEREST_FOR_BUYER_MEMORY_ID, MEMORY_MANAGER, XRC_CANISTER_MEMORY_ID, + ICP_LEDGER_CANISTER_MEMORY_ID, INTEREST_FOR_BUYER_MEMORY_ID, MEMORY_MANAGER, + XRC_CANISTER_MEMORY_ID, }; use crate::client::FlyClient; use crate::constants::DEFAULT_INTEREST_MULTIPLIER_FOR_BUYER; @@ -51,6 +52,12 @@ thread_local! { RefCell::new(StableCell::new(MEMORY_MANAGER.with(|mm| mm.get(XRC_CANISTER_MEMORY_ID)), Principal::anonymous().into()).unwrap() ); + + /// ICP ledger canister + static ICP_LEDGER_CANISTER: RefCell>> = + RefCell::new(StableCell::new(MEMORY_MANAGER.with(|mm| mm.get(ICP_LEDGER_CANISTER_MEMORY_ID)), + Principal::anonymous().into()).unwrap() + ); } /// canister configuration @@ -127,6 +134,19 @@ impl Configuration { pub fn get_xrc_canister() -> Principal { XRC_CANISTER.with(|xrc| xrc.borrow().get().0) } + + /// Set icp ledger canister address + pub fn set_icp_ledger_canister(canister_id: Principal) { + ICP_LEDGER_CANISTER.with_borrow_mut(|cell| { + cell.set(canister_id.into()).unwrap(); + }); + } + + /// Get icp ledger canister address + #[allow(dead_code)] + pub fn get_icp_ledger_canister() -> Principal { + ICP_LEDGER_CANISTER.with(|icp| icp.borrow().get().0) + } } #[cfg(test)] @@ -182,4 +202,13 @@ mod test { Configuration::set_xrc_canister(principal); assert_eq!(Configuration::get_xrc_canister(), principal); } + + #[test] + fn test_should_set_icp_canister() { + let principal = + Principal::from_str("bs5l3-6b3zu-dpqyj-p2x4a-jyg4k-goneb-afof2-y5d62-skt67-3756q-dqe") + .unwrap(); + Configuration::set_icp_ledger_canister(principal); + assert_eq!(Configuration::get_icp_ledger_canister(), principal); + } } diff --git a/src/marketplace/src/app/memory.rs b/src/marketplace/src/app/memory.rs index 9fb6fa2..e42baf8 100644 --- a/src/marketplace/src/app/memory.rs +++ b/src/marketplace/src/app/memory.rs @@ -8,6 +8,7 @@ pub const FLY_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(22); pub const INTEREST_FOR_BUYER_MEMORY_ID: MemoryId = MemoryId::new(23); pub const FLY_LIQUIDITY_POOL_ACCOUNT_MEMORY_ID: MemoryId = MemoryId::new(24); pub const XRC_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(25); +pub const ICP_LEDGER_CANISTER_MEMORY_ID: MemoryId = MemoryId::new(26); thread_local! { /// Memory manager diff --git a/src/marketplace/src/client/icp_ledger.rs b/src/marketplace/src/client/icp_ledger.rs index b2827e4..6f81b25 100644 --- a/src/marketplace/src/client/icp_ledger.rs +++ b/src/marketplace/src/client/icp_ledger.rs @@ -75,6 +75,6 @@ impl IcpLedgerClient { #[inline] #[cfg(target_arch = "wasm32")] fn icp_ledger_canister() -> Principal { - Principal::from_text(crate::constants::ICP_LEDGER_CANISTER).unwrap() + crate::app::Configuration::get_icp_ledger_canister() } } diff --git a/src/marketplace/src/constants.rs b/src/marketplace/src/constants.rs index 3a5b892..7541ca8 100644 --- a/src/marketplace/src/constants.rs +++ b/src/marketplace/src/constants.rs @@ -3,7 +3,3 @@ pub const NANOSECONDS_IN_A_DAY: u64 = 86_400_000_000_000; /// The interest multiplier to apply to a deferred nft price in case the caller is a contract buyer (10%) pub const DEFAULT_INTEREST_MULTIPLIER_FOR_BUYER: f64 = 1.1; - -/// The ledger canister id of the ICP token -#[cfg(target_arch = "wasm32")] -pub const ICP_LEDGER_CANISTER: &str = "ryjl3-tyaaa-aaaaa-aaaba-cai"; diff --git a/src/marketplace/src/lib.rs b/src/marketplace/src/lib.rs index a54e2db..f2248be 100644 --- a/src/marketplace/src/lib.rs +++ b/src/marketplace/src/lib.rs @@ -41,6 +41,12 @@ pub fn admin_set_xrc_canister(canister: Principal) { Marketplace::admin_set_xrc_canister(canister) } +#[update] +#[candid_method(update)] +pub fn admin_set_icp_ledger_canister(canister: Principal) { + Marketplace::admin_set_icp_ledger_canister(canister) +} + #[update] #[candid_method(update)] pub async fn admin_set_fly_canister(canister: Principal) -> MarketplaceResult<()> {