Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: withdrawals screen enhancements #20

Merged
merged 11 commits into from
Oct 29, 2024
4 changes: 2 additions & 2 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::platform::contract::ContractTask;
use crate::platform::core::{CoreItem, CoreTask};
use crate::platform::document::DocumentTask;
use crate::platform::identity::IdentityTask;
use crate::platform::withdrawals::{WithdrawStatusData, WithdrawalsTask};
use crate::platform::withdrawals::{WithdrawStatusPartialData, WithdrawalsTask};
use dash_sdk::dpp::voting::votes::Vote;
use dash_sdk::query_types::Documents;
use std::sync::Arc;
Expand Down Expand Up @@ -35,7 +35,7 @@ pub(crate) enum BackendTaskSuccessResult {
Documents(Documents),
CoreItem(CoreItem),
SuccessfulVotes(Vec<Vote>),
WithdrawalStatus(WithdrawStatusData),
WithdrawalStatus(WithdrawStatusPartialData),
}

impl BackendTaskSuccessResult {}
Expand Down
200 changes: 161 additions & 39 deletions src/platform/withdrawals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,31 @@ use dash_sdk::drive::grovedb::element::SumValue;
use dash_sdk::drive::query::{OrderClause, WhereClause, WhereOperator};
use dash_sdk::platform::{Document, DocumentQuery, FetchMany, Identifier};
use dash_sdk::platform::fetch_current_no_parameters::FetchCurrent;
use dash_sdk::query_types::{KeysInPath, TotalCreditsInPlatform};
use dash_sdk::platform::proto::get_documents_request::get_documents_request_v0::Start;
use dash_sdk::query_types::{Documents, KeysInPath, TotalCreditsInPlatform};
use dash_sdk::Sdk;
use crate::context::AppContext;
use crate::platform::BackendTaskSuccessResult;

/// constant id for subtree containing the sum of withdrawals
pub const WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY: [u8; 1] = [2];

pub type WithdrawalsPerPage = u32;

pub type StartAfterIdentifier = Identifier;

pub type RequestRecentSumOfWithdrawals = bool;
pub type RequestTotalCreditsInPlatform = bool;

#[derive(Debug, Clone, PartialEq)]
pub(crate) enum WithdrawalsTask {
QueryWithdrawals(Vec<Value>),
QueryWithdrawals(
Vec<WithdrawalStatus>,
WithdrawalsPerPage,
Option<StartAfterIdentifier>,
RequestRecentSumOfWithdrawals,
RequestTotalCreditsInPlatform,
),
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -34,9 +48,19 @@ pub struct WithdrawRecord {
pub status: WithdrawalStatus,
pub amount: Credits,
pub owner_id: Identifier,
pub document_id: Identifier,
pub address: Address,
}

#[derive(Debug, Clone, PartialEq)]
pub struct WithdrawStatusPartialData {
pub withdrawals: Vec<WithdrawRecord>,
pub total_amount: Credits,
pub recent_withdrawal_amounts: Option<SumValue>,
pub daily_withdrawal_limit: Option<Credits>,
pub total_credits_on_platform: Option<Credits>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct WithdrawStatusData {
pub withdrawals: Vec<WithdrawRecord>,
Expand All @@ -46,6 +70,62 @@ pub struct WithdrawStatusData {
pub total_credits_on_platform: Credits,
}

impl WithdrawStatusData {
pub fn merge_with_data(&mut self, partial: WithdrawStatusPartialData) {
// Merge withdrawals (append new ones)
self.withdrawals.extend(partial.withdrawals);

// Reset total amount
self.total_amount = self
.withdrawals
.iter()
.map(|withdrawal| withdrawal.amount)
.sum();

// Merge recent withdrawal amounts if available
if let Some(recent_amounts) = partial.recent_withdrawal_amounts {
self.recent_withdrawal_amounts = recent_amounts;
}

// Merge daily withdrawal limit if available
if let Some(daily_limit) = partial.daily_withdrawal_limit {
self.daily_withdrawal_limit = daily_limit;
}

// Merge total credits on platform if available
if let Some(total_credits) = partial.total_credits_on_platform {
self.total_credits_on_platform = total_credits;
}
}
}

use std::convert::TryFrom;

#[derive(Debug)]
pub enum ConversionError {
MissingField(&'static str),
}

impl TryFrom<WithdrawStatusPartialData> for WithdrawStatusData {
type Error = ConversionError;

fn try_from(partial: WithdrawStatusPartialData) -> Result<Self, Self::Error> {
Ok(WithdrawStatusData {
withdrawals: partial.withdrawals,
total_amount: partial.total_amount,
recent_withdrawal_amounts: partial
.recent_withdrawal_amounts
.ok_or(ConversionError::MissingField("recent_withdrawal_amounts"))?,
daily_withdrawal_limit: partial
.daily_withdrawal_limit
.ok_or(ConversionError::MissingField("daily_withdrawal_limit"))?,
total_credits_on_platform: partial
.total_credits_on_platform
.ok_or(ConversionError::MissingField("total_credits_on_platform"))?,
})
}
}

impl AppContext {
pub async fn run_withdraws_task(
self: &Arc<Self>,
Expand All @@ -54,24 +134,47 @@ impl AppContext {
) -> Result<BackendTaskSuccessResult, String> {
let sdk = sdk.clone();
match &task {
WithdrawalsTask::QueryWithdrawals(status_filter) => {
self.query_withdrawals(sdk, status_filter).await
WithdrawalsTask::QueryWithdrawals(
status_filter,
per_page_count,
start_after,
request_recent_sum_of_withdrawals,
request_total_credits_in_platform,
) => {
self.query_withdrawals(
sdk,
status_filter,
*per_page_count,
start_after,
*request_recent_sum_of_withdrawals,
*request_total_credits_in_platform,
)
.await
}
}
}

pub(super) async fn query_withdrawals(
self: &Arc<Self>,
sdk: Sdk,
status_filter: &Vec<Value>,
status_filter: &[WithdrawalStatus],
per_page_count: WithdrawalsPerPage,
start_after: &Option<StartAfterIdentifier>,
request_recent_sum_of_withdrawals: bool,
request_total_credits_in_platform: bool,
) -> Result<BackendTaskSuccessResult, String> {
let queued_document_query = DocumentQuery {
data_contract: self.withdraws_contract.clone(),
document_type_name: "withdrawal".to_string(),
where_clauses: vec![WhereClause {
field: "status".to_string(),
operator: WhereOperator::In,
value: Value::Array(status_filter.to_vec()),
value: Value::Array(
status_filter
.iter()
.map(|status| (*status as u8).into())
.collect(),
),
}],
order_by_clauses: vec![
OrderClause {
Expand All @@ -83,60 +186,75 @@ impl AppContext {
ascending: true,
},
],
limit: 100,
start: None,
limit: per_page_count,
start: start_after.map(|start| Start::StartAfter(start.to_vec())),
};

let documents = Document::fetch_many(&sdk, queued_document_query.clone())
.await
.map_err(|e| e.to_string())?;

let keys_in_path = KeysInPath {
path: vec![vec![RootTree::WithdrawalTransactions as u8]],
keys: vec![WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY.to_vec()],
};
let recent_sum_of_withdrawals = if request_recent_sum_of_withdrawals {
let keys_in_path = KeysInPath {
path: vec![vec![RootTree::WithdrawalTransactions as u8]],
keys: vec![WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY.to_vec()],
};

let elements = Element::fetch_many(&sdk, keys_in_path)
.await
.map_err(|e| e.to_string())?;
let elements = Element::fetch_many(&sdk, keys_in_path)
.await
.map_err(|e| e.to_string())?;

let sum_tree_element_option = elements
.get(&WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY.to_vec())
.ok_or_else(|| {
"could not get sum tree value for current withdrawal maximum".to_string()
})?;

let sum_tree_element_option = elements
.get(&WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY.to_vec())
.ok_or_else(|| {
let sum_tree_element = sum_tree_element_option.as_ref().ok_or_else(|| {
"could not get sum tree value for current withdrawal maximum".to_string()
})?;

let sum_tree_element = sum_tree_element_option.as_ref().ok_or_else(|| {
"could not get sum tree value for current withdrawal maximum".to_string()
})?;
let value = if let Element::SumTree(_, value, _) = sum_tree_element {
value
} else {
return Err(
"could not get sum tree value for current withdrawal maximum".to_string(),
);
};

let value = if let Element::SumTree(_, value, _) = sum_tree_element {
value
Some(*value)
} else {
return Err("could not get sum tree value for current withdrawal maximum".to_string());
None
};

let total_credits = TotalCreditsInPlatform::fetch_current(&sdk)
.await
.map_err(|e| e.to_string())?;
let total_credits = if request_total_credits_in_platform {
Some(
TotalCreditsInPlatform::fetch_current(&sdk)
.await
.map_err(|e| e.to_string())?
.0,
)
} else {
None
};

Ok(BackendTaskSuccessResult::WithdrawalStatus(
util_transform_documents_to_withdrawal_status(
*value,
total_credits.0,
&documents.values().filter_map(|a| a.clone()).collect(),
recent_sum_of_withdrawals,
total_credits,
documents.into_values().filter_map(|a| a).collect(),
sdk.network,
)?,
))
}
}

fn util_transform_documents_to_withdrawal_status(
recent_withdrawal_amounts: SumValue,
total_credits_on_platform: Credits,
withdrawal_documents: &Vec<Document>,
recent_withdrawal_amounts: Option<SumValue>,
total_credits_on_platform: Option<Credits>,
withdrawal_documents: Vec<Document>,
network: Network,
) -> Result<WithdrawStatusData, String> {
) -> Result<WithdrawStatusPartialData, String> {
let total_amount = withdrawal_documents.iter().try_fold(0, |acc, document| {
document
.properties()
Expand All @@ -145,16 +263,19 @@ fn util_transform_documents_to_withdrawal_status(
.map(|amount| acc + amount)
})?;

let daily_withdrawal_limit =
daily_withdrawal_limit(total_credits_on_platform, PlatformVersion::latest())
.map_err(|_| "expected to get daily withdrawal limit".to_string())?;
let daily_withdrawal_limit = total_credits_on_platform
.map(|total_credits_on_platform| {
daily_withdrawal_limit(total_credits_on_platform, PlatformVersion::latest())
.map_err(|_| "expected to get daily withdrawal limit".to_string())
})
.transpose()?;

let mut vec_withdraws = vec![];
for document in withdrawal_documents {
vec_withdraws.push(util_convert_document_to_record(document, network)?);
vec_withdraws.push(util_convert_document_to_record(&document, network)?);
}

Ok(WithdrawStatusData {
Ok(WithdrawStatusPartialData {
withdrawals: vec_withdraws,
total_amount,
recent_withdrawal_amounts,
Expand Down Expand Up @@ -206,6 +327,7 @@ fn util_convert_document_to_record(
status,
amount,
owner_id,
document_id: document.id(),
address,
})
}
Loading