From 31cb15197604b7a0fc57a819450da872cc556d37 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Tue, 5 Nov 2024 06:43:41 +0530 Subject: [PATCH 01/20] init --- .../src/api/cdao/mod.rs | 1 + .../src/api/cdao/swap.rs | 57 +++++++++++++++++++ .../individual_user_template/src/lib.rs | 5 +- .../individual_user_template/types/error.rs | 16 +++++- 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/canister/individual_user_template/src/api/cdao/swap.rs diff --git a/src/canister/individual_user_template/src/api/cdao/mod.rs b/src/canister/individual_user_template/src/api/cdao/mod.rs index be5a9e6a..42d45d14 100644 --- a/src/canister/individual_user_template/src/api/cdao/mod.rs +++ b/src/canister/individual_user_template/src/api/cdao/mod.rs @@ -1,5 +1,6 @@ mod token; mod airdrop; +pub mod swap; use std::collections::{HashMap, HashSet, VecDeque}; use candid::{Encode, Principal}; diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs new file mode 100644 index 00000000..7eddd706 --- /dev/null +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -0,0 +1,57 @@ +use candid::{CandidType, Nat, Principal}; +use ic_cdk::api::time; +use ic_cdk_macros::update; +use icrc_ledger_types::icrc2::approve::{ApproveArgs, ApproveError}; +use serde::Deserialize; +use serde_json::Value; +use shared_utils::canister_specific::individual_user_template::types::error::SwapError; + +#[update] +pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ + let TokenPairs{token_a, token_b} = token_pairs; + + if !is_icrc2_supported_token(token_a.ledger).await? || !is_icrc2_supported_token(token_b.ledger).await?{ + return Err(SwapError::UnsupportedToken); + } + + let start_time = time(); + let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ + from_subaccount: None, + spender: ic_cdk::id().into(), + amount: token_a.amt, + expected_allowance: None, + memo: None, + expires_at: Some(start_time + SWAP_REQUEST_EXPIRY), + fee: None, + created_at_time: Some(start_time) + }, )).await?; + + allocation_res.0.map_err(SwapError::ApproveError)?; + // TODO: Add the request to the push notification + Ok(()) +} + +const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk + +async fn is_icrc2_supported_token(token_ledger: Principal) -> Result{ + let res: (Vec, ) = ic_cdk::call(token_ledger, "icrc1_supported_standards", ()).await?; + Ok(res.0.iter().any(|v| v.name == "ICRC2")) +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +struct SupportedStandards{ + name: String, + url: String +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct SwapTokenData{ + pub ledger: Principal, + pub amt: Nat +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct TokenPairs{ + pub token_a: SwapTokenData, + pub token_b: SwapTokenData +} diff --git a/src/canister/individual_user_template/src/lib.rs b/src/canister/individual_user_template/src/lib.rs index 5d20f0df..33825110 100644 --- a/src/canister/individual_user_template/src/lib.rs +++ b/src/canister/individual_user_template/src/lib.rs @@ -21,7 +21,7 @@ use shared_utils::{ device_id::DeviceIdentity, error::{ BetOnCurrentlyViewingPostError, CdaoDeployError, CdaoTokenError, - FollowAnotherUserProfileError, GetPostsOfUserProfileError, AirdropError + FollowAnotherUserProfileError, GetPostsOfUserProfileError, AirdropError, SwapError }, follow::{FollowEntryDetail, FollowEntryId}, hot_or_not::{BetDetails, BetOutcomeForBetMaker, BettingStatus, PlacedBetDetail}, @@ -48,13 +48,16 @@ use shared_utils::{ types::canister_specific::individual_user_template::error_types::{ GetUserUtilityTokenTransactionHistoryError, UpdateProfileSetUniqueUsernameError, }, + }; +use crate::api::cdao::swap::TokenPairs; mod api; pub mod data_model; mod util; thread_local! { + static CANISTER_DATA: RefCell = RefCell::default(); static SNAPSHOT_DATA: RefCell> = RefCell::default(); } diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs index 3da15695..0f7a97d0 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs @@ -1,6 +1,6 @@ use candid::{CandidType, Deserialize}; use ic_cdk::api::call::RejectionCode; -use icrc_ledger_types::icrc1::transfer::TransferError; +use icrc_ledger_types::{icrc1::transfer::TransferError, icrc2::approve::ApproveError}; #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] pub enum GetPostsOfUserProfileError { @@ -89,3 +89,17 @@ impl From<(RejectionCode, String)> for AirdropError { AirdropError::CallError(value.0, value.1) } } + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub enum SwapError{ + InvalidLedger, + SwapAllocationExhausted, + UnsupportedToken, + CallError(RejectionCode, String), + ApproveError(ApproveError), +} +impl From<(RejectionCode, String)> for SwapError { + fn from(value: (RejectionCode, String)) -> Self { + SwapError::CallError(value.0, value.1) + } +} \ No newline at end of file From ac7d7f4dd582c90f14d9edbc51c7930c8fdcaac2 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 05:02:23 +0530 Subject: [PATCH 02/20] added request pipeline --- .../src/api/cdao/mod.rs | 1 + .../src/api/cdao/swap.rs | 158 +++++++++++++++++- .../individual_user_template/src/lib.rs | 1 + .../types/cdao/mod.rs | 5 +- .../individual_user_template/types/error.rs | 5 +- 5 files changed, 160 insertions(+), 10 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/mod.rs b/src/canister/individual_user_template/src/api/cdao/mod.rs index 42d45d14..968fa391 100644 --- a/src/canister/individual_user_template/src/api/cdao/mod.rs +++ b/src/canister/individual_user_template/src/api/cdao/mod.rs @@ -276,6 +276,7 @@ async fn deploy_cdao_sns( swap: swap.0, index: index.0, airdrop_info: AirdropInfo { principals_who_successfully_claimed: HashMap::new() }, + last_swapped_price: None, }; CANISTER_DATA.with(|cdata| { let mut cdata = cdata.borrow_mut(); diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 7eddd706..452ca045 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -1,10 +1,11 @@ use candid::{CandidType, Nat, Principal}; -use ic_cdk::api::time; -use ic_cdk_macros::update; -use icrc_ledger_types::icrc2::approve::{ApproveArgs, ApproveError}; +use ic_cdk::api::{management_canister::main::CanisterInfoRequest, time}; +use ic_cdk_macros::{query, update}; +use icrc_ledger_types::icrc2::{approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; use serde::Deserialize; -use serde_json::Value; -use shared_utils::canister_specific::individual_user_template::types::error::SwapError; +use shared_utils::canister_specific::individual_user_template::types::{cdao::DeployedCdaoCanisters, error::SwapError}; + +use crate::CANISTER_DATA; #[update] pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ @@ -14,16 +15,19 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ return Err(SwapError::UnsupportedToken); } - let start_time = time(); + if CANISTER_DATA.with_borrow(|data| data.cdao_canisters.iter().find(|cdao| cdao.ledger == token_b.ledger).cloned()).is_none(){ + return Err(SwapError::IsNotTokenCreator); + } + let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ from_subaccount: None, spender: ic_cdk::id().into(), amount: token_a.amt, expected_allowance: None, memo: None, - expires_at: Some(start_time + SWAP_REQUEST_EXPIRY), + expires_at: Some(time() + SWAP_REQUEST_EXPIRY), fee: None, - created_at_time: Some(start_time) + created_at_time: None }, )).await?; allocation_res.0.map_err(SwapError::ApproveError)?; @@ -31,6 +35,144 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ Ok(()) } +// Creator principal is the caller here +#[update] +pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError>{ + match op{ + SwapRequestActions::Accept { token_a, token_b, requester } => { + let transfer_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_transfer_from", (TransferFromArgs{ + from: requester.into(), + spender_subaccount: None, + to: ic_cdk::caller().into(), + amount: token_a.amt, + fee: None, + memo: None, + created_at_time: None + }, )).await?; + transfer_res.0.map_err(SwapError::TransferFromError)?; + + let transfer_res: (Result, ) = ic_cdk::call(token_b.ledger, "icrc2_transfer_from", (TransferFromArgs{ + from: ic_cdk::caller().into(), + spender_subaccount: None, + to: requester.into(), + amount: token_b.amt, + fee: None, + memo: None, + created_at_time: None + }, )).await?; + transfer_res.0.map_err(SwapError::TransferFromError)?; + + let token_a_price = get_token_price(token_a.ledger).await?; + if let Some(price) = token_a_price{ + CANISTER_DATA.with_borrow_mut(|data| { + data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_b.ledger).map(|cdao| cdao.last_swapped_price = Some(price)); + }); + } + // Clear and send push notifs to both parties + }, + SwapRequestActions::Reject { token_a, token_b, requester } => { + // Reject and send push notifs to both parties + // Cannot remove the approval as it is not possible to do so + } + } + + Ok(()) +} + +async fn get_token_price(token_ledger: Principal) -> Result, SwapError>{ + let price: Result<(PriceData, ), _> = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await; + + match price{ + Ok((price, )) => Ok(Some(price.price_usd)), + Err(_) => { + let owner_canister = get_token_owner_from_ledger(token_ledger).await?; + let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; + Ok(price.0) + } + } +} + +#[query] +pub fn get_token_price_by_ledger(token_ledger: Principal) -> Result, SwapError>{ + CANISTER_DATA.with_borrow(|data| { + data.cdao_canisters.iter().find(|cdao| cdao.ledger == token_ledger).map(|cdao| cdao.last_swapped_price.clone()).ok_or(SwapError::NoController) + }) +} +async fn get_token_owner_from_ledger(ledger: Principal) -> Result{ + let res = ic_cdk::api::management_canister::main::canister_info(CanisterInfoRequest{ + canister_id: ledger, + num_requested_changes: None + }).await?.0; + for f in res.controllers.into_iter().filter(|f| f.to_text().ends_with("-cai")){ + let res: Result<(Vec, ), _> = ic_cdk::call(f, "deployed_cdao_canisters", ()).await; + + match res{ + Ok((cdao, )) => { + if cdao.iter().any(|cdao| cdao.ledger == ledger){ + return Ok(f); + } + }, + Err(_) => continue + }; + } + + Err(SwapError::NoController) +} +#[derive(CandidType, Deserialize, PartialEq, Debug)] +pub struct PriceData{ + id: Nat, + #[serde(rename = "volumeUSD1d")] + volume_usd_1d: f64, + #[serde(rename = "volumeUSD7d")] + volume_usd_7d: f64, + #[serde(rename = "totalVolumeUSD")] + total_volume_usd: f64, + name: String, + #[serde(rename = "volumeUSD")] + volume_usd: f64, + #[serde(rename = "feesUSD")] + fees_usd: f64, + #[serde(rename = "priceUSDChange")] + price_usd_change: f64, + address: String, + #[serde(rename = "txCount")] + tx_count: u64, + #[serde(rename = "priceUSD")] + price_usd: f64, + standard: String, + symbol: String +} +// Caller needs to be the requester principal +pub async fn cancel_swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ + let TokenPairs{token_a, ..} = token_pairs; + let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ + from_subaccount: None, + spender: ic_cdk::id().into(), + amount: 0u32.into(), // icrc2 docs revoking approval + expected_allowance: None, + memo: None, + expires_at: Some(time() + SWAP_REQUEST_EXPIRY), + fee: None, + created_at_time: None + }, )).await?; + + allocation_res.0.map_err(SwapError::ApproveError)?; + Ok(()) +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub enum SwapRequestActions{ + Accept{ + token_a: SwapTokenData, + token_b: SwapTokenData, + requester: Principal + }, + Reject{ + token_a: SwapTokenData, + token_b: SwapTokenData, + requester: Principal + } +} const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk async fn is_icrc2_supported_token(token_ledger: Principal) -> Result{ diff --git a/src/canister/individual_user_template/src/lib.rs b/src/canister/individual_user_template/src/lib.rs index 33825110..b34f7f10 100644 --- a/src/canister/individual_user_template/src/lib.rs +++ b/src/canister/individual_user_template/src/lib.rs @@ -51,6 +51,7 @@ use shared_utils::{ }; use crate::api::cdao::swap::TokenPairs; +use crate::api::cdao::swap::SwapRequestActions; mod api; pub mod data_model; diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs index 2b630fe2..4d229e67 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use candid::{CandidType, Principal}; use serde::{Deserialize, Serialize}; -#[derive(CandidType, PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] +#[derive(CandidType, PartialEq, Debug, Serialize, Deserialize, Clone)] pub struct DeployedCdaoCanisters { pub governance: Principal, pub ledger: Principal, @@ -13,6 +13,9 @@ pub struct DeployedCdaoCanisters { #[serde(default)] pub airdrop_info: AirdropInfo, + + #[serde(default)] + pub last_swapped_price: Option } #[derive(CandidType, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)] diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs index 0f7a97d0..ece10c3d 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs @@ -1,6 +1,6 @@ use candid::{CandidType, Deserialize}; use ic_cdk::api::call::RejectionCode; -use icrc_ledger_types::{icrc1::transfer::TransferError, icrc2::approve::ApproveError}; +use icrc_ledger_types::{icrc1::transfer::TransferError, icrc2::{approve::ApproveError, transfer_from::TransferFromError}}; #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] pub enum GetPostsOfUserProfileError { @@ -97,6 +97,9 @@ pub enum SwapError{ UnsupportedToken, CallError(RejectionCode, String), ApproveError(ApproveError), + IsNotTokenCreator, + NoController, + TransferFromError(TransferFromError) } impl From<(RejectionCode, String)> for SwapError { fn from(value: (RejectionCode, String)) -> Self { From 75fe8aef402018f6ea3b9464176525f9d5caf3e4 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 14:00:06 +0530 Subject: [PATCH 03/20] added swap tests --- .../src/api/cdao/swap.rs | 76 +- .../tests/creator_dao/main.rs | 704 +++++++++++++++++- .../tests/creator_dao/types.rs | 32 +- 3 files changed, 765 insertions(+), 47 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 452ca045..70c074a2 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -11,7 +11,7 @@ use crate::CANISTER_DATA; pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ let TokenPairs{token_a, token_b} = token_pairs; - if !is_icrc2_supported_token(token_a.ledger).await? || !is_icrc2_supported_token(token_b.ledger).await?{ + if !is_icrc2_supported_token(token_a.ledger).await?{ return Err(SwapError::UnsupportedToken); } @@ -31,7 +31,7 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ }, )).await?; allocation_res.0.map_err(SwapError::ApproveError)?; - // TODO: Add the request to the push notification + // TODO: Push notifications Ok(()) } @@ -39,7 +39,8 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ #[update] pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError>{ match op{ - SwapRequestActions::Accept { token_a, token_b, requester } => { + SwapRequestActions::Accept { token_pairs, requester } => { + let TokenPairs{token_a, token_b} = token_pairs; let transfer_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_transfer_from", (TransferFromArgs{ from: requester.into(), spender_subaccount: None, @@ -65,12 +66,15 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError let token_a_price = get_token_price(token_a.ledger).await?; if let Some(price) = token_a_price{ CANISTER_DATA.with_borrow_mut(|data| { - data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_b.ledger).map(|cdao| cdao.last_swapped_price = Some(price)); + if let Some(cdao) = data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_b.ledger){ + cdao.last_swapped_price = Some(price); + } }); } - // Clear and send push notifs to both parties + // send push notifs to both parties + // Cannot remove the approval as it is not possible to do so }, - SwapRequestActions::Reject { token_a, token_b, requester } => { + SwapRequestActions::Reject { token_pairs, requester } => { // Reject and send push notifs to both parties // Cannot remove the approval as it is not possible to do so } @@ -80,24 +84,29 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError } async fn get_token_price(token_ledger: Principal) -> Result, SwapError>{ - let price: Result<(PriceData, ), _> = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await; - - match price{ - Ok((price, )) => Ok(Some(price.price_usd)), - Err(_) => { - let owner_canister = get_token_owner_from_ledger(token_ledger).await?; - let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; - Ok(price.0) - } - } + // let price: Result<(PriceData, ), _> = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await; + + // match price{ + // Ok((price, )) => Ok(Some(price.price_usd)), + // Err(_) => { + // let owner_canister = get_token_owner_from_ledger(token_ledger).await?; + // let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; + // Ok(price.0) + // } + // } + + let owner_canister = get_token_owner_from_ledger(token_ledger).await?; + let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; + Ok(price.0) } #[query] pub fn get_token_price_by_ledger(token_ledger: Principal) -> Result, SwapError>{ CANISTER_DATA.with_borrow(|data| { - data.cdao_canisters.iter().find(|cdao| cdao.ledger == token_ledger).map(|cdao| cdao.last_swapped_price.clone()).ok_or(SwapError::NoController) + data.cdao_canisters.iter().find(|cdao| cdao.ledger == token_ledger).map(|cdao| cdao.last_swapped_price).ok_or(SwapError::NoController) }) } + async fn get_token_owner_from_ledger(ledger: Principal) -> Result{ let res = ic_cdk::api::management_canister::main::canister_info(CanisterInfoRequest{ canister_id: ledger, @@ -118,30 +127,8 @@ async fn get_token_owner_from_ledger(ledger: Principal) -> Result Result<(), SwapError>{ let TokenPairs{token_a, ..} = token_pairs; @@ -163,16 +150,15 @@ pub async fn cancel_swap_request(token_pairs: TokenPairs) -> Result<(), SwapErro #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] pub enum SwapRequestActions{ Accept{ - token_a: SwapTokenData, - token_b: SwapTokenData, + token_pairs: TokenPairs, requester: Principal }, Reject{ - token_a: SwapTokenData, - token_b: SwapTokenData, + token_pairs: TokenPairs, requester: Principal } } + const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk async fn is_icrc2_supported_token(token_ledger: Principal) -> Result{ diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index dc19c9ef..9352af0d 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -15,8 +15,9 @@ use ic_sns_swap::pb::v1::{ NewSaleTicketResponse, RefreshBuyerTokensRequest, RefreshBuyerTokensResponse, }; use sha2::{Digest, Sha256}; -use shared_utils::canister_specific::individual_user_template::types::error::{AirdropError, CdaoTokenError}; +use shared_utils::canister_specific::individual_user_template::types::error::{AirdropError, CdaoTokenError, SwapError}; use test_utils::setup::test_constants::get_mock_user_bob_principal_id; +use types::{SwapRequestActions, SwapTokenData, TokenPairs}; use std::time::{Duration, UNIX_EPOCH}; use std::{collections::HashMap, fmt::Debug, str::FromStr, time::SystemTime, vec}; @@ -884,4 +885,705 @@ fn creator_dao_tests() { ic_cdk::println!("๐Ÿงช SNS token Balance of alice canister: {:?}", alice_bal); assert!(bob_bal == 100u64); + + + + // Swap Tests + // initing another cdao token for bob + + let bob_initial_cycle_balance = pocket_ic.cycle_balance(bob_canister_id); + + let sns_wasm_w_canister_wasm = include_bytes!("../../../../../wasms/sns-wasm-canister.wasm"); + let sns_wasm_w_canister_id = Principal::from_text(SNS_WASM_W_PRINCIPAL_ID).unwrap(); + + let _ = pocket_ic.create_canister_with_id( + Some(super_admin), + None, + Principal::from_text(SNS_WASM_W_PRINCIPAL_ID).unwrap(), + ); + + let sns_wasm_canister_init_payload = SnsWasmCanisterInitPayload { + sns_subnet_ids: vec![], + access_controls_enabled: false, + allowed_principals: vec![], + }; + + pocket_ic.install_canister( + sns_wasm_w_canister_id, + sns_wasm_w_canister_wasm.to_vec(), + Encode!(&sns_wasm_canister_init_payload).unwrap(), + Some(super_admin), + ); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/root.wasm.gz"), + 1, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/governance.wasm.gz"), + 2, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/ledger.wasm.gz"), + 3, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/swap.wasm.gz"), + 4, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/archive.wasm.gz"), + 5, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + super_admin, + "add_wasm", + candid::encode_one(add_wasm( + include_bytes!("../../../../../wasms/index.wasm.gz"), + 6, + )) + .unwrap(), + ) + .map(|res| { + let response: AddWasmResultRecord = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + for _ in 0..50 { + pocket_ic.tick(); + } + + let res = pocket_ic + .update_call( + sns_wasm_w_canister_id, + Principal::anonymous(), + "get_latest_sns_version_pretty".into(), + candid::encode_one(()).unwrap(), + ) + .map(|res| { + let response: HashMap = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + + ic_cdk::println!("๐Ÿงช HASHMAP {:?}", res); + assert_eq!(res.len(), 6); + let start = SystemTime::now(); + + let tx_fee = 1u64; + + let sns_init_args = SnsInitPayload { + confirmation_text: Some("GET RICH QUICK".to_string()), + transaction_fee_e8s: Some(tx_fee), + token_name: Some("Simulation Governance".to_string()), + token_symbol: Some("SIMG".to_string()), + proposal_reject_cost_e8s: Some(1u64), + neuron_minimum_stake_e8s: Some(2u64), + fallback_controller_principal_ids: vec![super_admin.to_string().clone()], + logo: Some("data:image/png;base64,iVBORw0".to_string()), + url: Some("https://google.com".to_string()), + name: Some("Simulation Gov".to_string()), + description: Some("Simulation gov desc".to_string()), + neuron_minimum_dissolve_delay_to_vote_seconds: Some(1), + initial_reward_rate_basis_points: Some(30u64), + final_reward_rate_basis_points: Some(20u64), + reward_rate_transition_duration_seconds: Some(1u64), + max_dissolve_delay_seconds: Some(5u64), + max_neuron_age_seconds_for_age_bonus: Some(1u64), + max_dissolve_delay_bonus_percentage: Some(10u64), + max_age_bonus_percentage: Some(10u64), + initial_voting_period_seconds: Some(86401u64), + wait_for_quiet_deadline_increase_seconds: Some(1u64), + restricted_countries: None, + dapp_canisters: None, + min_participants: Some(1), + min_icp_e8s: None, + max_icp_e8s: None, + min_direct_participation_icp_e8s: Some(15u64), + min_participant_icp_e8s: Some(2000u64), + max_direct_participation_icp_e8s: Some(100_000_000u64), + max_participant_icp_e8s: Some(100_000_000u64), + swap_start_timestamp_seconds: Some(start.duration_since(UNIX_EPOCH).unwrap().as_secs()), + swap_due_timestamp_seconds: Some(start.duration_since(UNIX_EPOCH).unwrap().as_secs() + 300), // year 3000 - hopefully we'll all be gone by then, + neuron_basket_construction_parameters: Some(NeuronBasketConstructionParameters { + count: 2, + dissolve_delay_interval_seconds: 2, + }), + nns_proposal_id: Some(1), + neurons_fund_participation: Some(false), + neurons_fund_participants: None, + token_logo: Some("data:image/png;base64,iVBORw0".to_string()), + neurons_fund_participation_constraints: None, + initial_token_distribution: Some(InitialTokenDistribution::FractionalDeveloperVotingPower( + FractionalDeveloperVotingPower { + airdrop_distribution: Some(AirdropDistribution { + airdrop_neurons: vec![], + }), + developer_distribution: Some(DeveloperDistribution { + developer_neurons: vec![ + NeuronDistribution { + controller: Some( + PrincipalId::from_str(&alice_principal.to_string()).unwrap(), + ), + stake_e8s: 4_400_000, + memo: 0, + dissolve_delay_seconds: 0, + vesting_period_seconds: None, + }, + NeuronDistribution { + controller: Some( + PrincipalId::from_str(&alice_principal.to_string()).unwrap(), + ), + stake_e8s: 100_000, + memo: 1, + dissolve_delay_seconds: 2, + vesting_period_seconds: None, + }, + ], + }), + treasury_distribution: Some(TreasuryDistribution { + total_e8s: 10_000_000, + }), + swap_distribution: Some(SwapDistribution { + total_e8s: 5_000_000, + initial_swap_amount_e8s: 5_000_000, + }), + }, + )), + }; + + let res = pocket_ic + .update_call( + bob_canister_id, + bob, + "deploy_cdao_sns", + candid::encode_args((sns_init_args, 300 as u64)).unwrap(), + ) + .map(|res| { + let response: Result = match res { + WasmResult::Reply(payload) => { + ic_cdk::println!("๐Ÿงช Call made"); + Decode!(&payload, Result).unwrap() + } + _ => panic!("\n๐Ÿ›‘ deploy cdao failed with {:?}", res), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .query_call( + bob_canister_id, + bob, + "get_well_known_principal_value", + candid::encode_one((KnownPrincipalType::CanisterIdSnsWasm)).unwrap(), + ) + .map(|res| { + let response: Option = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get_well_known_principal_value failed"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res.unwrap().to_string()); + + let res = pocket_ic + .query_call( + alice_canister_id, + alice_principal, + "deployed_cdao_canisters", + candid::encode_one(()).unwrap(), + ) + .map(|res| { + let response: Vec = match res { + WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + for can in &res { + ic_cdk::println!("๐Ÿงช Gov Canister ID: {:?}", can.governance.to_string()); + ic_cdk::println!("๐Ÿงช Ind Canister ID: {:?}", can.index.to_string()); + ic_cdk::println!("๐Ÿงช Ldg Canister ID: {:?}", can.ledger.to_string()); + ic_cdk::println!("๐Ÿงช Rrt Canister ID: {:?}", can.root.to_string()); + ic_cdk::println!("๐Ÿงช Swp Canister ID: {:?}", can.swap.to_string()); + } + + assert!(res.len() == 1); + let res = res[0].clone(); + let root_canister = res.root; + let swap_canister = res.swap; + let gov_canister = res.governance; + let bob_ledger_canister = res.ledger; + + ic_cdk::println!("๐Ÿงช๐Ÿงช๐Ÿงช Swap Canister ID: {:?}", swap_canister.to_string()); + + let res = pocket_ic + .query_call( + Principal::from_text(ICP_LEDGER_CANISTER_ID).unwrap(), + super_admin, + "icrc1_total_supply", + candid::encode_one(()).unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Nat).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + // check super admin icp balance + let res = pocket_ic + .query_call( + Principal::from_text(ICP_LEDGER_CANISTER_ID).unwrap(), + super_admin, + "icrc1_balance_of", + candid::encode_one(types::Icrc1BalanceOfArg { + owner: super_admin, + subaccount: None, + }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Nat).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + swap_canister, + super_admin, + "new_sale_ticket", + candid::encode_one(NewSaleTicketRequest { + amount_icp_e8s: 1000000, + subaccount: None, + }) + .unwrap(), + ) + .map(|res| { + let response: NewSaleTicketResponse = match res { + WasmResult::Reply(payload) => Decode!(&payload, NewSaleTicketResponse).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let subaccount = Subaccount::from(&PrincipalId(super_admin)); + let transfer_args = types::Transaction { + memo: Some(vec![0]), + amount: Nat::from(1000000 as u64), + fee: Some(Nat::from(0 as u64)), + from_subaccount: None, + to: types::Recipient { + owner: swap_canister, + subaccount: Some(subaccount.to_vec()), + }, + created_at_time: None, + }; + let res = pocket_ic + .update_call( + Principal::from_text(ICP_LEDGER_CANISTER_ID).unwrap(), + super_admin, + "icrc1_transfer", + Encode!(&transfer_args).unwrap(), + ) + .map(|res| { + let response: types::TransferResult = match res { + WasmResult::Reply(payload) => Decode!(&payload, types::TransferResult).unwrap(), + _ => panic!("\n๐Ÿ›‘ icrc1_transfer failed with: {:?}", res), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + swap_canister, + super_admin, + "refresh_buyer_tokens", + candid::encode_one(RefreshBuyerTokensRequest { + buyer: super_admin.to_string(), + confirmation_text: Some("GET RICH QUICK".to_string()), + }) + .unwrap(), + ) + .map(|res| { + let response: RefreshBuyerTokensResponse = match res { + WasmResult::Reply(payload) => { + Decode!(&payload, RefreshBuyerTokensResponse).unwrap() + } + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + pocket_ic.advance_time(Duration::from_secs(301)); + for _ in 0..500 { + pocket_ic.tick(); + } + + let res = pocket_ic + .query_call( + swap_canister, + super_admin, + "get_init", + candid::encode_one(GetInitRequest {}).unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, GetInitResponse).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .update_call( + gov_canister, + super_admin, + "list_neurons", + candid::encode_one(ListNeurons { + of_principal: Some(PrincipalId(alice_principal)), + limit: 2, + start_page_at: None, + }) + .unwrap(), + ) + .map(|res| { + let response: ListNeuronsResponse = match res { + WasmResult::Reply(payload) => Decode!(&payload, ListNeuronsResponse).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let neurons = res.neurons; + let mut ix = 0; + if neurons[1].dissolve_state.is_some() { + if let Some(neuron::DissolveState::DissolveDelaySeconds(x)) = + neurons[1].dissolve_state.as_ref() + { + if *x == 0 { + ix = 1; + } + } + } + let neuron_id = neurons[ix].id.as_ref().unwrap().id.clone(); + let amount = neurons[ix].cached_neuron_stake_e8s; + let manage_neuron_arg = ManageNeuron { + subaccount: neuron_id, + command: Some(manage_neuron::Command::Disburse(manage_neuron::Disburse { + to_account: Some(Account { + owner: Some(PrincipalId(bob)), + subaccount: None, + }), + amount: Some(manage_neuron::disburse::Amount { e8s: amount }), + })), + }; + let res = pocket_ic + .update_call( + gov_canister, + bob, + "manage_neuron", + candid::encode_one(manage_neuron_arg).unwrap(), + ) + .map(|res| { + let response: ManageNeuronResponse = match res { + WasmResult::Reply(payload) => Decode!(&payload, ManageNeuronResponse).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Result: {:?}", res); + + let res = pocket_ic + .query_call( + ledger_canister, + bob, + "icrc1_balance_of", + candid::encode_one(types::Icrc1BalanceOfArg { + owner: bob, + subaccount: None, + }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Nat).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช SNS token Balance of bob: {:?}", res); + + let expected_balance = Nat::from(4_400_000 - tx_fee); + ic_cdk::println!("๐Ÿงช Expected Balance: {:?}", expected_balance); + + let bob_canister_final_cycle_balance = pocket_ic.cycle_balance(bob_canister_id); + + assert!(bob_canister_final_cycle_balance > bob_initial_cycle_balance); + + assert!(res == expected_balance); + + let res = pocket_ic + .update_call( + alice_canister_id, + bob, + "swap_request", + candid::encode_one(TokenPairs{ + token_a: SwapTokenData{ + ledger: bob_ledger_canister, + amt: 100u32.into() + }, + token_b: SwapTokenData{ + ledger: ledger_canister, + amt: 10u32.into() + } + }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Result<(), SwapError>).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Swap Request from bob principal to alice canister: {:?}", res); + assert!(res.is_ok()); + + let res = pocket_ic + .update_call( + alice_canister_id, + alice_principal, + "swap_request_action", + candid::encode_one(SwapRequestActions::Accept { + token_pairs: TokenPairs{ + token_a: SwapTokenData{ + ledger: bob_ledger_canister, + amt: 100u32.into() + }, + token_b: SwapTokenData{ + ledger: ledger_canister, + amt: 10u32.into() + } + }, + requester: bob }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Result<(), SwapError>).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Accepting the swap from alice pricpal to alice canister: {:?}", res); + assert!(res.is_ok()); + + let res = pocket_ic + .update_call( + alice_canister_id, + bob, + "swap_request", + candid::encode_one(TokenPairs{ + token_a: SwapTokenData{ + ledger: bob_ledger_canister, + amt: 100u32.into() + }, + token_b: SwapTokenData{ + ledger: ledger_canister, + amt: 10u32.into() + } + }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Result<(), SwapError>).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Swap Request from bob principal to alice canister: {:?}", res); + assert!(res.is_ok()); + + let res = pocket_ic + .update_call( + alice_canister_id, + alice_principal, + "swap_request_action", + candid::encode_one(SwapRequestActions::Reject { + token_pairs: TokenPairs{ + token_a: SwapTokenData{ + ledger: bob_ledger_canister, + amt: 100u32.into() + }, + token_b: SwapTokenData{ + ledger: ledger_canister, + amt: 10u32.into() + } + }, + requester: bob }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Result<(), SwapError>).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Rejecting the swap from alice pricpal to alice canister: {:?}", res); + assert!(res.is_ok()); + + let res = pocket_ic + .update_call( + bob_canister_id, + bob, + "cancel_swap_request", + candid::encode_one(TokenPairs{ + token_a: SwapTokenData{ + ledger: bob_ledger_canister, + amt: 100u32.into() + }, + token_b: SwapTokenData{ + ledger: ledger_canister, + amt: 10u32.into() + } + }) + .unwrap(), + ) + .map(|res| { + let response = match res { + WasmResult::Reply(payload) => Decode!(&payload, Result<(), SwapError>).unwrap(), + _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), + }; + response + }) + .unwrap(); + ic_cdk::println!("๐Ÿงช Cancelling the Swap Request after done: {:?}", res); + assert!(res.is_ok()); } \ No newline at end of file diff --git a/src/lib/integration_tests/tests/creator_dao/types.rs b/src/lib/integration_tests/tests/creator_dao/types.rs index b8cba9fd..3dbaed77 100644 --- a/src/lib/integration_tests/tests/creator_dao/types.rs +++ b/src/lib/integration_tests/tests/creator_dao/types.rs @@ -2,7 +2,7 @@ // You may want to manually adjust some of the types. #![allow(dead_code, unused_imports)] use candid::{self, CandidType, Decode, Deserialize, Encode, Nat, Principal}; -use ic_cdk::api::call::CallResult as Result; +use ic_cdk::api::call::{CallResult as Result, RejectionCode}; use serde_bytes; #[derive(CandidType, Deserialize)] @@ -496,3 +496,33 @@ pub enum CustomTransferError { TooOld, InsufficientFunds { balance: Nat }, } + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +struct SupportedStandards{ + name: String, + url: String +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct SwapTokenData{ + pub ledger: Principal, + pub amt: Nat +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct TokenPairs{ + pub token_a: SwapTokenData, + pub token_b: SwapTokenData +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub enum SwapRequestActions{ + Accept{ + token_pairs: TokenPairs, + requester: Principal + }, + Reject{ + token_pairs: TokenPairs, + requester: Principal + } +} \ No newline at end of file From 596039f2a22fd28788e1481349cade8b7f2dae0e Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 14:16:03 +0530 Subject: [PATCH 04/20] fixed tests --- .../src/api/cdao/swap.rs | 25 +++ .../tests/creator_dao/main.rs | 182 +----------------- 2 files changed, 31 insertions(+), 176 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 70c074a2..c540d7b3 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -183,3 +183,28 @@ pub struct TokenPairs{ pub token_a: SwapTokenData, pub token_b: SwapTokenData } + +#[derive(CandidType, Deserialize, PartialEq, Debug)] +pub struct PriceData{ + id: Nat, + #[serde(rename = "volumeUSD1d")] + volume_usd_1d: f64, + #[serde(rename = "volumeUSD7d")] + volume_usd_7d: f64, + #[serde(rename = "totalVolumeUSD")] + total_volume_usd: f64, + name: String, + #[serde(rename = "volumeUSD")] + volume_usd: f64, + #[serde(rename = "feesUSD")] + fees_usd: f64, + #[serde(rename = "priceUSDChange")] + price_usd_change: f64, + address: String, + #[serde(rename = "txCount")] + tx_count: u64, + #[serde(rename = "priceUSD")] + price_usd: f64, + standard: String, + symbol: String +} \ No newline at end of file diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 9352af0d..6a7e3c29 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -893,192 +893,22 @@ fn creator_dao_tests() { let bob_initial_cycle_balance = pocket_ic.cycle_balance(bob_canister_id); - let sns_wasm_w_canister_wasm = include_bytes!("../../../../../wasms/sns-wasm-canister.wasm"); - let sns_wasm_w_canister_id = Principal::from_text(SNS_WASM_W_PRINCIPAL_ID).unwrap(); - - let _ = pocket_ic.create_canister_with_id( - Some(super_admin), - None, - Principal::from_text(SNS_WASM_W_PRINCIPAL_ID).unwrap(), - ); - - let sns_wasm_canister_init_payload = SnsWasmCanisterInitPayload { - sns_subnet_ids: vec![], - access_controls_enabled: false, - allowed_principals: vec![], - }; - - pocket_ic.install_canister( - sns_wasm_w_canister_id, - sns_wasm_w_canister_wasm.to_vec(), - Encode!(&sns_wasm_canister_init_payload).unwrap(), - Some(super_admin), - ); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/root.wasm.gz"), - 1, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/governance.wasm.gz"), - 2, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/ledger.wasm.gz"), - 3, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/swap.wasm.gz"), - 4, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/archive.wasm.gz"), - 5, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - super_admin, - "add_wasm", - candid::encode_one(add_wasm( - include_bytes!("../../../../../wasms/index.wasm.gz"), - 6, - )) - .unwrap(), - ) - .map(|res| { - let response: AddWasmResultRecord = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - ic_cdk::println!("๐Ÿงช Result: {:?}", res); - - for _ in 0..50 { - pocket_ic.tick(); - } - - let res = pocket_ic - .update_call( - sns_wasm_w_canister_id, - Principal::anonymous(), - "get_latest_sns_version_pretty".into(), - candid::encode_one(()).unwrap(), - ) - .map(|res| { - let response: HashMap = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("\n๐Ÿ›‘ get requester principals canister id failed\n"), - }; - response - }) - .unwrap(); - - ic_cdk::println!("๐Ÿงช HASHMAP {:?}", res); - assert_eq!(res.len(), 6); let start = SystemTime::now(); let tx_fee = 1u64; let sns_init_args = SnsInitPayload { - confirmation_text: Some("GET RICH QUICK".to_string()), + confirmation_text: Some("GET RICH QUICK2".to_string()), transaction_fee_e8s: Some(tx_fee), - token_name: Some("Simulation Governance".to_string()), - token_symbol: Some("SIMG".to_string()), + token_name: Some("Simulation Governance2".to_string()), + token_symbol: Some("SIMG2".to_string()), proposal_reject_cost_e8s: Some(1u64), neuron_minimum_stake_e8s: Some(2u64), fallback_controller_principal_ids: vec![super_admin.to_string().clone()], logo: Some("data:image/png;base64,iVBORw0".to_string()), - url: Some("https://google.com".to_string()), - name: Some("Simulation Gov".to_string()), - description: Some("Simulation gov desc".to_string()), + url: Some("https://google.com2".to_string()), + name: Some("Simulation Gov2".to_string()), + description: Some("Simulation gov desc2".to_string()), neuron_minimum_dissolve_delay_to_vote_seconds: Some(1), initial_reward_rate_basis_points: Some(30u64), final_reward_rate_basis_points: Some(20u64), From 993d10d41530b71194e2c8248ad590d5fda8ce8c Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 14:19:56 +0530 Subject: [PATCH 05/20] fixed tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 6a7e3c29..25c71575 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -1245,7 +1245,7 @@ fn creator_dao_tests() { let res = pocket_ic .query_call( - ledger_canister, + bob_ledger_canister, bob, "icrc1_balance_of", candid::encode_one(types::Icrc1BalanceOfArg { From 89138312d503660295ef5d67a6fe30545d99a835 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 14:36:11 +0530 Subject: [PATCH 06/20] fixed tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 25c71575..8b4f3b53 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -1016,8 +1016,8 @@ fn creator_dao_tests() { let res = pocket_ic .query_call( - alice_canister_id, - alice_principal, + bob_canister_id, + bob, "deployed_cdao_canisters", candid::encode_one(()).unwrap(), ) From 68b0b42a54aecf14a6173bd2a3f5e32d6f01b99e Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 14:47:14 +0530 Subject: [PATCH 07/20] fixed tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 8b4f3b53..7ee61005 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -1143,7 +1143,7 @@ fn creator_dao_tests() { "refresh_buyer_tokens", candid::encode_one(RefreshBuyerTokensRequest { buyer: super_admin.to_string(), - confirmation_text: Some("GET RICH QUICK".to_string()), + confirmation_text: Some("GET RICH QUICK2".to_string()), }) .unwrap(), ) From 82d3480e9c8ef2338f068841b895f9e9345c53c2 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 15:01:42 +0530 Subject: [PATCH 08/20] fixed tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 7ee61005..0e025ed5 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -1187,7 +1187,7 @@ fn creator_dao_tests() { super_admin, "list_neurons", candid::encode_one(ListNeurons { - of_principal: Some(PrincipalId(alice_principal)), + of_principal: Some(PrincipalId(bob)), limit: 2, start_page_at: None, }) From 4b3b6c736e1feaa77c64a48d4cd345707d126853 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Wed, 6 Nov 2024 15:47:40 +0530 Subject: [PATCH 09/20] fix tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 0e025ed5..e632919d 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -948,7 +948,7 @@ fn creator_dao_tests() { developer_neurons: vec![ NeuronDistribution { controller: Some( - PrincipalId::from_str(&alice_principal.to_string()).unwrap(), + PrincipalId::from_str(&bob.to_string()).unwrap(), ), stake_e8s: 4_400_000, memo: 0, @@ -957,7 +957,7 @@ fn creator_dao_tests() { }, NeuronDistribution { controller: Some( - PrincipalId::from_str(&alice_principal.to_string()).unwrap(), + PrincipalId::from_str(&bob.to_string()).unwrap(), ), stake_e8s: 100_000, memo: 1, From a8a9c2d4042de0312a81d103a8d355f977d1f572 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Fri, 8 Nov 2024 09:41:37 +0530 Subject: [PATCH 10/20] refactored structs --- .../src/api/cdao/swap.rs | 151 ++++++++---------- .../individual_user_template/src/lib.rs | 4 +- .../tests/creator_dao/main.rs | 2 +- .../tests/creator_dao/types.rs | 30 ---- .../types/cdao/mod.rs | 26 ++- 5 files changed, 98 insertions(+), 115 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index c540d7b3..b4cc0f7b 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -1,12 +1,45 @@ use candid::{CandidType, Nat, Principal}; use ic_cdk::api::{management_canister::main::CanisterInfoRequest, time}; use ic_cdk_macros::{query, update}; -use icrc_ledger_types::icrc2::{approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; +use icrc_ledger_types::icrc2::{allowance::{Allowance, AllowanceArgs}, approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; use serde::Deserialize; -use shared_utils::canister_specific::individual_user_template::types::{cdao::DeployedCdaoCanisters, error::SwapError}; +use shared_utils::canister_specific::individual_user_template::types::{cdao::{DeployedCdaoCanisters, SwapRequestActions, TokenPairs}, error::SwapError}; use crate::CANISTER_DATA; +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +struct SupportedStandards{ + name: String, + url: String +} + +#[derive(CandidType, Deserialize, PartialEq, Debug)] +pub struct PriceData{ + id: Nat, + #[serde(rename = "volumeUSD1d")] + volume_usd_1d: f64, + #[serde(rename = "volumeUSD7d")] + volume_usd_7d: f64, + #[serde(rename = "totalVolumeUSD")] + total_volume_usd: f64, + name: String, + #[serde(rename = "volumeUSD")] + volume_usd: f64, + #[serde(rename = "feesUSD")] + fees_usd: f64, + #[serde(rename = "priceUSDChange")] + price_usd_change: f64, + address: String, + #[serde(rename = "txCount")] + tx_count: u64, + #[serde(rename = "priceUSD")] + price_usd: f64, + standard: String, + symbol: String +} + +const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk + #[update] pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ let TokenPairs{token_a, token_b} = token_pairs; @@ -19,10 +52,12 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ return Err(SwapError::IsNotTokenCreator); } + let previous_approval_amt = get_previous_approval_amount(ic_cdk::caller(), ic_cdk::id(), token_a.ledger).await?; + let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ from_subaccount: None, spender: ic_cdk::id().into(), - amount: token_a.amt, + amount: previous_approval_amt + token_a.amt, expected_allowance: None, memo: None, expires_at: Some(time() + SWAP_REQUEST_EXPIRY), @@ -38,9 +73,13 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ // Creator principal is the caller here #[update] pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError>{ + //auth + match op{ SwapRequestActions::Accept { token_pairs, requester } => { let TokenPairs{token_a, token_b} = token_pairs; + let token_a_price = get_token_price(token_a.ledger).await?; + let transfer_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_transfer_from", (TransferFromArgs{ from: requester.into(), spender_subaccount: None, @@ -63,7 +102,6 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError }, )).await?; transfer_res.0.map_err(SwapError::TransferFromError)?; - let token_a_price = get_token_price(token_a.ledger).await?; if let Some(price) = token_a_price{ CANISTER_DATA.with_borrow_mut(|data| { if let Some(cdao) = data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_b.ledger){ @@ -83,6 +121,35 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError Ok(()) } +#[update] +pub async fn cancel_swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ + let TokenPairs{token_a, ..} = token_pairs; + let previous_approval_amt = get_previous_approval_amount(ic_cdk::caller(), ic_cdk::id(), token_a.ledger).await?; + + let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ + from_subaccount: None, + spender: ic_cdk::id().into(), + amount: previous_approval_amt - token_a.amt, // https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#alice-removes-her-allowance-for-canister-c + expected_allowance: None, + memo: None, + expires_at: Some(time() + SWAP_REQUEST_EXPIRY), + fee: None, + created_at_time: None + }, )).await?; + + allocation_res.0.map_err(SwapError::ApproveError)?; + Ok(()) +} + +async fn get_previous_approval_amount(requester: Principal, spender: Principal, ledger: Principal) -> Result{ + let previous_approval: (Allowance, ) = ic_cdk::call(ledger, "icrc2_allowance", (AllowanceArgs{ + account: requester.into(), + spender: spender.into() + }, )).await?; + + Ok(previous_approval.0.allowance) +} + async fn get_token_price(token_ledger: Principal) -> Result, SwapError>{ // let price: Result<(PriceData, ), _> = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await; @@ -128,83 +195,7 @@ async fn get_token_owner_from_ledger(ledger: Principal) -> Result Result<(), SwapError>{ - let TokenPairs{token_a, ..} = token_pairs; - let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ - from_subaccount: None, - spender: ic_cdk::id().into(), - amount: 0u32.into(), // icrc2 docs revoking approval - expected_allowance: None, - memo: None, - expires_at: Some(time() + SWAP_REQUEST_EXPIRY), - fee: None, - created_at_time: None - }, )).await?; - - allocation_res.0.map_err(SwapError::ApproveError)?; - Ok(()) -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub enum SwapRequestActions{ - Accept{ - token_pairs: TokenPairs, - requester: Principal - }, - Reject{ - token_pairs: TokenPairs, - requester: Principal - } -} - -const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk - async fn is_icrc2_supported_token(token_ledger: Principal) -> Result{ let res: (Vec, ) = ic_cdk::call(token_ledger, "icrc1_supported_standards", ()).await?; Ok(res.0.iter().any(|v| v.name == "ICRC2")) -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -struct SupportedStandards{ - name: String, - url: String -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub struct SwapTokenData{ - pub ledger: Principal, - pub amt: Nat -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub struct TokenPairs{ - pub token_a: SwapTokenData, - pub token_b: SwapTokenData -} - -#[derive(CandidType, Deserialize, PartialEq, Debug)] -pub struct PriceData{ - id: Nat, - #[serde(rename = "volumeUSD1d")] - volume_usd_1d: f64, - #[serde(rename = "volumeUSD7d")] - volume_usd_7d: f64, - #[serde(rename = "totalVolumeUSD")] - total_volume_usd: f64, - name: String, - #[serde(rename = "volumeUSD")] - volume_usd: f64, - #[serde(rename = "feesUSD")] - fees_usd: f64, - #[serde(rename = "priceUSDChange")] - price_usd_change: f64, - address: String, - #[serde(rename = "txCount")] - tx_count: u64, - #[serde(rename = "priceUSD")] - price_usd: f64, - standard: String, - symbol: String } \ No newline at end of file diff --git a/src/canister/individual_user_template/src/lib.rs b/src/canister/individual_user_template/src/lib.rs index b34f7f10..56ecfeb2 100644 --- a/src/canister/individual_user_template/src/lib.rs +++ b/src/canister/individual_user_template/src/lib.rs @@ -17,7 +17,7 @@ use icrc_ledger_types::icrc1::transfer::Memo; use shared_utils::{ canister_specific::individual_user_template::types::{ arg::{FolloweeArg, IndividualUserTemplateInitArgs, PlaceBetArg}, - cdao::DeployedCdaoCanisters, + cdao::{DeployedCdaoCanisters, SwapRequestActions, TokenPairs}, device_id::DeviceIdentity, error::{ BetOnCurrentlyViewingPostError, CdaoDeployError, CdaoTokenError, @@ -50,8 +50,6 @@ use shared_utils::{ }, }; -use crate::api::cdao::swap::TokenPairs; -use crate::api::cdao::swap::SwapRequestActions; mod api; pub mod data_model; diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index e632919d..4626335d 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -15,9 +15,9 @@ use ic_sns_swap::pb::v1::{ NewSaleTicketResponse, RefreshBuyerTokensRequest, RefreshBuyerTokensResponse, }; use sha2::{Digest, Sha256}; +use shared_utils::canister_specific::individual_user_template::types::cdao::{SwapRequestActions, SwapTokenData, TokenPairs}; use shared_utils::canister_specific::individual_user_template::types::error::{AirdropError, CdaoTokenError, SwapError}; use test_utils::setup::test_constants::get_mock_user_bob_principal_id; -use types::{SwapRequestActions, SwapTokenData, TokenPairs}; use std::time::{Duration, UNIX_EPOCH}; use std::{collections::HashMap, fmt::Debug, str::FromStr, time::SystemTime, vec}; diff --git a/src/lib/integration_tests/tests/creator_dao/types.rs b/src/lib/integration_tests/tests/creator_dao/types.rs index 3dbaed77..5d68dc13 100644 --- a/src/lib/integration_tests/tests/creator_dao/types.rs +++ b/src/lib/integration_tests/tests/creator_dao/types.rs @@ -495,34 +495,4 @@ pub enum CustomTransferError { CreatedInFuture { ledger_time: u64 }, TooOld, InsufficientFunds { balance: Nat }, -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -struct SupportedStandards{ - name: String, - url: String -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub struct SwapTokenData{ - pub ledger: Principal, - pub amt: Nat -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub struct TokenPairs{ - pub token_a: SwapTokenData, - pub token_b: SwapTokenData -} - -#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] -pub enum SwapRequestActions{ - Accept{ - token_pairs: TokenPairs, - requester: Principal - }, - Reject{ - token_pairs: TokenPairs, - requester: Principal - } } \ No newline at end of file diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs index 4d229e67..dc4f0f74 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use candid::{CandidType, Principal}; +use candid::{CandidType, Nat, Principal}; use serde::{Deserialize, Serialize}; #[derive(CandidType, PartialEq, Debug, Serialize, Deserialize, Clone)] @@ -87,4 +87,28 @@ pub enum ClaimStatus { Unclaimed, Claimed, Claiming, +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct SwapTokenData{ + pub ledger: Principal, + pub amt: Nat +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub struct TokenPairs{ + pub token_a: SwapTokenData, + pub token_b: SwapTokenData +} + +#[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] +pub enum SwapRequestActions{ + Accept{ + token_pairs: TokenPairs, + requester: Principal + }, + Reject{ + token_pairs: TokenPairs, + requester: Principal + } } \ No newline at end of file From 5cf97f1a2d4bca85bc451f00c26124c293e3a742 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Fri, 8 Nov 2024 09:46:00 +0530 Subject: [PATCH 11/20] added auth --- src/canister/individual_user_template/src/api/cdao/swap.rs | 6 ++++-- .../src/api/profile/get_profile_details_v2.rs | 2 +- .../individual_user_template/types/error.rs | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index b4cc0f7b..543318bd 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -5,7 +5,7 @@ use icrc_ledger_types::icrc2::{allowance::{Allowance, AllowanceArgs}, approve::{ use serde::Deserialize; use shared_utils::canister_specific::individual_user_template::types::{cdao::{DeployedCdaoCanisters, SwapRequestActions, TokenPairs}, error::SwapError}; -use crate::CANISTER_DATA; +use crate::{api::profile::get_profile_details_v2::{self, get_profile_details_v2}, CANISTER_DATA}; #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] struct SupportedStandards{ @@ -74,7 +74,9 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ #[update] pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError>{ //auth - + if ic_cdk::caller() != get_profile_details_v2().principal_id{ + return Err(SwapError::Unauthenticated); + } match op{ SwapRequestActions::Accept { token_pairs, requester } => { let TokenPairs{token_a, token_b} = token_pairs; diff --git a/src/canister/individual_user_template/src/api/profile/get_profile_details_v2.rs b/src/canister/individual_user_template/src/api/profile/get_profile_details_v2.rs index 5b92eb08..f6b0c024 100644 --- a/src/canister/individual_user_template/src/api/profile/get_profile_details_v2.rs +++ b/src/canister/individual_user_template/src/api/profile/get_profile_details_v2.rs @@ -3,7 +3,7 @@ use ic_cdk_macros::query; use shared_utils::canister_specific::individual_user_template::types::profile::UserProfileDetailsForFrontendV2; #[query] -fn get_profile_details_v2() -> UserProfileDetailsForFrontendV2 { +pub fn get_profile_details_v2() -> UserProfileDetailsForFrontendV2 { CANISTER_DATA.with_borrow(|canister_data_ref_cell| { let profile = canister_data_ref_cell.profile.clone(); let token_balance = &canister_data_ref_cell.my_token_balance; diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs index ece10c3d..50a67406 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/error.rs @@ -95,6 +95,7 @@ pub enum SwapError{ InvalidLedger, SwapAllocationExhausted, UnsupportedToken, + Unauthenticated, CallError(RejectionCode, String), ApproveError(ApproveError), IsNotTokenCreator, From 2da5066f12b58d68acaac66990fb9e805b348e44 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Mon, 11 Nov 2024 11:19:05 +0530 Subject: [PATCH 12/20] added price fixing --- Cargo.lock | 14 ++ Cargo.toml | 2 - .../individual_user_template/Cargo.toml | 1 + .../src/api/cdao/swap.rs | 181 ++++++++++++++++-- 4 files changed, 182 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bc875d8..5ed76f90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,19 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bigdecimal" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "bincode" version = "1.3.3" @@ -3399,6 +3412,7 @@ dependencies = [ name = "individual_user_template" version = "0.1.0" dependencies = [ + "bigdecimal", "candid", "ciborium", "futures", diff --git a/Cargo.toml b/Cargo.toml index 7155d2fc..089c9330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,9 +32,7 @@ ic-sns-root = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024 ic-sns-wasm = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-base-types = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-nns-constants = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-nervous-system-proto = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-nns-governance = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-nervous-system-humanize = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-icrc1-ledger = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-icrc1-index-ng = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } ic-icrc1-index = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } diff --git a/src/canister/individual_user_template/Cargo.toml b/src/canister/individual_user_template/Cargo.toml index 8f324188..26e953fa 100644 --- a/src/canister/individual_user_template/Cargo.toml +++ b/src/canister/individual_user_template/Cargo.toml @@ -32,6 +32,7 @@ ic-icrc1-index-ng.workspace = true ic-icrc1-index.workspace = true icrc-ledger-types.workspace = true hex = "0.4.3" +bigdecimal = "0.4.6" [dev-dependencies] test_utils = { workspace = true } diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 543318bd..982f10cb 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -4,6 +4,7 @@ use ic_cdk_macros::{query, update}; use icrc_ledger_types::icrc2::{allowance::{Allowance, AllowanceArgs}, approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; use serde::Deserialize; use shared_utils::canister_specific::individual_user_template::types::{cdao::{DeployedCdaoCanisters, SwapRequestActions, TokenPairs}, error::SwapError}; +use std::str::FromStr; use crate::{api::profile::get_profile_details_v2::{self, get_profile_details_v2}, CANISTER_DATA}; @@ -39,6 +40,88 @@ pub struct PriceData{ } const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk +const XRC_FETCHABLE_TOKENS_LEDGER_IDS:[&str; 4] = ["xevnm-gaaaa-aaaar-qafnq-cai", "mxzaz-hqaaa-aaaar-qaada-cai", "ss2fx-dyaaa-aaaar-qacoq-cai", "ryjl3-tyaaa-aaaaa-aaaba-cai"]; // [ckusdc, ckbtc, cketh, icp] +#[derive(CandidType, Deserialize)] +pub enum AssetClass { Cryptocurrency, FiatCurrency } + +#[derive(CandidType, Deserialize)] +pub struct Asset { pub class: AssetClass, pub symbol: String } + +#[derive(CandidType, Deserialize)] +pub struct GetExchangeRateRequest { + pub timestamp: Option, + pub quote_asset: Asset, + pub base_asset: Asset, +} + +#[derive(CandidType, Deserialize)] +pub struct ExchangeRateMetadata { + pub decimals: u32, + pub forex_timestamp: Option, + pub quote_asset_num_received_rates: u64, + pub base_asset_num_received_rates: u64, + pub base_asset_num_queried_sources: u64, + pub standard_deviation: u64, + pub quote_asset_num_queried_sources: u64, +} + +#[derive(CandidType, Deserialize)] +pub struct ExchangeRate { + pub metadata: ExchangeRateMetadata, + pub rate: u64, + pub timestamp: u64, + pub quote_asset: Asset, + pub base_asset: Asset, +} + +#[derive(CandidType, Deserialize)] +pub enum ExchangeRateError { + AnonymousPrincipalNotAllowed, + CryptoQuoteAssetNotFound, + FailedToAcceptCycles, + ForexBaseAssetNotFound, + CryptoBaseAssetNotFound, + StablecoinRateTooFewRates, + ForexAssetsNotFound, + InconsistentRatesReceived, + RateLimited, + StablecoinRateZeroRate, + Other{ code: u32, description: String }, + ForexInvalidTimestamp, + NotEnoughCycles, + ForexQuoteAssetNotFound, + StablecoinRateNotFound, + Pending, +} + +#[derive(CandidType, Deserialize)] +pub enum GetExchangeRateResult { Ok(ExchangeRate), Err(ExchangeRateError) } + +#[derive(CandidType, Deserialize)] +pub struct PairInfoExt { + pub id: String, + #[serde(rename = "price0CumulativeLast")] + pub price0_cumulative_last: candid::Nat, + + pub creator: Principal, + pub reserve0: candid::Nat, + pub reserve1: candid::Nat, + pub lptoken: String, + + #[serde(rename = "totalSupply")] + pub total_supply: candid::Nat, + pub token0: String, + pub token1: String, + + #[serde(rename = "price1CumulativeLast")] + pub price1_cumulative_last: candid::Nat, + #[serde(rename = "kLast")] + pub k_last: candid::Nat, + + #[serde(rename = "blockTimestampLast")] + pub block_timestamp_last: candid::Int, +} + #[update] pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ @@ -80,7 +163,7 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError match op{ SwapRequestActions::Accept { token_pairs, requester } => { let TokenPairs{token_a, token_b} = token_pairs; - let token_a_price = get_token_price(token_a.ledger).await?; + let token_a_price = get_token_price(token_a.ledger).await; let transfer_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_transfer_from", (TransferFromArgs{ from: requester.into(), @@ -152,21 +235,91 @@ async fn get_previous_approval_amount(requester: Principal, spender: Principal, Ok(previous_approval.0.allowance) } -async fn get_token_price(token_ledger: Principal) -> Result, SwapError>{ - // let price: Result<(PriceData, ), _> = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await; +async fn get_token_price(token_ledger: Principal) -> Option { + if let Some(price) = get_token_price_from_creator_canister(token_ledger).await { + Some(price) + } else if XRC_FETCHABLE_TOKENS_LEDGER_IDS + .iter() + .any(|id| token_ledger.to_text() == *id) + { + get_token_price_from_xrc(token_ledger).await + } else { + let icpswap_price = get_token_price_from_icpswap(token_ledger).await; + let sonicswap_price = get_token_price_from_sonicswap(token_ledger).await; + + if icpswap_price.is_some() && sonicswap_price.is_some() { + Some((icpswap_price.unwrap() + sonicswap_price.unwrap()) / 2.0) + } else { + icpswap_price.or(sonicswap_price) + } + } +} + +pub async fn get_token_price_from_icpswap(token_ledger: Principal) -> Option{ + let price: (PriceData, ) = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await.ok()?; + + Some(price.0.price_usd) +} + +pub async fn get_token_price_from_sonicswap(token_ledger: Principal) -> Option { + let icp_ledger = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").ok()?; + + let (token_icp_pair_option,): (Option,) = ic_cdk::call( + Principal::from_text("3xwpq-ziaaa-aaaah-qcn4a-cai").ok()?, + "getPair", + (token_ledger, icp_ledger), + ) + .await + .ok()?; + + let token_icp_pair = token_icp_pair_option?; + + let icp_price = get_token_price_from_xrc(icp_ledger).await?; + + let reserve0_f64 = nat_to_f64(&token_icp_pair.reserve0)?; + let reserve1_f64 = nat_to_f64(&token_icp_pair.reserve1)?; + + if reserve0_f64 == 0.0 { + return None; + } + + let token_price_in_icp = reserve1_f64 / reserve0_f64; - // match price{ - // Ok((price, )) => Ok(Some(price.price_usd)), - // Err(_) => { - // let owner_canister = get_token_owner_from_ledger(token_ledger).await?; - // let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; - // Ok(price.0) - // } - // } + let token_price_in_usd = token_price_in_icp * icp_price; + + Some(token_price_in_usd) +} + +fn nat_to_f64(n: &Nat) -> Option { + let n_str = n.to_string(); + f64::from_str(&n_str).ok() +} + +pub async fn get_token_price_from_xrc(token_ledger: Principal) -> Option{ + let symbol: (String, ) = ic_cdk::call(token_ledger, "icrc1_symbol", ()).await.ok()?; + let symbol = symbol.0.replace("ck", ""); + let exchange_rate:(GetExchangeRateResult, ) = ic_cdk::call(Principal::from_text("uf6dk-hyaaa-aaaaq-qaaaq-cai").unwrap(), "get_exchange_rate", (GetExchangeRateRequest{ + base_asset:Asset{ + class: AssetClass::Cryptocurrency, + symbol + }, + quote_asset: Asset{ + class: AssetClass::FiatCurrency, + symbol: "USD".to_string() + }, + timestamp: None + }, )).await.ok()?; + + match exchange_rate.0{ + GetExchangeRateResult::Ok(exchange_rate) => Some(exchange_rate.rate as f64), + _ => None + } +} - let owner_canister = get_token_owner_from_ledger(token_ledger).await?; - let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await?; - Ok(price.0) +pub async fn get_token_price_from_creator_canister(token_ledger: Principal) -> Option{ + let owner_canister = get_token_owner_from_ledger(token_ledger).await.ok()?; + let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await.ok()?; + price.0 } #[query] From 152a99e19b72a467daf0c7bc0e59174bd91f00d8 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Mon, 11 Nov 2024 11:25:59 +0530 Subject: [PATCH 13/20] merged --- .../individual_user_template/types/cdao/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs index 2f38ed0f..6bfd6091 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/cdao/mod.rs @@ -103,7 +103,6 @@ pub enum ClaimStatus { Claimed, Claiming, } -<<<<<<< HEAD #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] pub struct SwapTokenData{ @@ -128,5 +127,3 @@ pub enum SwapRequestActions{ requester: Principal } } -======= ->>>>>>> 90197f776dae884a8404c02d9cfa50a66c54ad66 From 300c852a78c380837257ab5ca31c098b6b47a7c3 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Mon, 11 Nov 2024 12:00:53 +0530 Subject: [PATCH 14/20] fixed tests --- src/canister/individual_user_template/src/api/cdao/mod.rs | 3 --- src/lib/integration_tests/tests/creator_dao/main.rs | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/mod.rs b/src/canister/individual_user_template/src/api/cdao/mod.rs index 23192db5..46770821 100644 --- a/src/canister/individual_user_template/src/api/cdao/mod.rs +++ b/src/canister/individual_user_template/src/api/cdao/mod.rs @@ -283,9 +283,6 @@ async fn deploy_cdao_sns( index: index.0, airdrop_info: AirdropInfo { principals_who_successfully_claimed: HashMap::new() }, last_swapped_price: None, - airdrop_info: AirdropInfo { - principals_who_successfully_claimed: HashMap::new(), - }, }; CANISTER_DATA.with(|cdata| { let mut cdata = cdata.borrow_mut(); diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 84fff6c6..9e87ec72 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -17,16 +17,14 @@ use ic_sns_swap::pb::v1::{ }; use sha2::{Digest, Sha256}; use shared_utils::canister_specific::individual_user_template::types::cdao::{SwapRequestActions, SwapTokenData, TokenPairs}; -use shared_utils::canister_specific::individual_user_template::types::error::{AirdropError, CdaoTokenError, SwapError}; +use shared_utils::canister_specific::individual_user_template::types::error::{AirdropError, SwapError}; use test_utils::setup::test_constants::get_mock_user_bob_principal_id; -use shared_utils::canister_specific::individual_user_template::types::error::AirdropError; use shared_utils::constant::{ SNS_TOKEN_ARCHIVE_MODULE_HASH, SNS_TOKEN_GOVERNANCE_MODULE_HASH, SNS_TOKEN_INDEX_MODULE_HASH, SNS_TOKEN_LEDGER_MODULE_HASH, SNS_TOKEN_ROOT_MODULE_HASH, SNS_TOKEN_SWAP_MODULE_HASH, }; use std::time::{Duration, UNIX_EPOCH}; use std::{collections::HashMap, fmt::Debug, str::FromStr, time::SystemTime, vec}; -use test_utils::setup::test_constants::get_mock_user_bob_principal_id; use candid::{encode_args, CandidType, Decode, Encode, Nat, Principal}; use ic_base_types::PrincipalId; From 3420cccfd9a9ae12df1a0d456734df1c42802807 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Thu, 14 Nov 2024 17:25:55 +0530 Subject: [PATCH 15/20] remove unecessary structs --- .../src/api/cdao/swap.rs | 251 ++---------------- 1 file changed, 15 insertions(+), 236 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 982f10cb..94ea82d8 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -1,12 +1,11 @@ use candid::{CandidType, Nat, Principal}; -use ic_cdk::api::{management_canister::main::CanisterInfoRequest, time}; -use ic_cdk_macros::{query, update}; +use ic_cdk::api::time; +use ic_cdk_macros::update; use icrc_ledger_types::icrc2::{allowance::{Allowance, AllowanceArgs}, approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; use serde::Deserialize; -use shared_utils::canister_specific::individual_user_template::types::{cdao::{DeployedCdaoCanisters, SwapRequestActions, TokenPairs}, error::SwapError}; -use std::str::FromStr; +use shared_utils::canister_specific::individual_user_template::types::{cdao::{SwapRequestActions, TokenPairs}, error::SwapError}; -use crate::{api::profile::get_profile_details_v2::{self, get_profile_details_v2}, CANISTER_DATA}; +use crate::{api::profile::get_profile_details_v2::get_profile_details_v2, CANISTER_DATA}; #[derive(CandidType, Deserialize, PartialEq, Eq, Debug)] struct SupportedStandards{ @@ -14,113 +13,7 @@ struct SupportedStandards{ url: String } -#[derive(CandidType, Deserialize, PartialEq, Debug)] -pub struct PriceData{ - id: Nat, - #[serde(rename = "volumeUSD1d")] - volume_usd_1d: f64, - #[serde(rename = "volumeUSD7d")] - volume_usd_7d: f64, - #[serde(rename = "totalVolumeUSD")] - total_volume_usd: f64, - name: String, - #[serde(rename = "volumeUSD")] - volume_usd: f64, - #[serde(rename = "feesUSD")] - fees_usd: f64, - #[serde(rename = "priceUSDChange")] - price_usd_change: f64, - address: String, - #[serde(rename = "txCount")] - tx_count: u64, - #[serde(rename = "priceUSD")] - price_usd: f64, - standard: String, - symbol: String -} - const SWAP_REQUEST_EXPIRY: u64 = 7 * 24 * 60 * 60 * 1_000_000_000; // 1 wk -const XRC_FETCHABLE_TOKENS_LEDGER_IDS:[&str; 4] = ["xevnm-gaaaa-aaaar-qafnq-cai", "mxzaz-hqaaa-aaaar-qaada-cai", "ss2fx-dyaaa-aaaar-qacoq-cai", "ryjl3-tyaaa-aaaaa-aaaba-cai"]; // [ckusdc, ckbtc, cketh, icp] -#[derive(CandidType, Deserialize)] -pub enum AssetClass { Cryptocurrency, FiatCurrency } - -#[derive(CandidType, Deserialize)] -pub struct Asset { pub class: AssetClass, pub symbol: String } - -#[derive(CandidType, Deserialize)] -pub struct GetExchangeRateRequest { - pub timestamp: Option, - pub quote_asset: Asset, - pub base_asset: Asset, -} - -#[derive(CandidType, Deserialize)] -pub struct ExchangeRateMetadata { - pub decimals: u32, - pub forex_timestamp: Option, - pub quote_asset_num_received_rates: u64, - pub base_asset_num_received_rates: u64, - pub base_asset_num_queried_sources: u64, - pub standard_deviation: u64, - pub quote_asset_num_queried_sources: u64, -} - -#[derive(CandidType, Deserialize)] -pub struct ExchangeRate { - pub metadata: ExchangeRateMetadata, - pub rate: u64, - pub timestamp: u64, - pub quote_asset: Asset, - pub base_asset: Asset, -} - -#[derive(CandidType, Deserialize)] -pub enum ExchangeRateError { - AnonymousPrincipalNotAllowed, - CryptoQuoteAssetNotFound, - FailedToAcceptCycles, - ForexBaseAssetNotFound, - CryptoBaseAssetNotFound, - StablecoinRateTooFewRates, - ForexAssetsNotFound, - InconsistentRatesReceived, - RateLimited, - StablecoinRateZeroRate, - Other{ code: u32, description: String }, - ForexInvalidTimestamp, - NotEnoughCycles, - ForexQuoteAssetNotFound, - StablecoinRateNotFound, - Pending, -} - -#[derive(CandidType, Deserialize)] -pub enum GetExchangeRateResult { Ok(ExchangeRate), Err(ExchangeRateError) } - -#[derive(CandidType, Deserialize)] -pub struct PairInfoExt { - pub id: String, - #[serde(rename = "price0CumulativeLast")] - pub price0_cumulative_last: candid::Nat, - - pub creator: Principal, - pub reserve0: candid::Nat, - pub reserve1: candid::Nat, - pub lptoken: String, - - #[serde(rename = "totalSupply")] - pub total_supply: candid::Nat, - pub token0: String, - pub token1: String, - - #[serde(rename = "price1CumulativeLast")] - pub price1_cumulative_last: candid::Nat, - #[serde(rename = "kLast")] - pub k_last: candid::Nat, - - #[serde(rename = "blockTimestampLast")] - pub block_timestamp_last: candid::Int, -} #[update] @@ -163,8 +56,7 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError match op{ SwapRequestActions::Accept { token_pairs, requester } => { let TokenPairs{token_a, token_b} = token_pairs; - let token_a_price = get_token_price(token_a.ledger).await; - + let transfer_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_transfer_from", (TransferFromArgs{ from: requester.into(), spender_subaccount: None, @@ -186,14 +78,7 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError created_at_time: None }, )).await?; transfer_res.0.map_err(SwapError::TransferFromError)?; - - if let Some(price) = token_a_price{ - CANISTER_DATA.with_borrow_mut(|data| { - if let Some(cdao) = data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_b.ledger){ - cdao.last_swapped_price = Some(price); - } - }); - } + // send push notifs to both parties // Cannot remove the approval as it is not possible to do so }, @@ -206,6 +91,15 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError Ok(()) } +#[update] +fn update_last_swap_price(token_ledger: Principal, price: f64){ + CANISTER_DATA.with_borrow_mut(|data| { + if let Some(cdao) = data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_ledger){ + cdao.last_swapped_price = Some(price); + } + }); +} + #[update] pub async fn cancel_swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ let TokenPairs{token_a, ..} = token_pairs; @@ -235,121 +129,6 @@ async fn get_previous_approval_amount(requester: Principal, spender: Principal, Ok(previous_approval.0.allowance) } -async fn get_token_price(token_ledger: Principal) -> Option { - if let Some(price) = get_token_price_from_creator_canister(token_ledger).await { - Some(price) - } else if XRC_FETCHABLE_TOKENS_LEDGER_IDS - .iter() - .any(|id| token_ledger.to_text() == *id) - { - get_token_price_from_xrc(token_ledger).await - } else { - let icpswap_price = get_token_price_from_icpswap(token_ledger).await; - let sonicswap_price = get_token_price_from_sonicswap(token_ledger).await; - - if icpswap_price.is_some() && sonicswap_price.is_some() { - Some((icpswap_price.unwrap() + sonicswap_price.unwrap()) / 2.0) - } else { - icpswap_price.or(sonicswap_price) - } - } -} - -pub async fn get_token_price_from_icpswap(token_ledger: Principal) -> Option{ - let price: (PriceData, ) = ic_cdk::call(Principal::from_text("moe7a-tiaaa-aaaag-qclfq-cai").unwrap(), "getToken", (token_ledger.to_text(), )).await.ok()?; - - Some(price.0.price_usd) -} - -pub async fn get_token_price_from_sonicswap(token_ledger: Principal) -> Option { - let icp_ledger = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").ok()?; - - let (token_icp_pair_option,): (Option,) = ic_cdk::call( - Principal::from_text("3xwpq-ziaaa-aaaah-qcn4a-cai").ok()?, - "getPair", - (token_ledger, icp_ledger), - ) - .await - .ok()?; - - let token_icp_pair = token_icp_pair_option?; - - let icp_price = get_token_price_from_xrc(icp_ledger).await?; - - let reserve0_f64 = nat_to_f64(&token_icp_pair.reserve0)?; - let reserve1_f64 = nat_to_f64(&token_icp_pair.reserve1)?; - - if reserve0_f64 == 0.0 { - return None; - } - - let token_price_in_icp = reserve1_f64 / reserve0_f64; - - let token_price_in_usd = token_price_in_icp * icp_price; - - Some(token_price_in_usd) -} - -fn nat_to_f64(n: &Nat) -> Option { - let n_str = n.to_string(); - f64::from_str(&n_str).ok() -} - -pub async fn get_token_price_from_xrc(token_ledger: Principal) -> Option{ - let symbol: (String, ) = ic_cdk::call(token_ledger, "icrc1_symbol", ()).await.ok()?; - let symbol = symbol.0.replace("ck", ""); - let exchange_rate:(GetExchangeRateResult, ) = ic_cdk::call(Principal::from_text("uf6dk-hyaaa-aaaaq-qaaaq-cai").unwrap(), "get_exchange_rate", (GetExchangeRateRequest{ - base_asset:Asset{ - class: AssetClass::Cryptocurrency, - symbol - }, - quote_asset: Asset{ - class: AssetClass::FiatCurrency, - symbol: "USD".to_string() - }, - timestamp: None - }, )).await.ok()?; - - match exchange_rate.0{ - GetExchangeRateResult::Ok(exchange_rate) => Some(exchange_rate.rate as f64), - _ => None - } -} - -pub async fn get_token_price_from_creator_canister(token_ledger: Principal) -> Option{ - let owner_canister = get_token_owner_from_ledger(token_ledger).await.ok()?; - let price: (Option, ) = ic_cdk::call(owner_canister, "get_token_price_by_ledger", (token_ledger, )).await.ok()?; - price.0 -} - -#[query] -pub fn get_token_price_by_ledger(token_ledger: Principal) -> Result, SwapError>{ - CANISTER_DATA.with_borrow(|data| { - data.cdao_canisters.iter().find(|cdao| cdao.ledger == token_ledger).map(|cdao| cdao.last_swapped_price).ok_or(SwapError::NoController) - }) -} - -async fn get_token_owner_from_ledger(ledger: Principal) -> Result{ - let res = ic_cdk::api::management_canister::main::canister_info(CanisterInfoRequest{ - canister_id: ledger, - num_requested_changes: None - }).await?.0; - for f in res.controllers.into_iter().filter(|f| f.to_text().ends_with("-cai")){ - let res: Result<(Vec, ), _> = ic_cdk::call(f, "deployed_cdao_canisters", ()).await; - - match res{ - Ok((cdao, )) => { - if cdao.iter().any(|cdao| cdao.ledger == ledger){ - return Ok(f); - } - }, - Err(_) => continue - }; - } - - Err(SwapError::NoController) -} - async fn is_icrc2_supported_token(token_ledger: Principal) -> Result{ let res: (Vec, ) = ic_cdk::call(token_ledger, "icrc1_supported_standards", ()).await?; Ok(res.0.iter().any(|v| v.name == "ICRC2")) From 90e3a218388ddda8150a586727259987dc4f9ee9 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Tue, 19 Nov 2024 15:40:37 +0530 Subject: [PATCH 16/20] merge conflicts --- Cargo.toml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9ce1e74b..931f1da5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,18 +27,6 @@ futures = "0.3.29" shared_utils = { path = "./src/lib/shared_utils" } test_utils = { path = "./src/lib/test_utils" } serde_json = "1.0.113" -<<<<<<< HEAD -ic-sns-init = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-sns-root = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-sns-wasm = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-sns-governance = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-base-types = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-nns-constants = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-nns-governance = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-icrc1-ledger = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-icrc1-index-ng = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -ic-icrc1-index = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-05-29_23-02-base" } -======= ic-sns-init = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } ic-sns-root = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } ic-sns-wasm = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } @@ -53,5 +41,4 @@ ic-icrc1-index-ng = { git = "https://github.com/dfinity/ic", rev = "tags/release ic-icrc1-index = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } ic-sns-swap = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } icp-ledger = { git = "https://github.com/dfinity/ic", rev = "tags/release-2024-10-17_03-07-base" } ->>>>>>> eac8b32d6c5ea68090918dd9fab4ed1df1f34a3a icrc-ledger-types = "0.1.6" From c2d1fe44cff868d796f9d63b41ed21b804ddea61 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Tue, 19 Nov 2024 15:57:31 +0530 Subject: [PATCH 17/20] fixed tests --- src/lib/integration_tests/tests/creator_dao/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 7e3f4fc0..1f9bf8d5 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -934,7 +934,6 @@ fn creator_dao_tests() { }), nns_proposal_id: Some(1), neurons_fund_participation: Some(false), - neurons_fund_participants: None, token_logo: Some("data:image/png;base64,iVBORw0".to_string()), neurons_fund_participation_constraints: None, initial_token_distribution: Some(InitialTokenDistribution::FractionalDeveloperVotingPower( @@ -975,6 +974,7 @@ fn creator_dao_tests() { )), }; + let res = pocket_ic .update_call( bob_canister_id, From d491db61da6dce0ae1b9da48550f177b813e80ac Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Tue, 19 Nov 2024 16:19:41 +0530 Subject: [PATCH 18/20] fix tests --- src/canister/individual_user_template/src/api/cdao/swap.rs | 4 ++-- src/lib/integration_tests/tests/creator_dao/main.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index 94ea82d8..d1522e6f 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -3,7 +3,7 @@ use ic_cdk::api::time; use ic_cdk_macros::update; use icrc_ledger_types::icrc2::{allowance::{Allowance, AllowanceArgs}, approve::{ApproveArgs, ApproveError}, transfer_from::{TransferFromArgs, TransferFromError}}; use serde::Deserialize; -use shared_utils::canister_specific::individual_user_template::types::{cdao::{SwapRequestActions, TokenPairs}, error::SwapError}; +use shared_utils::{canister_specific::individual_user_template::types::{cdao::{SwapRequestActions, TokenPairs}, error::SwapError}, common::utils::permissions::is_caller_global_admin}; use crate::{api::profile::get_profile_details_v2::get_profile_details_v2, CANISTER_DATA}; @@ -91,7 +91,7 @@ pub async fn swap_request_action(op: SwapRequestActions) -> Result<(), SwapError Ok(()) } -#[update] +#[update(guard = "is_caller_global_admin")] fn update_last_swap_price(token_ledger: Principal, price: f64){ CANISTER_DATA.with_borrow_mut(|data| { if let Some(cdao) = data.cdao_canisters.iter_mut().find(|cdao| cdao.ledger == token_ledger){ diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 1f9bf8d5..377bd7d0 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -1083,7 +1083,10 @@ fn creator_dao_tests() { }) .unwrap(); ic_cdk::println!("๐Ÿงช Result: {:?}", res); - + + pocket_ic.advance_time(Duration::from_secs(200)); + pocket_ic.tick(); + let res = pocket_ic .update_call( swap_canister, From 2c7ca74aca4f46d3e96f3c43a1021abea6583131 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Tue, 19 Nov 2024 16:47:38 +0530 Subject: [PATCH 19/20] fix tests --- .../integration_tests/tests/creator_dao/main.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index 377bd7d0..2ea7b412 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -947,7 +947,7 @@ fn creator_dao_tests() { controller: Some( PrincipalId::from_str(&bob.to_string()).unwrap(), ), - stake_e8s: 4_400_000, + stake_e8s: 60_000_000_000, memo: 0, dissolve_delay_seconds: 0, vesting_period_seconds: None, @@ -967,7 +967,7 @@ fn creator_dao_tests() { total_e8s: 10_000_000, }), swap_distribution: Some(SwapDistribution { - total_e8s: 5_000_000, + total_e8s: 65_000_000_000, initial_swap_amount_e8s: 5_000_000, }), }, @@ -1086,7 +1086,7 @@ fn creator_dao_tests() { pocket_ic.advance_time(Duration::from_secs(200)); pocket_ic.tick(); - + let res = pocket_ic .update_call( swap_canister, @@ -1227,6 +1227,12 @@ fn creator_dao_tests() { amount: Some(manage_neuron::disburse::Amount { e8s: amount }), })), }; + + pocket_ic.advance_time(Duration::from_secs(250)); + for _ in 0..10 { + pocket_ic.tick(); + } + let res = pocket_ic .update_call( gov_canister, @@ -1265,7 +1271,7 @@ fn creator_dao_tests() { .unwrap(); ic_cdk::println!("๐Ÿงช SNS token Balance of bob: {:?}", res); - let expected_balance = Nat::from(4_400_000 - tx_fee); + let expected_balance = Nat::from(60_000_000_000 - tx_fee); ic_cdk::println!("๐Ÿงช Expected Balance: {:?}", expected_balance); let bob_canister_final_cycle_balance = pocket_ic.cycle_balance(bob_canister_id); From b388e974cc0a762179a70b7ee780960ea2f2bb10 Mon Sep 17 00:00:00 2001 From: JoeruCodes Date: Sat, 23 Nov 2024 18:31:28 +0530 Subject: [PATCH 20/20] comments --- src/canister/individual_user_template/src/api/cdao/swap.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/canister/individual_user_template/src/api/cdao/swap.rs b/src/canister/individual_user_template/src/api/cdao/swap.rs index d1522e6f..80646e3d 100644 --- a/src/canister/individual_user_template/src/api/cdao/swap.rs +++ b/src/canister/individual_user_template/src/api/cdao/swap.rs @@ -28,8 +28,9 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ return Err(SwapError::IsNotTokenCreator); } + + // todo: replace this by a frontend server function instead because this uses the caller to infer the from field in approve... let previous_approval_amt = get_previous_approval_amount(ic_cdk::caller(), ic_cdk::id(), token_a.ledger).await?; - let allocation_res: (Result, ) = ic_cdk::call(token_a.ledger, "icrc2_approve", (ApproveArgs{ from_subaccount: None, spender: ic_cdk::id().into(), @@ -40,6 +41,8 @@ pub async fn swap_request(token_pairs: TokenPairs) -> Result<(), SwapError>{ fee: None, created_at_time: None }, )).await?; + // .... + allocation_res.0.map_err(SwapError::ApproveError)?; // TODO: Push notifications