From 0df9544fcdf053f4f2d20bfcbec014566f355c35 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Fri, 5 Apr 2024 18:03:07 +0800 Subject: [PATCH 01/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5eb2ec93..0f1e2958 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Setheum's Blockchain Network node Implementation in Rust, ready for hacking :roc [![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2FSetheum)](https://twitter.com/Setheum) [![Telegram](https://img.shields.io/badge/Telegram-gray?logo=telegram)](https://t.me/SetheumNetwork) [![Medium](https://img.shields.io/badge/Medium-gray?logo=medium)](https://medium.com/setheum-labs) - +[![Lines of Code](https://img.shields.io/badge/LinesOfCode-gray?logo=LinesOfCode)](https://cloc.info/github.com/Setheum-Labs/Setheum) > NOTE: SETHEUM means `Salam Ethereum`, it also means `The house of gifts` from the name `Seth/Sheeth` meaning `gift` in hebrew and the name of the Prophet Sheeth/Seth in Islam, it also stands for `Secure, Evergreen, Truthful, Heterogeneous, Economically Unbiased Market`. From 7def07b260e8e3d44e1cfb3e8d71e864f98b9ded Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sat, 6 Apr 2024 23:09:55 +0800 Subject: [PATCH 02/10] Edfis Pay --- blockchain/modules/edfis-pay/Cargo.toml | 46 + blockchain/modules/edfis-pay/README.md | 85 ++ blockchain/modules/edfis-pay/TODO.md | 56 + blockchain/modules/edfis-pay/src/lib.rs | 680 +++++++++ blockchain/modules/edfis-pay/src/mock.rs | 184 +++ blockchain/modules/edfis-pay/src/tests.rs | 1470 +++++++++++++++++++ blockchain/modules/edfis-pay/src/types.rs | 142 ++ blockchain/modules/edfis-pay/src/weights.rs | 205 +++ 8 files changed, 2868 insertions(+) create mode 100644 blockchain/modules/edfis-pay/Cargo.toml create mode 100644 blockchain/modules/edfis-pay/README.md create mode 100644 blockchain/modules/edfis-pay/TODO.md create mode 100644 blockchain/modules/edfis-pay/src/lib.rs create mode 100644 blockchain/modules/edfis-pay/src/mock.rs create mode 100644 blockchain/modules/edfis-pay/src/tests.rs create mode 100644 blockchain/modules/edfis-pay/src/types.rs create mode 100644 blockchain/modules/edfis-pay/src/weights.rs diff --git a/blockchain/modules/edfis-pay/Cargo.toml b/blockchain/modules/edfis-pay/Cargo.toml new file mode 100644 index 00000000..0a160b55 --- /dev/null +++ b/blockchain/modules/edfis-pay/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "module-setheum-pay" +version = "0.9.81-dev" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + + +[dependencies] +parity-scale-codec = { workspace = true } +log = { workspace = true } +scale-info = { workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +orml-traits = {workspace = true, default-features = false } + +[dev-dependencies] +serde = "1.0.136" + +sp-core = { workspace = true } +sp-io = { workspace = true } + +orml-tokens = { workspace = true } + +[features] +default = [ 'std' ] +std = [ + 'frame-support/std', + 'frame-system/std', + 'log/std', + 'orml-traits/std', + 'parity-scale-codec/std', + 'scale-info/std', + 'sp-runtime/std', + 'sp-std/std', +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/blockchain/modules/edfis-pay/README.md b/blockchain/modules/edfis-pay/README.md new file mode 100644 index 00000000..4aef81cc --- /dev/null +++ b/blockchain/modules/edfis-pay/README.md @@ -0,0 +1,85 @@ +بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +# Payments Module + +This module allows users to create secure reversible payments that keep funds locked in a merchant's account until the off-chain goods are confirmed to be received. Each payment gets assigned its own *judge* that can help resolve any disputes between the two parties. + +## Terminology + +- Created: A payment has been created and the amount arrived to its destination but it's locked. +- NeedsReview: The payment has bee disputed and is awaiting settlement by a judge. +- IncentivePercentage: A small share of the payment amount is held in escrow until a payment is completed/cancelled. The Incentive Percentage represents this value. +- Resolver Account: A resolver account is assigned to every payment created, this account has the privilege to cancel/release a payment that has been disputed. +- Remark: The module allows to create payments by optionally providing some extra(limited) amount of bytes, this is referred to as Remark. This can be used by a marketplace to separate/tag payments. +- CancelBufferBlockLength: This is the time window where the recipient can dispute a cancellation request from the payment creator. + +## Interface + +#### Events + +- `PaymentCreated { from: T::AccountId, asset: AssetIdOf, amount: BalanceOf },`, +- `PaymentReleased { from: T::AccountId, to: T::AccountId }`, +- `PaymentCancelled { from: T::AccountId, to: T::AccountId }`, +- `PaymentCreatorRequestedRefund { from: T::AccountId, to: T::AccountId, expiry: BlockNumberFor}` +- `PaymentRefundDisputed { from: T::AccountId, to: T::AccountId }` +- `PaymentRequestCreated { from: T::AccountId, to: T::AccountId }` +- `PaymentRequestCompleted { from: T::AccountId, to: T::AccountId }` + +#### Extrinsics + +- `pay` - Create an payment for the given currencyid/amount +- `pay_with_remark` - Create a payment with a remark, can be used to tag payments +- `release` - Release the payment amount to recipent +- `cancel` - Allows the recipient to cancel the payment and release the payment amount to creator +- `resolve_release_payment` - Allows assigned judge to release a payment +- `resolve_cancel_payment` - Allows assigned judge to cancel a payment +- `request_refund` - Allows the creator of the payment to trigger cancel with a buffer time. +- `claim_refund` - Allows the creator to claim payment refund after buffer time +- `dispute_refund` - Allows the recipient to dispute the payment request of sender +- `request_payment` - Create a payment that can be completed by the sender using the `accept_and_pay` extrinsic. +- `accept_and_pay` - Allows the sender to fulfill a payment request created by a recipient + +## Implementations + +The RatesProvider module provides implementations for the following traits. +- [`PaymentHandler`](./src/types.rs) + +## Types + +The `PaymentDetail` struct stores information about the payment/escrow. A "payment" in Setheum Pay is similar to an escrow, it is used to guarantee proof of funds and can be released once an agreed upon condition has reached between the payment creator and recipient. The payment lifecycle is tracked using the state field. + +```rust +pub struct PaymentDetail { + /// type of asset used for payment + pub asset: AssetIdOf, + /// amount of asset used for payment + pub amount: BalanceOf, + /// incentive amount that is credited to creator for resolving + pub incentive_amount: BalanceOf, + /// enum to track payment lifecycle [Created, NeedsReview] + pub state: PaymentState>, + /// account that can settle any disputes created in the payment + pub resolver_account: T::AccountId, + /// fee charged and recipient account details + pub fee_detail: Option<(T::AccountId, BalanceOf)>, + /// remarks to give context to payment + pub remark: Option>, +} +``` + +The `PaymentState` enum tracks the possible states that a payment can be in. When a payment is 'completed' or 'cancelled' it is removed from storage and hence not tracked by a state. + +```rust +pub enum PaymentState { + /// Amounts have been reserved and waiting for release/cancel + Created, + /// A judge needs to review and release manually + NeedsReview, + /// The user has requested refund and will be processed by `BlockNumber` + RefundRequested(BlockNumber), +} +``` + +## GenesisConfig + +The rates_provider pallet does not depend on the `GenesisConfig` diff --git a/blockchain/modules/edfis-pay/TODO.md b/blockchain/modules/edfis-pay/TODO.md new file mode 100644 index 00000000..c8c94c13 --- /dev/null +++ b/blockchain/modules/edfis-pay/TODO.md @@ -0,0 +1,56 @@ +# To-Do List + +This list contains all TODOs in the Repo + + + +- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) + - [1. Introduction](#1-guidelines) + - [2. Contribution](#2-contribution) + - [3. Lists](#3-lists) + - [4. Tasks](#4-tasks) + + + +## 1. Guidelines + +Note: Before you write a ToDo in this repo, please read the below guidelines carefully. + +Whenever you write a ToDo, you need to follow this standard syntax + +```rust +//TODO:[file_name:task_number] - task_details +``` + +for example: + +```rust +//TODO:[TODO.md:0] - Add Todo Guidelines +``` + +Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. + +Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. + +Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. + +Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: + +```rust +//TODO:[TODO.md-C:0] - Add Todo Guidelines +``` + +## 2. Contribution + +You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. + +## 3. Lists + +Each package/module/directory has its own `TODO.md`. + +## 4. Tasks + +These tasks are just for this file specifically. + +- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. +- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/edfis-pay/src/lib.rs b/blockchain/modules/edfis-pay/src/lib.rs new file mode 100644 index 00000000..28e8e545 --- /dev/null +++ b/blockchain/modules/edfis-pay/src/lib.rs @@ -0,0 +1,680 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// This file is part of Setheum. + +// Copyright (C) 2019-Present Setheum Labs. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//!This module allows users to create secure reversible payments that keep +//! funds locked in a merchant's account until the off-chain goods are confirmed +//! to be received. Each payment gets assigned its own *judge* that can help +//! resolve any disputes between the two parties. + +//! ## Terminology +//! +//! - Created: A payment has been created and the amount arrived to its +//! destination but it's locked. +//! - NeedsReview: The payment has bee disputed and is awaiting settlement by a +//! judge. +//! - IncentivePercentage: A small share of the payment amount is held in escrow +//! until a payment is completed/cancelled. The Incentive Percentage +//! represents this value. +//! - Resolver Account: A resolver account is assigned to every payment created, +//! this account has the privilege to cancel/release a payment that has been +//! disputed. +//! - Remark: The module allows to create payments by optionally providing some +//! extra(limited) amount of bytes, this is referred to as Remark. This can be +//! used by a marketplace to separate/tag payments. +//! - CancelBufferBlockLength: This is the time window where the recipient can +//! dispute a cancellation request from the payment creator. + +//! Extrinsics +//! +//! - `pay` - Create an payment for the given currencyid/amount +//! - `pay_with_remark` - Create a payment with a remark, can be used to tag +//! payments +//! - `release` - Release the payment amount to recipent +//! - `cancel` - Allows the recipient to cancel the payment and release the +//! payment amount to creator +//! - `resolve_release_payment` - Allows assigned judge to release a payment +//! - `resolve_cancel_payment` - Allows assigned judge to cancel a payment +//! - `request_refund` - Allows the creator of the payment to trigger cancel +//! with a buffer time. +//! - `claim_refund` - Allows the creator to claim payment refund after buffer +//! time +//! - `dispute_refund` - Allows the recipient to dispute the payment request of +//! sender +//! - `request_payment` - Create a payment that can be completed by the sender +//! using the `accept_and_pay` extrinsic. +//! - `accept_and_pay` - Allows the sender to fulfill a payment request created +//! by a recipient + +//! Types +//! +//! The `PaymentDetail` struct stores information about the payment/escrow. A +//! "payment" on Setheum Pay is similar to an escrow, it is used to guarantee +//! proof of funds and can be released once an agreed upon condition has reached +//! between the payment creator and recipient. The payment lifecycle is tracked +//! using the state field. + +//! The `PaymentState` enum tracks the possible states that a payment can be in. +//! When a payment is 'completed' or 'cancelled' it is removed from storage and +//! hence not tracked by a state. +#![cfg_attr(not(feature = "std"), no_std)] +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +pub mod types; +pub mod weights; + +#[frame_support::pallet] +pub mod pallet { + pub use crate::{ + types::{DisputeResolver, FeeHandler, PaymentDetail, PaymentHandler, PaymentState, ScheduledTask, Task}, + weights::WeightInfo, + }; + use frame_support::{ + dispatch::DispatchResultWithPostInfo, fail, pallet_prelude::*, require_transactional, + storage::bounded_btree_map::BoundedBTreeMap, traits::tokens::BalanceStatus, + }; + use frame_system::pallet_prelude::*; + use orml_traits::{MultiCurrency, MultiReservableCurrency}; + use sp_runtime::{ + traits::{CheckedAdd, Saturating}, + Percent, + }; + use sp_std::vec::Vec; + + pub type BalanceOf = <::Asset as MultiCurrency<::AccountId>>::Balance; + pub type AssetIdOf = <::Asset as MultiCurrency<::AccountId>>::CurrencyId; + pub type BoundedDataOf = BoundedVec::MaxRemarkLength>; + /// type of ScheduledTask used by the pallet + pub type ScheduledTaskOf = ScheduledTask>; + /// list of ScheduledTasks, stored as a BoundedBTreeMap + pub type ScheduledTaskList = BoundedBTreeMap< + ( + ::AccountId, + ::AccountId, + ), + ScheduledTaskOf, + ::MaxRemarkLength, + >; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's + /// definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// the type of assets this pallet can hold in payment + type Asset: MultiReservableCurrency; + /// Dispute resolution account + type DisputeResolver: DisputeResolver; + /// Fee handler trait + type FeeHandler: FeeHandler; + /// Incentive percentage - amount withheld from sender + #[pallet::constant] + type IncentivePercentage: Get; + /// Maximum permitted size of `Remark` + #[pallet::constant] + type MaxRemarkLength: Get; + /// Buffer period - number of blocks to wait before user can claim + /// canceled payment + #[pallet::constant] + type CancelBufferBlockLength: Get>; + /// Buffer period - number of blocks to wait before user can claim + /// canceled payment + #[pallet::constant] + type MaxScheduledTaskListLength: Get; + //// Type representing the weight of this pallet + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn payment)] + /// Payments created by a user, this method of storageDoubleMap is chosen + /// since there is no usecase for listing payments by provider/currency. The + /// payment will only be referenced by the creator in any transaction of + /// interest. The storage map keys are the creator and the recipient, this + /// also ensures that for any (sender,recipient) combo, only a single + /// payment is active. The history of payment is not stored. + pub(super) type Payment = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, // payment creator + Blake2_128Concat, + T::AccountId, // payment recipient + PaymentDetail, + >; + + #[pallet::storage] + #[pallet::getter(fn tasks)] + /// Store the list of tasks to be executed in the on_idle function + pub(super) type ScheduledTasks = StorageValue<_, ScheduledTaskList, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A new payment has been created + PaymentCreated { + from: T::AccountId, + asset: AssetIdOf, + amount: BalanceOf, + remark: Option>, + }, + /// Payment amount released to the recipient + PaymentReleased { from: T::AccountId, to: T::AccountId }, + /// Payment has been cancelled by the creator + PaymentCancelled { from: T::AccountId, to: T::AccountId }, + /// A payment that NeedsReview has been resolved by Judge + PaymentResolved { + from: T::AccountId, + to: T::AccountId, + recipient_share: Percent, + }, + /// the payment creator has created a refund request + PaymentCreatorRequestedRefund { + from: T::AccountId, + to: T::AccountId, + expiry: BlockNumberFor, + }, + /// the refund request from creator was disputed by recipient + PaymentRefundDisputed { from: T::AccountId, to: T::AccountId }, + /// Payment request was created by recipient + PaymentRequestCreated { from: T::AccountId, to: T::AccountId }, + /// Payment request was completed by sender + PaymentRequestCompleted { from: T::AccountId, to: T::AccountId }, + } + + #[pallet::error] + pub enum Error { + /// The selected payment does not exist + InvalidPayment, + /// The selected payment cannot be released + PaymentAlreadyReleased, + /// The selected payment already exists and is in process + PaymentAlreadyInProcess, + /// Action permitted only for whitelisted users + InvalidAction, + /// Payment is in review state and cannot be modified + PaymentNeedsReview, + /// Unexpeted math error + MathError, + /// Payment request has not been created + RefundNotRequested, + /// Dispute period has not passed + DisputePeriodNotPassed, + /// The automatic cancelation queue cannot accept + RefundQueueFull, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + /// Hook that execute when there is leftover space in a block + /// This function will look for any pending scheduled tasks that can + /// be executed and will process them. + fn on_idle(now: BlockNumberFor, remaining_weight: Weight) -> Weight { + const MAX_TASKS_TO_PROCESS: usize = 5; + // used to read the task list + let mut used_weight = T::WeightInfo::remove_task(); + let cancel_weight = T::WeightInfo::cancel(); + + // calculate count of tasks that can be processed with remaining weight + let possible_task_count: usize = remaining_weight + .saturating_sub(used_weight) + .saturating_div(cancel_weight.ref_time()) + .ref_time() + .try_into() + .unwrap_or(MAX_TASKS_TO_PROCESS); + + ScheduledTasks::::mutate(|tasks| { + let mut task_list: Vec<_> = tasks + .clone() + .into_iter() + .take(possible_task_count) + // leave out tasks in the future + .filter(|(_, ScheduledTask { when, task })| when <= &now && matches!(task, Task::Cancel)) + .collect(); + + // order by oldest task to process + task_list.sort_by(|(_, t), (_, x)| x.when.cmp(&t.when)); + + while !task_list.is_empty() && used_weight.all_lte(remaining_weight) { + if let Some((account_pair, _)) = task_list.pop() { + used_weight = used_weight.saturating_add(cancel_weight); + // remove the task form the tasks storage + tasks.remove(&account_pair); + + // process the cancel payment + if >::settle_payment( + &account_pair.0, + &account_pair.1, + Percent::from_percent(0), + ) + .is_err() + { + // log the payment refund failure + log::warn!( + target: "runtime::payments", + "Warning: Unable to process payment refund!" + ); + } else { + // emit the cancel event if the refund was successful + Self::deposit_event(Event::PaymentCancelled { + from: account_pair.0, + to: account_pair.1, + }); + } + } + } + }); + used_weight + } + } + + #[pallet::call] + impl Pallet { + /// This allows any user to create a new payment, that releases only to + /// specified recipient The only action is to store the details of this + /// payment in storage and reserve the specified amount. User also has + /// the option to add a remark, this remark can then be used to run + /// custom logic and trigger alternate payment flows. the specified + /// amount. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::pay(T::MaxRemarkLength::get()))] + pub fn pay( + origin: OriginFor, + recipient: T::AccountId, + asset: AssetIdOf, + #[pallet::compact] amount: BalanceOf, + remark: Option>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + // create PaymentDetail and add to storage + let payment_detail = >::create_payment( + &who, + &recipient, + asset, + amount, + PaymentState::Created, + T::IncentivePercentage::get(), + remark.as_ref().map(|x| x.as_slice()), + )?; + // reserve funds for payment + >::reserve_payment_amount(&who, &recipient, payment_detail)?; + // emit paymentcreated event + Self::deposit_event(Event::PaymentCreated { + from: who, + asset, + amount, + remark, + }); + Ok(().into()) + } + + /// Release any created payment, this will transfer the reserved amount + /// from the creator of the payment to the assigned recipient + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::release())] + pub fn release(origin: OriginFor, to: T::AccountId) -> DispatchResultWithPostInfo { + let from = ensure_signed(origin)?; + + // ensure the payment is in Created state + let payment = Payment::::get(&from, &to).ok_or(Error::::InvalidPayment)?; + ensure!(payment.state == PaymentState::Created, Error::::InvalidAction); + + // release is a settle_payment with 100% recipient_share + >::settle_payment(&from, &to, Percent::from_percent(100))?; + + Self::deposit_event(Event::PaymentReleased { from, to }); + Ok(().into()) + } + + /// Cancel a payment in created state, this will release the reserved + /// back to creator of the payment. This extrinsic can only be called by + /// the recipient of the payment + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::cancel())] + pub fn cancel(origin: OriginFor, creator: T::AccountId) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + if let Some(payment) = Payment::::get(&creator, &who) { + match payment.state { + // call settle payment with recipient_share=0, this refunds the sender + PaymentState::Created => { + >::settle_payment(&creator, &who, Percent::from_percent(0))?; + Self::deposit_event(Event::PaymentCancelled { from: creator, to: who }); + } + // if the payment is in state PaymentRequested, remove from storage + PaymentState::PaymentRequested => Payment::::remove(&creator, &who), + _ => fail!(Error::::InvalidAction), + } + } + Ok(().into()) + } + + /// This extrinsic is used to resolve disputes between the creator and + /// recipient of the payment. + /// This extrinsic allows the assigned judge to + /// cancel/release/partial_release the payment. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::resolve_payment())] + pub fn resolve_payment( + origin: OriginFor, + from: T::AccountId, + recipient: T::AccountId, + recipient_share: Percent, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let account_pair = (from, recipient); + // ensure the caller is the assigned resolver + if let Some(payment) = Payment::::get(&account_pair.0, &account_pair.1) { + ensure!(who == payment.resolver_account, Error::::InvalidAction); + ensure!( + payment.state != PaymentState::PaymentRequested, + Error::::InvalidAction + ); + if matches!(payment.state, PaymentState::RefundRequested { .. }) { + ScheduledTasks::::mutate(|tasks| { + tasks.remove(&account_pair); + }) + } + } + // try to update the payment to new state + >::settle_payment(&account_pair.0, &account_pair.1, recipient_share)?; + Self::deposit_event(Event::PaymentResolved { + from: account_pair.0, + to: account_pair.1, + recipient_share, + }); + Ok(().into()) + } + + /// Allow the creator of a payment to initiate a refund that will return + /// the funds after a configured amount of time that the reveiver has to + /// react and oppose the request + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::request_refund())] + pub fn request_refund(origin: OriginFor, recipient: T::AccountId) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + Payment::::try_mutate(who.clone(), recipient.clone(), |maybe_payment| -> DispatchResult { + // ensure the payment exists + let payment = maybe_payment.as_mut().ok_or(Error::::InvalidPayment)?; + // refunds only possible for payments in created state + ensure!(payment.state == PaymentState::Created, Error::::InvalidAction); + + // set the payment to requested refund + let current_block = frame_system::Pallet::::block_number(); + let cancel_block = current_block + .checked_add(&T::CancelBufferBlockLength::get()) + .ok_or(Error::::MathError)?; + + ScheduledTasks::::try_mutate(|task_list| -> DispatchResult { + task_list + .try_insert( + (who.clone(), recipient.clone()), + ScheduledTask { + task: Task::Cancel, + when: cancel_block, + }, + ) + .map_err(|_| Error::::RefundQueueFull)?; + Ok(()) + })?; + + payment.state = PaymentState::RefundRequested { cancel_block }; + + Self::deposit_event(Event::PaymentCreatorRequestedRefund { + from: who, + to: recipient, + expiry: cancel_block, + }); + + Ok(()) + })?; + + Ok(().into()) + } + + /// Allow payment recipient to dispute the refund request from the + /// payment creator This does not cancel the request, instead sends the + /// payment to a NeedsReview state The assigned resolver account can + /// then change the state of the payment after review. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::dispute_refund())] + pub fn dispute_refund(origin: OriginFor, creator: T::AccountId) -> DispatchResultWithPostInfo { + use PaymentState::*; + let who = ensure_signed(origin)?; + + Payment::::try_mutate( + creator.clone(), + who.clone(), // should be called by the payment recipient + |maybe_payment| -> DispatchResult { + // ensure the payment exists + let payment = maybe_payment.as_mut().ok_or(Error::::InvalidPayment)?; + // ensure the payment is in Requested Refund state + match payment.state { + RefundRequested { cancel_block } => { + ensure!( + cancel_block > frame_system::Pallet::::block_number(), + Error::::InvalidAction + ); + + payment.state = PaymentState::NeedsReview; + + // remove the payment from scheduled tasks + ScheduledTasks::::try_mutate(|task_list| -> DispatchResult { + task_list + .remove(&(creator.clone(), who.clone())) + .ok_or(Error::::InvalidAction)?; + Ok(()) + })?; + + Self::deposit_event(Event::PaymentRefundDisputed { from: creator, to: who }); + } + _ => fail!(Error::::InvalidAction), + } + + Ok(()) + }, + )?; + + Ok(().into()) + } + + // Creates a new payment with the given details. This can be called by the + // recipient of the payment to create a payment and then completed by the sender + // using the `accept_and_pay` extrinsic. The payment will be in + // PaymentRequested State and can only be modified by the `accept_and_pay` + // extrinsic. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::request_payment())] + pub fn request_payment( + origin: OriginFor, + from: T::AccountId, + asset: AssetIdOf, + #[pallet::compact] amount: BalanceOf, + ) -> DispatchResultWithPostInfo { + let to = ensure_signed(origin)?; + + // create PaymentDetail and add to storage + >::create_payment( + &from, + &to, + asset, + amount, + PaymentState::PaymentRequested, + Percent::from_percent(0), + None, + )?; + + Self::deposit_event(Event::PaymentRequestCreated { from, to }); + + Ok(().into()) + } + + // This extrinsic allows the sender to fulfill a payment request created by a + // recipient. The amount will be transferred to the recipient and payment + // removed from storage + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::accept_and_pay())] + pub fn accept_and_pay(origin: OriginFor, to: T::AccountId) -> DispatchResultWithPostInfo { + let from = ensure_signed(origin)?; + + let payment = Payment::::get(&from, &to).ok_or(Error::::InvalidPayment)?; + + ensure!( + payment.state == PaymentState::PaymentRequested, + Error::::InvalidAction + ); + + // reserve all the fees from the sender + >::reserve_payment_amount(&from, &to, payment)?; + + // release the payment and delete the payment from storage + >::settle_payment(&from, &to, Percent::from_percent(100))?; + + Self::deposit_event(Event::PaymentRequestCompleted { from, to }); + + Ok(().into()) + } + } + + impl PaymentHandler for Pallet { + /// The function will create a new payment. The fee and incentive + /// amounts will be calculated and the `PaymentDetail` will be added to + /// storage. + #[require_transactional] + fn create_payment( + from: &T::AccountId, + recipient: &T::AccountId, + asset: AssetIdOf, + amount: BalanceOf, + payment_state: PaymentState, + incentive_percentage: Percent, + remark: Option<&[u8]>, + ) -> Result, sp_runtime::DispatchError> { + Payment::::try_mutate( + from, + recipient, + |maybe_payment| -> Result, sp_runtime::DispatchError> { + // only payment requests can be overwritten + if let Some(payment) = maybe_payment { + ensure!( + payment.state == PaymentState::PaymentRequested, + Error::::PaymentAlreadyInProcess + ); + } + + // Calculate incentive amount - this is to insentivise the user to release + // the funds once a transaction has been completed + let incentive_amount = incentive_percentage.mul_floor(amount); + + let mut new_payment = PaymentDetail { + asset, + amount, + incentive_amount, + state: payment_state, + resolver_account: T::DisputeResolver::get_resolver_account(), + fee_detail: None, + }; + + // Calculate fee amount - this will be implemented based on the custom + // implementation of the fee provider + let (fee_recipient, fee_percent) = T::FeeHandler::apply_fees(from, recipient, &new_payment, remark); + let fee_amount = fee_percent.mul_floor(amount); + new_payment.fee_detail = Some((fee_recipient, fee_amount)); + + *maybe_payment = Some(new_payment.clone()); + + Ok(new_payment) + }, + ) + } + + /// The function will reserve the fees+transfer amount from the `from` + /// account. After reserving the payment.amount will be transferred to + /// the recipient but will stay in Reserve state. + #[require_transactional] + fn reserve_payment_amount(from: &T::AccountId, to: &T::AccountId, payment: PaymentDetail) -> DispatchResult { + let fee_amount = payment.fee_detail.map(|(_, f)| f).unwrap_or_else(|| 0u32.into()); + + let total_fee_amount = payment.incentive_amount.saturating_add(fee_amount); + let total_amount = total_fee_amount.saturating_add(payment.amount); + + // reserve the total amount from payment creator + T::Asset::reserve(payment.asset, from, total_amount)?; + // transfer payment amount to recipient -- keeping reserve status + T::Asset::repatriate_reserved(payment.asset, from, to, payment.amount, BalanceStatus::Reserved)?; + Ok(()) + } + + /// This function allows the caller to settle the payment by specifying + /// a recipient_share this will unreserve the fee+incentive to sender + /// and unreserve transferred amount to recipient if the settlement is a + /// release (ie recipient_share=100), the fee is transferred to + /// fee_recipient For cancelling a payment, recipient_share = 0 + /// For releasing a payment, recipient_share = 100 + /// In other cases, the custom recipient_share can be specified + fn settle_payment(from: &T::AccountId, to: &T::AccountId, recipient_share: Percent) -> DispatchResult { + Payment::::try_mutate(from, to, |maybe_payment| -> DispatchResult { + let payment = maybe_payment.take().ok_or(Error::::InvalidPayment)?; + + // unreserve the incentive amount and fees from the owner account + match payment.fee_detail { + Some((fee_recipient, fee_amount)) => { + T::Asset::unreserve(payment.asset, from, payment.incentive_amount.saturating_add(fee_amount)); + // transfer fee to marketplace if operation is not cancel + if recipient_share != Percent::zero() { + T::Asset::transfer( + payment.asset, + from, // fee is paid by payment creator + &fee_recipient, // account of fee recipient + fee_amount, // amount of fee + )?; + } + } + None => { + T::Asset::unreserve(payment.asset, from, payment.incentive_amount); + } + }; + + // Unreserve the transfer amount + T::Asset::unreserve(payment.asset, to, payment.amount); + + let amount_to_recipient = recipient_share.mul_floor(payment.amount); + let amount_to_sender = payment.amount.saturating_sub(amount_to_recipient); + // send share to recipient + T::Asset::transfer(payment.asset, to, from, amount_to_sender)?; + + Ok(()) + })?; + Ok(()) + } + + fn get_payment_details(from: &T::AccountId, to: &T::AccountId) -> Option> { + Payment::::get(from, to) + } + } +} diff --git a/blockchain/modules/edfis-pay/src/mock.rs b/blockchain/modules/edfis-pay/src/mock.rs new file mode 100644 index 00000000..20a8ae4e --- /dev/null +++ b/blockchain/modules/edfis-pay/src/mock.rs @@ -0,0 +1,184 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// This file is part of Setheum. + +// Copyright (C) 2019-Present Setheum Labs. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate as pay; +use crate::PaymentDetail; +use frame_support::{ + derive_impl, + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, Contains, Hooks, OnFinalize}, +}; +use frame_system as system; +use orml_traits::parameter_type_with_key; +use sp_runtime::{traits::IdentityLookup, BuildStorage, Percent}; + +type Block = frame_system::mocking::MockBlock; +pub type Balance = u128; + +pub type AccountId = u8; +pub const PAYMENT_CREATOR: AccountId = 10; +pub const PAYMENT_RECIPENT: AccountId = 11; +pub const PAYMENT_CREATOR_TWO: AccountId = 30; +pub const PAYMENT_RECIPENT_TWO: AccountId = 31; +pub const CURRENCY_ID: u32 = 1; +pub const RESOLVER_ACCOUNT: AccountId = 12; +pub const FEE_RECIPIENT_ACCOUNT: AccountId = 20; +pub const PAYMENT_RECIPENT_FEE_CHARGED: AccountId = 21; +pub const INCENTIVE_PERCENTAGE: u8 = 10; +pub const MARKETPLACE_FEE_PERCENTAGE: u8 = 10; +pub const CANCEL_BLOCK_BUFFER: u64 = 600; + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system, + Tokens: orml_tokens, + Payment: pay, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl system::Config for Test { + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: u32| -> Balance { + 0u128 + }; +} +parameter_types! { + pub const MaxLocks: u32 = 50; +} +pub type ReserveIdentifier = [u8; 8]; + +pub struct MockDustRemovalWhitelist; +impl Contains for MockDustRemovalWhitelist { + fn contains(_a: &AccountId) -> bool { + false + } +} + +impl orml_tokens::Config for Test { + type Amount = i64; + type Balance = Balance; + type CurrencyId = u32; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type WeightInfo = (); + type MaxLocks = MaxLocks; + type DustRemovalWhitelist = MockDustRemovalWhitelist; + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = ReserveIdentifier; +} + +pub struct MockDisputeResolver; +impl crate::types::DisputeResolver for MockDisputeResolver { + fn get_resolver_account() -> AccountId { + RESOLVER_ACCOUNT + } +} + +pub struct MockFeeHandler; +impl crate::types::FeeHandler for MockFeeHandler { + fn apply_fees( + _from: &AccountId, + to: &AccountId, + _detail: &PaymentDetail, + _remark: Option<&[u8]>, + ) -> (AccountId, Percent) { + match to { + &PAYMENT_RECIPENT_FEE_CHARGED => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(MARKETPLACE_FEE_PERCENTAGE)), + _ => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(0)), + } + } +} + +parameter_types! { + pub const IncentivePercentage: Percent = Percent::from_percent(INCENTIVE_PERCENTAGE); + pub const MaxRemarkLength: u32 = 50; + pub const CancelBufferBlockLength: u64 = CANCEL_BLOCK_BUFFER; + pub const MaxScheduledTaskListLength : u32 = 5; +} + +impl pay::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Asset = Tokens; + type DisputeResolver = MockDisputeResolver; + type IncentivePercentage = IncentivePercentage; + type FeeHandler = MockFeeHandler; + type MaxRemarkLength = MaxRemarkLength; + type CancelBufferBlockLength = CancelBufferBlockLength; + type MaxScheduledTaskListLength = MaxScheduledTaskListLength; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + + orml_tokens::GenesisConfig:: { + balances: vec![ + (PAYMENT_CREATOR, CURRENCY_ID, 100), + (PAYMENT_CREATOR_TWO, CURRENCY_ID, 100), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext: sp_io::TestExternalities = t.into(); + // need to set block number to 1 to test events + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub fn run_n_blocks(n: u64) -> u64 { + const IDLE_WEIGHT: u64 = 10_000_000_000; + const BUSY_WEIGHT: u64 = IDLE_WEIGHT / 1000; + + let start_block = System::block_number(); + + for block_number in (0..=n).map(|n| n + start_block) { + System::set_block_number(block_number); + + // Odd blocks gets busy + let idle_weight = if block_number % 2 == 0 { + IDLE_WEIGHT + } else { + BUSY_WEIGHT + }; + // ensure the on_idle is executed + >::register_extra_weight_unchecked( + Payment::on_idle(block_number, frame_support::weights::Weight::from_parts(idle_weight, 0)), + DispatchClass::Mandatory, + ); + + as OnFinalize>::on_finalize(block_number); + } + System::block_number() +} diff --git a/blockchain/modules/edfis-pay/src/tests.rs b/blockchain/modules/edfis-pay/src/tests.rs new file mode 100644 index 00000000..97f54116 --- /dev/null +++ b/blockchain/modules/edfis-pay/src/tests.rs @@ -0,0 +1,1470 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// This file is part of Setheum. + +// Copyright (C) 2019-Present Setheum Labs. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + mock::*, + types::{PaymentDetail, PaymentState}, + weights::WeightInfo, + Payment as PaymentStore, PaymentHandler, ScheduledTask, ScheduledTasks, Task, +}; +use frame_support::{assert_noop, assert_ok, storage::with_transaction, traits::OnIdle, weights::Weight}; +use orml_traits::MultiCurrency; +use sp_runtime::{Percent, TransactionOutcome}; + +type Error = crate::Error; + +fn last_event() -> RuntimeEvent { + System::events().pop().expect("Event expected").event +} + +#[test] +fn test_pay_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + + // the payment amount should not be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + assert_eq!( + last_event(), + crate::Event::::PaymentCreated { + from: PAYMENT_CREATOR, + asset: CURRENCY_ID, + amount: payment_amount, + remark: None + } + .into() + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + // the payment amount should be reserved correctly + // the amount + incentive should be removed from the sender account + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_incentive_amount + ); + // the incentive amount should be reserved in the sender account + assert_eq!( + Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + // the transferred amount should be reserved in the recipent account + assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // the payment should not be overwritten + assert_noop!( + Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + ), + crate::Error::::PaymentAlreadyInProcess + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: 2, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + }); +} + +#[test] +fn test_cancel_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + // the payment amount should be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_incentive_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // cancel should succeed when caller is the recipent + assert_ok!(Payment::cancel( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR + )); + assert_eq!( + last_event(), + crate::Event::::PaymentCancelled { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT + } + .into() + ); + // the payment amount should be released back to creator + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be released from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + }); +} + +#[test] +fn test_release_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + // the payment amount should be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_incentive_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should succeed for valid payment + assert_ok!(Payment::release( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + assert_eq!( + last_event(), + crate::Event::::PaymentReleased { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT + } + .into() + ); + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // should be deleted from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + + // should be able to create another payment since previous is released + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + // the payment amount should be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - (payment_amount * 2) - expected_incentive_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + }); +} + +#[test] +fn test_resolve_payment_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + // should fail for non whitelisted caller + assert_noop!( + Payment::resolve_payment( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_CREATOR, + PAYMENT_RECIPENT, + Percent::from_percent(100) + ), + Error::InvalidAction + ); + + // should be able to release a payment + assert_ok!(Payment::resolve_payment( + RuntimeOrigin::signed(RESOLVER_ACCOUNT), + PAYMENT_CREATOR, + PAYMENT_RECIPENT, + Percent::from_percent(100) + )); + assert_eq!( + last_event(), + crate::Event::::PaymentResolved { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + recipient_share: Percent::from_percent(100) + } + .into() + ); + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // should be removed from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + // should be able to cancel a payment + assert_ok!(Payment::resolve_payment( + RuntimeOrigin::signed(RESOLVER_ACCOUNT), + PAYMENT_CREATOR, + PAYMENT_RECIPENT, + Percent::from_percent(0) + )); + assert_eq!( + last_event(), + crate::Event::::PaymentResolved { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + recipient_share: Percent::from_percent(0) + } + .into() + ); + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // should be released from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + }); +} + +#[test] +fn test_charging_fee_payment_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED, + CURRENCY_ID, + payment_amount, + None + )); + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + // the payment amount should be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_fee_amount - expected_incentive_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); + + // should succeed for valid payment + assert_ok!(Payment::release( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED + )); + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_fee_amount + ); + assert_eq!( + Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_fee_amount + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), + payment_amount + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), + expected_fee_amount + ); + }); +} + +#[test] +fn test_charging_fee_payment_works_when_canceled() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED, + CURRENCY_ID, + payment_amount, + None + )); + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + // the payment amount should be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_fee_amount - expected_incentive_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); + + // should succeed for valid payment + assert_ok!(Payment::cancel( + RuntimeOrigin::signed(PAYMENT_RECIPENT_FEE_CHARGED), + PAYMENT_CREATOR + )); + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), 0); + }); +} + +#[test] +fn test_pay_with_remark_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + + // should be able to create a payment with available balance + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + Some(vec![1u8; 10].try_into().unwrap()) + )); + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + // the payment amount should be reserved correctly + // the amount + incentive should be removed from the sender account + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_incentive_amount + ); + // the incentive amount should be reserved in the sender account + assert_eq!( + Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + // the transferred amount should be reserved in the recipent account + assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // the payment should not be overwritten + assert_noop!( + Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + ), + crate::Error::::PaymentAlreadyInProcess + ); + + assert_eq!( + last_event(), + crate::Event::::PaymentCreated { + from: PAYMENT_CREATOR, + asset: CURRENCY_ID, + amount: payment_amount, + remark: Some(vec![1u8; 10].try_into().unwrap()) + } + .into() + ); + }); +} + +#[test] +fn test_do_not_overwrite_logic_works() { + new_test_ext().execute_with(|| { + let payment_amount = 40; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_noop!( + Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + ), + crate::Error::::PaymentAlreadyInProcess + ); + + // set payment state to NeedsReview + PaymentStore::::insert( + PAYMENT_CREATOR, + PAYMENT_RECIPENT, + PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::NeedsReview, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }, + ); + + // the payment should not be overwritten + assert_noop!( + Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + ), + crate::Error::::PaymentAlreadyInProcess + ); + }); +} + +#[test] +fn test_request_refund() { + new_test_ext().execute_with(|| { + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + + // do not overwrite payment + assert_noop!( + Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + ), + crate::Error::::PaymentAlreadyInProcess + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::RefundRequested { + cancel_block: expected_cancel_block + }, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + + assert_eq!( + last_event(), + crate::Event::::PaymentCreatorRequestedRefund { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + expiry: expected_cancel_block + } + .into() + ); + }); +} + +#[test] +fn test_dispute_refund() { + new_test_ext().execute_with(|| { + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + // cannot dispute if refund is not requested + assert_noop!( + Payment::dispute_refund(RuntimeOrigin::signed(PAYMENT_RECIPENT), PAYMENT_CREATOR), + Error::InvalidAction + ); + // creator requests a refund + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + // ensure the request is added to the refund queue + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!( + scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), + &ScheduledTask { + task: Task::Cancel, + when: expected_cancel_block + } + ); + + // recipient disputes the refund request + assert_ok!(Payment::dispute_refund( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR + )); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::NeedsReview, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + + assert_eq!( + last_event(), + crate::Event::::PaymentRefundDisputed { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + } + .into() + ); + + // ensure the request is removed from the refund queue + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); + }); +} + +#[test] +fn test_request_payment() { + new_test_ext().execute_with(|| { + let payment_amount = 20; + let expected_incentive_amount = 0; + + assert_ok!(Payment::request_payment( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR, + CURRENCY_ID, + payment_amount, + )); + + assert_noop!( + Payment::request_refund(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT), + crate::Error::::InvalidAction + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::PaymentRequested, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + + assert_eq!( + last_event(), + crate::Event::::PaymentRequestCreated { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + } + .into() + ); + }); +} + +#[test] +fn test_requested_payment_cannot_be_released() { + new_test_ext().execute_with(|| { + let payment_amount = 20; + + assert_ok!(Payment::request_payment( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR, + CURRENCY_ID, + payment_amount, + )); + + // requested payment cannot be released + assert_noop!( + Payment::release(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT), + Error::InvalidAction + ); + }); +} + +#[test] +fn test_requested_payment_can_be_cancelled_by_requestor() { + new_test_ext().execute_with(|| { + let payment_amount = 20; + + assert_ok!(Payment::request_payment( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR, + CURRENCY_ID, + payment_amount, + )); + + assert_ok!(Payment::cancel( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR + )); + + // the request should be removed from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + }); +} + +#[test] +fn test_accept_and_pay() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = 0; + + assert_ok!(Payment::request_payment( + RuntimeOrigin::signed(PAYMENT_RECIPENT), + PAYMENT_CREATOR, + CURRENCY_ID, + payment_amount, + )); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::PaymentRequested, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + + assert_ok!(Payment::accept_and_pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + )); + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // should be deleted from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + + assert_eq!( + last_event(), + crate::Event::::PaymentRequestCompleted { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT, + } + .into() + ); + }); +} + +#[test] +fn test_accept_and_pay_should_fail_for_non_payment_requested() { + new_test_ext().execute_with(|| { + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + 20, + None + )); + + assert_noop!( + Payment::accept_and_pay(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT,), + Error::InvalidAction + ); + }); +} + +#[test] +fn test_accept_and_pay_should_charge_fee_correctly() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = 0; + let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; + + assert_ok!(Payment::request_payment( + RuntimeOrigin::signed(PAYMENT_RECIPENT_FEE_CHARGED), + PAYMENT_CREATOR, + CURRENCY_ID, + payment_amount, + )); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::PaymentRequested, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + + assert_ok!(Payment::accept_and_pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED, + )); + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_fee_amount + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), + payment_amount + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), + expected_fee_amount + ); + + // should be deleted from storage + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + None + ); + + assert_eq!( + last_event(), + crate::Event::::PaymentRequestCompleted { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT_FEE_CHARGED, + } + .into() + ); + }); +} + +#[test] +fn test_create_payment_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_fee_amount = 0; + + // the payment amount should not be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::create_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + PaymentState::Created, + Percent::from_percent(INCENTIVE_PERCENTAGE), + Some(&[1u8; 10]), + ) + }))); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + + // the payment should not be overwritten + assert_noop!( + with_transaction(|| TransactionOutcome::Commit({ + >::create_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + PaymentState::Created, + Percent::from_percent(INCENTIVE_PERCENTAGE), + Some(&[1u8; 10]), + ) + })), + Error::PaymentAlreadyInProcess + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + }); +} + +#[test] +fn test_reserve_payment_amount_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + let expected_fee_amount = 0; + + // the payment amount should not be reserved + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::create_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + PaymentState::Created, + Percent::from_percent(INCENTIVE_PERCENTAGE), + Some(&[1u8; 10]), + ) + }))); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::reserve_payment_amount( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).unwrap(), + ) + }))); + // the payment amount should be reserved correctly + // the amount + incentive should be removed from the sender account + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount - expected_incentive_amount + ); + // the incentive amount should be reserved in the sender account + assert_eq!( + Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + // the transferred amount should be reserved in the recipent account + assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // the payment should not be overwritten + assert_noop!( + with_transaction(|| TransactionOutcome::Commit({ + >::create_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + PaymentState::Created, + Percent::from_percent(INCENTIVE_PERCENTAGE), + Some(&[1u8; 10]), + ) + })), + Error::PaymentAlreadyInProcess + ); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::Created, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), + }) + ); + }); +} + +#[test] +fn test_settle_payment_works_for_cancel() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + + // the payment amount should not be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::settle_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + Percent::from_percent(0), + ) + }))); + + // the payment amount should be released back to creator + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be released from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + }); +} + +#[test] +fn test_settle_payment_works_for_release() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + + // the payment amount should not be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::settle_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT, + Percent::from_percent(100), + ) + }))); + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance - payment_amount + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); + + // should be deleted from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + }); +} + +#[test] +fn test_settle_payment_works_for_70_30() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 10; + let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; + + // the payment amount should not be reserved + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::settle_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT_FEE_CHARGED, + Percent::from_percent(70), + ) + }))); + + let expected_amount_for_creator = creator_initial_balance - payment_amount - expected_fee_amount + + (Percent::from_percent(30) * payment_amount); + let expected_amount_for_recipient = Percent::from_percent(70) * payment_amount; + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + expected_amount_for_creator + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), + expected_amount_for_recipient + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), + expected_fee_amount + ); + + // should be deleted from storage + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + None + ); + }); +} + +#[test] +fn test_settle_payment_works_for_50_50() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 10; + let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; + + // the payment amount should not be reserved + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); + + // should be able to create a payment with available balance within a + // transaction + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT_FEE_CHARGED, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(with_transaction(|| TransactionOutcome::Commit({ + >::settle_payment( + &PAYMENT_CREATOR, + &PAYMENT_RECIPENT_FEE_CHARGED, + Percent::from_percent(50), + ) + }))); + + let expected_amount_for_creator = creator_initial_balance - payment_amount - expected_fee_amount + + (Percent::from_percent(50) * payment_amount); + let expected_amount_for_recipient = Percent::from_percent(50) * payment_amount; + + // the payment amount should be transferred + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + expected_amount_for_creator + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), + expected_amount_for_recipient + ); + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), + expected_fee_amount + ); + + // should be deleted from storage + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), + None + ); + }); +} + +#[test] +fn test_automatic_refund_works() { + new_test_ext().execute_with(|| { + let creator_initial_balance = 100; + let payment_amount = 20; + let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; + const CANCEL_PERIOD: u64 = 600; + const CANCEL_BLOCK: u64 = CANCEL_PERIOD + 1; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), + Some(PaymentDetail { + asset: CURRENCY_ID, + amount: payment_amount, + incentive_amount: expected_incentive_amount, + state: PaymentState::RefundRequested { + cancel_block: CANCEL_BLOCK + }, + resolver_account: RESOLVER_ACCOUNT, + fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), + }) + ); + + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!( + scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), + &ScheduledTask { + task: Task::Cancel, + when: CANCEL_BLOCK + } + ); + + // run to one block before cancel and make sure data is same + assert_eq!(run_n_blocks(CANCEL_PERIOD - 1), 600); + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!( + scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), + &ScheduledTask { + task: Task::Cancel, + when: CANCEL_BLOCK + } + ); + + // run to after cancel block but odd blocks are busy + assert_eq!(run_n_blocks(1), 601); + // the payment is still not processed since the block was busy + assert!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).is_some()); + + // next block has spare weight to process the payment + assert_eq!(run_n_blocks(1), 602); + // the payment should be removed from storage + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + + // the scheduled storage should be cleared + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); + + // test that the refund happened correctly + assert_eq!( + last_event(), + crate::Event::::PaymentCancelled { + from: PAYMENT_CREATOR, + to: PAYMENT_RECIPENT + } + .into() + ); + // the payment amount should be released back to creator + assert_eq!( + Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), + creator_initial_balance + ); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + }); +} + +#[test] +fn test_automatic_refund_works_for_multiple_payments() { + new_test_ext().execute_with(|| { + const CANCEL_PERIOD: u64 = 600; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + 20, + None + )); + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR_TWO), + PAYMENT_RECIPENT_TWO, + CURRENCY_ID, + 20, + None + )); + + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + run_n_blocks(1); + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR_TWO), + PAYMENT_RECIPENT_TWO + )); + + assert_eq!(run_n_blocks(CANCEL_PERIOD - 1), 601); + + // Odd block 601 was busy so we still haven't processed the first payment + assert_ok!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).ok_or(())); + + // Even block 602 has enough room to process both pending payments + assert_eq!(run_n_blocks(1), 602); + assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); + assert_eq!( + PaymentStore::::get(PAYMENT_CREATOR_TWO, PAYMENT_RECIPENT_TWO), + None + ); + + // the scheduled storage should be cleared + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); + assert_eq!( + scheduled_tasks_list.get(&(PAYMENT_CREATOR_TWO, PAYMENT_RECIPENT_TWO)), + None + ); + + // test that the refund happened correctly + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); + + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR_TWO), 100); + assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_TWO), 0); + }); +} + +#[test] +fn on_idle_works() { + new_test_ext().execute_with(|| { + assert_eq!( + Payment::on_idle(System::block_number(), Weight::MAX), + <()>::remove_task() + ); + + let payment_amount = 20; + let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; + + assert_ok!(Payment::pay( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT, + CURRENCY_ID, + payment_amount, + None + )); + + // creator requests a refund + assert_ok!(Payment::request_refund( + RuntimeOrigin::signed(PAYMENT_CREATOR), + PAYMENT_RECIPENT + )); + // ensure the request is added to the refund queue + let scheduled_tasks_list = ScheduledTasks::::get(); + assert_eq!(scheduled_tasks_list.len(), 1); + assert_eq!( + scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), + &ScheduledTask { + task: Task::Cancel, + when: expected_cancel_block + } + ); + + assert_eq!(run_n_blocks(CANCEL_BLOCK_BUFFER - 1), 600); + assert_eq!( + Payment::on_idle(System::block_number(), Weight::MAX), + <()>::remove_task() + ); + + assert_eq!(run_n_blocks(1), 601); + assert_eq!( + Payment::on_idle(System::block_number(), Weight::MAX), + <()>::remove_task() + <()>::cancel() + ); + }); +} diff --git a/blockchain/modules/edfis-pay/src/types.rs b/blockchain/modules/edfis-pay/src/types.rs new file mode 100644 index 00000000..9f719338 --- /dev/null +++ b/blockchain/modules/edfis-pay/src/types.rs @@ -0,0 +1,142 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// This file is part of Setheum. + +// Copyright (C) 2019-Present Setheum Labs. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![allow(unused_qualifications)] +use crate::{pallet, AssetIdOf, BalanceOf}; +use frame_system::pallet_prelude::*; +use parity_scale_codec::{Decode, Encode, HasCompact, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{DispatchResult, Percent}; + +/// The PaymentDetail struct stores information about the payment/escrow +/// A "payment" in Setheum Pay is similar to an escrow, it is used to +/// guarantee proof of funds and can be released once an agreed upon condition +/// has reached between the payment creator and recipient. The payment lifecycle +/// is tracked using the state field. +#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound(T: pallet::Config))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct PaymentDetail { + /// type of asset used for payment + pub asset: AssetIdOf, + /// amount of asset used for payment + #[codec(compact)] + pub amount: BalanceOf, + /// incentive amount that is credited to creator for resolving + #[codec(compact)] + pub incentive_amount: BalanceOf, + /// enum to track payment lifecycle [Created, NeedsReview, RefundRequested, + /// Requested] + pub state: PaymentState, + /// account that can settle any disputes created in the payment + pub resolver_account: T::AccountId, + /// fee charged and recipient account details + pub fee_detail: Option<(T::AccountId, BalanceOf)>, +} + +/// The `PaymentState` enum tracks the possible states that a payment can be in. +/// When a payment is 'completed' or 'cancelled' it is removed from storage and +/// hence not tracked by a state. +#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound(T: pallet::Config))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PaymentState { + /// Amounts have been reserved and waiting for release/cancel + Created, + /// A judge needs to review and release manually + NeedsReview, + /// The user has requested refund and will be processed by `BlockNumber` + RefundRequested { cancel_block: BlockNumberFor }, + /// The recipient of this transaction has created a request + PaymentRequested, +} + +/// trait that defines how to create/release payments for users +pub trait PaymentHandler { + /// Create a PaymentDetail from the given payment details + /// Calculate the fee amount and store PaymentDetail in storage + /// Possible reasons for failure include: + /// - Payment already exists and cannot be overwritten + fn create_payment( + from: &T::AccountId, + to: &T::AccountId, + asset: AssetIdOf, + amount: BalanceOf, + payment_state: PaymentState, + incentive_percentage: Percent, + remark: Option<&[u8]>, + ) -> Result, sp_runtime::DispatchError>; + + /// Attempt to reserve an amount of the given asset from the caller + /// If not possible then return Error. Possible reasons for failure include: + /// - User does not have enough balance. + fn reserve_payment_amount(from: &T::AccountId, to: &T::AccountId, payment: PaymentDetail) -> DispatchResult; + + // Settle a payment of `from` to `to`. To release a payment, the + // recipient_share=100, to cancel a payment recipient_share=0 + // Possible reasonse for failure include + /// + /// - The payment does not exist + /// - The unreserve operation fails + /// - The transfer operation fails + fn settle_payment(from: &T::AccountId, to: &T::AccountId, recipient_share: Percent) -> DispatchResult; + + /// Attempt to fetch the details of a payment from the given payment_id + /// Possible reasons for failure include: + /// - The payment does not exist + fn get_payment_details(from: &T::AccountId, to: &T::AccountId) -> Option>; +} + +/// DisputeResolver trait defines how to create/assign judges for solving +/// payment disputes +pub trait DisputeResolver { + /// Returns an `Account` + fn get_resolver_account() -> Account; +} + +/// Fee Handler trait that defines how to handle marketplace fees to every +/// payment/swap +pub trait FeeHandler { + /// Get the distribution of fees to marketplace participants + fn apply_fees( + from: &T::AccountId, + to: &T::AccountId, + detail: &PaymentDetail, + remark: Option<&[u8]>, + ) -> (T::AccountId, Percent); +} + +/// Types of Tasks that can be scheduled in the pallet +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +pub enum Task { + // payment `from` to `to` has to be cancelled + Cancel, +} + +/// The details of a scheduled task +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +pub struct ScheduledTask { + /// the type of scheduled task + pub task: Task, + /// the 'time' at which the task should be executed + pub when: Time, +} diff --git a/blockchain/modules/edfis-pay/src/weights.rs b/blockchain/modules/edfis-pay/src/weights.rs new file mode 100644 index 00000000..2cb181bb --- /dev/null +++ b/blockchain/modules/edfis-pay/src/weights.rs @@ -0,0 +1,205 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// This file is part of Setheum. + +// Copyright (C) 2019-Present Setheum Labs. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Autogenerated weights for module_payment +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-03-19, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/setheum-node +// benchmark +// --chain +// dev +// --execution=wasm +// --wasm-execution +// compiled +// --extrinsic=* +// --pallet=module_payment +// --steps=20 +// --repeat=10 +// --heap-pages=4096 +// --output +// ./pallets/payment/src/weights.rs +// --template +// ./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for module_payment. +pub trait WeightInfo { + fn pay(x: u32, ) -> Weight; + fn release() -> Weight; + fn cancel() -> Weight; + fn resolve_payment() -> Weight; + fn request_refund() -> Weight; + fn dispute_refund() -> Weight; + fn request_payment() -> Weight; + fn accept_and_pay() -> Weight; + fn remove_task() -> Weight; +} + +/// Weights for module_payment using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Payment Payment (r:1 w:1) + // Storage: Sudo Key (r:1 w:0) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn pay(_x: u32, ) -> Weight { + Weight::from_parts(55_900_000, 0) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + fn release() -> Weight { + Weight::from_parts(36_000_000, 0) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:0) + fn cancel() -> Weight { + Weight::from_parts(48_000_000, 0) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + fn resolve_payment() -> Weight { + Weight::from_parts(35_000_000, 0) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Payment ScheduledTasks (r:1 w:1) + fn request_refund() -> Weight { + Weight::from_parts(20_000_000, 0) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Payment ScheduledTasks (r:1 w:1) + fn dispute_refund() -> Weight { + Weight::from_parts(21_000_000, 0) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Sudo Key (r:1 w:0) + fn request_payment() -> Weight { + Weight::from_parts(17_000_000, 0) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn accept_and_pay() -> Weight { + Weight::from_parts(58_000_000, 0) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } + // Storage: Payment ScheduledTasks (r:1 w:1) + fn remove_task() -> Weight { + Weight::from_parts(4_000_000, 0) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Payment Payment (r:1 w:1) + // Storage: Sudo Key (r:1 w:0) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn pay(_x: u32, ) -> Weight { + Weight::from_parts(55_900_000, 0) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + fn release() -> Weight { + Weight::from_parts(36_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:0) + fn cancel() -> Weight { + Weight::from_parts(48_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + fn resolve_payment() -> Weight { + Weight::from_parts(35_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Payment ScheduledTasks (r:1 w:1) + fn request_refund() -> Weight { + Weight::from_parts(20_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Payment ScheduledTasks (r:1 w:1) + fn dispute_refund() -> Weight { + Weight::from_parts(21_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Sudo Key (r:1 w:0) + fn request_payment() -> Weight { + Weight::from_parts(17_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Payment Payment (r:1 w:1) + // Storage: Assets Accounts (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn accept_and_pay() -> Weight { + Weight::from_parts(58_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) + } + // Storage: Payment ScheduledTasks (r:1 w:1) + fn remove_task() -> Weight { + Weight::from_parts(4_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} From 38cb3fcaa8448f202a5c13232340a03044d1ae2e Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sat, 6 Apr 2024 23:10:28 +0800 Subject: [PATCH 03/10] fix --- blockchain/modules/ecdp-ussd-engine/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchain/modules/ecdp-ussd-engine/src/lib.rs b/blockchain/modules/ecdp-ussd-engine/src/lib.rs index d6859d13..13a4a54e 100644 --- a/blockchain/modules/ecdp-ussd-engine/src/lib.rs +++ b/blockchain/modules/ecdp-ussd-engine/src/lib.rs @@ -56,7 +56,7 @@ use sp_runtime::{ Duration, }, traits::{ - AccountIdConversion, BlockNumberProvider, Bounded, One, Saturating, StaticLookup, UniqueSaturatedInto, Zero, + AccountIdConversion, Bounded, One, Saturating, StaticLookup, UniqueSaturatedInto, Zero, }, transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, ValidTransaction, @@ -1296,7 +1296,7 @@ impl LiquidateCollateral for LiquidateViaContracts { let stable_coin = T::GetUSSDCurrencyId::get(); let contracts_by_priority = { - let now: usize = frame_system::Pallet::::current_block_number() + let now: usize = frame_system::Pallet::::block_number() .try_into() .map_err(|_| ArithmeticError::Overflow)?; // can't fail as ensured `liquidation_contracts_len` non-zero From 622eb2b86f406d27ee534c3553ca90f8f2803696 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sat, 6 Apr 2024 23:12:43 +0800 Subject: [PATCH 04/10] Launchpool --- .../modules/edfis-launchpool/Cargo.toml | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/blockchain/modules/edfis-launchpool/Cargo.toml b/blockchain/modules/edfis-launchpool/Cargo.toml index 1d154c15..dabb3d76 100644 --- a/blockchain/modules/edfis-launchpool/Cargo.toml +++ b/blockchain/modules/edfis-launchpool/Cargo.toml @@ -8,36 +8,44 @@ homepage.workspace = true repository.workspace = true [dependencies] -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } +sp-arithmetic = { workspace = true } +sp-runtime = { workspace = true } +sp-core = { workspace = true } +sp-std = { workspace = true } +primitives = { workspace = true } +orml-traits = { workspace = true } +module-support = { workspace = true } [dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } +sp-io = { workspace = true, features = ["std"] } +pallet-balances = { workspace = true, features = ["std"] } +module-currencies = { workspace = true, features = ["std"] } +orml-tokens = { workspace = true, features = ["std"] } +xcm = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", - "primitives/std", - "support/std", + "module-support/std", "orml-traits/std", + "primitives/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", ] runtime-benchmarks = [ + "frame-benchmarking", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", From 29357aacc92fdf981f1cf497427538e4fed76d4e Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sat, 4 May 2024 21:37:35 +0800 Subject: [PATCH 05/10] Remove BlockNumberProvider --- .../edfis-liquid-edf-validators/src/lib.rs | 15 +++++------- .../edfis-liquid-edf-validators/src/mock.rs | 13 ---------- .../edfis-liquid-edf-validators/src/tests.rs | 17 ++++++------- .../edfis-liquid-see-validators/src/lib.rs | 15 +++++------- .../edfis-liquid-see-validators/src/mock.rs | 13 ---------- .../edfis-liquid-see-validators/src/tests.rs | 17 ++++++------- blockchain/modules/evm/src/bench/mock.rs | 2 +- blockchain/modules/evm/src/mock.rs | 2 +- blockchain/modules/prices/src/lib.rs | 5 +--- blockchain/modules/prices/src/mock.rs | 10 -------- blockchain/modules/vesting/src/lib.rs | 7 ++---- blockchain/modules/vesting/src/mock.rs | 19 --------------- blockchain/modules/vesting/src/tests.rs | 24 +++++++++---------- 13 files changed, 43 insertions(+), 116 deletions(-) diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs b/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs index 1b472b6f..f246e005 100644 --- a/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs +++ b/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs @@ -40,7 +40,7 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_runtime::{ - traits::{BlockNumberProvider, Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, + traits::{Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, DispatchResult, FixedPointNumber, RuntimeDebug, }; use sp_std::{fmt::Debug, vec::Vec}; @@ -151,7 +151,7 @@ pub struct SlashInfo { /// Address of a validator validator: AccountId, /// The amount of tokens a validator has in backing - token_amount: Balance, + amount: Balance, } /// Validator insurance and frozen status @@ -194,9 +194,6 @@ pub mod module { type OnIncreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; /// Callback to be called when a validator's insurance decreases. type OnDecreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - - // The block number provider - type BlockNumberProvider: BlockNumberProvider>; } #[pallet::error] @@ -337,7 +334,7 @@ pub mod module { guarantee.bonded.is_zero() || guarantee.bonded >= T::MinBondAmount::get(), Error::::BelowMinBondAmount, ); - let expired_block = T::BlockNumberProvider::current_block_number() + T::BondingDuration::get(); + let expired_block = frame_system::Pallet::::block_number() + T::BondingDuration::get(); guarantee.unbonding = Some((amount, expired_block)); Self::deposit_event(Event::UnbondGuarantee { @@ -387,7 +384,7 @@ pub mod module { ); Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { let old_total = guarantee.total; - *guarantee = guarantee.consolidate_unbonding(T::BlockNumberProvider::current_block_number()); + *guarantee = guarantee.consolidate_unbonding(frame_system::Pallet::::block_number()); let new_total = guarantee .bonded .saturating_add(guarantee.unbonding.unwrap_or_default().0); @@ -466,12 +463,12 @@ pub mod module { for SlashInfo { validator, - token_amount, + amount, } in slashes { let ValidatorBacking { total_insurance, .. } = Self::validator_backings(&validator).unwrap_or_default(); let insurance_loss = staking_liquid_exchange_rate - .saturating_mul_int(token_amount) + .saturating_mul_int(amount) .min(total_insurance); for (guarantor, _) in Guarantees::::iter_prefix(&validator) { diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs b/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs index 77ab6593..7c4f616a 100644 --- a/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs +++ b/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs @@ -163,18 +163,6 @@ impl ExchangeRateProvider for MockLiquidStakingExchangeProvider { } } -parameter_types! { - pub static MockBlockNumberProvider: u64 = 0; -} - -impl BlockNumberProvider for MockBlockNumberProvider { - type BlockNumber = u64; - - fn current_block_number() -> Self::BlockNumber { - Self::get() - } -} - ord_parameter_types! { pub const Admin: AccountId = 10; } @@ -192,7 +180,6 @@ impl Config for Runtime { type WeightInfo = (); type OnIncreaseGuarantee = MockOnIncreaseGuarantee; type OnDecreaseGuarantee = MockOnDecreaseGuarantee; - type BlockNumberProvider = MockBlockNumberProvider; } type Block = frame_system::mocking::MockBlock; diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs b/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs index 2a281a56..50abcad4 100644 --- a/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs +++ b/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs @@ -381,7 +381,6 @@ fn bond_work() { fn unbond_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -464,7 +463,6 @@ fn unbond_work() { fn rebond_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -536,7 +534,6 @@ fn rebond_work() { fn withdraw_unbonded_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -583,7 +580,7 @@ fn withdraw_unbonded_work() { 200 ); - MockBlockNumberProvider::set(100); + System::set_block_number(100) assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( RuntimeOrigin::signed(ALICE), VALIDATOR_1 @@ -612,7 +609,7 @@ fn withdraw_unbonded_work() { 200 ); System::reset_events(); - MockBlockNumberProvider::set(101); + System::set_block_number(101) assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( RuntimeOrigin::signed(ALICE), VALIDATOR_1 @@ -743,11 +740,11 @@ fn slash_work() { vec![ SlashInfo { validator: VALIDATOR_1, - relaychain_token_amount: 90 + amount: 90 }, SlashInfo { validator: VALIDATOR_2, - relaychain_token_amount: 50 + amount: 50 }, ] ), @@ -759,11 +756,11 @@ fn slash_work() { vec![ SlashInfo { validator: VALIDATOR_1, - relaychain_token_amount: 90 + amount: 90 }, SlashInfo { validator: VALIDATOR_2, - relaychain_token_amount: 50 + amount: 50 }, ] )); @@ -850,7 +847,7 @@ fn slash_work() { #[test] fn contains_work() { ExtBuilder::default().build().execute_with(|| { - MockBlockNumberProvider::set(1); + System::set_block_number(1) assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), diff --git a/blockchain/modules/edfis-liquid-see-validators/src/lib.rs b/blockchain/modules/edfis-liquid-see-validators/src/lib.rs index bfcd9e1a..946c8db7 100644 --- a/blockchain/modules/edfis-liquid-see-validators/src/lib.rs +++ b/blockchain/modules/edfis-liquid-see-validators/src/lib.rs @@ -40,7 +40,7 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_runtime::{ - traits::{BlockNumberProvider, Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, + traits::{Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, DispatchResult, FixedPointNumber, RuntimeDebug, }; use sp_std::{fmt::Debug, vec::Vec}; @@ -151,7 +151,7 @@ pub struct SlashInfo { /// Address of a validator validator: AccountId, /// The amount of tokens a validator has in backing - token_amount: Balance, + amount: Balance, } /// Validator insurance and frozen status @@ -194,9 +194,6 @@ pub mod module { type OnIncreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; /// Callback to be called when a validator's insurance decreases. type OnDecreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - - // The block number provider - type BlockNumberProvider: BlockNumberProvider>; } #[pallet::error] @@ -337,7 +334,7 @@ pub mod module { guarantee.bonded.is_zero() || guarantee.bonded >= T::MinBondAmount::get(), Error::::BelowMinBondAmount, ); - let expired_block = T::BlockNumberProvider::current_block_number() + T::BondingDuration::get(); + let expired_block = frame_system::Pallet::::block_number() + T::BondingDuration::get(); guarantee.unbonding = Some((amount, expired_block)); Self::deposit_event(Event::UnbondGuarantee { @@ -387,7 +384,7 @@ pub mod module { ); Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { let old_total = guarantee.total; - *guarantee = guarantee.consolidate_unbonding(T::BlockNumberProvider::current_block_number()); + *guarantee = guarantee.consolidate_unbonding(frame_system::Pallet::::block_number()); let new_total = guarantee .bonded .saturating_add(guarantee.unbonding.unwrap_or_default().0); @@ -466,12 +463,12 @@ pub mod module { for SlashInfo { validator, - token_amount, + amount, } in slashes { let ValidatorBacking { total_insurance, .. } = Self::validator_backings(&validator).unwrap_or_default(); let insurance_loss = staking_liquid_exchange_rate - .saturating_mul_int(token_amount) + .saturating_mul_int(amount) .min(total_insurance); for (guarantor, _) in Guarantees::::iter_prefix(&validator) { diff --git a/blockchain/modules/edfis-liquid-see-validators/src/mock.rs b/blockchain/modules/edfis-liquid-see-validators/src/mock.rs index c142ed6e..a49ff1a8 100644 --- a/blockchain/modules/edfis-liquid-see-validators/src/mock.rs +++ b/blockchain/modules/edfis-liquid-see-validators/src/mock.rs @@ -163,18 +163,6 @@ impl ExchangeRateProvider for MockLiquidStakingExchangeProvider { } } -parameter_types! { - pub static MockBlockNumberProvider: u64 = 0; -} - -impl BlockNumberProvider for MockBlockNumberProvider { - type BlockNumber = u64; - - fn current_block_number() -> Self::BlockNumber { - Self::get() - } -} - ord_parameter_types! { pub const Admin: AccountId = 10; } @@ -192,7 +180,6 @@ impl Config for Runtime { type WeightInfo = (); type OnIncreaseGuarantee = MockOnIncreaseGuarantee; type OnDecreaseGuarantee = MockOnDecreaseGuarantee; - type BlockNumberProvider = MockBlockNumberProvider; } type Block = frame_system::mocking::MockBlock; diff --git a/blockchain/modules/edfis-liquid-see-validators/src/tests.rs b/blockchain/modules/edfis-liquid-see-validators/src/tests.rs index 67dbc557..c43fd1c8 100644 --- a/blockchain/modules/edfis-liquid-see-validators/src/tests.rs +++ b/blockchain/modules/edfis-liquid-see-validators/src/tests.rs @@ -381,7 +381,6 @@ fn bond_work() { fn unbond_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -464,7 +463,6 @@ fn unbond_work() { fn rebond_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -536,7 +534,6 @@ fn rebond_work() { fn withdraw_unbonded_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - MockBlockNumberProvider::set(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), @@ -583,7 +580,7 @@ fn withdraw_unbonded_work() { 200 ); - MockBlockNumberProvider::set(100); + System::set_block_number(100) assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( RuntimeOrigin::signed(ALICE), VALIDATOR_1 @@ -612,7 +609,7 @@ fn withdraw_unbonded_work() { 200 ); System::reset_events(); - MockBlockNumberProvider::set(101); + System::set_block_number(101) assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( RuntimeOrigin::signed(ALICE), VALIDATOR_1 @@ -743,11 +740,11 @@ fn slash_work() { vec![ SlashInfo { validator: VALIDATOR_1, - relaychain_token_amount: 90 + amount: 90 }, SlashInfo { validator: VALIDATOR_2, - relaychain_token_amount: 50 + amount: 50 }, ] ), @@ -759,11 +756,11 @@ fn slash_work() { vec![ SlashInfo { validator: VALIDATOR_1, - relaychain_token_amount: 90 + amount: 90 }, SlashInfo { validator: VALIDATOR_2, - relaychain_token_amount: 50 + amount: 50 }, ] )); @@ -850,7 +847,7 @@ fn slash_work() { #[test] fn contains_work() { ExtBuilder::default().build().execute_with(|| { - MockBlockNumberProvider::set(1); + System::set_block_number(1); assert_ok!(EdfisLiquidSeeValidatorsModule::bond( RuntimeOrigin::signed(ALICE), diff --git a/blockchain/modules/evm/src/bench/mock.rs b/blockchain/modules/evm/src/bench/mock.rs index 87c546ec..f41ffb2e 100644 --- a/blockchain/modules/evm/src/bench/mock.rs +++ b/blockchain/modules/evm/src/bench/mock.rs @@ -40,7 +40,7 @@ pub use primitives::{Address, Amount, BlockNumber, CurrencyId, Header, Multiplie use sp_core::H160; use sp_runtime::{ generic, - traits::{AccountIdConversion, BlockNumberProvider, IdentityLookup}, + traits::{AccountIdConversion, IdentityLookup}, AccountId32, FixedU128, Percent, }; diff --git a/blockchain/modules/evm/src/mock.rs b/blockchain/modules/evm/src/mock.rs index b6a9c4a1..a46f70f6 100644 --- a/blockchain/modules/evm/src/mock.rs +++ b/blockchain/modules/evm/src/mock.rs @@ -33,7 +33,7 @@ use orml_traits::parameter_type_with_key; use primitives::{Amount, BlockNumber, CurrencyId, ReserveIdentifier, TokenSymbol}; use sp_core::{bytes::from_hex, H160}; use sp_runtime::{ - traits::{BlockNumberProvider, IdentityLookup}, + traits::IdentityLookup, AccountId32, BuildStorage, }; use std::{collections::BTreeMap, str::FromStr}; diff --git a/blockchain/modules/prices/src/lib.rs b/blockchain/modules/prices/src/lib.rs index 4201d5ae..a29d98a1 100644 --- a/blockchain/modules/prices/src/lib.rs +++ b/blockchain/modules/prices/src/lib.rs @@ -38,7 +38,7 @@ use orml_traits::{DataFeeder, DataProvider, GetByKey, MultiCurrency}; use primitives::{Balance, CurrencyId, Lease}; use sp_core::U256; use sp_runtime::{ - traits::{BlockNumberProvider, CheckedMul, One, Saturating, UniqueSaturatedInto}, + traits::{CheckedMul, One, Saturating, UniqueSaturatedInto}, FixedPointNumber, }; use sp_std::marker::PhantomData; @@ -101,9 +101,6 @@ pub mod module { /// Mapping between CurrencyId and ERC20 address so user can use Erc20. type Erc20InfoMapping: Erc20InfoMapping; - /// Block number provider. - type BlockNumber: BlockNumberProvider>; - /// If a currency is pegged to another currency in price, price of this currency is /// equal to the price of another. type PricingPegged: GetByKey>; diff --git a/blockchain/modules/prices/src/mock.rs b/blockchain/modules/prices/src/mock.rs index 824a5d7a..af6f288b 100644 --- a/blockchain/modules/prices/src/mock.rs +++ b/blockchain/modules/prices/src/mock.rs @@ -206,14 +206,6 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Nothing; } -impl BlockNumberProvider for MockBlockNumberProvider { - type BlockNumber = BlockNumber; - - fn current_block_number() -> Self::BlockNumber { - Self::get() - } -} - ord_parameter_types! { pub const One: AccountId = 1; } @@ -223,7 +215,6 @@ parameter_types! { pub const GetSEECurrencyId: CurrencyId = SEE; pub const GetLiquidSEECurrencyId: CurrencyId = LSEE; pub USSDFixedPrice: Price = Price::one(); - pub static MockBlockNumberProvider: BlockNumber = 0; } impl Config for Runtime { @@ -238,7 +229,6 @@ impl Config for Runtime { type SwapManager = MockSwapManager; type Currency = Tokens; type Erc20InfoMapping = MockErc20InfoMapping; - type BlockNumber = MockBlockNumberProvider; type PricingPegged = PricingPegged; type WeightInfo = (); } diff --git a/blockchain/modules/vesting/src/lib.rs b/blockchain/modules/vesting/src/lib.rs index 4a6429f3..dcf67c12 100644 --- a/blockchain/modules/vesting/src/lib.rs +++ b/blockchain/modules/vesting/src/lib.rs @@ -57,7 +57,7 @@ use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; use parity_scale_codec::{HasCompact, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32Bit, BlockNumberProvider, CheckedAdd, Saturating, StaticLookup, Zero}, + traits::{AtLeast32Bit, CheckedAdd, Saturating, StaticLookup, Zero}, ArithmeticError, DispatchResult, RuntimeDebug, }; use sp_std::{ @@ -164,9 +164,6 @@ pub mod module { /// The maximum vesting schedules for EDF type MaxEDFVestingSchedules: Get; - - // The block number provider - type BlockNumberProvider: BlockNumberProvider>; } #[pallet::error] @@ -422,7 +419,7 @@ impl Pallet { /// Returns locked balance based on current block number. fn locked_balance(currency_id: CurrencyIdOf, who: &T::AccountId) -> BalanceOf { - let now = T::BlockNumberProvider::current_block_number(); + let now = frame_system::Pallet::::block_number(); if currency_id == T::GetNativeCurrencyId::get() { >::mutate_exists(who, |maybe_schedules| { let total = if let Some(schedules) = maybe_schedules.as_mut() { diff --git a/blockchain/modules/vesting/src/mock.rs b/blockchain/modules/vesting/src/mock.rs index 924d6d63..9a6b044e 100644 --- a/blockchain/modules/vesting/src/mock.rs +++ b/blockchain/modules/vesting/src/mock.rs @@ -67,15 +67,6 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Nothing; } -impl BlockNumberProvider for MockBlockNumberProvider { - type BlockNumber = BlockNumber; - - fn current_block_number() -> Self::BlockNumber { - Self::get() - } -} - - pub struct EnsureAliceOrBob; impl EnsureOrigin for EnsureAliceOrBob { type Success = AccountId; @@ -102,15 +93,6 @@ pub const EDF: CurrencyId = CurrencyId::Token(TokenSymbol::edf); parameter_types! { pub const GetNativeCurrencyId: CurrencyId = SEE; pub const GetDEFCurrencyId: CurrencyId = EDF; - pub static MockBlockNumberProvider: u64 = 0; -} - -impl BlockNumberProvider for MockBlockNumberProvider { - type BlockNumber = u64; - - fn current_block_number() -> BlockNumberFor { - Self::get() - } } impl Config for Runtime { @@ -124,7 +106,6 @@ impl Config for Runtime { type WeightInfo = (); type MaxNativeVestingSchedules = ConstU32<2>; type MaxEDFVestingSchedules = ConstU32<2>; - type BlockNumberProvider = MockBlockNumberProvider; } type Block = frame_system::mocking::MockBlock; diff --git a/blockchain/modules/vesting/src/tests.rs b/blockchain/modules/vesting/src/tests.rs index df4ce576..71674e34 100644 --- a/blockchain/modules/vesting/src/tests.rs +++ b/blockchain/modules/vesting/src/tests.rs @@ -36,7 +36,7 @@ fn vesting_from_chain_spec_works() { ] ); - MockBlockNumberProvider::set(13); + System::set_block_number(13); assert_ok!(Vesting::claim(RuntimeOrigin::signed(CHARLIE), SEE)); @@ -47,7 +47,7 @@ fn vesting_from_chain_spec_works() { )); assert!(Tokens::ensure_can_withdraw(SEE, &CHARLIE, 26).is_err()); - MockBlockNumberProvider::set(14); + System::set_block_number(14); assert_ok!(Vesting::claim(RuntimeOrigin::signed(CHARLIE), SEE)); @@ -136,7 +136,7 @@ fn add_new_vesting_schedule_merges_with_current_locked_balance_and_until() { }; assert_ok!(Vesting::vested_transfer(RuntimeOrigin::signed(ALICE), SEE, BOB, schedule)); - MockBlockNumberProvider::set(12); + System::set_block_number(12); let another_schedule = VestingSchedule { start: 10u64, @@ -272,7 +272,7 @@ fn claim_works() { }; assert_ok!(Vesting::vested_transfer(RuntimeOrigin::signed(ALICE), SEE, BOB, schedule)); - MockBlockNumberProvider::set(11); + System::set_block_number(11); // remain locked if not claimed assert!(Tokens::transfer(&BOB, &ALICE, SEE, 10).is_err()); // unlocked after claiming @@ -282,7 +282,7 @@ fn claim_works() { // more are still locked assert!(Tokens::transfer(&BOB, &ALICE, SEE, 1).is_err()); - MockBlockNumberProvider::set(21); + System::set_block_number(21); // claim more assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB), SEE)); assert!(!NativeVestingSchedules::::contains_key(BOB)); @@ -317,7 +317,7 @@ fn claim_for_works() { ); assert!(NativeVestingSchedules::::contains_key(&BOB)); - MockBlockNumberProvider::set(21); + System::set_block_number(21); assert_ok!(Vesting::claim_for(RuntimeOrigin::signed(ALICE), SEE, BOB)); @@ -351,11 +351,11 @@ fn update_vesting_schedules_works() { vec![updated_schedule] )); - MockBlockNumberProvider::set(11); + System::set_block_number(11); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB), SEE)); assert!(Tokens::transfer(&BOB, &ALICE, SEE, 1).is_err()); - MockBlockNumberProvider::set(21); + System::set_block_number(21); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB), SEE)); assert_ok!(Tokens::transfer(&BOB, &ALICE, SEE, 10)); @@ -419,13 +419,13 @@ fn multiple_vesting_schedule_claim_works() { assert_eq!(Vesting::native_vesting_schedules(&BOB), vec![schedule, schedule2.clone()]); - MockBlockNumberProvider::set(21); + System::set_block_number(21); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB, SEE))); assert_eq!(Vesting::native_vesting_schedules(&BOB), vec![schedule2]); - MockBlockNumberProvider::set(31); + System::set_block_number(31); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB, SEE))); @@ -495,7 +495,7 @@ fn cliff_vesting_works() { assert_eq!(Tokens::locks(&BOB, SEE), vec![balance_lock.clone()]); for i in 1..VESTING_PERIOD { - MockBlockNumberProvider::set(i); + System::set_block_number(i); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB), SEE)); assert_eq!(Tokens::free_balance(SEE, BOB), VESTING_AMOUNT); assert_eq!(Tokens::locks(&BOB, SEE), vec![balance_lock.clone()]); @@ -505,7 +505,7 @@ fn cliff_vesting_works() { ); } - MockBlockNumberProvider::set(VESTING_PERIOD); + System::set_block_number(VESTING_PERIOD); assert_ok!(Vesting::claim(RuntimeOrigin::signed(BOB), SEE)); assert!(Tokens::locks(&BOB, SEE).is_empty()); assert_ok!(Tokens::transfer( From 2258d7c1b75655942f96cdc91489f82a10246131 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sun, 5 May 2024 01:44:18 +0800 Subject: [PATCH 06/10] Remove Moya LSEE, LEDF --- README.md | 4 +- blockchain/modules/edfis-launchpool/README.md | 9 +- .../edfis-liquid-edf-validators/Cargo.toml | 52 -- .../edfis-liquid-edf-validators/README.md | 7 - .../edfis-liquid-edf-validators/TODO.md | 57 -- .../edfis-liquid-edf-validators/src/lib.rs | 572 ------------ .../edfis-liquid-edf-validators/src/mock.rs | 223 ----- .../edfis-liquid-edf-validators/src/tests.rs | 878 ------------------ .../edfis-liquid-see-validators/Cargo.toml | 52 -- .../edfis-liquid-see-validators/README.md | 7 - .../edfis-liquid-see-validators/TODO.md | 57 -- .../edfis-liquid-see-validators/src/lib.rs | 572 ------------ .../edfis-liquid-see-validators/src/mock.rs | 223 ----- .../edfis-liquid-see-validators/src/tests.rs | 878 ------------------ blockchain/modules/incentives/README.md | 2 - blockchain/modules/incentives/src/lib.rs | 2 - blockchain/modules/incentives/src/mock.rs | 1 - blockchain/modules/incentives/src/tests.rs | 9 - .../modules/moya-liquid-edf-earn/Cargo.toml | 52 -- .../modules/moya-liquid-edf-earn/README.md | 5 - .../modules/moya-liquid-edf-earn/TODO.md | 56 -- .../moya-liquid-edf-validators/Cargo.toml | 52 -- .../moya-liquid-edf-validators/README.md | 5 - .../moya-liquid-edf-validators/TODO.md | 56 -- blockchain/modules/moya-liquid-edf/Cargo.toml | 52 -- blockchain/modules/moya-liquid-edf/README.md | 5 - blockchain/modules/moya-liquid-edf/TODO.md | 56 -- .../modules/moya-liquid-see-earn/Cargo.toml | 52 -- .../modules/moya-liquid-see-earn/README.md | 5 - .../modules/moya-liquid-see-earn/TODO.md | 56 -- .../moya-liquid-see-validators/Cargo.toml | 52 -- .../moya-liquid-see-validators/README.md | 5 - .../moya-liquid-see-validators/TODO.md | 56 -- blockchain/modules/moya-liquid-see/Cargo.toml | 52 -- blockchain/modules/moya-liquid-see/README.md | 5 - blockchain/modules/moya-liquid-see/TODO.md | 56 -- blockchain/modules/prices/src/lib.rs | 20 - blockchain/modules/prices/src/mock.rs | 17 - blockchain/modules/prices/src/tests.rs | 56 -- 39 files changed, 4 insertions(+), 4372 deletions(-) delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/Cargo.toml delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/README.md delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/TODO.md delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/src/lib.rs delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/src/mock.rs delete mode 100644 blockchain/modules/edfis-liquid-edf-validators/src/tests.rs delete mode 100644 blockchain/modules/edfis-liquid-see-validators/Cargo.toml delete mode 100644 blockchain/modules/edfis-liquid-see-validators/README.md delete mode 100644 blockchain/modules/edfis-liquid-see-validators/TODO.md delete mode 100644 blockchain/modules/edfis-liquid-see-validators/src/lib.rs delete mode 100644 blockchain/modules/edfis-liquid-see-validators/src/mock.rs delete mode 100644 blockchain/modules/edfis-liquid-see-validators/src/tests.rs delete mode 100644 blockchain/modules/moya-liquid-edf-earn/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-edf-earn/README.md delete mode 100644 blockchain/modules/moya-liquid-edf-earn/TODO.md delete mode 100644 blockchain/modules/moya-liquid-edf-validators/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-edf-validators/README.md delete mode 100644 blockchain/modules/moya-liquid-edf-validators/TODO.md delete mode 100644 blockchain/modules/moya-liquid-edf/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-edf/README.md delete mode 100644 blockchain/modules/moya-liquid-edf/TODO.md delete mode 100644 blockchain/modules/moya-liquid-see-earn/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-see-earn/README.md delete mode 100644 blockchain/modules/moya-liquid-see-earn/TODO.md delete mode 100644 blockchain/modules/moya-liquid-see-validators/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-see-validators/README.md delete mode 100644 blockchain/modules/moya-liquid-see-validators/TODO.md delete mode 100644 blockchain/modules/moya-liquid-see/Cargo.toml delete mode 100644 blockchain/modules/moya-liquid-see/README.md delete mode 100644 blockchain/modules/moya-liquid-see/TODO.md diff --git a/README.md b/README.md index 0f1e2958..4a53cba3 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Setheum is a light-speed decentralised blockchain network with EVM and WASM smar ### 1.2. Ethical DeFi -Ethical DeFi Suite is the DeFi powerhouse of the Setheum Network, providing all kinds of top notch DeFi protocols including an AMM DEX (inspired by Uniswap v3), Decentralised Liquid Staking and Ethical Zero-interest Halal sStablecoins that gives us the properties of both Fiat and Crypto with SlickUSD (USSD) and the Setter (SETR) using an Ethical Collateralized Debt Position (ECDP) mechanism that is over-Collateralized and multi-Collateralised and stable without compromising decentralisation or economic stability, offering stable cryptocurrencies that have scalable value and reliability, setheum provides just that, backed by crypto assets on an efficient zero-interest debt-based system. +Ethical DeFi Suite is the DeFi powerhouse of the Setheum Network, providing all kinds of top notch DeFi protocols including an AMM DEX (inspired by Uniswap v3), Ethical Zero-interest Halal Stablecoins that gives us the properties of both Fiat and Crypto with SlickUSD (USSD) and the Setter (SETR) using an Ethical Collateralized Debt Position (ECDP) mechanism that is over-Collateralized and multi-Collateralised and stable without compromising decentralisation or economic stability, offering stable cryptocurrencies that have scalable value and reliability, setheum provides just that, backed by crypto assets on an efficient zero-interest debt-based system. #### 1.2.1. Ethical DeFi Projects: @@ -86,7 +86,7 @@ Ethical DeFi Suite is the DeFi powerhouse of the Setheum Network, providing all - `Edfis Exchange`: AMM (Automated Market Maker) DEX Protocol inspired by Uniswap v3 design - `Edfis Launchpad`: Launchpad Crowdsales protocol for bootstrapping pools on Edfis Exchange - `Edfis Launchpool`: Launchpool protocol for bootstrapping pools on Edfis Exchange - - `Edfis Liquid Staking`: Liquid Staking Protocol + - `Edfis Pay`: Decentralised Escrow Payment Protocol - `Setter`: Unpegged ECDP Stablecoin - `SlickUSD`: USD Pegged ECDP Stablecoin diff --git a/blockchain/modules/edfis-launchpool/README.md b/blockchain/modules/edfis-launchpool/README.md index 187dbe59..e0652d74 100644 --- a/blockchain/modules/edfis-launchpool/README.md +++ b/blockchain/modules/edfis-launchpool/README.md @@ -11,10 +11,5 @@ Based on the `LaunchpoolStakingCurrency` set by teams, the protocol takes a `Lau ### Launchpool Commission Teers Below are the commission teers: - - 1. `LSEE` Pools: No Commissions taken; - - 2. `LEDF` Pools: No Commissions taken; - - 3. `SETR` Pools: No Commissions taken; - - 4. `USSD` Pools: `USSDLaunchpoolCommission` (should be 0.25%) - - 5. `SEE` Pools: `SEELaunchpoolCommission` (should be 0.5%); - - 6. `EDF` Pools: `EDFLaunchpoolCommission` (should be 0.5%) - - 7. Other Tokens Pools: `OtherTokensLaunchpoolCommission` (should be 5%) + - 1. `SETR`, `SEE`, `EDF` and `USSD` Pools: No Commissions taken; + - 5. Other Tokens Pools: `LaunchpoolCommission` should be 5%; diff --git a/blockchain/modules/edfis-liquid-edf-validators/Cargo.toml b/blockchain/modules/edfis-liquid-edf-validators/Cargo.toml deleted file mode 100644 index ce937788..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-edfis-liquid-edf-validators" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -orml-traits = { workspace = true, default-features = false } - -primitives = { workspace = true, default-features = false } -module-support = { workspace = true, default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -sp-io = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-currencies = { workspace = true, features = ["std"] } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "module-support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", -] diff --git a/blockchain/modules/edfis-liquid-edf-validators/README.md b/blockchain/modules/edfis-liquid-edf-validators/README.md deleted file mode 100644 index 6f0eb820..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/README.md +++ /dev/null @@ -1,7 +0,0 @@ -بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -# Edfis Liquid EDF Validators Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `EDF` tokens. The module requires validators to lock some Liquid EDF into insurance fund and if slash happened, EthicalDeFiCouncil can burn those Liquid EDF to compensate Liquid EDF holders. diff --git a/blockchain/modules/edfis-liquid-edf-validators/TODO.md b/blockchain/modules/edfis-liquid-edf-validators/TODO.md deleted file mode 100644 index efe9a53b..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/TODO.md +++ /dev/null @@ -1,57 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. -- [ ] [[src/lib.rs:0]: Do benchmarking test](src/lib.rs) diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs b/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs deleted file mode 100644 index f246e005..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/src/lib.rs +++ /dev/null @@ -1,572 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! # Edfis Liquid EDF Validator List Module -//! -//! ## Overview -//! -//! This will require validators to lock some Liquid EDF into insurance fund -//! and if slash happened, EthicalDeFiCouncil can burn those Liquid EDF to compensate -//! Liquid EDF holders. - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::unused_unit)] -#![allow(clippy::collapsible_if)] - -use frame_support::{pallet_prelude::*, traits::Contains}; -use frame_system::pallet_prelude::*; -use module_support::{ExchangeRateProvider, Ratio}; -use orml_traits::{BasicCurrency, BasicLockableCurrency, Happened, LockIdentifier}; -use parity_scale_codec::MaxEncodedLen; -use primitives::Balance; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_runtime::{ - traits::{Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, - DispatchResult, FixedPointNumber, RuntimeDebug, -}; -use sp_std::{fmt::Debug, vec::Vec}; - -mod mock; -mod tests; - -pub use module::*; - -pub const EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID: LockIdentifier = *b"edf/lqdedfvld"; - -pub trait WeightInfo { - fn bond() -> Weight; - fn unbond() -> Weight; - fn rebond() -> Weight; - fn withdraw_unbonded() -> Weight; - fn freeze(u: u32) -> Weight; - fn thaw() -> Weight; - fn slash() -> Weight; -} - -// TODO[src/lib.rs:0]: Do benchmarking test. -impl WeightInfo for () { - fn bond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn unbond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn rebond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn withdraw_unbonded() -> Weight { - Weight::from_parts(10_000, 0) - } - fn freeze(_u: u32) -> Weight { - Weight::from_parts(10_000, 0) - } - fn thaw() -> Weight { - Weight::from_parts(10_000, 0) - } - fn slash() -> Weight { - Weight::from_parts(10_000, 0) - } -} - -/// Insurance for a validator from a single address -#[derive(Encode, Decode, Clone, Copy, RuntimeDebug, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -pub struct Guarantee { - /// The total tokens the validator has in insurance - total: Balance, - /// The number of tokens that are actively bonded for insurance - bonded: Balance, - /// The number of tokens that are in the process of unbonding for insurance - unbonding: Option<(Balance, BlockNumber)>, -} - -impl Guarantee { - /// Take `unbonding` that are sufficiently old - fn consolidate_unbonding(mut self, current_block: BlockNumber) -> Self { - match self.unbonding { - Some((_, expired_block)) if expired_block <= current_block => { - self.unbonding = None; - } - _ => {} - } - self - } - - /// Re-bond funds that were scheduled for unbonding. - fn rebond(mut self, rebond_amount: Balance) -> Self { - if let Some((amount, _)) = self.unbonding.as_mut() { - let rebond_amount = rebond_amount.min(*amount); - self.bonded = self.bonded.saturating_add(rebond_amount); - *amount = amount.saturating_sub(rebond_amount); - if amount.is_zero() { - self.unbonding = None; - } - } - self - } - - fn slash(mut self, slash_amount: Balance) -> Self { - let mut remains = slash_amount; - let slash_from_bonded = self.bonded.min(remains); - self.bonded = self.bonded.saturating_sub(remains); - self.total = self.total.saturating_sub(remains); - remains = remains.saturating_sub(slash_from_bonded); - - if !remains.is_zero() { - if let Some((unbonding_amount, _)) = self.unbonding.as_mut() { - let slash_from_unbonding = remains.min(*unbonding_amount); - *unbonding_amount = unbonding_amount.saturating_sub(slash_from_unbonding); - if unbonding_amount.is_zero() { - self.unbonding = None; - } - } - } - - self - } -} - -/// Information on a validator's slash -#[derive(Encode, Decode, Clone, RuntimeDebug, Eq, PartialEq, MaxEncodedLen, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct SlashInfo { - /// Address of a validator - validator: AccountId, - /// The amount of tokens a validator has in backing - amount: Balance, -} - -/// Validator insurance and frozen status -#[derive(Encode, Decode, Clone, Copy, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)] -pub struct ValidatorBacking { - /// Total insurance from all guarantors - total_insurance: Balance, - is_frozen: bool, -} - -#[frame_support::pallet] -pub mod module { - use super::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The liquid representation of the staking token. - type LiquidEDFCurrency: BasicLockableCurrency; - #[pallet::constant] - /// The minimum amount of tokens that can be bonded to a validator. - type MinBondAmount: Get; - #[pallet::constant] - /// The number of blocks a token is bonded to a validator for. - type BondingDuration: Get>; - #[pallet::constant] - /// The minimum amount of insurance a validator needs. - type ValidatorInsuranceThreshold: Get; - /// The AccountId that can perform a freeze. - type FreezeOrigin: EnsureOrigin; - /// The AccountId that can perform a slash. - type SlashOrigin: EnsureOrigin; - /// Callback to be called when a slash occurs. - type OnSlash: Happened; - /// Exchange rate between staked token and liquid token equivalent. - type LiquidStakingExchangeRateProvider: ExchangeRateProvider; - type WeightInfo: WeightInfo; - /// Callback to be called when a validator's insurance increases. - type OnIncreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - /// Callback to be called when a validator's insurance decreases. - type OnDecreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - } - - #[pallet::error] - pub enum Error { - BelowMinBondAmount, - UnbondingExists, - FrozenValidator, - } - - #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event { - FreezeValidator { - validator: T::AccountId, - }, - ThawValidator { - validator: T::AccountId, - }, - BondGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - UnbondGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - WithdrawnGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - SlashGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - } - - /// The slash guarantee deposits for validators. - /// - /// Guarantees: double_map AccountId, AccountId => Option - #[pallet::storage] - #[pallet::getter(fn guarantees)] - pub type Guarantees = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Twox64Concat, - T::AccountId, - Guarantee>, - OptionQuery, - >; - - /// Total deposits for users. - /// - /// TotalLockedByGuarantor: map AccountId => Option - #[pallet::storage] - #[pallet::getter(fn total_locked_by_guarantor)] - pub type TotalLockedByGuarantor = StorageMap<_, Twox64Concat, T::AccountId, Balance, OptionQuery>; - - /// Total deposit for validators. - /// - /// ValidatorBackings: map AccountId => Option - #[pallet::storage] - #[pallet::getter(fn validator_backings)] - pub type ValidatorBackings = - StorageMap<_, Blake2_128Concat, T::AccountId, ValidatorBacking, OptionQuery>; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet { - /// Bond tokens to a validator. - /// Ensures the amount to bond is greater than the minimum bond amount. - /// - /// - `validator`: the AccountId of a validator to bond to - /// - `amount`: the number of tokens to bond to the given validator - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::bond())] - pub fn bond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - let free_balance = T::LiquidEDFCurrency::free_balance(&guarantor); - let total_should_locked = Self::total_locked_by_guarantor(&guarantor).unwrap_or_default(); - - if let Some(extra) = free_balance.checked_sub(total_should_locked) { - let amount = amount.min(extra); - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - guarantee.total = guarantee.total.saturating_add(amount); - guarantee.bonded = guarantee.bonded.saturating_add(amount); - ensure!( - guarantee.bonded >= T::MinBondAmount::get(), - Error::::BelowMinBondAmount - ); - Ok(()) - })?; - Self::deposit_event(Event::BondGuarantee { - who: guarantor, - validator: validator.clone(), - bond: amount, - }); - } - } - Ok(()) - } - - /// Unbond tokens from a validator. - /// Ensures the bonded amount is zero or greater than the minimum bond amount. - /// - /// - `validator`: the AccountId of a validator to unbond from - /// - `amount`: the number of tokens to unbond from the given validator - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::unbond())] - pub fn unbond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - ensure!(guarantee.unbonding.is_none(), Error::::UnbondingExists); - let amount = amount.min(guarantee.bonded); - guarantee.bonded = guarantee.bonded.saturating_sub(amount); - ensure!( - guarantee.bonded.is_zero() || guarantee.bonded >= T::MinBondAmount::get(), - Error::::BelowMinBondAmount, - ); - let expired_block = frame_system::Pallet::::block_number() + T::BondingDuration::get(); - guarantee.unbonding = Some((amount, expired_block)); - - Self::deposit_event(Event::UnbondGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: amount, - }); - Ok(()) - })?; - } - Ok(()) - } - - /// Rebond tokens to a validator. - /// - /// - `validator`: The AccountId of a validator to rebond to - /// - `amount`: The amount of tokens to to rebond to the given validator - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::rebond())] - pub fn rebond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - *guarantee = guarantee.rebond(amount); - Ok(()) - })?; - } - Ok(()) - } - - /// Withdraw the unbonded tokens from a validator. - /// Ensures the validator is not frozen. - /// - /// - `validator`: The AccountId of a validator to withdraw from - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::withdraw_unbonded())] - pub fn withdraw_unbonded(origin: OriginFor, validator: T::AccountId) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - ensure!( - !Self::validator_backings(&validator).unwrap_or_default().is_frozen, - Error::::FrozenValidator - ); - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - let old_total = guarantee.total; - *guarantee = guarantee.consolidate_unbonding(frame_system::Pallet::::block_number()); - let new_total = guarantee - .bonded - .saturating_add(guarantee.unbonding.unwrap_or_default().0); - if old_total != new_total { - guarantee.total = new_total; - Self::deposit_event(Event::WithdrawnGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: old_total.saturating_sub(new_total), - }); - } - Ok(()) - })?; - Ok(()) - } - - /// Freezes validators if they are not already frozen. - /// Ensures the caller can freeze validators. - /// - /// - `validators`: The AccountIds of the validators to freeze - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::freeze(validators.len() as u32))] - pub fn freeze(origin: OriginFor, validators: Vec) -> DispatchResult { - T::FreezeOrigin::ensure_origin(origin)?; - validators.iter().for_each(|validator| { - ValidatorBackings::::mutate_exists(validator, |maybe_validator| { - let mut v = maybe_validator.take().unwrap_or_default(); - if !v.is_frozen { - v.is_frozen = true; - Self::deposit_event(Event::FreezeValidator { - validator: validator.clone(), - }); - } - *maybe_validator = Some(v); - }); - }); - Ok(()) - } - - /// Unfreezes validators if they are frozen. - /// Ensures the caller can perform a slash. - /// - /// - `validators`: The AccountIds of the validators to unfreeze - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::thaw())] - pub fn thaw(origin: OriginFor, validators: Vec) -> DispatchResult { - // Using SlashOrigin instead of FreezeOrigin so that un-freezing requires more council members than - // freezing - T::SlashOrigin::ensure_origin(origin)?; - validators.iter().for_each(|validator| { - ValidatorBackings::::mutate_exists(validator, |maybe_validator| { - let mut v = maybe_validator.take().unwrap_or_default(); - if v.is_frozen { - v.is_frozen = false; - Self::deposit_event(Event::ThawValidator { - validator: validator.clone(), - }); - } - *maybe_validator = Some(v); - }); - }); - Ok(()) - } - - /// Slash validators. - /// Ensures the the caller can perform a slash. - /// - /// - `slashes`: The SlashInfos of the validators to be slashed - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::slash())] - pub fn slash(origin: OriginFor, slashes: Vec>) -> DispatchResult { - T::SlashOrigin::ensure_origin(origin)?; - let liquid_staking_exchange_rate = T::LiquidStakingExchangeRateProvider::get_exchange_rate(); - let staking_liquid_exchange_rate = liquid_staking_exchange_rate.reciprocal().unwrap_or_default(); - let mut actual_total_slashing: Balance = Zero::zero(); - - for SlashInfo { - validator, - amount, - } in slashes - { - let ValidatorBacking { total_insurance, .. } = Self::validator_backings(&validator).unwrap_or_default(); - let insurance_loss = staking_liquid_exchange_rate - .saturating_mul_int(amount) - .min(total_insurance); - - for (guarantor, _) in Guarantees::::iter_prefix(&validator) { - // NOTE: ignoring result because the closure will not throw err. - let res = Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - let should_slashing = Ratio::checked_from_rational(guarantee.total, total_insurance) - .unwrap_or_else(Ratio::max_value) - .saturating_mul_int(insurance_loss); - let gap = T::LiquidEDFCurrency::slash(&guarantor, should_slashing); - let actual_slashing = should_slashing.saturating_sub(gap); - *guarantee = guarantee.slash(actual_slashing); - Self::deposit_event(Event::SlashGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: actual_slashing, - }); - actual_total_slashing = actual_total_slashing.saturating_add(actual_slashing); - Ok(()) - }); - debug_assert!(res.is_ok()); - } - } - - T::OnSlash::happened(&actual_total_slashing); - Ok(()) - } - } -} - -impl Pallet { - fn update_guarantee( - guarantor: &T::AccountId, - validator: &T::AccountId, - f: impl FnOnce(&mut Guarantee>) -> DispatchResult, - ) -> DispatchResult { - Guarantees::::try_mutate_exists(validator, guarantor, |maybe_guarantee| -> DispatchResult { - let mut guarantee = maybe_guarantee.take().unwrap_or_default(); - let old_total = guarantee.total; - - f(&mut guarantee).and_then(|_| -> DispatchResult { - let new_total = guarantee.total; - if guarantee.total.is_zero() { - *maybe_guarantee = None; - } else { - *maybe_guarantee = Some(guarantee); - } - - // adjust total locked of nominator, validator backing and update the lock. - if new_total != old_total { - TotalLockedByGuarantor::::try_mutate_exists( - guarantor, - |maybe_total_locked| -> DispatchResult { - let mut tl = maybe_total_locked.take().unwrap_or_default(); - - ValidatorBackings::::try_mutate_exists( - validator, - |maybe_validator_backing| -> DispatchResult { - let mut vb = maybe_validator_backing.take().unwrap_or_default(); - - if new_total > old_total { - let gap = new_total - old_total; - vb.total_insurance = vb.total_insurance.saturating_add(gap); - tl = tl.saturating_add(gap); - T::OnIncreaseGuarantee::happened(&(guarantor.clone(), validator.clone(), gap)); - } else { - let gap = old_total - new_total; - vb.total_insurance = vb.total_insurance.saturating_sub(gap); - tl = tl.saturating_sub(gap); - T::OnDecreaseGuarantee::happened(&(guarantor.clone(), validator.clone(), gap)); - }; - - if tl.is_zero() { - *maybe_total_locked = None; - T::LiquidEDFCurrency::remove_lock(EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID, guarantor)?; - } else { - *maybe_total_locked = Some(tl); - T::LiquidEDFCurrency::set_lock(EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID, guarantor, tl)?; - } - - *maybe_validator_backing = Some(vb); - Ok(()) - }, - ) - }, - )?; - } - - Ok(()) - }) - }) - } -} - -impl Contains for Pallet { - fn contains(account_id: &T::AccountId) -> bool { - Self::validator_backings(account_id) - .unwrap_or_default() - .total_insurance - >= T::ValidatorInsuranceThreshold::get() - } -} diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs b/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs deleted file mode 100644 index 7c4f616a..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/src/mock.rs +++ /dev/null @@ -1,223 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Mocks for Edfis Liquid EDF Validator List Module. - -#![cfg(test)] - -use super::*; -use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, Nothing}, -}; -use frame_system::EnsureSignedBy; -use module_support::ExchangeRate; -use orml_traits::parameter_type_with_key; -use primitives::{Amount, Balance, CurrencyId, TokenSymbol}; -use sp_runtime::{traits::IdentityLookup, BuildStorage}; -use sp_std::cell::RefCell; -use std::collections::HashMap; - -pub type AccountId = u128; -pub type BlockNumber = u64; - -pub const ALICE: AccountId = 0; -pub const BOB: AccountId = 1; -pub const VALIDATOR_1: AccountId = 2; -pub const VALIDATOR_2: AccountId = 3; -pub const VALIDATOR_3: AccountId = 4; -pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); -pub const LEDF: CurrencyId = CurrencyId::Token(TokenSymbol::LEDF); - -mod edfis_liquid_edf_validator_list { - pub use super::super::*; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for Runtime { - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -parameter_type_with_key! { - pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { - Default::default() - }; -} - -impl orml_tokens::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type Amount = Amount; - type CurrencyId = CurrencyId; - type WeightInfo = (); - type ExistentialDeposits = ExistentialDeposits; - type CurrencyHooks = (); - type MaxLocks = ConstU32<100>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type DustRemovalWhitelist = Nothing; -} - -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxHolds = (); - type MaxFreezes = (); -} - -parameter_types! { - pub const GetNativeCurrencyId: CurrencyId = SEE; - pub const GetLiquidCurrencyId: CurrencyId = LEDF; -} - -pub type NativeCurrency = orml_currencies::BasicCurrencyAdapter; -pub type LEDFCurrency = orml_currencies::Currency; - -impl orml_currencies::Config for Runtime { - type MultiCurrency = OrmlTokens; - type NativeCurrency = NativeCurrency; - type GetNativeCurrencyId = GetNativeCurrencyId; - type WeightInfo = (); -} - -thread_local! { - pub static SHARES: RefCell> = RefCell::new(HashMap::new()); - pub static ACCUMULATED_SLASH: RefCell = RefCell::new(0); -} - -pub struct MockOnSlash; -impl Happened for MockOnSlash { - fn happened(amount: &Balance) { - ACCUMULATED_SLASH.with(|v| *v.borrow_mut() += amount); - } -} - -pub struct MockOnIncreaseGuarantee; -impl Happened<(AccountId, AccountId, Balance)> for MockOnIncreaseGuarantee { - fn happened(info: &(AccountId, AccountId, Balance)) { - let (account_id, validator_account_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); - if let Some(share) = old_map.get_mut(&(*account_id, *validator_account_id)) { - *share = share.saturating_add(*amount); - } else { - old_map.insert((*account_id, *validator_account_id), *amount); - }; - - *v.borrow_mut() = old_map; - }); - } -} - -pub struct MockOnDecreaseGuarantee; -impl Happened<(AccountId, AccountId, Balance)> for MockOnDecreaseGuarantee { - fn happened(info: &(AccountId, AccountId, Balance)) { - let (account_id, validator_account_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); - if let Some(share) = old_map.get_mut(&(*account_id, *validator_account_id)) { - *share = share.saturating_sub(*amount); - } else { - old_map.insert((*account_id, *validator_account_id), Default::default()); - }; - - *v.borrow_mut() = old_map; - }); - } -} - -pub struct MockLiquidStakingExchangeProvider; -impl ExchangeRateProvider for MockLiquidStakingExchangeProvider { - fn get_exchange_rate() -> ExchangeRate { - ExchangeRate::saturating_from_rational(1, 2) - } -} - -ord_parameter_types! { - pub const Admin: AccountId = 10; -} - -impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type LiquidEDFCurrency = LEDFCurrency; - type MinBondAmount = ConstU128<100>; - type BondingDuration = ConstU64<100>; - type ValidatorInsuranceThreshold = ConstU128<200>; - type FreezeOrigin = EnsureSignedBy; - type SlashOrigin = EnsureSignedBy; - type OnSlash = MockOnSlash; - type LiquidStakingExchangeRateProvider = MockLiquidStakingExchangeProvider; - type WeightInfo = (); - type OnIncreaseGuarantee = MockOnIncreaseGuarantee; - type OnDecreaseGuarantee = MockOnDecreaseGuarantee; -} - -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Runtime { - System: frame_system, - OrmlTokens: orml_tokens, - PalletBalances: pallet_balances, - OrmlCurrencies: orml_currencies, - EdfisLiquidSeeValidatorsModule: edfis_liquid_edf_validator_list, - } -); - -pub struct ExtBuilder { - balances: Vec<(AccountId, CurrencyId, Balance)>, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![(ALICE, LEDF, 1000), (BOB, LEDF, 1000)], - } - } -} - -impl ExtBuilder { - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - orml_tokens::GenesisConfig:: { - balances: self.balances, - } - .assimilate_storage(&mut t) - .unwrap(); - - t.into() - } -} diff --git a/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs b/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs deleted file mode 100644 index 50abcad4..00000000 --- a/blockchain/modules/edfis-liquid-edf-validators/src/tests.rs +++ /dev/null @@ -1,878 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Unit tests for homa validator list module. - -#![cfg(test)] - -use super::*; -use frame_support::{assert_noop, assert_ok}; -use mock::*; -use sp_runtime::traits::BadOrigin; - -#[test] -fn guarantee_work() { - ExtBuilder::default().build().execute_with(|| { - let guarantee = Guarantee { - total: 1000, - bonded: 800, - unbonding: Some((200, 10)), - }; - - assert_eq!(guarantee.consolidate_unbonding(9).unbonding, Some((200, 10))); - assert_eq!(guarantee.consolidate_unbonding(10).unbonding, None); - - assert_eq!( - guarantee.rebond(50), - Guarantee { - total: 1000, - bonded: 850, - unbonding: Some((150, 10)), - } - ); - assert_eq!( - guarantee.rebond(200), - Guarantee { - total: 1000, - bonded: 1000, - unbonding: None, - } - ); - - assert_eq!( - guarantee.slash(200), - Guarantee { - total: 800, - bonded: 600, - unbonding: Some((200, 10)), - } - ); - assert_eq!( - guarantee.slash(850), - Guarantee { - total: 150, - bonded: 0, - unbonding: Some((150, 10)), - } - ); - assert_eq!( - guarantee.slash(1000), - Guarantee { - total: 0, - bonded: 0, - unbonding: None, - } - ); - }); -} - -#[test] -fn freeze_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_noop!( - EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(ALICE), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - ), - BadOrigin - ); - - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen, - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen, - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen, - ); - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - )); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_1 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_2 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_3 }, - )); - }); -} - -#[test] -fn thaw_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_noop!( - EdfisLiquidSeeValidatorsModule::thaw( - RuntimeOrigin::signed(ALICE), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - ), - BadOrigin - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2] - )); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - assert_ok!(EdfisLiquidSeeValidatorsModule::thaw( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - )); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::ThawValidator { validator: VALIDATOR_1 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::ThawValidator { validator: VALIDATOR_2 }, - )); - }); -} - -#[test] -fn bond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::bond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 99), - Error::::BelowMinBondAmount - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 0); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 0 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 0 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 0); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 0 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 300 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: BOB, - validator: VALIDATOR_1, - bond: 300, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 300, - bonded: 300, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 300); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 300); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 300); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 0 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_2, - 200 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: BOB, - validator: VALIDATOR_2, - bond: 200, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 500); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 500 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 200); - }); -} - -#[test] -fn unbond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::unbond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 199), - Error::::BelowMinBondAmount - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::UnbondGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::unbond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 100), - Error::::UnbondingExists - ); - }); -} - -#[test] -fn rebond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::rebond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 50 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 150, - unbonding: Some((50, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - }); -} - -#[test] -fn withdraw_unbonded_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 100 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - System::set_block_number(100) - assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - System::reset_events(); - System::set_block_number(101) - assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1 - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::WithdrawnGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 300 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1] - )); - assert_noop!( - EdfisLiquidSeeValidatorsModule::withdraw_unbonded(RuntimeOrigin::signed(BOB), VALIDATOR_1), - Error::::FrozenValidator - ); - }); -} - -#[test] -fn slash_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_2, - 300 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 300 - ); - - // ALICE - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - - // BOB - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 300, - bonded: 300, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 300); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 500); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 500 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::slash( - RuntimeOrigin::signed(ALICE), - vec![ - SlashInfo { - validator: VALIDATOR_1, - amount: 90 - }, - SlashInfo { - validator: VALIDATOR_2, - amount: 50 - }, - ] - ), - BadOrigin - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::slash( - RuntimeOrigin::signed(10), - vec![ - SlashInfo { - validator: VALIDATOR_1, - amount: 90 - }, - SlashInfo { - validator: VALIDATOR_2, - amount: 50 - }, - ] - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 59, - }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: BOB, - validator: VALIDATOR_1, - bond: 119, - }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: BOB, - validator: VALIDATOR_2, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 122 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 200 - ); - - // ALICE - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 41, - bonded: 41, - unbonding: None - } - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 41 - ); - assert_eq!(OrmlTokens::accounts(ALICE, LEDF).frozen, 41); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 41 - ); - - // BOB - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 81, - bonded: 81, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 81); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 200); - assert_eq!(OrmlTokens::accounts(BOB, LEDF).frozen, 281); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 281 - ); - }); -} - -#[test] -fn contains_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1) - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert!(!EdfisLiquidSeeValidatorsModule::contains(&VALIDATOR_1)); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert!(EdfisLiquidSeeValidatorsModule::contains(&VALIDATOR_1)); - }); -} diff --git a/blockchain/modules/edfis-liquid-see-validators/Cargo.toml b/blockchain/modules/edfis-liquid-see-validators/Cargo.toml deleted file mode 100644 index 0d5dd3fc..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-edfis-liquid-see-validators" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -orml-traits = { workspace = true, default-features = false } - -primitives = { workspace = true, default-features = false } -module-support = { workspace = true, default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -sp-io = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-currencies = { workspace = true, features = ["std"] } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "module-support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", -] diff --git a/blockchain/modules/edfis-liquid-see-validators/README.md b/blockchain/modules/edfis-liquid-see-validators/README.md deleted file mode 100644 index 03d7e588..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/README.md +++ /dev/null @@ -1,7 +0,0 @@ -بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -# Edfis Liquid SEE Validators Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `SEE` tokens. The module requires validators to lock some Liquid SEE into insurance fund and if slash happened, EthicalDeFiCouncil can burn those Liquid SEE to compensate Liquid SEE holders. diff --git a/blockchain/modules/edfis-liquid-see-validators/TODO.md b/blockchain/modules/edfis-liquid-see-validators/TODO.md deleted file mode 100644 index efe9a53b..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/TODO.md +++ /dev/null @@ -1,57 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. -- [ ] [[src/lib.rs:0]: Do benchmarking test](src/lib.rs) diff --git a/blockchain/modules/edfis-liquid-see-validators/src/lib.rs b/blockchain/modules/edfis-liquid-see-validators/src/lib.rs deleted file mode 100644 index 946c8db7..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/src/lib.rs +++ /dev/null @@ -1,572 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! # Edfis Liquid SEE Validator List Module -//! -//! ## Overview -//! -//! This will require validators to lock some Liquid SEE into insurance fund -//! and if slash happened, EthicalDeFiCouncil can burn those Liquid SEE to compensate -//! Liquid SEE holders. - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::unused_unit)] -#![allow(clippy::collapsible_if)] - -use frame_support::{pallet_prelude::*, traits::Contains}; -use frame_system::pallet_prelude::*; -use module_support::{ExchangeRateProvider, Ratio}; -use orml_traits::{BasicCurrency, BasicLockableCurrency, Happened, LockIdentifier}; -use parity_scale_codec::MaxEncodedLen; -use primitives::Balance; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_runtime::{ - traits::{Bounded, MaybeDisplay, MaybeSerializeDeserialize, Member, Zero}, - DispatchResult, FixedPointNumber, RuntimeDebug, -}; -use sp_std::{fmt::Debug, vec::Vec}; - -mod mock; -mod tests; - -pub use module::*; - -pub const EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID: LockIdentifier = *b"edf/lqdseevld"; - -pub trait WeightInfo { - fn bond() -> Weight; - fn unbond() -> Weight; - fn rebond() -> Weight; - fn withdraw_unbonded() -> Weight; - fn freeze(u: u32) -> Weight; - fn thaw() -> Weight; - fn slash() -> Weight; -} - -// TODO[src/lib.rs:0]: Do benchmarking test. -impl WeightInfo for () { - fn bond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn unbond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn rebond() -> Weight { - Weight::from_parts(10_000, 0) - } - fn withdraw_unbonded() -> Weight { - Weight::from_parts(10_000, 0) - } - fn freeze(_u: u32) -> Weight { - Weight::from_parts(10_000, 0) - } - fn thaw() -> Weight { - Weight::from_parts(10_000, 0) - } - fn slash() -> Weight { - Weight::from_parts(10_000, 0) - } -} - -/// Insurance for a validator from a single address -#[derive(Encode, Decode, Clone, Copy, RuntimeDebug, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -pub struct Guarantee { - /// The total tokens the validator has in insurance - total: Balance, - /// The number of tokens that are actively bonded for insurance - bonded: Balance, - /// The number of tokens that are in the process of unbonding for insurance - unbonding: Option<(Balance, BlockNumber)>, -} - -impl Guarantee { - /// Take `unbonding` that are sufficiently old - fn consolidate_unbonding(mut self, current_block: BlockNumber) -> Self { - match self.unbonding { - Some((_, expired_block)) if expired_block <= current_block => { - self.unbonding = None; - } - _ => {} - } - self - } - - /// Re-bond funds that were scheduled for unbonding. - fn rebond(mut self, rebond_amount: Balance) -> Self { - if let Some((amount, _)) = self.unbonding.as_mut() { - let rebond_amount = rebond_amount.min(*amount); - self.bonded = self.bonded.saturating_add(rebond_amount); - *amount = amount.saturating_sub(rebond_amount); - if amount.is_zero() { - self.unbonding = None; - } - } - self - } - - fn slash(mut self, slash_amount: Balance) -> Self { - let mut remains = slash_amount; - let slash_from_bonded = self.bonded.min(remains); - self.bonded = self.bonded.saturating_sub(remains); - self.total = self.total.saturating_sub(remains); - remains = remains.saturating_sub(slash_from_bonded); - - if !remains.is_zero() { - if let Some((unbonding_amount, _)) = self.unbonding.as_mut() { - let slash_from_unbonding = remains.min(*unbonding_amount); - *unbonding_amount = unbonding_amount.saturating_sub(slash_from_unbonding); - if unbonding_amount.is_zero() { - self.unbonding = None; - } - } - } - - self - } -} - -/// Information on a validator's slash -#[derive(Encode, Decode, Clone, RuntimeDebug, Eq, PartialEq, MaxEncodedLen, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct SlashInfo { - /// Address of a validator - validator: AccountId, - /// The amount of tokens a validator has in backing - amount: Balance, -} - -/// Validator insurance and frozen status -#[derive(Encode, Decode, Clone, Copy, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)] -pub struct ValidatorBacking { - /// Total insurance from all guarantors - total_insurance: Balance, - is_frozen: bool, -} - -#[frame_support::pallet] -pub mod module { - use super::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The liquid representation of the staking token. - type LiquidSEECurrency: BasicLockableCurrency; - #[pallet::constant] - /// The minimum amount of tokens that can be bonded to a validator. - type MinBondAmount: Get; - #[pallet::constant] - /// The number of blocks a token is bonded to a validator for. - type BondingDuration: Get>; - #[pallet::constant] - /// The minimum amount of insurance a validator needs. - type ValidatorInsuranceThreshold: Get; - /// The AccountId that can perform a freeze. - type FreezeOrigin: EnsureOrigin; - /// The AccountId that can perform a slash. - type SlashOrigin: EnsureOrigin; - /// Callback to be called when a slash occurs. - type OnSlash: Happened; - /// Exchange rate between staked token and liquid token equivalent. - type LiquidStakingExchangeRateProvider: ExchangeRateProvider; - type WeightInfo: WeightInfo; - /// Callback to be called when a validator's insurance increases. - type OnIncreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - /// Callback to be called when a validator's insurance decreases. - type OnDecreaseGuarantee: Happened<(Self::AccountId, Self::AccountId, Balance)>; - } - - #[pallet::error] - pub enum Error { - BelowMinBondAmount, - UnbondingExists, - FrozenValidator, - } - - #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event { - FreezeValidator { - validator: T::AccountId, - }, - ThawValidator { - validator: T::AccountId, - }, - BondGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - UnbondGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - WithdrawnGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - SlashGuarantee { - who: T::AccountId, - validator: T::AccountId, - bond: Balance, - }, - } - - /// The slash guarantee deposits for validators. - /// - /// Guarantees: double_map AccountId, AccountId => Option - #[pallet::storage] - #[pallet::getter(fn guarantees)] - pub type Guarantees = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Twox64Concat, - T::AccountId, - Guarantee>, - OptionQuery, - >; - - /// Total deposits for users. - /// - /// TotalLockedByGuarantor: map AccountId => Option - #[pallet::storage] - #[pallet::getter(fn total_locked_by_guarantor)] - pub type TotalLockedByGuarantor = StorageMap<_, Twox64Concat, T::AccountId, Balance, OptionQuery>; - - /// Total deposit for validators. - /// - /// ValidatorBackings: map AccountId => Option - #[pallet::storage] - #[pallet::getter(fn validator_backings)] - pub type ValidatorBackings = - StorageMap<_, Blake2_128Concat, T::AccountId, ValidatorBacking, OptionQuery>; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet { - /// Bond tokens to a validator. - /// Ensures the amount to bond is greater than the minimum bond amount. - /// - /// - `validator`: the AccountId of a validator to bond to - /// - `amount`: the number of tokens to bond to the given validator - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::bond())] - pub fn bond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - let free_balance = T::LiquidSEECurrency::free_balance(&guarantor); - let total_should_locked = Self::total_locked_by_guarantor(&guarantor).unwrap_or_default(); - - if let Some(extra) = free_balance.checked_sub(total_should_locked) { - let amount = amount.min(extra); - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - guarantee.total = guarantee.total.saturating_add(amount); - guarantee.bonded = guarantee.bonded.saturating_add(amount); - ensure!( - guarantee.bonded >= T::MinBondAmount::get(), - Error::::BelowMinBondAmount - ); - Ok(()) - })?; - Self::deposit_event(Event::BondGuarantee { - who: guarantor, - validator: validator.clone(), - bond: amount, - }); - } - } - Ok(()) - } - - /// Unbond tokens from a validator. - /// Ensures the bonded amount is zero or greater than the minimum bond amount. - /// - /// - `validator`: the AccountId of a validator to unbond from - /// - `amount`: the number of tokens to unbond from the given validator - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::unbond())] - pub fn unbond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - ensure!(guarantee.unbonding.is_none(), Error::::UnbondingExists); - let amount = amount.min(guarantee.bonded); - guarantee.bonded = guarantee.bonded.saturating_sub(amount); - ensure!( - guarantee.bonded.is_zero() || guarantee.bonded >= T::MinBondAmount::get(), - Error::::BelowMinBondAmount, - ); - let expired_block = frame_system::Pallet::::block_number() + T::BondingDuration::get(); - guarantee.unbonding = Some((amount, expired_block)); - - Self::deposit_event(Event::UnbondGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: amount, - }); - Ok(()) - })?; - } - Ok(()) - } - - /// Rebond tokens to a validator. - /// - /// - `validator`: The AccountId of a validator to rebond to - /// - `amount`: The amount of tokens to to rebond to the given validator - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::rebond())] - pub fn rebond( - origin: OriginFor, - validator: T::AccountId, - #[pallet::compact] amount: Balance, - ) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - - if !amount.is_zero() { - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - *guarantee = guarantee.rebond(amount); - Ok(()) - })?; - } - Ok(()) - } - - /// Withdraw the unbonded tokens from a validator. - /// Ensures the validator is not frozen. - /// - /// - `validator`: The AccountId of a validator to withdraw from - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::withdraw_unbonded())] - pub fn withdraw_unbonded(origin: OriginFor, validator: T::AccountId) -> DispatchResult { - let guarantor = ensure_signed(origin)?; - ensure!( - !Self::validator_backings(&validator).unwrap_or_default().is_frozen, - Error::::FrozenValidator - ); - Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - let old_total = guarantee.total; - *guarantee = guarantee.consolidate_unbonding(frame_system::Pallet::::block_number()); - let new_total = guarantee - .bonded - .saturating_add(guarantee.unbonding.unwrap_or_default().0); - if old_total != new_total { - guarantee.total = new_total; - Self::deposit_event(Event::WithdrawnGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: old_total.saturating_sub(new_total), - }); - } - Ok(()) - })?; - Ok(()) - } - - /// Freezes validators if they are not already frozen. - /// Ensures the caller can freeze validators. - /// - /// - `validators`: The AccountIds of the validators to freeze - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::freeze(validators.len() as u32))] - pub fn freeze(origin: OriginFor, validators: Vec) -> DispatchResult { - T::FreezeOrigin::ensure_origin(origin)?; - validators.iter().for_each(|validator| { - ValidatorBackings::::mutate_exists(validator, |maybe_validator| { - let mut v = maybe_validator.take().unwrap_or_default(); - if !v.is_frozen { - v.is_frozen = true; - Self::deposit_event(Event::FreezeValidator { - validator: validator.clone(), - }); - } - *maybe_validator = Some(v); - }); - }); - Ok(()) - } - - /// Unfreezes validators if they are frozen. - /// Ensures the caller can perform a slash. - /// - /// - `validators`: The AccountIds of the validators to unfreeze - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::thaw())] - pub fn thaw(origin: OriginFor, validators: Vec) -> DispatchResult { - // Using SlashOrigin instead of FreezeOrigin so that un-freezing requires more council members than - // freezing - T::SlashOrigin::ensure_origin(origin)?; - validators.iter().for_each(|validator| { - ValidatorBackings::::mutate_exists(validator, |maybe_validator| { - let mut v = maybe_validator.take().unwrap_or_default(); - if v.is_frozen { - v.is_frozen = false; - Self::deposit_event(Event::ThawValidator { - validator: validator.clone(), - }); - } - *maybe_validator = Some(v); - }); - }); - Ok(()) - } - - /// Slash validators. - /// Ensures the the caller can perform a slash. - /// - /// - `slashes`: The SlashInfos of the validators to be slashed - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::slash())] - pub fn slash(origin: OriginFor, slashes: Vec>) -> DispatchResult { - T::SlashOrigin::ensure_origin(origin)?; - let liquid_staking_exchange_rate = T::LiquidStakingExchangeRateProvider::get_exchange_rate(); - let staking_liquid_exchange_rate = liquid_staking_exchange_rate.reciprocal().unwrap_or_default(); - let mut actual_total_slashing: Balance = Zero::zero(); - - for SlashInfo { - validator, - amount, - } in slashes - { - let ValidatorBacking { total_insurance, .. } = Self::validator_backings(&validator).unwrap_or_default(); - let insurance_loss = staking_liquid_exchange_rate - .saturating_mul_int(amount) - .min(total_insurance); - - for (guarantor, _) in Guarantees::::iter_prefix(&validator) { - // NOTE: ignoring result because the closure will not throw err. - let res = Self::update_guarantee(&guarantor, &validator, |guarantee| -> DispatchResult { - let should_slashing = Ratio::checked_from_rational(guarantee.total, total_insurance) - .unwrap_or_else(Ratio::max_value) - .saturating_mul_int(insurance_loss); - let gap = T::LiquidSEECurrency::slash(&guarantor, should_slashing); - let actual_slashing = should_slashing.saturating_sub(gap); - *guarantee = guarantee.slash(actual_slashing); - Self::deposit_event(Event::SlashGuarantee { - who: guarantor.clone(), - validator: validator.clone(), - bond: actual_slashing, - }); - actual_total_slashing = actual_total_slashing.saturating_add(actual_slashing); - Ok(()) - }); - debug_assert!(res.is_ok()); - } - } - - T::OnSlash::happened(&actual_total_slashing); - Ok(()) - } - } -} - -impl Pallet { - fn update_guarantee( - guarantor: &T::AccountId, - validator: &T::AccountId, - f: impl FnOnce(&mut Guarantee>) -> DispatchResult, - ) -> DispatchResult { - Guarantees::::try_mutate_exists(validator, guarantor, |maybe_guarantee| -> DispatchResult { - let mut guarantee = maybe_guarantee.take().unwrap_or_default(); - let old_total = guarantee.total; - - f(&mut guarantee).and_then(|_| -> DispatchResult { - let new_total = guarantee.total; - if guarantee.total.is_zero() { - *maybe_guarantee = None; - } else { - *maybe_guarantee = Some(guarantee); - } - - // adjust total locked of nominator, validator backing and update the lock. - if new_total != old_total { - TotalLockedByGuarantor::::try_mutate_exists( - guarantor, - |maybe_total_locked| -> DispatchResult { - let mut tl = maybe_total_locked.take().unwrap_or_default(); - - ValidatorBackings::::try_mutate_exists( - validator, - |maybe_validator_backing| -> DispatchResult { - let mut vb = maybe_validator_backing.take().unwrap_or_default(); - - if new_total > old_total { - let gap = new_total - old_total; - vb.total_insurance = vb.total_insurance.saturating_add(gap); - tl = tl.saturating_add(gap); - T::OnIncreaseGuarantee::happened(&(guarantor.clone(), validator.clone(), gap)); - } else { - let gap = old_total - new_total; - vb.total_insurance = vb.total_insurance.saturating_sub(gap); - tl = tl.saturating_sub(gap); - T::OnDecreaseGuarantee::happened(&(guarantor.clone(), validator.clone(), gap)); - }; - - if tl.is_zero() { - *maybe_total_locked = None; - T::LiquidSEECurrency::remove_lock(EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID, guarantor)?; - } else { - *maybe_total_locked = Some(tl); - T::LiquidSEECurrency::set_lock(EDFIS_LIQUID_STAKING_VALIDATOR_LIST_ID, guarantor, tl)?; - } - - *maybe_validator_backing = Some(vb); - Ok(()) - }, - ) - }, - )?; - } - - Ok(()) - }) - }) - } -} - -impl Contains for Pallet { - fn contains(account_id: &T::AccountId) -> bool { - Self::validator_backings(account_id) - .unwrap_or_default() - .total_insurance - >= T::ValidatorInsuranceThreshold::get() - } -} diff --git a/blockchain/modules/edfis-liquid-see-validators/src/mock.rs b/blockchain/modules/edfis-liquid-see-validators/src/mock.rs deleted file mode 100644 index a49ff1a8..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/src/mock.rs +++ /dev/null @@ -1,223 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Mocks for Edfis Liquid SEE Validator List Module. - -#![cfg(test)] - -use super::*; -use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, Nothing}, -}; -use frame_system::EnsureSignedBy; -use module_support::ExchangeRate; -use orml_traits::parameter_type_with_key; -use primitives::{Amount, Balance, CurrencyId, TokenSymbol}; -use sp_runtime::{traits::IdentityLookup, BuildStorage}; -use sp_std::cell::RefCell; -use std::collections::HashMap; - -pub type AccountId = u128; -pub type BlockNumber = u64; - -pub const ALICE: AccountId = 0; -pub const BOB: AccountId = 1; -pub const VALIDATOR_1: AccountId = 2; -pub const VALIDATOR_2: AccountId = 3; -pub const VALIDATOR_3: AccountId = 4; -pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); -pub const LSEE: CurrencyId = CurrencyId::Token(TokenSymbol::LSEE); - -mod edfis_liquid_see_validator_list { - pub use super::super::*; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for Runtime { - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -parameter_type_with_key! { - pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { - Default::default() - }; -} - -impl orml_tokens::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type Amount = Amount; - type CurrencyId = CurrencyId; - type WeightInfo = (); - type ExistentialDeposits = ExistentialDeposits; - type CurrencyHooks = (); - type MaxLocks = ConstU32<100>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type DustRemovalWhitelist = Nothing; -} - -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxHolds = (); - type MaxFreezes = (); -} - -parameter_types! { - pub const GetNativeCurrencyId: CurrencyId = SEE; - pub const GetLiquidCurrencyId: CurrencyId = LSEE; -} - -pub type NativeCurrency = orml_currencies::BasicCurrencyAdapter; -pub type LSEECurrency = orml_currencies::Currency; - -impl orml_currencies::Config for Runtime { - type MultiCurrency = OrmlTokens; - type NativeCurrency = NativeCurrency; - type GetNativeCurrencyId = GetNativeCurrencyId; - type WeightInfo = (); -} - -thread_local! { - pub static SHARES: RefCell> = RefCell::new(HashMap::new()); - pub static ACCUMULATED_SLASH: RefCell = RefCell::new(0); -} - -pub struct MockOnSlash; -impl Happened for MockOnSlash { - fn happened(amount: &Balance) { - ACCUMULATED_SLASH.with(|v| *v.borrow_mut() += amount); - } -} - -pub struct MockOnIncreaseGuarantee; -impl Happened<(AccountId, AccountId, Balance)> for MockOnIncreaseGuarantee { - fn happened(info: &(AccountId, AccountId, Balance)) { - let (account_id, validator_account_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); - if let Some(share) = old_map.get_mut(&(*account_id, *validator_account_id)) { - *share = share.saturating_add(*amount); - } else { - old_map.insert((*account_id, *validator_account_id), *amount); - }; - - *v.borrow_mut() = old_map; - }); - } -} - -pub struct MockOnDecreaseGuarantee; -impl Happened<(AccountId, AccountId, Balance)> for MockOnDecreaseGuarantee { - fn happened(info: &(AccountId, AccountId, Balance)) { - let (account_id, validator_account_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); - if let Some(share) = old_map.get_mut(&(*account_id, *validator_account_id)) { - *share = share.saturating_sub(*amount); - } else { - old_map.insert((*account_id, *validator_account_id), Default::default()); - }; - - *v.borrow_mut() = old_map; - }); - } -} - -pub struct MockLiquidStakingExchangeProvider; -impl ExchangeRateProvider for MockLiquidStakingExchangeProvider { - fn get_exchange_rate() -> ExchangeRate { - ExchangeRate::saturating_from_rational(1, 2) - } -} - -ord_parameter_types! { - pub const Admin: AccountId = 10; -} - -impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type LiquidSEECurrency = LSEECurrency; - type MinBondAmount = ConstU128<100>; - type BondingDuration = ConstU64<100>; - type ValidatorInsuranceThreshold = ConstU128<200>; - type FreezeOrigin = EnsureSignedBy; - type SlashOrigin = EnsureSignedBy; - type OnSlash = MockOnSlash; - type LiquidStakingExchangeRateProvider = MockLiquidStakingExchangeProvider; - type WeightInfo = (); - type OnIncreaseGuarantee = MockOnIncreaseGuarantee; - type OnDecreaseGuarantee = MockOnDecreaseGuarantee; -} - -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Runtime { - System: frame_system, - OrmlTokens: orml_tokens, - PalletBalances: pallet_balances, - OrmlCurrencies: orml_currencies, - EdfisLiquidSeeValidatorsModule: edfis_liquid_see_validator_list, - } -); - -pub struct ExtBuilder { - balances: Vec<(AccountId, CurrencyId, Balance)>, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![(ALICE, LSEE, 1000), (BOB, LSEE, 1000)], - } - } -} - -impl ExtBuilder { - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - orml_tokens::GenesisConfig:: { - balances: self.balances, - } - .assimilate_storage(&mut t) - .unwrap(); - - t.into() - } -} diff --git a/blockchain/modules/edfis-liquid-see-validators/src/tests.rs b/blockchain/modules/edfis-liquid-see-validators/src/tests.rs deleted file mode 100644 index c43fd1c8..00000000 --- a/blockchain/modules/edfis-liquid-see-validators/src/tests.rs +++ /dev/null @@ -1,878 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Unit tests for homa validator list module. - -#![cfg(test)] - -use super::*; -use frame_support::{assert_noop, assert_ok}; -use mock::*; -use sp_runtime::traits::BadOrigin; - -#[test] -fn guarantee_work() { - ExtBuilder::default().build().execute_with(|| { - let guarantee = Guarantee { - total: 1000, - bonded: 800, - unbonding: Some((200, 10)), - }; - - assert_eq!(guarantee.consolidate_unbonding(9).unbonding, Some((200, 10))); - assert_eq!(guarantee.consolidate_unbonding(10).unbonding, None); - - assert_eq!( - guarantee.rebond(50), - Guarantee { - total: 1000, - bonded: 850, - unbonding: Some((150, 10)), - } - ); - assert_eq!( - guarantee.rebond(200), - Guarantee { - total: 1000, - bonded: 1000, - unbonding: None, - } - ); - - assert_eq!( - guarantee.slash(200), - Guarantee { - total: 800, - bonded: 600, - unbonding: Some((200, 10)), - } - ); - assert_eq!( - guarantee.slash(850), - Guarantee { - total: 150, - bonded: 0, - unbonding: Some((150, 10)), - } - ); - assert_eq!( - guarantee.slash(1000), - Guarantee { - total: 0, - bonded: 0, - unbonding: None, - } - ); - }); -} - -#[test] -fn freeze_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_noop!( - EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(ALICE), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - ), - BadOrigin - ); - - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen, - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen, - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen, - ); - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - )); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_1 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_2 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::FreezeValidator { validator: VALIDATOR_3 }, - )); - }); -} - -#[test] -fn thaw_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_noop!( - EdfisLiquidSeeValidatorsModule::thaw( - RuntimeOrigin::signed(ALICE), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - ), - BadOrigin - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2] - )); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - assert_ok!(EdfisLiquidSeeValidatorsModule::thaw( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1, VALIDATOR_2, VALIDATOR_3] - )); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .is_frozen - ); - assert!( - !EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_3) - .unwrap_or_default() - .is_frozen - ); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::ThawValidator { validator: VALIDATOR_1 }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::ThawValidator { validator: VALIDATOR_2 }, - )); - }); -} - -#[test] -fn bond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::bond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 99), - Error::::BelowMinBondAmount - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 0); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 0 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 0 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 0); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 0 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 300 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: BOB, - validator: VALIDATOR_1, - bond: 300, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 300, - bonded: 300, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 300); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 300); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 0, - bonded: 0, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 300); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 0 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 0); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_2, - 200 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::BondGuarantee { - who: BOB, - validator: VALIDATOR_2, - bond: 200, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 500); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 500 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 200); - }); -} - -#[test] -fn unbond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::unbond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 199), - Error::::BelowMinBondAmount - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - System::assert_last_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::UnbondGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::unbond(RuntimeOrigin::signed(ALICE), VALIDATOR_1, 100), - Error::::UnbondingExists - ); - }); -} - -#[test] -fn rebond_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::rebond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 50 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 150, - unbonding: Some((50, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - }); -} - -#[test] -fn withdraw_unbonded_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::unbond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 100 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - - System::set_block_number(100) - assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 100, - unbonding: Some((100, 101)) - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 200 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 400 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 200 - ); - System::reset_events(); - System::set_block_number(101) - assert_ok!(EdfisLiquidSeeValidatorsModule::withdraw_unbonded( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1 - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::WithdrawnGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 300 - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::freeze( - RuntimeOrigin::signed(10), - vec![VALIDATOR_1] - )); - assert_noop!( - EdfisLiquidSeeValidatorsModule::withdraw_unbonded(RuntimeOrigin::signed(BOB), VALIDATOR_1), - Error::::FrozenValidator - ); - }); -} - -#[test] -fn slash_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_1, - 200 - )); - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(BOB), - VALIDATOR_2, - 300 - )); - - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 300 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 300 - ); - - // ALICE - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 100, - bonded: 100, - unbonding: None - } - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 100 - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 100); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 100 - ); - - // BOB - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 200); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 300, - bonded: 300, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 300); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 500); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 500 - ); - - assert_noop!( - EdfisLiquidSeeValidatorsModule::slash( - RuntimeOrigin::signed(ALICE), - vec![ - SlashInfo { - validator: VALIDATOR_1, - amount: 90 - }, - SlashInfo { - validator: VALIDATOR_2, - amount: 50 - }, - ] - ), - BadOrigin - ); - - assert_ok!(EdfisLiquidSeeValidatorsModule::slash( - RuntimeOrigin::signed(10), - vec![ - SlashInfo { - validator: VALIDATOR_1, - amount: 90 - }, - SlashInfo { - validator: VALIDATOR_2, - amount: 50 - }, - ] - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: ALICE, - validator: VALIDATOR_1, - bond: 59, - }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: BOB, - validator: VALIDATOR_1, - bond: 119, - }, - )); - System::assert_has_event(mock::RuntimeEvent::EdfisLiquidSeeValidatorsModule( - crate::Event::SlashGuarantee { - who: BOB, - validator: VALIDATOR_2, - bond: 100, - }, - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 122 - ); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_2) - .unwrap_or_default() - .total_insurance, - 200 - ); - - // ALICE - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, ALICE).unwrap_or_default(), - Guarantee { - total: 41, - bonded: 41, - unbonding: None - } - ); - assert_eq!( - SHARES.with(|v| *v.borrow().get(&(ALICE, VALIDATOR_1)).unwrap_or(&0)), - 41 - ); - assert_eq!(OrmlTokens::accounts(ALICE, LSEE).frozen, 41); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(ALICE).unwrap_or_default(), - 41 - ); - - // BOB - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_1, BOB).unwrap_or_default(), - Guarantee { - total: 81, - bonded: 81, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_1)).unwrap_or(&0)), 81); - assert_eq!( - EdfisLiquidSeeValidatorsModule::guarantees(VALIDATOR_2, BOB).unwrap_or_default(), - Guarantee { - total: 200, - bonded: 200, - unbonding: None - } - ); - assert_eq!(SHARES.with(|v| *v.borrow().get(&(BOB, VALIDATOR_2)).unwrap_or(&0)), 200); - assert_eq!(OrmlTokens::accounts(BOB, LSEE).frozen, 281); - assert_eq!( - EdfisLiquidSeeValidatorsModule::total_locked_by_guarantor(BOB).unwrap_or_default(), - 281 - ); - }); -} - -#[test] -fn contains_work() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 100 - ); - assert!(!EdfisLiquidSeeValidatorsModule::contains(&VALIDATOR_1)); - - assert_ok!(EdfisLiquidSeeValidatorsModule::bond( - RuntimeOrigin::signed(ALICE), - VALIDATOR_1, - 100 - )); - assert_eq!( - EdfisLiquidSeeValidatorsModule::validator_backings(VALIDATOR_1) - .unwrap_or_default() - .total_insurance, - 200 - ); - assert!(EdfisLiquidSeeValidatorsModule::contains(&VALIDATOR_1)); - }); -} diff --git a/blockchain/modules/incentives/README.md b/blockchain/modules/incentives/README.md index f4306f20..f77207a7 100644 --- a/blockchain/modules/incentives/README.md +++ b/blockchain/modules/incentives/README.md @@ -11,8 +11,6 @@ Edfis Exchange needs to support multiple open liquidity reward mechanisms. Each 1. EcdpSetrLiquidityRewards: record the shares and rewards for Setter (SETR) ECDP users who are staking LP tokens. 2. EcdpUssdLiquidityRewards: record the shares and rewards for Slick USD (USSD) ECDP users who are staking LP tokens. 3. EdfisLiquidityRewards: record the shares and rewards for Edfis makers who are staking LP token. -4. EdfisXLiquidityRewards: record the shares and rewards for Edfis X (Cross-chain) makers who are staking XLP token. -5. MoyaEarnRewards: record the shares and rewards for users of Moya Earn (Moya Liquid Staking Protocol). ## Rewards accumulation: diff --git a/blockchain/modules/incentives/src/lib.rs b/blockchain/modules/incentives/src/lib.rs index 3132fc60..7c857489 100644 --- a/blockchain/modules/incentives/src/lib.rs +++ b/blockchain/modules/incentives/src/lib.rs @@ -32,8 +32,6 @@ //! 1. EcdpSetrLiquidityRewards: record the shares and rewards for Setter (SETR) ECDP users who are staking LP tokens. //! 2. EcdpUssdLiquidityRewards: record the shares and rewards for Slick USD (USSD) ECDP users who are staking LP tokens. //! 3. EdfisLiquidityRewards: record the shares and rewards for Edfis makers who are staking LP token. -//! 4. EdfisXLiquidityRewards: record the shares and rewards for Edfis X (Cross-chain) makers who are staking XLP token. -//! 5. MoyaEarnRewards: record the shares and rewards for users of Moya Earn (Moya Liquid Staking Protocol). //! //! Rewards accumulation: //! 1. Rewards: periodicly(AccumulatePeriod), accumulate fixed amount according to Rewards. diff --git a/blockchain/modules/incentives/src/mock.rs b/blockchain/modules/incentives/src/mock.rs index 8f458b33..769f74ac 100644 --- a/blockchain/modules/incentives/src/mock.rs +++ b/blockchain/modules/incentives/src/mock.rs @@ -38,7 +38,6 @@ pub type AccountId = AccountId32; pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); pub const USSD: CurrencyId = CurrencyId::Token(TokenSymbol::USSD); -pub const LEDF: CurrencyId = CurrencyId::Token(TokenSymbol::LEDF); pub const BTC: CurrencyId = CurrencyId::ForeignAsset(255); pub const EDF: CurrencyId = CurrencyId::Token(TokenSymbol::EDF); pub const BTC_USSD_LP: CurrencyId = diff --git a/blockchain/modules/incentives/src/tests.rs b/blockchain/modules/incentives/src/tests.rs index 36615333..6e2e31d6 100644 --- a/blockchain/modules/incentives/src/tests.rs +++ b/blockchain/modules/incentives/src/tests.rs @@ -344,7 +344,6 @@ fn on_initialize_should_work() { ExtBuilder::default().build().execute_with(|| { assert_ok!(TokensModule::deposit(SEE, &RewardsSource::get(), 10000)); assert_ok!(TokensModule::deposit(USSD, &RewardsSource::get(), 10000)); - assert_ok!(TokensModule::deposit(LEDF, &RewardsSource::get(), 10000)); assert_ok!(IncentivesModule::update_incentive_rewards( RuntimeOrigin::signed(ROOT::get()), @@ -361,7 +360,6 @@ fn on_initialize_should_work() { assert_eq!(TokensModule::free_balance(SEE, &RewardsSource::get()), 10000); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 10000); - assert_eq!(TokensModule::free_balance(LEDF, &RewardsSource::get()), 10000); assert_eq!( RewardsModule::pool_infos(PoolId::Dex(BTC_USSD_LP)), @@ -394,7 +392,6 @@ fn on_initialize_should_work() { 10000 - (1000 + 200 + 100 + 100) ); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 10000 - 500); - assert_eq!(TokensModule::free_balance(LEDF, &RewardsSource::get()), 10000); // 100 SEE is incentive reward assert_eq!( @@ -427,7 +424,6 @@ fn on_initialize_should_work() { 8600 - (1000 + 2000 + 100 + 200 + 100) ); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 9500 - 500); - assert_eq!(TokensModule::free_balance(LEDF, &RewardsSource::get()), 10000 - 50); // 100 SEE is incentive reward assert_eq!( @@ -461,7 +457,6 @@ fn on_initialize_should_work() { 5200 - (100 + 200 + 100) ); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 9000); - assert_eq!(TokensModule::free_balance(LEDF, &RewardsSource::get()), 9950); // after shutdown, PoolId::Dex will accumulate incentive rewards // reward @@ -541,10 +536,6 @@ fn transfer_reward_and_update_rewards_storage_atomically_when_accumulate_incenti assert_ok!(TokensModule::deposit(SEE, &RewardsSource::get(), 100)); assert_eq!(TokensModule::free_balance(SEE, &RewardsSource::get()), 100); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 100); - assert_eq!( - orml_rewards::PoolInfos::::contains_key(PoolId::Dex(LEDF)), - false - ); assert_eq!(TokensModule::free_balance(SEE, &RewardsSource::get()), 100); assert_eq!(TokensModule::free_balance(USSD, &RewardsSource::get()), 100); diff --git a/blockchain/modules/moya-liquid-edf-earn/Cargo.toml b/blockchain/modules/moya-liquid-edf-earn/Cargo.toml deleted file mode 100644 index df60e0b9..00000000 --- a/blockchain/modules/moya-liquid-edf-earn/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-edf-earn" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-edf-earn/README.md b/blockchain/modules/moya-liquid-edf-earn/README.md deleted file mode 100644 index 0752fdce..00000000 --- a/blockchain/modules/moya-liquid-edf-earn/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Moya Liquid EDF Earn Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `EDF` tokens. diff --git a/blockchain/modules/moya-liquid-edf-earn/TODO.md b/blockchain/modules/moya-liquid-edf-earn/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-edf-earn/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/moya-liquid-edf-validators/Cargo.toml b/blockchain/modules/moya-liquid-edf-validators/Cargo.toml deleted file mode 100644 index 250e0bdc..00000000 --- a/blockchain/modules/moya-liquid-edf-validators/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-edf-validators" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-edf-validators/README.md b/blockchain/modules/moya-liquid-edf-validators/README.md deleted file mode 100644 index 777cf460..00000000 --- a/blockchain/modules/moya-liquid-edf-validators/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Moya Liquid EDF Validators Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `EDF` tokens. diff --git a/blockchain/modules/moya-liquid-edf-validators/TODO.md b/blockchain/modules/moya-liquid-edf-validators/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-edf-validators/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/moya-liquid-edf/Cargo.toml b/blockchain/modules/moya-liquid-edf/Cargo.toml deleted file mode 100644 index 30611b24..00000000 --- a/blockchain/modules/moya-liquid-edf/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-edf" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-edf/README.md b/blockchain/modules/moya-liquid-edf/README.md deleted file mode 100644 index b62821b2..00000000 --- a/blockchain/modules/moya-liquid-edf/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Moya Liquid EDF Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `EDF` tokens. diff --git a/blockchain/modules/moya-liquid-edf/TODO.md b/blockchain/modules/moya-liquid-edf/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-edf/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/moya-liquid-see-earn/Cargo.toml b/blockchain/modules/moya-liquid-see-earn/Cargo.toml deleted file mode 100644 index 86b8ed93..00000000 --- a/blockchain/modules/moya-liquid-see-earn/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-see-earn" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-see-earn/README.md b/blockchain/modules/moya-liquid-see-earn/README.md deleted file mode 100644 index 0752fdce..00000000 --- a/blockchain/modules/moya-liquid-see-earn/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Moya Liquid EDF Earn Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `EDF` tokens. diff --git a/blockchain/modules/moya-liquid-see-earn/TODO.md b/blockchain/modules/moya-liquid-see-earn/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-see-earn/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/moya-liquid-see-validators/Cargo.toml b/blockchain/modules/moya-liquid-see-validators/Cargo.toml deleted file mode 100644 index d84aef5b..00000000 --- a/blockchain/modules/moya-liquid-see-validators/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-see-validators" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-see-validators/README.md b/blockchain/modules/moya-liquid-see-validators/README.md deleted file mode 100644 index a8fe17ec..00000000 --- a/blockchain/modules/moya-liquid-see-validators/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Moya Liquid SEE Validators Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `SEE` tokens. diff --git a/blockchain/modules/moya-liquid-see-validators/TODO.md b/blockchain/modules/moya-liquid-see-validators/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-see-validators/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/moya-liquid-see/Cargo.toml b/blockchain/modules/moya-liquid-see/Cargo.toml deleted file mode 100644 index 1f12a27d..00000000 --- a/blockchain/modules/moya-liquid-see/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "module-moya-liquid-see" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -scale-info = { workspace = true } -serde = { workspace = true, optional = true } -parity-scale-codec = { version = "3.0.0", default-features = false, features = ["max-encoded-len"] } -sp-runtime = { workspace = true } -sp-io = { workspace = true } -sp-std = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -primitives = { package = "setheum-primitives", path = "../primitives", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } -orml-traits = { path = "../submodules/orml/traits", default-features = false } - -[dev-dependencies] -sp-core = { workspace = true, features = ["std"] } -pallet-balances = { workspace = true } -orml-tokens = { workspace = true } - -[features] -default = ["std"] -std = [ - "scale-info/std", - "serde", - "parity-scale-codec/std", - "sp-runtime/std", - "sp-std/std", - "sp-io/std", - "frame-support/std", - "frame-system/std", - "primitives/std", - "support/std", - "orml-traits/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/moya-liquid-see/README.md b/blockchain/modules/moya-liquid-see/README.md deleted file mode 100644 index c11d84ec..00000000 --- a/blockchain/modules/moya-liquid-see/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Edfis Liquid SEE Module - -## Overview - -Provides a liquid staking platform on Ethical DeFi for `SEE` tokens. diff --git a/blockchain/modules/moya-liquid-see/TODO.md b/blockchain/modules/moya-liquid-see/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/moya-liquid-see/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/prices/src/lib.rs b/blockchain/modules/prices/src/lib.rs index a29d98a1..40a0c2e1 100644 --- a/blockchain/modules/prices/src/lib.rs +++ b/blockchain/modules/prices/src/lib.rs @@ -73,25 +73,13 @@ pub mod module { #[pallet::constant] type GetSEECurrencyId: Get; - /// The Liquid SEE currency id, it should be LSEE in Setheum. - #[pallet::constant] - type GetLiquidSEECurrencyId: Get; - /// The EDF currency id, it should be EDF in Setheum. #[pallet::constant] type GetEDFCurrencyId: Get; - /// The Liquid EDF currency id, it should be LEDF in Setheum. - #[pallet::constant] - type GetLiquidEDFCurrencyId: Get; - /// The origin which may lock and unlock prices feed to system. type LockOrigin: EnsureOrigin; - /// The provider of the exchange rate between liquid currency and - /// staking currency. - type LiquidStakingExchangeRateProvider: ExchangeRateProvider; - /// SwapManager provide liquidity info. type SwapManager: SwapManager; @@ -188,14 +176,6 @@ impl Pallet { let maybe_price = if currency_id == T::GetUSSDCurrencyId::get() { // if is USSD stablecoin, use fixed price Some(T::USSDFixedPrice::get()) - } else if currency_id == T::GetLiquidSEECurrencyId::get() { - // directly return real-time the multiple of the price of SEECurrencyId and the exchange rate - return Self::access_price(T::GetSEECurrencyId::get()) - .and_then(|n| n.checked_mul(&T::LiquidStakingExchangeRateProvider::get_exchange_rate())); - } else if currency_id == T::GetLiquidEDFCurrencyId::get() { - // directly return real-time the multiple of the price of EDFCurrencyId and the exchange rate - return Self::access_price(T::GetEDFCurrencyId::get()) - .and_then(|n| n.checked_mul(&T::LiquidStakingExchangeRateProvider::get_exchange_rate())); } else if let CurrencyId::DexShare(dex_share_0, dex_share_1) = currency_id { let token_0: CurrencyId = dex_share_0.into(); let token_1: CurrencyId = dex_share_1.into(); diff --git a/blockchain/modules/prices/src/mock.rs b/blockchain/modules/prices/src/mock.rs index af6f288b..c8a0e5e3 100644 --- a/blockchain/modules/prices/src/mock.rs +++ b/blockchain/modules/prices/src/mock.rs @@ -42,7 +42,6 @@ pub type AccountId = u128; pub type BlockNumber = u64; pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); -pub const LSEE: CurrencyId = CurrencyId::Token(TokenSymbol::LSEE); pub const USSD: CurrencyId = CurrencyId::Token(TokenSymbol::USSD); pub const EDF: CurrencyId = CurrencyId::Token(TokenSymbol::EDF); pub const LP_USSD_SEE: CurrencyId = @@ -94,7 +93,6 @@ impl DataProvider for MockDataProvider { match *currency_id { USSD => None, SEE => Some(Price::saturating_from_integer(10)), - LSEE => Some(Price::saturating_from_integer(30)), EDF => Some(Price::saturating_from_integer(200)), _ => None, } @@ -102,7 +100,6 @@ impl DataProvider for MockDataProvider { match *currency_id { USSD => Some(Price::saturating_from_rational(99, 100)), SEE => Some(Price::saturating_from_integer(100)), - LSEE => Some(Price::zero()), EDF => None, _ => None, } @@ -116,17 +113,6 @@ impl DataFeeder for MockDataProvider { } } -pub struct MockLiquidStakingExchangeProvider; -impl ExchangeRateProvider for MockLiquidStakingExchangeProvider { - fn get_exchange_rate() -> ExchangeRate { - if CHANGED.with(|v| *v.borrow_mut()) { - ExchangeRate::saturating_from_rational(3, 5) - } else { - ExchangeRate::saturating_from_rational(1, 2) - } - } -} - pub struct MockSwapManager; impl SwapManager for MockSwapManager { fn get_liquidity_pool(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance) { @@ -213,7 +199,6 @@ ord_parameter_types! { parameter_types! { pub const GetUSSDCurrencyId: CurrencyId = USSD; pub const GetSEECurrencyId: CurrencyId = SEE; - pub const GetLiquidSEECurrencyId: CurrencyId = LSEE; pub USSDFixedPrice: Price = Price::one(); } @@ -223,9 +208,7 @@ impl Config for Runtime { type GetUSSDCurrencyId = GetUSSDCurrencyId; type USSDFixedPrice = USSDFixedPrice; type GetSEECurrencyId = GetSEECurrencyId; - type GetLiquidSEECurrencyId = GetLiquidSEECurrencyId; type LockOrigin = EnsureSignedBy; - type LiquidStakingExchangeRateProvider = MockLiquidStakingExchangeProvider; type SwapManager = MockSwapManager; type Currency = Tokens; type Erc20InfoMapping = MockErc20InfoMapping; diff --git a/blockchain/modules/prices/src/tests.rs b/blockchain/modules/prices/src/tests.rs index 95d9410f..cddd3ad7 100644 --- a/blockchain/modules/prices/src/tests.rs +++ b/blockchain/modules/prices/src/tests.rs @@ -134,30 +134,6 @@ fn access_price_of_ussd() { }); } -#[test] -fn access_price_of_liquid_currency() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!( - PricesModule::access_price(SEE), - Some(Price::saturating_from_integer(10000000000u128)) - ); // 100 USD, right shift the decimal point (18-12) places - assert_eq!( - PricesModule::access_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); // see_price * 1/2 - - mock_oracle_update(); - assert_eq!( - PricesModule::access_price(SEE), - Some(Price::saturating_from_integer(1000000000u128)) - ); // 10 USD, right shift the decimal point (18-12) places - assert_eq!( - PricesModule::access_price(LSEE), - Some(Price::saturating_from_integer(600000000u128)) - ); // see_price * 3/5 - }); -} - #[test] fn access_price_of_dex_share_currency() { ExtBuilder::default().build().execute_with(|| { @@ -311,10 +287,6 @@ fn price_providers_work() { RealTimePriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - RealTimePriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); assert_eq!(RealTimePriceProvider::::get_price(EDF), None); assert_eq!(RealTimePriceProvider::::get_price(LP_USSD_SEE), lp_price_1); @@ -322,10 +294,6 @@ fn price_providers_work() { PriorityLockedPriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - PriorityLockedPriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); assert_eq!(PriorityLockedPriceProvider::::get_price(EDF), None); assert_eq!( PriorityLockedPriceProvider::::get_price(LP_USSD_SEE), @@ -333,13 +301,11 @@ fn price_providers_work() { ); assert_eq!(LockedPriceProvider::::get_price(USSD), None); - assert_eq!(LockedPriceProvider::::get_price(LSEE), None); assert_eq!(LockedPriceProvider::::get_price(EDF), None); assert_eq!(LockedPriceProvider::::get_price(LP_USSD_SEE), None); // lock price assert_ok!(PricesModule::lock_price(RuntimeOrigin::signed(1), USSD)); - assert_ok!(PricesModule::lock_price(RuntimeOrigin::signed(1), LSEE)); assert_noop!( PricesModule::lock_price(RuntimeOrigin::signed(1), EDF), Error::::AccessPriceFailed @@ -350,10 +316,6 @@ fn price_providers_work() { LockedPriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - LockedPriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); assert_eq!(LockedPriceProvider::::get_price(EDF), None); assert_eq!(LockedPriceProvider::::get_price(LP_USSD_SEE), lp_price_1); @@ -371,10 +333,6 @@ fn price_providers_work() { RealTimePriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - RealTimePriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(600000000u128)) - ); assert_eq!( RealTimePriceProvider::::get_price(EDF), Some(Price::saturating_from_integer(200000000u128)) @@ -385,10 +343,6 @@ fn price_providers_work() { PriorityLockedPriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - PriorityLockedPriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); assert_eq!( PriorityLockedPriceProvider::::get_price(EDF), Some(Price::saturating_from_integer(200000000u128)) @@ -402,16 +356,11 @@ fn price_providers_work() { LockedPriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - LockedPriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(5000000000u128)) - ); assert_eq!(LockedPriceProvider::::get_price(EDF), None); assert_eq!(LockedPriceProvider::::get_price(LP_USSD_SEE), lp_price_1); // unlock price assert_ok!(PricesModule::unlock_price(RuntimeOrigin::signed(1), USSD)); - assert_ok!(PricesModule::unlock_price(RuntimeOrigin::signed(1), LSEE)); assert_noop!( PricesModule::unlock_price(RuntimeOrigin::signed(1), EDF), Error::::NoLockedPrice @@ -422,10 +371,6 @@ fn price_providers_work() { PriorityLockedPriceProvider::::get_price(USSD), Some(Price::saturating_from_integer(1000000u128)) ); - assert_eq!( - PriorityLockedPriceProvider::::get_price(LSEE), - Some(Price::saturating_from_integer(600000000u128)) - ); assert_eq!( PriorityLockedPriceProvider::::get_price(EDF), Some(Price::saturating_from_integer(200000000u128)) @@ -436,7 +381,6 @@ fn price_providers_work() { ); assert_eq!(LockedPriceProvider::::get_price(USSD), None); - assert_eq!(LockedPriceProvider::::get_price(LSEE), None); assert_eq!(LockedPriceProvider::::get_price(EDF), None); assert_eq!(LockedPriceProvider::::get_price(LP_USSD_SEE), None); }); From 9c00559521a71eba5a58262888b05436c7ea2c40 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sun, 5 May 2024 01:45:19 +0800 Subject: [PATCH 07/10] rm Setheum Pay --- blockchain/modules/setheum-pay/Cargo.toml | 46 - blockchain/modules/setheum-pay/README.md | 85 - blockchain/modules/setheum-pay/TODO.md | 56 - blockchain/modules/setheum-pay/src/lib.rs | 680 -------- blockchain/modules/setheum-pay/src/mock.rs | 184 --- blockchain/modules/setheum-pay/src/tests.rs | 1470 ----------------- blockchain/modules/setheum-pay/src/types.rs | 142 -- blockchain/modules/setheum-pay/src/weights.rs | 205 --- 8 files changed, 2868 deletions(-) delete mode 100644 blockchain/modules/setheum-pay/Cargo.toml delete mode 100644 blockchain/modules/setheum-pay/README.md delete mode 100644 blockchain/modules/setheum-pay/TODO.md delete mode 100644 blockchain/modules/setheum-pay/src/lib.rs delete mode 100644 blockchain/modules/setheum-pay/src/mock.rs delete mode 100644 blockchain/modules/setheum-pay/src/tests.rs delete mode 100644 blockchain/modules/setheum-pay/src/types.rs delete mode 100644 blockchain/modules/setheum-pay/src/weights.rs diff --git a/blockchain/modules/setheum-pay/Cargo.toml b/blockchain/modules/setheum-pay/Cargo.toml deleted file mode 100644 index 1bb2dd02..00000000 --- a/blockchain/modules/setheum-pay/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "module-setheum-pay" -version = "0.9.81-dev" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - - -[dependencies] -parity-scale-codec = { workspace = true } -log = { workspace = true } -scale-info = { workspace = true } - -frame-support = { workspace = true } -frame-system = { workspace = true } -sp-runtime = { workspace = true } -sp-std = { workspace = true } - -orml-traits = {path = "../traits", version = "0.7.0", default-features = false } - -[dev-dependencies] -serde = "1.0.136" - -sp-core = { workspace = true } -sp-io = { workspace = true } - -orml-tokens = { path = "../tokens" } - -[features] -default = [ 'std' ] -std = [ - 'frame-support/std', - 'frame-system/std', - 'log/std', - 'orml-traits/std', - 'parity-scale-codec/std', - 'scale-info/std', - 'sp-runtime/std', - 'sp-std/std', -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/blockchain/modules/setheum-pay/README.md b/blockchain/modules/setheum-pay/README.md deleted file mode 100644 index 4aef81cc..00000000 --- a/blockchain/modules/setheum-pay/README.md +++ /dev/null @@ -1,85 +0,0 @@ -بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -# Payments Module - -This module allows users to create secure reversible payments that keep funds locked in a merchant's account until the off-chain goods are confirmed to be received. Each payment gets assigned its own *judge* that can help resolve any disputes between the two parties. - -## Terminology - -- Created: A payment has been created and the amount arrived to its destination but it's locked. -- NeedsReview: The payment has bee disputed and is awaiting settlement by a judge. -- IncentivePercentage: A small share of the payment amount is held in escrow until a payment is completed/cancelled. The Incentive Percentage represents this value. -- Resolver Account: A resolver account is assigned to every payment created, this account has the privilege to cancel/release a payment that has been disputed. -- Remark: The module allows to create payments by optionally providing some extra(limited) amount of bytes, this is referred to as Remark. This can be used by a marketplace to separate/tag payments. -- CancelBufferBlockLength: This is the time window where the recipient can dispute a cancellation request from the payment creator. - -## Interface - -#### Events - -- `PaymentCreated { from: T::AccountId, asset: AssetIdOf, amount: BalanceOf },`, -- `PaymentReleased { from: T::AccountId, to: T::AccountId }`, -- `PaymentCancelled { from: T::AccountId, to: T::AccountId }`, -- `PaymentCreatorRequestedRefund { from: T::AccountId, to: T::AccountId, expiry: BlockNumberFor}` -- `PaymentRefundDisputed { from: T::AccountId, to: T::AccountId }` -- `PaymentRequestCreated { from: T::AccountId, to: T::AccountId }` -- `PaymentRequestCompleted { from: T::AccountId, to: T::AccountId }` - -#### Extrinsics - -- `pay` - Create an payment for the given currencyid/amount -- `pay_with_remark` - Create a payment with a remark, can be used to tag payments -- `release` - Release the payment amount to recipent -- `cancel` - Allows the recipient to cancel the payment and release the payment amount to creator -- `resolve_release_payment` - Allows assigned judge to release a payment -- `resolve_cancel_payment` - Allows assigned judge to cancel a payment -- `request_refund` - Allows the creator of the payment to trigger cancel with a buffer time. -- `claim_refund` - Allows the creator to claim payment refund after buffer time -- `dispute_refund` - Allows the recipient to dispute the payment request of sender -- `request_payment` - Create a payment that can be completed by the sender using the `accept_and_pay` extrinsic. -- `accept_and_pay` - Allows the sender to fulfill a payment request created by a recipient - -## Implementations - -The RatesProvider module provides implementations for the following traits. -- [`PaymentHandler`](./src/types.rs) - -## Types - -The `PaymentDetail` struct stores information about the payment/escrow. A "payment" in Setheum Pay is similar to an escrow, it is used to guarantee proof of funds and can be released once an agreed upon condition has reached between the payment creator and recipient. The payment lifecycle is tracked using the state field. - -```rust -pub struct PaymentDetail { - /// type of asset used for payment - pub asset: AssetIdOf, - /// amount of asset used for payment - pub amount: BalanceOf, - /// incentive amount that is credited to creator for resolving - pub incentive_amount: BalanceOf, - /// enum to track payment lifecycle [Created, NeedsReview] - pub state: PaymentState>, - /// account that can settle any disputes created in the payment - pub resolver_account: T::AccountId, - /// fee charged and recipient account details - pub fee_detail: Option<(T::AccountId, BalanceOf)>, - /// remarks to give context to payment - pub remark: Option>, -} -``` - -The `PaymentState` enum tracks the possible states that a payment can be in. When a payment is 'completed' or 'cancelled' it is removed from storage and hence not tracked by a state. - -```rust -pub enum PaymentState { - /// Amounts have been reserved and waiting for release/cancel - Created, - /// A judge needs to review and release manually - NeedsReview, - /// The user has requested refund and will be processed by `BlockNumber` - RefundRequested(BlockNumber), -} -``` - -## GenesisConfig - -The rates_provider pallet does not depend on the `GenesisConfig` diff --git a/blockchain/modules/setheum-pay/TODO.md b/blockchain/modules/setheum-pay/TODO.md deleted file mode 100644 index c8c94c13..00000000 --- a/blockchain/modules/setheum-pay/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -# To-Do List - -This list contains all TODOs in the Repo - - - -- [ToDo List - The Monofile for Setheum Repo ToDos](#to-do-list) - - [1. Introduction](#1-guidelines) - - [2. Contribution](#2-contribution) - - [3. Lists](#3-lists) - - [4. Tasks](#4-tasks) - - - -## 1. Guidelines - -Note: Before you write a ToDo in this repo, please read the below guidelines carefully. - -Whenever you write a ToDo, you need to follow this standard syntax - -```rust -//TODO:[file_name:task_number] - task_details -``` - -for example: - -```rust -//TODO:[TODO.md:0] - Add Todo Guidelines -``` - -Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`. - -Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\. - -Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task. - -Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example: - -```rust -//TODO:[TODO.md-C:0] - Add Todo Guidelines -``` - -## 2. Contribution - -You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard. - -## 3. Lists - -Each package/module/directory has its own `TODO.md`. - -## 4. Tasks - -These tasks are just for this file specifically. - -- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo. -- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`. diff --git a/blockchain/modules/setheum-pay/src/lib.rs b/blockchain/modules/setheum-pay/src/lib.rs deleted file mode 100644 index 28e8e545..00000000 --- a/blockchain/modules/setheum-pay/src/lib.rs +++ /dev/null @@ -1,680 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//!This module allows users to create secure reversible payments that keep -//! funds locked in a merchant's account until the off-chain goods are confirmed -//! to be received. Each payment gets assigned its own *judge* that can help -//! resolve any disputes between the two parties. - -//! ## Terminology -//! -//! - Created: A payment has been created and the amount arrived to its -//! destination but it's locked. -//! - NeedsReview: The payment has bee disputed and is awaiting settlement by a -//! judge. -//! - IncentivePercentage: A small share of the payment amount is held in escrow -//! until a payment is completed/cancelled. The Incentive Percentage -//! represents this value. -//! - Resolver Account: A resolver account is assigned to every payment created, -//! this account has the privilege to cancel/release a payment that has been -//! disputed. -//! - Remark: The module allows to create payments by optionally providing some -//! extra(limited) amount of bytes, this is referred to as Remark. This can be -//! used by a marketplace to separate/tag payments. -//! - CancelBufferBlockLength: This is the time window where the recipient can -//! dispute a cancellation request from the payment creator. - -//! Extrinsics -//! -//! - `pay` - Create an payment for the given currencyid/amount -//! - `pay_with_remark` - Create a payment with a remark, can be used to tag -//! payments -//! - `release` - Release the payment amount to recipent -//! - `cancel` - Allows the recipient to cancel the payment and release the -//! payment amount to creator -//! - `resolve_release_payment` - Allows assigned judge to release a payment -//! - `resolve_cancel_payment` - Allows assigned judge to cancel a payment -//! - `request_refund` - Allows the creator of the payment to trigger cancel -//! with a buffer time. -//! - `claim_refund` - Allows the creator to claim payment refund after buffer -//! time -//! - `dispute_refund` - Allows the recipient to dispute the payment request of -//! sender -//! - `request_payment` - Create a payment that can be completed by the sender -//! using the `accept_and_pay` extrinsic. -//! - `accept_and_pay` - Allows the sender to fulfill a payment request created -//! by a recipient - -//! Types -//! -//! The `PaymentDetail` struct stores information about the payment/escrow. A -//! "payment" on Setheum Pay is similar to an escrow, it is used to guarantee -//! proof of funds and can be released once an agreed upon condition has reached -//! between the payment creator and recipient. The payment lifecycle is tracked -//! using the state field. - -//! The `PaymentState` enum tracks the possible states that a payment can be in. -//! When a payment is 'completed' or 'cancelled' it is removed from storage and -//! hence not tracked by a state. -#![cfg_attr(not(feature = "std"), no_std)] -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -pub mod types; -pub mod weights; - -#[frame_support::pallet] -pub mod pallet { - pub use crate::{ - types::{DisputeResolver, FeeHandler, PaymentDetail, PaymentHandler, PaymentState, ScheduledTask, Task}, - weights::WeightInfo, - }; - use frame_support::{ - dispatch::DispatchResultWithPostInfo, fail, pallet_prelude::*, require_transactional, - storage::bounded_btree_map::BoundedBTreeMap, traits::tokens::BalanceStatus, - }; - use frame_system::pallet_prelude::*; - use orml_traits::{MultiCurrency, MultiReservableCurrency}; - use sp_runtime::{ - traits::{CheckedAdd, Saturating}, - Percent, - }; - use sp_std::vec::Vec; - - pub type BalanceOf = <::Asset as MultiCurrency<::AccountId>>::Balance; - pub type AssetIdOf = <::Asset as MultiCurrency<::AccountId>>::CurrencyId; - pub type BoundedDataOf = BoundedVec::MaxRemarkLength>; - /// type of ScheduledTask used by the pallet - pub type ScheduledTaskOf = ScheduledTask>; - /// list of ScheduledTasks, stored as a BoundedBTreeMap - pub type ScheduledTaskList = BoundedBTreeMap< - ( - ::AccountId, - ::AccountId, - ), - ScheduledTaskOf, - ::MaxRemarkLength, - >; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// Because this pallet emits events, it depends on the runtime's - /// definition of an event. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// the type of assets this pallet can hold in payment - type Asset: MultiReservableCurrency; - /// Dispute resolution account - type DisputeResolver: DisputeResolver; - /// Fee handler trait - type FeeHandler: FeeHandler; - /// Incentive percentage - amount withheld from sender - #[pallet::constant] - type IncentivePercentage: Get; - /// Maximum permitted size of `Remark` - #[pallet::constant] - type MaxRemarkLength: Get; - /// Buffer period - number of blocks to wait before user can claim - /// canceled payment - #[pallet::constant] - type CancelBufferBlockLength: Get>; - /// Buffer period - number of blocks to wait before user can claim - /// canceled payment - #[pallet::constant] - type MaxScheduledTaskListLength: Get; - //// Type representing the weight of this pallet - type WeightInfo: WeightInfo; - } - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn payment)] - /// Payments created by a user, this method of storageDoubleMap is chosen - /// since there is no usecase for listing payments by provider/currency. The - /// payment will only be referenced by the creator in any transaction of - /// interest. The storage map keys are the creator and the recipient, this - /// also ensures that for any (sender,recipient) combo, only a single - /// payment is active. The history of payment is not stored. - pub(super) type Payment = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, // payment creator - Blake2_128Concat, - T::AccountId, // payment recipient - PaymentDetail, - >; - - #[pallet::storage] - #[pallet::getter(fn tasks)] - /// Store the list of tasks to be executed in the on_idle function - pub(super) type ScheduledTasks = StorageValue<_, ScheduledTaskList, ValueQuery>; - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// A new payment has been created - PaymentCreated { - from: T::AccountId, - asset: AssetIdOf, - amount: BalanceOf, - remark: Option>, - }, - /// Payment amount released to the recipient - PaymentReleased { from: T::AccountId, to: T::AccountId }, - /// Payment has been cancelled by the creator - PaymentCancelled { from: T::AccountId, to: T::AccountId }, - /// A payment that NeedsReview has been resolved by Judge - PaymentResolved { - from: T::AccountId, - to: T::AccountId, - recipient_share: Percent, - }, - /// the payment creator has created a refund request - PaymentCreatorRequestedRefund { - from: T::AccountId, - to: T::AccountId, - expiry: BlockNumberFor, - }, - /// the refund request from creator was disputed by recipient - PaymentRefundDisputed { from: T::AccountId, to: T::AccountId }, - /// Payment request was created by recipient - PaymentRequestCreated { from: T::AccountId, to: T::AccountId }, - /// Payment request was completed by sender - PaymentRequestCompleted { from: T::AccountId, to: T::AccountId }, - } - - #[pallet::error] - pub enum Error { - /// The selected payment does not exist - InvalidPayment, - /// The selected payment cannot be released - PaymentAlreadyReleased, - /// The selected payment already exists and is in process - PaymentAlreadyInProcess, - /// Action permitted only for whitelisted users - InvalidAction, - /// Payment is in review state and cannot be modified - PaymentNeedsReview, - /// Unexpeted math error - MathError, - /// Payment request has not been created - RefundNotRequested, - /// Dispute period has not passed - DisputePeriodNotPassed, - /// The automatic cancelation queue cannot accept - RefundQueueFull, - } - - #[pallet::hooks] - impl Hooks> for Pallet { - /// Hook that execute when there is leftover space in a block - /// This function will look for any pending scheduled tasks that can - /// be executed and will process them. - fn on_idle(now: BlockNumberFor, remaining_weight: Weight) -> Weight { - const MAX_TASKS_TO_PROCESS: usize = 5; - // used to read the task list - let mut used_weight = T::WeightInfo::remove_task(); - let cancel_weight = T::WeightInfo::cancel(); - - // calculate count of tasks that can be processed with remaining weight - let possible_task_count: usize = remaining_weight - .saturating_sub(used_weight) - .saturating_div(cancel_weight.ref_time()) - .ref_time() - .try_into() - .unwrap_or(MAX_TASKS_TO_PROCESS); - - ScheduledTasks::::mutate(|tasks| { - let mut task_list: Vec<_> = tasks - .clone() - .into_iter() - .take(possible_task_count) - // leave out tasks in the future - .filter(|(_, ScheduledTask { when, task })| when <= &now && matches!(task, Task::Cancel)) - .collect(); - - // order by oldest task to process - task_list.sort_by(|(_, t), (_, x)| x.when.cmp(&t.when)); - - while !task_list.is_empty() && used_weight.all_lte(remaining_weight) { - if let Some((account_pair, _)) = task_list.pop() { - used_weight = used_weight.saturating_add(cancel_weight); - // remove the task form the tasks storage - tasks.remove(&account_pair); - - // process the cancel payment - if >::settle_payment( - &account_pair.0, - &account_pair.1, - Percent::from_percent(0), - ) - .is_err() - { - // log the payment refund failure - log::warn!( - target: "runtime::payments", - "Warning: Unable to process payment refund!" - ); - } else { - // emit the cancel event if the refund was successful - Self::deposit_event(Event::PaymentCancelled { - from: account_pair.0, - to: account_pair.1, - }); - } - } - } - }); - used_weight - } - } - - #[pallet::call] - impl Pallet { - /// This allows any user to create a new payment, that releases only to - /// specified recipient The only action is to store the details of this - /// payment in storage and reserve the specified amount. User also has - /// the option to add a remark, this remark can then be used to run - /// custom logic and trigger alternate payment flows. the specified - /// amount. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::pay(T::MaxRemarkLength::get()))] - pub fn pay( - origin: OriginFor, - recipient: T::AccountId, - asset: AssetIdOf, - #[pallet::compact] amount: BalanceOf, - remark: Option>, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - // create PaymentDetail and add to storage - let payment_detail = >::create_payment( - &who, - &recipient, - asset, - amount, - PaymentState::Created, - T::IncentivePercentage::get(), - remark.as_ref().map(|x| x.as_slice()), - )?; - // reserve funds for payment - >::reserve_payment_amount(&who, &recipient, payment_detail)?; - // emit paymentcreated event - Self::deposit_event(Event::PaymentCreated { - from: who, - asset, - amount, - remark, - }); - Ok(().into()) - } - - /// Release any created payment, this will transfer the reserved amount - /// from the creator of the payment to the assigned recipient - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::release())] - pub fn release(origin: OriginFor, to: T::AccountId) -> DispatchResultWithPostInfo { - let from = ensure_signed(origin)?; - - // ensure the payment is in Created state - let payment = Payment::::get(&from, &to).ok_or(Error::::InvalidPayment)?; - ensure!(payment.state == PaymentState::Created, Error::::InvalidAction); - - // release is a settle_payment with 100% recipient_share - >::settle_payment(&from, &to, Percent::from_percent(100))?; - - Self::deposit_event(Event::PaymentReleased { from, to }); - Ok(().into()) - } - - /// Cancel a payment in created state, this will release the reserved - /// back to creator of the payment. This extrinsic can only be called by - /// the recipient of the payment - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::cancel())] - pub fn cancel(origin: OriginFor, creator: T::AccountId) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - if let Some(payment) = Payment::::get(&creator, &who) { - match payment.state { - // call settle payment with recipient_share=0, this refunds the sender - PaymentState::Created => { - >::settle_payment(&creator, &who, Percent::from_percent(0))?; - Self::deposit_event(Event::PaymentCancelled { from: creator, to: who }); - } - // if the payment is in state PaymentRequested, remove from storage - PaymentState::PaymentRequested => Payment::::remove(&creator, &who), - _ => fail!(Error::::InvalidAction), - } - } - Ok(().into()) - } - - /// This extrinsic is used to resolve disputes between the creator and - /// recipient of the payment. - /// This extrinsic allows the assigned judge to - /// cancel/release/partial_release the payment. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::resolve_payment())] - pub fn resolve_payment( - origin: OriginFor, - from: T::AccountId, - recipient: T::AccountId, - recipient_share: Percent, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - let account_pair = (from, recipient); - // ensure the caller is the assigned resolver - if let Some(payment) = Payment::::get(&account_pair.0, &account_pair.1) { - ensure!(who == payment.resolver_account, Error::::InvalidAction); - ensure!( - payment.state != PaymentState::PaymentRequested, - Error::::InvalidAction - ); - if matches!(payment.state, PaymentState::RefundRequested { .. }) { - ScheduledTasks::::mutate(|tasks| { - tasks.remove(&account_pair); - }) - } - } - // try to update the payment to new state - >::settle_payment(&account_pair.0, &account_pair.1, recipient_share)?; - Self::deposit_event(Event::PaymentResolved { - from: account_pair.0, - to: account_pair.1, - recipient_share, - }); - Ok(().into()) - } - - /// Allow the creator of a payment to initiate a refund that will return - /// the funds after a configured amount of time that the reveiver has to - /// react and oppose the request - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::request_refund())] - pub fn request_refund(origin: OriginFor, recipient: T::AccountId) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - Payment::::try_mutate(who.clone(), recipient.clone(), |maybe_payment| -> DispatchResult { - // ensure the payment exists - let payment = maybe_payment.as_mut().ok_or(Error::::InvalidPayment)?; - // refunds only possible for payments in created state - ensure!(payment.state == PaymentState::Created, Error::::InvalidAction); - - // set the payment to requested refund - let current_block = frame_system::Pallet::::block_number(); - let cancel_block = current_block - .checked_add(&T::CancelBufferBlockLength::get()) - .ok_or(Error::::MathError)?; - - ScheduledTasks::::try_mutate(|task_list| -> DispatchResult { - task_list - .try_insert( - (who.clone(), recipient.clone()), - ScheduledTask { - task: Task::Cancel, - when: cancel_block, - }, - ) - .map_err(|_| Error::::RefundQueueFull)?; - Ok(()) - })?; - - payment.state = PaymentState::RefundRequested { cancel_block }; - - Self::deposit_event(Event::PaymentCreatorRequestedRefund { - from: who, - to: recipient, - expiry: cancel_block, - }); - - Ok(()) - })?; - - Ok(().into()) - } - - /// Allow payment recipient to dispute the refund request from the - /// payment creator This does not cancel the request, instead sends the - /// payment to a NeedsReview state The assigned resolver account can - /// then change the state of the payment after review. - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::dispute_refund())] - pub fn dispute_refund(origin: OriginFor, creator: T::AccountId) -> DispatchResultWithPostInfo { - use PaymentState::*; - let who = ensure_signed(origin)?; - - Payment::::try_mutate( - creator.clone(), - who.clone(), // should be called by the payment recipient - |maybe_payment| -> DispatchResult { - // ensure the payment exists - let payment = maybe_payment.as_mut().ok_or(Error::::InvalidPayment)?; - // ensure the payment is in Requested Refund state - match payment.state { - RefundRequested { cancel_block } => { - ensure!( - cancel_block > frame_system::Pallet::::block_number(), - Error::::InvalidAction - ); - - payment.state = PaymentState::NeedsReview; - - // remove the payment from scheduled tasks - ScheduledTasks::::try_mutate(|task_list| -> DispatchResult { - task_list - .remove(&(creator.clone(), who.clone())) - .ok_or(Error::::InvalidAction)?; - Ok(()) - })?; - - Self::deposit_event(Event::PaymentRefundDisputed { from: creator, to: who }); - } - _ => fail!(Error::::InvalidAction), - } - - Ok(()) - }, - )?; - - Ok(().into()) - } - - // Creates a new payment with the given details. This can be called by the - // recipient of the payment to create a payment and then completed by the sender - // using the `accept_and_pay` extrinsic. The payment will be in - // PaymentRequested State and can only be modified by the `accept_and_pay` - // extrinsic. - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::request_payment())] - pub fn request_payment( - origin: OriginFor, - from: T::AccountId, - asset: AssetIdOf, - #[pallet::compact] amount: BalanceOf, - ) -> DispatchResultWithPostInfo { - let to = ensure_signed(origin)?; - - // create PaymentDetail and add to storage - >::create_payment( - &from, - &to, - asset, - amount, - PaymentState::PaymentRequested, - Percent::from_percent(0), - None, - )?; - - Self::deposit_event(Event::PaymentRequestCreated { from, to }); - - Ok(().into()) - } - - // This extrinsic allows the sender to fulfill a payment request created by a - // recipient. The amount will be transferred to the recipient and payment - // removed from storage - #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::accept_and_pay())] - pub fn accept_and_pay(origin: OriginFor, to: T::AccountId) -> DispatchResultWithPostInfo { - let from = ensure_signed(origin)?; - - let payment = Payment::::get(&from, &to).ok_or(Error::::InvalidPayment)?; - - ensure!( - payment.state == PaymentState::PaymentRequested, - Error::::InvalidAction - ); - - // reserve all the fees from the sender - >::reserve_payment_amount(&from, &to, payment)?; - - // release the payment and delete the payment from storage - >::settle_payment(&from, &to, Percent::from_percent(100))?; - - Self::deposit_event(Event::PaymentRequestCompleted { from, to }); - - Ok(().into()) - } - } - - impl PaymentHandler for Pallet { - /// The function will create a new payment. The fee and incentive - /// amounts will be calculated and the `PaymentDetail` will be added to - /// storage. - #[require_transactional] - fn create_payment( - from: &T::AccountId, - recipient: &T::AccountId, - asset: AssetIdOf, - amount: BalanceOf, - payment_state: PaymentState, - incentive_percentage: Percent, - remark: Option<&[u8]>, - ) -> Result, sp_runtime::DispatchError> { - Payment::::try_mutate( - from, - recipient, - |maybe_payment| -> Result, sp_runtime::DispatchError> { - // only payment requests can be overwritten - if let Some(payment) = maybe_payment { - ensure!( - payment.state == PaymentState::PaymentRequested, - Error::::PaymentAlreadyInProcess - ); - } - - // Calculate incentive amount - this is to insentivise the user to release - // the funds once a transaction has been completed - let incentive_amount = incentive_percentage.mul_floor(amount); - - let mut new_payment = PaymentDetail { - asset, - amount, - incentive_amount, - state: payment_state, - resolver_account: T::DisputeResolver::get_resolver_account(), - fee_detail: None, - }; - - // Calculate fee amount - this will be implemented based on the custom - // implementation of the fee provider - let (fee_recipient, fee_percent) = T::FeeHandler::apply_fees(from, recipient, &new_payment, remark); - let fee_amount = fee_percent.mul_floor(amount); - new_payment.fee_detail = Some((fee_recipient, fee_amount)); - - *maybe_payment = Some(new_payment.clone()); - - Ok(new_payment) - }, - ) - } - - /// The function will reserve the fees+transfer amount from the `from` - /// account. After reserving the payment.amount will be transferred to - /// the recipient but will stay in Reserve state. - #[require_transactional] - fn reserve_payment_amount(from: &T::AccountId, to: &T::AccountId, payment: PaymentDetail) -> DispatchResult { - let fee_amount = payment.fee_detail.map(|(_, f)| f).unwrap_or_else(|| 0u32.into()); - - let total_fee_amount = payment.incentive_amount.saturating_add(fee_amount); - let total_amount = total_fee_amount.saturating_add(payment.amount); - - // reserve the total amount from payment creator - T::Asset::reserve(payment.asset, from, total_amount)?; - // transfer payment amount to recipient -- keeping reserve status - T::Asset::repatriate_reserved(payment.asset, from, to, payment.amount, BalanceStatus::Reserved)?; - Ok(()) - } - - /// This function allows the caller to settle the payment by specifying - /// a recipient_share this will unreserve the fee+incentive to sender - /// and unreserve transferred amount to recipient if the settlement is a - /// release (ie recipient_share=100), the fee is transferred to - /// fee_recipient For cancelling a payment, recipient_share = 0 - /// For releasing a payment, recipient_share = 100 - /// In other cases, the custom recipient_share can be specified - fn settle_payment(from: &T::AccountId, to: &T::AccountId, recipient_share: Percent) -> DispatchResult { - Payment::::try_mutate(from, to, |maybe_payment| -> DispatchResult { - let payment = maybe_payment.take().ok_or(Error::::InvalidPayment)?; - - // unreserve the incentive amount and fees from the owner account - match payment.fee_detail { - Some((fee_recipient, fee_amount)) => { - T::Asset::unreserve(payment.asset, from, payment.incentive_amount.saturating_add(fee_amount)); - // transfer fee to marketplace if operation is not cancel - if recipient_share != Percent::zero() { - T::Asset::transfer( - payment.asset, - from, // fee is paid by payment creator - &fee_recipient, // account of fee recipient - fee_amount, // amount of fee - )?; - } - } - None => { - T::Asset::unreserve(payment.asset, from, payment.incentive_amount); - } - }; - - // Unreserve the transfer amount - T::Asset::unreserve(payment.asset, to, payment.amount); - - let amount_to_recipient = recipient_share.mul_floor(payment.amount); - let amount_to_sender = payment.amount.saturating_sub(amount_to_recipient); - // send share to recipient - T::Asset::transfer(payment.asset, to, from, amount_to_sender)?; - - Ok(()) - })?; - Ok(()) - } - - fn get_payment_details(from: &T::AccountId, to: &T::AccountId) -> Option> { - Payment::::get(from, to) - } - } -} diff --git a/blockchain/modules/setheum-pay/src/mock.rs b/blockchain/modules/setheum-pay/src/mock.rs deleted file mode 100644 index 20a8ae4e..00000000 --- a/blockchain/modules/setheum-pay/src/mock.rs +++ /dev/null @@ -1,184 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate as pay; -use crate::PaymentDetail; -use frame_support::{ - derive_impl, - dispatch::DispatchClass, - parameter_types, - traits::{ConstU32, Contains, Hooks, OnFinalize}, -}; -use frame_system as system; -use orml_traits::parameter_type_with_key; -use sp_runtime::{traits::IdentityLookup, BuildStorage, Percent}; - -type Block = frame_system::mocking::MockBlock; -pub type Balance = u128; - -pub type AccountId = u8; -pub const PAYMENT_CREATOR: AccountId = 10; -pub const PAYMENT_RECIPENT: AccountId = 11; -pub const PAYMENT_CREATOR_TWO: AccountId = 30; -pub const PAYMENT_RECIPENT_TWO: AccountId = 31; -pub const CURRENCY_ID: u32 = 1; -pub const RESOLVER_ACCOUNT: AccountId = 12; -pub const FEE_RECIPIENT_ACCOUNT: AccountId = 20; -pub const PAYMENT_RECIPENT_FEE_CHARGED: AccountId = 21; -pub const INCENTIVE_PERCENTAGE: u8 = 10; -pub const MARKETPLACE_FEE_PERCENTAGE: u8 = 10; -pub const CANCEL_BLOCK_BUFFER: u64 = 600; - -frame_support::construct_runtime!( - pub enum Test { - System: frame_system, - Tokens: orml_tokens, - Payment: pay, - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] -impl system::Config for Test { - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; -} - -parameter_type_with_key! { - pub ExistentialDeposits: |_currency_id: u32| -> Balance { - 0u128 - }; -} -parameter_types! { - pub const MaxLocks: u32 = 50; -} -pub type ReserveIdentifier = [u8; 8]; - -pub struct MockDustRemovalWhitelist; -impl Contains for MockDustRemovalWhitelist { - fn contains(_a: &AccountId) -> bool { - false - } -} - -impl orml_tokens::Config for Test { - type Amount = i64; - type Balance = Balance; - type CurrencyId = u32; - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposits = ExistentialDeposits; - type CurrencyHooks = (); - type WeightInfo = (); - type MaxLocks = MaxLocks; - type DustRemovalWhitelist = MockDustRemovalWhitelist; - type MaxReserves = ConstU32<2>; - type ReserveIdentifier = ReserveIdentifier; -} - -pub struct MockDisputeResolver; -impl crate::types::DisputeResolver for MockDisputeResolver { - fn get_resolver_account() -> AccountId { - RESOLVER_ACCOUNT - } -} - -pub struct MockFeeHandler; -impl crate::types::FeeHandler for MockFeeHandler { - fn apply_fees( - _from: &AccountId, - to: &AccountId, - _detail: &PaymentDetail, - _remark: Option<&[u8]>, - ) -> (AccountId, Percent) { - match to { - &PAYMENT_RECIPENT_FEE_CHARGED => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(MARKETPLACE_FEE_PERCENTAGE)), - _ => (FEE_RECIPIENT_ACCOUNT, Percent::from_percent(0)), - } - } -} - -parameter_types! { - pub const IncentivePercentage: Percent = Percent::from_percent(INCENTIVE_PERCENTAGE); - pub const MaxRemarkLength: u32 = 50; - pub const CancelBufferBlockLength: u64 = CANCEL_BLOCK_BUFFER; - pub const MaxScheduledTaskListLength : u32 = 5; -} - -impl pay::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Asset = Tokens; - type DisputeResolver = MockDisputeResolver; - type IncentivePercentage = IncentivePercentage; - type FeeHandler = MockFeeHandler; - type MaxRemarkLength = MaxRemarkLength; - type CancelBufferBlockLength = CancelBufferBlockLength; - type MaxScheduledTaskListLength = MaxScheduledTaskListLength; - type WeightInfo = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - - orml_tokens::GenesisConfig:: { - balances: vec![ - (PAYMENT_CREATOR, CURRENCY_ID, 100), - (PAYMENT_CREATOR_TWO, CURRENCY_ID, 100), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext: sp_io::TestExternalities = t.into(); - // need to set block number to 1 to test events - ext.execute_with(|| System::set_block_number(1)); - ext -} - -pub fn run_n_blocks(n: u64) -> u64 { - const IDLE_WEIGHT: u64 = 10_000_000_000; - const BUSY_WEIGHT: u64 = IDLE_WEIGHT / 1000; - - let start_block = System::block_number(); - - for block_number in (0..=n).map(|n| n + start_block) { - System::set_block_number(block_number); - - // Odd blocks gets busy - let idle_weight = if block_number % 2 == 0 { - IDLE_WEIGHT - } else { - BUSY_WEIGHT - }; - // ensure the on_idle is executed - >::register_extra_weight_unchecked( - Payment::on_idle(block_number, frame_support::weights::Weight::from_parts(idle_weight, 0)), - DispatchClass::Mandatory, - ); - - as OnFinalize>::on_finalize(block_number); - } - System::block_number() -} diff --git a/blockchain/modules/setheum-pay/src/tests.rs b/blockchain/modules/setheum-pay/src/tests.rs deleted file mode 100644 index 97f54116..00000000 --- a/blockchain/modules/setheum-pay/src/tests.rs +++ /dev/null @@ -1,1470 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate::{ - mock::*, - types::{PaymentDetail, PaymentState}, - weights::WeightInfo, - Payment as PaymentStore, PaymentHandler, ScheduledTask, ScheduledTasks, Task, -}; -use frame_support::{assert_noop, assert_ok, storage::with_transaction, traits::OnIdle, weights::Weight}; -use orml_traits::MultiCurrency; -use sp_runtime::{Percent, TransactionOutcome}; - -type Error = crate::Error; - -fn last_event() -> RuntimeEvent { - System::events().pop().expect("Event expected").event -} - -#[test] -fn test_pay_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - - // the payment amount should not be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - assert_eq!( - last_event(), - crate::Event::::PaymentCreated { - from: PAYMENT_CREATOR, - asset: CURRENCY_ID, - amount: payment_amount, - remark: None - } - .into() - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - // the payment amount should be reserved correctly - // the amount + incentive should be removed from the sender account - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_incentive_amount - ); - // the incentive amount should be reserved in the sender account - assert_eq!( - Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - // the transferred amount should be reserved in the recipent account - assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // the payment should not be overwritten - assert_noop!( - Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - ), - crate::Error::::PaymentAlreadyInProcess - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: 2, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - }); -} - -#[test] -fn test_cancel_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - // the payment amount should be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_incentive_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // cancel should succeed when caller is the recipent - assert_ok!(Payment::cancel( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR - )); - assert_eq!( - last_event(), - crate::Event::::PaymentCancelled { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT - } - .into() - ); - // the payment amount should be released back to creator - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be released from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - }); -} - -#[test] -fn test_release_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - // the payment amount should be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_incentive_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should succeed for valid payment - assert_ok!(Payment::release( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - assert_eq!( - last_event(), - crate::Event::::PaymentReleased { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT - } - .into() - ); - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // should be deleted from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - - // should be able to create another payment since previous is released - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - // the payment amount should be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - (payment_amount * 2) - expected_incentive_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - }); -} - -#[test] -fn test_resolve_payment_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - // should fail for non whitelisted caller - assert_noop!( - Payment::resolve_payment( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_CREATOR, - PAYMENT_RECIPENT, - Percent::from_percent(100) - ), - Error::InvalidAction - ); - - // should be able to release a payment - assert_ok!(Payment::resolve_payment( - RuntimeOrigin::signed(RESOLVER_ACCOUNT), - PAYMENT_CREATOR, - PAYMENT_RECIPENT, - Percent::from_percent(100) - )); - assert_eq!( - last_event(), - crate::Event::::PaymentResolved { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - recipient_share: Percent::from_percent(100) - } - .into() - ); - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // should be removed from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - // should be able to cancel a payment - assert_ok!(Payment::resolve_payment( - RuntimeOrigin::signed(RESOLVER_ACCOUNT), - PAYMENT_CREATOR, - PAYMENT_RECIPENT, - Percent::from_percent(0) - )); - assert_eq!( - last_event(), - crate::Event::::PaymentResolved { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - recipient_share: Percent::from_percent(0) - } - .into() - ); - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // should be released from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - }); -} - -#[test] -fn test_charging_fee_payment_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED, - CURRENCY_ID, - payment_amount, - None - )); - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - // the payment amount should be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_fee_amount - expected_incentive_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); - - // should succeed for valid payment - assert_ok!(Payment::release( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED - )); - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_fee_amount - ); - assert_eq!( - Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_fee_amount - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), - payment_amount - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), - expected_fee_amount - ); - }); -} - -#[test] -fn test_charging_fee_payment_works_when_canceled() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED, - CURRENCY_ID, - payment_amount, - None - )); - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - // the payment amount should be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_fee_amount - expected_incentive_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); - - // should succeed for valid payment - assert_ok!(Payment::cancel( - RuntimeOrigin::signed(PAYMENT_RECIPENT_FEE_CHARGED), - PAYMENT_CREATOR - )); - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), 0); - }); -} - -#[test] -fn test_pay_with_remark_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - - // should be able to create a payment with available balance - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - Some(vec![1u8; 10].try_into().unwrap()) - )); - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - // the payment amount should be reserved correctly - // the amount + incentive should be removed from the sender account - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_incentive_amount - ); - // the incentive amount should be reserved in the sender account - assert_eq!( - Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - // the transferred amount should be reserved in the recipent account - assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // the payment should not be overwritten - assert_noop!( - Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - ), - crate::Error::::PaymentAlreadyInProcess - ); - - assert_eq!( - last_event(), - crate::Event::::PaymentCreated { - from: PAYMENT_CREATOR, - asset: CURRENCY_ID, - amount: payment_amount, - remark: Some(vec![1u8; 10].try_into().unwrap()) - } - .into() - ); - }); -} - -#[test] -fn test_do_not_overwrite_logic_works() { - new_test_ext().execute_with(|| { - let payment_amount = 40; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_noop!( - Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - ), - crate::Error::::PaymentAlreadyInProcess - ); - - // set payment state to NeedsReview - PaymentStore::::insert( - PAYMENT_CREATOR, - PAYMENT_RECIPENT, - PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::NeedsReview, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }, - ); - - // the payment should not be overwritten - assert_noop!( - Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - ), - crate::Error::::PaymentAlreadyInProcess - ); - }); -} - -#[test] -fn test_request_refund() { - new_test_ext().execute_with(|| { - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - - // do not overwrite payment - assert_noop!( - Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - ), - crate::Error::::PaymentAlreadyInProcess - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::RefundRequested { - cancel_block: expected_cancel_block - }, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - - assert_eq!( - last_event(), - crate::Event::::PaymentCreatorRequestedRefund { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - expiry: expected_cancel_block - } - .into() - ); - }); -} - -#[test] -fn test_dispute_refund() { - new_test_ext().execute_with(|| { - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - // cannot dispute if refund is not requested - assert_noop!( - Payment::dispute_refund(RuntimeOrigin::signed(PAYMENT_RECIPENT), PAYMENT_CREATOR), - Error::InvalidAction - ); - // creator requests a refund - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - // ensure the request is added to the refund queue - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!( - scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), - &ScheduledTask { - task: Task::Cancel, - when: expected_cancel_block - } - ); - - // recipient disputes the refund request - assert_ok!(Payment::dispute_refund( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR - )); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::NeedsReview, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - - assert_eq!( - last_event(), - crate::Event::::PaymentRefundDisputed { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - } - .into() - ); - - // ensure the request is removed from the refund queue - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); - }); -} - -#[test] -fn test_request_payment() { - new_test_ext().execute_with(|| { - let payment_amount = 20; - let expected_incentive_amount = 0; - - assert_ok!(Payment::request_payment( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR, - CURRENCY_ID, - payment_amount, - )); - - assert_noop!( - Payment::request_refund(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT), - crate::Error::::InvalidAction - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::PaymentRequested, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - - assert_eq!( - last_event(), - crate::Event::::PaymentRequestCreated { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - } - .into() - ); - }); -} - -#[test] -fn test_requested_payment_cannot_be_released() { - new_test_ext().execute_with(|| { - let payment_amount = 20; - - assert_ok!(Payment::request_payment( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR, - CURRENCY_ID, - payment_amount, - )); - - // requested payment cannot be released - assert_noop!( - Payment::release(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT), - Error::InvalidAction - ); - }); -} - -#[test] -fn test_requested_payment_can_be_cancelled_by_requestor() { - new_test_ext().execute_with(|| { - let payment_amount = 20; - - assert_ok!(Payment::request_payment( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR, - CURRENCY_ID, - payment_amount, - )); - - assert_ok!(Payment::cancel( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR - )); - - // the request should be removed from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - }); -} - -#[test] -fn test_accept_and_pay() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = 0; - - assert_ok!(Payment::request_payment( - RuntimeOrigin::signed(PAYMENT_RECIPENT), - PAYMENT_CREATOR, - CURRENCY_ID, - payment_amount, - )); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::PaymentRequested, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - - assert_ok!(Payment::accept_and_pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - )); - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // should be deleted from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - - assert_eq!( - last_event(), - crate::Event::::PaymentRequestCompleted { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT, - } - .into() - ); - }); -} - -#[test] -fn test_accept_and_pay_should_fail_for_non_payment_requested() { - new_test_ext().execute_with(|| { - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - 20, - None - )); - - assert_noop!( - Payment::accept_and_pay(RuntimeOrigin::signed(PAYMENT_CREATOR), PAYMENT_RECIPENT,), - Error::InvalidAction - ); - }); -} - -#[test] -fn test_accept_and_pay_should_charge_fee_correctly() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = 0; - let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; - - assert_ok!(Payment::request_payment( - RuntimeOrigin::signed(PAYMENT_RECIPENT_FEE_CHARGED), - PAYMENT_CREATOR, - CURRENCY_ID, - payment_amount, - )); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::PaymentRequested, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - - assert_ok!(Payment::accept_and_pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED, - )); - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_fee_amount - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), - payment_amount - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), - expected_fee_amount - ); - - // should be deleted from storage - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - None - ); - - assert_eq!( - last_event(), - crate::Event::::PaymentRequestCompleted { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT_FEE_CHARGED, - } - .into() - ); - }); -} - -#[test] -fn test_create_payment_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_fee_amount = 0; - - // the payment amount should not be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::create_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - PaymentState::Created, - Percent::from_percent(INCENTIVE_PERCENTAGE), - Some(&[1u8; 10]), - ) - }))); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - - // the payment should not be overwritten - assert_noop!( - with_transaction(|| TransactionOutcome::Commit({ - >::create_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - PaymentState::Created, - Percent::from_percent(INCENTIVE_PERCENTAGE), - Some(&[1u8; 10]), - ) - })), - Error::PaymentAlreadyInProcess - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - }); -} - -#[test] -fn test_reserve_payment_amount_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - let expected_fee_amount = 0; - - // the payment amount should not be reserved - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::create_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - PaymentState::Created, - Percent::from_percent(INCENTIVE_PERCENTAGE), - Some(&[1u8; 10]), - ) - }))); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::reserve_payment_amount( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).unwrap(), - ) - }))); - // the payment amount should be reserved correctly - // the amount + incentive should be removed from the sender account - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - expected_incentive_amount - ); - // the incentive amount should be reserved in the sender account - assert_eq!( - Tokens::total_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - // the transferred amount should be reserved in the recipent account - assert_eq!(Tokens::total_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // the payment should not be overwritten - assert_noop!( - with_transaction(|| TransactionOutcome::Commit({ - >::create_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - PaymentState::Created, - Percent::from_percent(INCENTIVE_PERCENTAGE), - Some(&[1u8; 10]), - ) - })), - Error::PaymentAlreadyInProcess - ); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::Created, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, expected_fee_amount)), - }) - ); - }); -} - -#[test] -fn test_settle_payment_works_for_cancel() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - - // the payment amount should not be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::settle_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - Percent::from_percent(0), - ) - }))); - - // the payment amount should be released back to creator - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be released from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - }); -} - -#[test] -fn test_settle_payment_works_for_release() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - - // the payment amount should not be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::settle_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT, - Percent::from_percent(100), - ) - }))); - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - payment_amount - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), payment_amount); - - // should be deleted from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - }); -} - -#[test] -fn test_settle_payment_works_for_70_30() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 10; - let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; - - // the payment amount should not be reserved - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::settle_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT_FEE_CHARGED, - Percent::from_percent(70), - ) - }))); - - let expected_amount_for_creator = creator_initial_balance - payment_amount - expected_fee_amount - + (Percent::from_percent(30) * payment_amount); - let expected_amount_for_recipient = Percent::from_percent(70) * payment_amount; - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - expected_amount_for_creator - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), - expected_amount_for_recipient - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), - expected_fee_amount - ); - - // should be deleted from storage - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - None - ); - }); -} - -#[test] -fn test_settle_payment_works_for_50_50() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 10; - let expected_fee_amount = payment_amount / MARKETPLACE_FEE_PERCENTAGE as u128; - - // the payment amount should not be reserved - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), 0); - - // should be able to create a payment with available balance within a - // transaction - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT_FEE_CHARGED, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(with_transaction(|| TransactionOutcome::Commit({ - >::settle_payment( - &PAYMENT_CREATOR, - &PAYMENT_RECIPENT_FEE_CHARGED, - Percent::from_percent(50), - ) - }))); - - let expected_amount_for_creator = creator_initial_balance - payment_amount - expected_fee_amount - + (Percent::from_percent(50) * payment_amount); - let expected_amount_for_recipient = Percent::from_percent(50) * payment_amount; - - // the payment amount should be transferred - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - expected_amount_for_creator - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_FEE_CHARGED), - expected_amount_for_recipient - ); - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &FEE_RECIPIENT_ACCOUNT), - expected_fee_amount - ); - - // should be deleted from storage - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT_FEE_CHARGED), - None - ); - }); -} - -#[test] -fn test_automatic_refund_works() { - new_test_ext().execute_with(|| { - let creator_initial_balance = 100; - let payment_amount = 20; - let expected_incentive_amount = payment_amount / INCENTIVE_PERCENTAGE as u128; - const CANCEL_PERIOD: u64 = 600; - const CANCEL_BLOCK: u64 = CANCEL_PERIOD + 1; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), - Some(PaymentDetail { - asset: CURRENCY_ID, - amount: payment_amount, - incentive_amount: expected_incentive_amount, - state: PaymentState::RefundRequested { - cancel_block: CANCEL_BLOCK - }, - resolver_account: RESOLVER_ACCOUNT, - fee_detail: Some((FEE_RECIPIENT_ACCOUNT, 0)), - }) - ); - - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!( - scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), - &ScheduledTask { - task: Task::Cancel, - when: CANCEL_BLOCK - } - ); - - // run to one block before cancel and make sure data is same - assert_eq!(run_n_blocks(CANCEL_PERIOD - 1), 600); - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!( - scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), - &ScheduledTask { - task: Task::Cancel, - when: CANCEL_BLOCK - } - ); - - // run to after cancel block but odd blocks are busy - assert_eq!(run_n_blocks(1), 601); - // the payment is still not processed since the block was busy - assert!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).is_some()); - - // next block has spare weight to process the payment - assert_eq!(run_n_blocks(1), 602); - // the payment should be removed from storage - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - - // the scheduled storage should be cleared - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); - - // test that the refund happened correctly - assert_eq!( - last_event(), - crate::Event::::PaymentCancelled { - from: PAYMENT_CREATOR, - to: PAYMENT_RECIPENT - } - .into() - ); - // the payment amount should be released back to creator - assert_eq!( - Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), - creator_initial_balance - ); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - }); -} - -#[test] -fn test_automatic_refund_works_for_multiple_payments() { - new_test_ext().execute_with(|| { - const CANCEL_PERIOD: u64 = 600; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - 20, - None - )); - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR_TWO), - PAYMENT_RECIPENT_TWO, - CURRENCY_ID, - 20, - None - )); - - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - run_n_blocks(1); - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR_TWO), - PAYMENT_RECIPENT_TWO - )); - - assert_eq!(run_n_blocks(CANCEL_PERIOD - 1), 601); - - // Odd block 601 was busy so we still haven't processed the first payment - assert_ok!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT).ok_or(())); - - // Even block 602 has enough room to process both pending payments - assert_eq!(run_n_blocks(1), 602); - assert_eq!(PaymentStore::::get(PAYMENT_CREATOR, PAYMENT_RECIPENT), None); - assert_eq!( - PaymentStore::::get(PAYMENT_CREATOR_TWO, PAYMENT_RECIPENT_TWO), - None - ); - - // the scheduled storage should be cleared - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!(scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)), None); - assert_eq!( - scheduled_tasks_list.get(&(PAYMENT_CREATOR_TWO, PAYMENT_RECIPENT_TWO)), - None - ); - - // test that the refund happened correctly - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR), 100); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT), 0); - - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_CREATOR_TWO), 100); - assert_eq!(Tokens::free_balance(CURRENCY_ID, &PAYMENT_RECIPENT_TWO), 0); - }); -} - -#[test] -fn on_idle_works() { - new_test_ext().execute_with(|| { - assert_eq!( - Payment::on_idle(System::block_number(), Weight::MAX), - <()>::remove_task() - ); - - let payment_amount = 20; - let expected_cancel_block = CANCEL_BLOCK_BUFFER + 1; - - assert_ok!(Payment::pay( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT, - CURRENCY_ID, - payment_amount, - None - )); - - // creator requests a refund - assert_ok!(Payment::request_refund( - RuntimeOrigin::signed(PAYMENT_CREATOR), - PAYMENT_RECIPENT - )); - // ensure the request is added to the refund queue - let scheduled_tasks_list = ScheduledTasks::::get(); - assert_eq!(scheduled_tasks_list.len(), 1); - assert_eq!( - scheduled_tasks_list.get(&(PAYMENT_CREATOR, PAYMENT_RECIPENT)).unwrap(), - &ScheduledTask { - task: Task::Cancel, - when: expected_cancel_block - } - ); - - assert_eq!(run_n_blocks(CANCEL_BLOCK_BUFFER - 1), 600); - assert_eq!( - Payment::on_idle(System::block_number(), Weight::MAX), - <()>::remove_task() - ); - - assert_eq!(run_n_blocks(1), 601); - assert_eq!( - Payment::on_idle(System::block_number(), Weight::MAX), - <()>::remove_task() + <()>::cancel() - ); - }); -} diff --git a/blockchain/modules/setheum-pay/src/types.rs b/blockchain/modules/setheum-pay/src/types.rs deleted file mode 100644 index 9f719338..00000000 --- a/blockchain/modules/setheum-pay/src/types.rs +++ /dev/null @@ -1,142 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#![allow(unused_qualifications)] -use crate::{pallet, AssetIdOf, BalanceOf}; -use frame_system::pallet_prelude::*; -use parity_scale_codec::{Decode, Encode, HasCompact, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{DispatchResult, Percent}; - -/// The PaymentDetail struct stores information about the payment/escrow -/// A "payment" in Setheum Pay is similar to an escrow, it is used to -/// guarantee proof of funds and can be released once an agreed upon condition -/// has reached between the payment creator and recipient. The payment lifecycle -/// is tracked using the state field. -#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound(T: pallet::Config))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct PaymentDetail { - /// type of asset used for payment - pub asset: AssetIdOf, - /// amount of asset used for payment - #[codec(compact)] - pub amount: BalanceOf, - /// incentive amount that is credited to creator for resolving - #[codec(compact)] - pub incentive_amount: BalanceOf, - /// enum to track payment lifecycle [Created, NeedsReview, RefundRequested, - /// Requested] - pub state: PaymentState, - /// account that can settle any disputes created in the payment - pub resolver_account: T::AccountId, - /// fee charged and recipient account details - pub fee_detail: Option<(T::AccountId, BalanceOf)>, -} - -/// The `PaymentState` enum tracks the possible states that a payment can be in. -/// When a payment is 'completed' or 'cancelled' it is removed from storage and -/// hence not tracked by a state. -#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound(T: pallet::Config))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum PaymentState { - /// Amounts have been reserved and waiting for release/cancel - Created, - /// A judge needs to review and release manually - NeedsReview, - /// The user has requested refund and will be processed by `BlockNumber` - RefundRequested { cancel_block: BlockNumberFor }, - /// The recipient of this transaction has created a request - PaymentRequested, -} - -/// trait that defines how to create/release payments for users -pub trait PaymentHandler { - /// Create a PaymentDetail from the given payment details - /// Calculate the fee amount and store PaymentDetail in storage - /// Possible reasons for failure include: - /// - Payment already exists and cannot be overwritten - fn create_payment( - from: &T::AccountId, - to: &T::AccountId, - asset: AssetIdOf, - amount: BalanceOf, - payment_state: PaymentState, - incentive_percentage: Percent, - remark: Option<&[u8]>, - ) -> Result, sp_runtime::DispatchError>; - - /// Attempt to reserve an amount of the given asset from the caller - /// If not possible then return Error. Possible reasons for failure include: - /// - User does not have enough balance. - fn reserve_payment_amount(from: &T::AccountId, to: &T::AccountId, payment: PaymentDetail) -> DispatchResult; - - // Settle a payment of `from` to `to`. To release a payment, the - // recipient_share=100, to cancel a payment recipient_share=0 - // Possible reasonse for failure include - /// - /// - The payment does not exist - /// - The unreserve operation fails - /// - The transfer operation fails - fn settle_payment(from: &T::AccountId, to: &T::AccountId, recipient_share: Percent) -> DispatchResult; - - /// Attempt to fetch the details of a payment from the given payment_id - /// Possible reasons for failure include: - /// - The payment does not exist - fn get_payment_details(from: &T::AccountId, to: &T::AccountId) -> Option>; -} - -/// DisputeResolver trait defines how to create/assign judges for solving -/// payment disputes -pub trait DisputeResolver { - /// Returns an `Account` - fn get_resolver_account() -> Account; -} - -/// Fee Handler trait that defines how to handle marketplace fees to every -/// payment/swap -pub trait FeeHandler { - /// Get the distribution of fees to marketplace participants - fn apply_fees( - from: &T::AccountId, - to: &T::AccountId, - detail: &PaymentDetail, - remark: Option<&[u8]>, - ) -> (T::AccountId, Percent); -} - -/// Types of Tasks that can be scheduled in the pallet -#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -pub enum Task { - // payment `from` to `to` has to be cancelled - Cancel, -} - -/// The details of a scheduled task -#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -pub struct ScheduledTask { - /// the type of scheduled task - pub task: Task, - /// the 'time' at which the task should be executed - pub when: Time, -} diff --git a/blockchain/modules/setheum-pay/src/weights.rs b/blockchain/modules/setheum-pay/src/weights.rs deleted file mode 100644 index 2cb181bb..00000000 --- a/blockchain/modules/setheum-pay/src/weights.rs +++ /dev/null @@ -1,205 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Autogenerated weights for module_payment -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-03-19, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/setheum-node -// benchmark -// --chain -// dev -// --execution=wasm -// --wasm-execution -// compiled -// --extrinsic=* -// --pallet=module_payment -// --steps=20 -// --repeat=10 -// --heap-pages=4096 -// --output -// ./pallets/payment/src/weights.rs -// --template -// ./.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(clippy::unnecessary_cast)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for module_payment. -pub trait WeightInfo { - fn pay(x: u32, ) -> Weight; - fn release() -> Weight; - fn cancel() -> Weight; - fn resolve_payment() -> Weight; - fn request_refund() -> Weight; - fn dispute_refund() -> Weight; - fn request_payment() -> Weight; - fn accept_and_pay() -> Weight; - fn remove_task() -> Weight; -} - -/// Weights for module_payment using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - // Storage: Payment Payment (r:1 w:1) - // Storage: Sudo Key (r:1 w:0) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:1) - fn pay(_x: u32, ) -> Weight { - Weight::from_parts(55_900_000, 0) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - fn release() -> Weight { - Weight::from_parts(36_000_000, 0) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:0) - fn cancel() -> Weight { - Weight::from_parts(48_000_000, 0) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - fn resolve_payment() -> Weight { - Weight::from_parts(35_000_000, 0) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Payment ScheduledTasks (r:1 w:1) - fn request_refund() -> Weight { - Weight::from_parts(20_000_000, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Payment ScheduledTasks (r:1 w:1) - fn dispute_refund() -> Weight { - Weight::from_parts(21_000_000, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Sudo Key (r:1 w:0) - fn request_payment() -> Weight { - Weight::from_parts(17_000_000, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:1) - fn accept_and_pay() -> Weight { - Weight::from_parts(58_000_000, 0) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } - // Storage: Payment ScheduledTasks (r:1 w:1) - fn remove_task() -> Weight { - Weight::from_parts(4_000_000, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - // Storage: Payment Payment (r:1 w:1) - // Storage: Sudo Key (r:1 w:0) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:1) - fn pay(_x: u32, ) -> Weight { - Weight::from_parts(55_900_000, 0) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - fn release() -> Weight { - Weight::from_parts(36_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:0) - fn cancel() -> Weight { - Weight::from_parts(48_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - fn resolve_payment() -> Weight { - Weight::from_parts(35_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Payment ScheduledTasks (r:1 w:1) - fn request_refund() -> Weight { - Weight::from_parts(20_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Payment ScheduledTasks (r:1 w:1) - fn dispute_refund() -> Weight { - Weight::from_parts(21_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Sudo Key (r:1 w:0) - fn request_payment() -> Weight { - Weight::from_parts(17_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Payment Payment (r:1 w:1) - // Storage: Assets Accounts (r:2 w:2) - // Storage: System Account (r:1 w:1) - fn accept_and_pay() -> Weight { - Weight::from_parts(58_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - // Storage: Payment ScheduledTasks (r:1 w:1) - fn remove_task() -> Weight { - Weight::from_parts(4_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } -} From 7646cbff67b5cea134bc814cc827abf6c6cec898 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sun, 5 May 2024 01:45:42 +0800 Subject: [PATCH 08/10] mv setheumpay to EdfisPay --- blockchain/modules/edfis-pay/Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blockchain/modules/edfis-pay/Cargo.toml b/blockchain/modules/edfis-pay/Cargo.toml index 0a160b55..22aca341 100644 --- a/blockchain/modules/edfis-pay/Cargo.toml +++ b/blockchain/modules/edfis-pay/Cargo.toml @@ -1,12 +1,11 @@ [package] -name = "module-setheum-pay" -version = "0.9.81-dev" +name = "module-edfis-pay" +version = "0.5.0-dev" authors.workspace = true edition.workspace = true homepage.workspace = true repository.workspace = true - [dependencies] parity-scale-codec = { workspace = true } log = { workspace = true } From 8956e5af67be8a30a0ab9d2418373bfc83e845e5 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sun, 5 May 2024 01:47:10 +0800 Subject: [PATCH 09/10] Remove Moya, LSEE & LEDF --- blockchain/modules/support/src/incentives.rs | 5 -- blockchain/modules/support/src/lib.rs | 2 - .../modules/support/src/liquid_staking.rs | 38 ------------ .../modules/transaction-payment/src/mock.rs | 5 +- .../modules/transaction-payment/src/tests.rs | 62 +++++-------------- primitives/src/currency.rs | 17 +---- 6 files changed, 21 insertions(+), 108 deletions(-) delete mode 100644 blockchain/modules/support/src/liquid_staking.rs diff --git a/blockchain/modules/support/src/incentives.rs b/blockchain/modules/support/src/incentives.rs index 4174e08e..0d7c093c 100644 --- a/blockchain/modules/support/src/incentives.rs +++ b/blockchain/modules/support/src/incentives.rs @@ -30,8 +30,6 @@ use sp_std::prelude::*; // 1. EcdpSetrLiquidityRewards: record the shares and rewards for Setter (SETR)) ECDP users who are staking LP tokens. // 2. EcdpUssdLiquidityRewards: record the shares and rewards for Slick USD (USSD) ECDP users who are staking LP tokens. // 3. EdfisLiquidityRewards: record the shares and rewards for Edfis makers who are staking LP token. -// 4. EdfisXLiquidityRewards: record the shares and rewards for Edfis X (Cross-chain) makers who are staking LP token. -// 5. MoyaEarnRewards: record the shares and rewards for users of Moya Earn (Moya Liquid Staking Protocol). #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum PoolId { /// Rewards and shares pool for Setter (SETR)) ECDP users who are staking LP token(LPCurrencyId) @@ -43,9 +41,6 @@ pub enum PoolId { /// Rewards and shares pool for Edfis market makers who stake LP token(LPCurrencyId) EdfisLiquidityRewards(CurrencyId), - /// Rewards and shares pool for Edfis X (Cross-chain) market makers who stake LP token(LPCurrencyId) - EdfisXLiquidityRewards(CurrencyId), - /// Rewards and shares pool for Moya Earn MoyaEarnRewards(CurrencyId), } diff --git a/blockchain/modules/support/src/lib.rs b/blockchain/modules/support/src/lib.rs index 3e1651d9..4de7336f 100644 --- a/blockchain/modules/support/src/lib.rs +++ b/blockchain/modules/support/src/lib.rs @@ -48,7 +48,6 @@ pub mod edfis_mining; pub mod edfis_swap; pub mod edfis_swap_legacy; pub mod evm; -pub mod liquid_staking; pub mod migration; pub mod mocks; @@ -59,7 +58,6 @@ pub use crate::edfis_mining::*; pub use crate::edfis_swap::*; pub use crate::edfis_swap_legacy::*; pub use crate::evm::*; -pub use crate::liquid_staking::*; pub use crate::migration::*; pub type Price = FixedU128; diff --git a/blockchain/modules/support/src/liquid_staking.rs b/blockchain/modules/support/src/liquid_staking.rs deleted file mode 100644 index 2054417b..00000000 --- a/blockchain/modules/support/src/liquid_staking.rs +++ /dev/null @@ -1,38 +0,0 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// This file is part of Setheum. - -// Copyright (C) 2019-Present Setheum Labs. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use crate::{ExchangeRate, Rate}; -use sp_runtime::DispatchResult; -use xcm::v3::prelude::*; - -pub trait LiquidStakingManager { - /// Mint liquid currency by locking up staking currency - fn mint(who: AccountId, amount: Balance) -> DispatchResult; - /// Request for protocol to redeem liquid currency for staking currency - fn request_redeem(who: AccountId, amount: Balance, fast_match: bool) -> DispatchResult; - /// Calculates current exchange rate between staking and liquid currencies (staking : liquid) - fn get_exchange_rate() -> ExchangeRate; - /// Estimated return rate per era from liquid staking - fn get_estimated_reward_rate() -> Rate; - /// Gets commission rate of the Liquid Staking protocol - fn get_commission_rate() -> Rate; - /// Fee for fast matching redeem request - fn get_fast_match_fee() -> Rate; -} diff --git a/blockchain/modules/transaction-payment/src/mock.rs b/blockchain/modules/transaction-payment/src/mock.rs index 36c7c7d5..dcad4fa0 100644 --- a/blockchain/modules/transaction-payment/src/mock.rs +++ b/blockchain/modules/transaction-payment/src/mock.rs @@ -56,7 +56,6 @@ pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); pub const SETR: CurrencyId = CurrencyId::Token(TokenSymbol::SETR); pub const USSD: CurrencyId = CurrencyId::Token(TokenSymbol::USSD); pub const EDF: CurrencyId = CurrencyId::Token(TokenSymbol::EDF); -pub const LSEE: CurrencyId = CurrencyId::Token(TokenSymbol::LSEE); parameter_types! { static ExtrinsicBaseWeight: Weight = Weight::zero(); @@ -91,7 +90,7 @@ parameter_type_with_key! { match *currency_id { USSD => 100, SETR => 100, - EDF | LSEE => 1, + EDF => 1, _ => Default::default(), } }; @@ -165,7 +164,6 @@ parameter_types! { TradingPair::from_currency_ids(USSD, SETR).unwrap(), TradingPair::from_currency_ids(SETR, SEE).unwrap(), TradingPair::from_currency_ids(USSD, EDF).unwrap(), - TradingPair::from_currency_ids(SEE, LSEE).unwrap(), ]; pub const TradingPathLimit: u32 = 4; } @@ -332,7 +330,6 @@ impl Default for ExtBuilder { (ALICE, USSD, 10000), (ALICE, SETR, 10000), (ALICE, EDF, 1000), - (ALICE, LSEE, 1000) ], base_weight: Weight::zero(), byte_fee: 2, diff --git a/blockchain/modules/transaction-payment/src/tests.rs b/blockchain/modules/transaction-payment/src/tests.rs index b181488b..687cbf7e 100644 --- a/blockchain/modules/transaction-payment/src/tests.rs +++ b/blockchain/modules/transaction-payment/src/tests.rs @@ -31,7 +31,7 @@ use frame_support::{ use mock::{ AccountId, BlockWeights, Currencies, EdfisSwapLegacyModule, ExtBuilder, FeePoolSize, MockPriceSource, Runtime, RuntimeCall, RuntimeOrigin, System, TransactionPayment, ALICE, BOB, CHARLIE, DAVE, FEE_UNBALANCED_AMOUNT, TIP_UNBALANCED_AMOUNT, - SEE, USSD, EDF, LSEE, + SEE, USSD, EDF, }; use module_support::{BuyWeightRate, SwapManager, Price, TransactionPayment as TransactionPaymentT}; use orml_traits::{MultiCurrency, MultiLockableCurrency}; @@ -114,7 +114,7 @@ fn enable_dex_and_tx_fee_pool() { SEE, (init_balance * 100).unique_saturated_into(), )); - vec![USSD, SETR, EDF, LSEE].iter().for_each(|token| { + vec![USSD, SETR, EDF].iter().for_each(|token| { let ed = (>::minimum_balance(token.clone())).unique_saturated_into(); assert_ok!(Currencies::update_balance( RuntimeOrigin::root(), @@ -153,18 +153,8 @@ fn enable_dex_and_tx_fee_pool() { 0, false )); - assert_ok!(EdfisSwapLegacyModule::add_liquidity( - RuntimeOrigin::signed(ALICE), - LSEE, - SEE, - 100, - 1000, - 0, - false - )); assert_eq!(EdfisSwapLegacyModule::get_liquidity_pool(SEE, USSD), (10000, 1000)); assert_eq!(EdfisSwapLegacyModule::get_liquidity_pool(EDF, USSD), (100, 1000)); - assert_eq!(EdfisSwapLegacyModule::get_liquidity_pool(LSEE, SEE), (100, 1000)); assert_eq!(EdfisSwapLegacyModule::get_liquidity_pool(EDF, SEE), (0, 0)); // enable tx fee pool for USSD and EDF token. @@ -534,16 +524,16 @@ fn pre_post_dispatch_and_refund_with_fee_currency_call(token: CurrencyId, surplu #[test] fn pre_post_dispatch_and_refund_with_fee_currency_call_use_dex() { - pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_currency_call(LSEE)); + pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_currency_call(EDF)); } #[test] fn pre_post_dispatch_and_refund_with_fee_path_call_use_dex() { - pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_path_call(vec![LSEE, SEE])); + pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_path_call(vec![EDF, SEE])); } fn pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_call: ::RuntimeCall) { - let (token, surplus_percent) = (LSEE, CustomFeeSurplus::get()); + let (token, surplus_percent) = (EDF, CustomFeeSurplus::get()); builder_with_dex_and_fee_pool(true).execute_with(|| { // without tip let dex_acc: AccountId = PalletId(*b"set/edfis").into_account_truncating(); @@ -561,7 +551,7 @@ fn pre_post_dispatch_and_refund_with_fee_call_use_dex(with_fee_call: ::RuntimeCall) { @@ -786,19 +776,19 @@ fn charges_fee_when_validate_with_fee_call_use_swap(with_fee_call: ::from(0).validate(&BOB, &with_fee_call, &INFO2, 50)); System::assert_has_event(crate::mock::RuntimeEvent::EdfisSwapLegacyModule(edfis_swap_legacy_module::Event::Swap { trader: BOB, - path: vec![LSEE, SEE], + path: vec![EDF, SEE], liquidity_changes: vec![46, 315], })); - assert_eq!(1000 - 46, Currencies::free_balance(LSEE, &BOB)); + assert_eq!(1000 - 46, Currencies::free_balance(EDF, &BOB)); assert_eq!(10, Currencies::free_balance(SEE, &BOB)); assert_eq!(dex_see - 315, Currencies::free_balance(SEE, &dex_acc)); @@ -810,10 +800,10 @@ fn charges_fee_when_validate_with_fee_call_use_swap(with_fee_call: ::from(0).validate(&BOB, &with_fee_call, &INFO2, 50)); System::assert_has_event(crate::mock::RuntimeEvent::EdfisSwapLegacyModule(edfis_swap_legacy_module::Event::Swap { trader: BOB, - path: vec![LSEE, SEE], + path: vec![EDF, SEE], liquidity_changes: vec![114, 300], })); - assert_eq!(1000 - 46 - 114, Currencies::free_balance(LSEE, &BOB)); + assert_eq!(1000 - 46 - 114, Currencies::free_balance(EDF, &BOB)); assert_eq!(10, Currencies::free_balance(SEE, &BOB)); assert_eq!(dex_see - 315 - 300, Currencies::free_balance(SEE, &dex_acc)); }); @@ -1630,7 +1620,7 @@ fn buy_weight_transaction_fee_pool_works() { assert_eq!(rate, None); // Token not in charge fee pool - let currency_id = CurrencyId::Token(TokenSymbol::LSEE); + let currency_id = CurrencyId::Token(TokenSymbol::EDF); let location = MultiLocation::new( 1, @@ -2113,11 +2103,11 @@ fn charge_fee_pool_operation_works() { ); assert_noop!( - Pallet::::enable_charge_fee_pool(RuntimeOrigin::signed(ALICE), LEDF, pool_size, swap_threshold), + Pallet::::enable_charge_fee_pool(RuntimeOrigin::signed(ALICE), EDF, pool_size, swap_threshold), Error::::DexNotAvailable ); assert_noop!( - Pallet::::disable_charge_fee_pool(RuntimeOrigin::signed(ALICE), LEDF), + Pallet::::disable_charge_fee_pool(RuntimeOrigin::signed(ALICE), EDF), Error::::InvalidToken ); @@ -2162,15 +2152,8 @@ fn with_fee_call_validation_works() { 1000000, )); assert_ok!(Currencies::update_balance(RuntimeOrigin::root(), CHARLIE, EDF, 1000000,)); - assert_ok!(Currencies::update_balance( - RuntimeOrigin::root(), - CHARLIE, - LSEE, - 1000000, - )); assert_eq!(1000000, Currencies::free_balance(USSD, &CHARLIE)); assert_eq!(1000000, Currencies::free_balance(EDF, &CHARLIE)); - assert_eq!(1000000, Currencies::free_balance(LSEE, &CHARLIE)); assert_eq!(OverrideChargeFeeMethod::::get(), None); @@ -2302,17 +2285,6 @@ fn with_fee_call_validation_works() { ); } - // LSEE is not enabled fee pool, cannot charge fee by with_fee_currency - assert_noop!( - ChargeTransactionPayment::::from(0).pre_dispatch( - &CHARLIE, - &with_fee_currency_call(LSEE), - &INFO, - 10 - ), - TransactionValidityError::Invalid(InvalidTransaction::Payment) - ); - for path in vec![vec![EDF, USSD, SEE], vec![USSD, SEE]] { assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( &CHARLIE, diff --git a/primitives/src/currency.rs b/primitives/src/currency.rs index f117ebf3..46508de9 100644 --- a/primitives/src/currency.rs +++ b/primitives/src/currency.rs @@ -139,14 +139,6 @@ macro_rules! create_currency_id { symbol: "LP_EDF_SETR".to_string(), address: EvmAddress::try_from(TradingPair::from_currency_ids(CurrencyId::Token(SETR), CurrencyId::Token(EDF)).unwrap().dex_share_currency_id()).unwrap(), }, - Token { - symbol: "LP_LSEE_SETR".to_string(), - address: EvmAddress::try_from(TradingPair::from_currency_ids(CurrencyId::Token(SETR), CurrencyId::Token(LSEE)).unwrap().dex_share_currency_id()).unwrap(), - }, - Token { - symbol: "LP_LEDF_SETR".to_string(), - address: EvmAddress::try_from(TradingPair::from_currency_ids(CurrencyId::Token(SETR), CurrencyId::Token(LEDF)).unwrap().dex_share_currency_id()).unwrap(), - }, Token { symbol: "LP_USSD_SETR".to_string(), address: EvmAddress::try_from(TradingPair::from_currency_ids(CurrencyId::Token(SETR), CurrencyId::Token(USSD)).unwrap().dex_share_currency_id()).unwrap(), @@ -180,17 +172,14 @@ create_currency_id! { #[repr(u8)] pub enum TokenSymbol { // 0 - 100: Reserved for Setheum Native Assets - // Primary Protocol Tokens + // Primary Protocol Tokens SEE("Setheum", 12) = 0, EDF("Ethical DeFi", 12) = 1, - // Liquid Staking Tokens - LSEE("Liquid SEE", 12) = 2, - LEDF("Liquid EDF", 12) = 3, - // ECDP Stablecoin Tokens + // ECDP Stablecoin Tokens SETR("Setter", 12) = 4, USSD("Slick USD", 12) = 5, - // 101-255: Reserved for Fiat Currencies + // 101-255: Reserved for Fiat Currencies represented on EdfisPay AED("UAE Dirham", 2) = 101, AMD("Armenian Dram", 2) = 102, AOA("Angolan Kwanza", 2) = 103, From d74875285135285c2b509e1785d79734b264e0cf Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Sun, 5 May 2024 02:14:58 +0800 Subject: [PATCH 10/10] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a53cba3..436ffbef 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم -# Setheum - Powering The New Internet +# Setheum - Powering Light-Speed Web3 Solutions

@@ -36,7 +36,7 @@ Setheum's Blockchain Network node Implementation in Rust, ready for hacking :roc [![Lines of Code](https://img.shields.io/badge/LinesOfCode-gray?logo=LinesOfCode)](https://cloc.info/github.com/Setheum-Labs/Setheum) -> NOTE: SETHEUM means `Salam Ethereum`, it also means `The house of gifts` from the name `Seth/Sheeth` meaning `gift` in hebrew and the name of the Prophet Sheeth/Seth in Islam, it also stands for `Secure, Evergreen, Truthful, Heterogeneous, Economically Unbiased Market`. +> NOTE: SETHEUM means `Salam Eth`, `Super Eth`, `The house of gifts` and `gifted`. Originally from the name `Seth/Sheeth` mixed with `Ethereum`, it also stands for `Secure, Evergreen, Truthful, Heterogeneous, Economically Unbiased Market`. - [Setheum - Powering The New Internet](#setheum---powering-the-new-internet)