From ad9acca15021f4edcfa191d444d031a15e8bff11 Mon Sep 17 00:00:00 2001 From: "Muhammad-Jibril B.A. (Khalifa MBA)" Date: Mon, 4 Mar 2024 18:01:14 +0800 Subject: [PATCH] Populate Edfis Launchpad Module --- .../modules/asset-registry/src/weights.rs | 2 +- blockchain/modules/currencies/src/weights.rs | 2 +- .../modules/ecdp-ussd-treasury/src/weights.rs | 6 +- blockchain/modules/edfis-launchpad/Cargo.toml | 4 - blockchain/modules/edfis-launchpad/README.md | 38 +- blockchain/modules/edfis-launchpad/src/lib.rs | 915 +++++++++++++ .../modules/edfis-launchpad/src/mock.rs | 223 ++++ .../modules/edfis-launchpad/src/tests.rs | 1144 +++++++++++++++++ .../modules/edfis-launchpad/src/weights.rs | 149 +++ .../modules/edfis-oracle/src/weights.rs | 2 +- .../modules/edfis-swap-legacy/src/weights.rs | 2 +- blockchain/modules/edfis-swap/src/weights.rs | 2 +- blockchain/modules/evm/src/weights.rs | 2 +- blockchain/modules/nft/src/weights.rs | 2 +- blockchain/modules/prices/src/weights.rs | 2 +- blockchain/modules/support/TODO.md | 2 + .../modules/support/src/edfis_launchpad.rs | 101 ++ .../support/src/{edfis.rs => edfis_swap.rs} | 460 +++---- .../{edfis_legacy.rs => edfis_swap_legacy.rs} | 446 +++---- blockchain/modules/support/src/lib.rs | 277 ++-- .../modules/transaction-pause/src/weights.rs | 2 +- .../transaction-payment/src/weights.rs | 2 +- .../modules/unified-accounts/src/weights.rs | 2 +- blockchain/modules/vesting/src/weights.rs | 120 +- blockchain/primitives/src/edfis_launchpad.rs | 90 ++ blockchain/primitives/src/lib.rs | 423 +++--- blockchain/runtime/src/weights/dex_oracle.rs | 170 +-- 27 files changed, 3624 insertions(+), 966 deletions(-) create mode 100644 blockchain/modules/edfis-launchpad/src/lib.rs create mode 100644 blockchain/modules/edfis-launchpad/src/mock.rs create mode 100644 blockchain/modules/edfis-launchpad/src/tests.rs create mode 100644 blockchain/modules/edfis-launchpad/src/weights.rs create mode 100644 blockchain/modules/support/src/edfis_launchpad.rs rename blockchain/modules/support/src/{edfis.rs => edfis_swap.rs} (96%) rename blockchain/modules/support/src/{edfis_legacy.rs => edfis_swap_legacy.rs} (96%) create mode 100644 blockchain/primitives/src/edfis_launchpad.rs diff --git a/blockchain/modules/asset-registry/src/weights.rs b/blockchain/modules/asset-registry/src/weights.rs index db889bdb..0147cfe3 100644 --- a/blockchain/modules/asset-registry/src/weights.rs +++ b/blockchain/modules/asset-registry/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/currencies/src/weights.rs b/blockchain/modules/currencies/src/weights.rs index eb3eab69..64f3c6d6 100644 --- a/blockchain/modules/currencies/src/weights.rs +++ b/blockchain/modules/currencies/src/weights.rs @@ -26,7 +26,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // pallet // --chain=dev diff --git a/blockchain/modules/ecdp-ussd-treasury/src/weights.rs b/blockchain/modules/ecdp-ussd-treasury/src/weights.rs index b1884981..d3843115 100644 --- a/blockchain/modules/ecdp-ussd-treasury/src/weights.rs +++ b/blockchain/modules/ecdp-ussd-treasury/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 @@ -35,8 +35,8 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./modules/cdp-treasury/src/weights.rs -// --template=./templates/module-weight-template.hbs +// --output=./blockchain/modules/ecdp-ussd-treasury/src/weights.rs +// --template=.maintain/module-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] diff --git a/blockchain/modules/edfis-launchpad/Cargo.toml b/blockchain/modules/edfis-launchpad/Cargo.toml index 6af98d3f..001b4d09 100644 --- a/blockchain/modules/edfis-launchpad/Cargo.toml +++ b/blockchain/modules/edfis-launchpad/Cargo.toml @@ -6,8 +6,6 @@ authors = ["Setheum Labs"] edition = "2021" [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 } @@ -27,8 +25,6 @@ orml-tokens = { workspace = true } [features] default = ["std"] std = [ - "scale-info/std", - "serde", "parity-scale-codec/std", "sp-runtime/std", "sp-std/std", diff --git a/blockchain/modules/edfis-launchpad/README.md b/blockchain/modules/edfis-launchpad/README.md index bf697908..68380c00 100644 --- a/blockchain/modules/edfis-launchpad/README.md +++ b/blockchain/modules/edfis-launchpad/README.md @@ -1,5 +1,41 @@ # Edfis Launchpad Module +Edfis Launchpad is a platform for projects to offer crowdsales (IDO) of their tokens and raise funds on Edfis while listing their liquidity pool on the exchange. ## Overview -Provides a launchpad crowdsales platform on Edfis. +This module is used to raise funds on launchpad crowdsales. Teams and projects that are just getting started launching their products would need to raise funds and even sell their tokens to the public. They need community backed by token holders of their token, that is the crowd so that they could have a strong start. By creating a crowdfunding campaign that ends with their project Tokens getting sold to the public, they can raise funds and sell their tokens to the public. + +There are four participants in a LaunchPad Crowdsales Protocol, the Campaign Creator, the Campaign Beneficiary, the Crowd/Contributors, and the Governance Council. + +* The Campaign Creator is the person who creates the campaign and the project. +* The Campaign Beneficiary is the person who receives the funds raised. +* The Crowd/Contributors are the people who contribute to the campaign. +* The Governance Council is the people who manage the campaign and the protocol. + +## How the protocol Works + +![Screenshot from 2022-01-23 13-31-41](https://user-images.githubusercontent.com/15086345/150666483-3f9a07b3-2e76-46f9-97f9-729679c03f1c.png) +The HighEnd LaunchPad Protocol lets teams/projects/campaigns achieve two (2) major goals at once, it raises money, and and sell their tokens to the public. +The protocol uses `MultiCurrency` to let the Campaign Creator choose which currency to raise/sell their tokens for. Therefore, Campaign Creator can choose to raise funds in any currency available on the chain. + +There is a `goal` that is set by the Campaign Creator, the beneficiary of the fund and the Period (campaign period - amount of blocks a campaign should stay active) of the campaign and other information that describes the campaign. + +### The Lifecycle of a Campaign + +A Launchpad Campaign has three stages in its lifecycle, they are as follows: + +1. **Pre-Funding/Proposal Stage**: The Campaign Creator creates the campaign and sets the Period/TimeCap and HardCap. In this stage, the Campaign Creator must submit the proposal to the Governance Council along with a `SubmissionDeposit` required by the protocol. + +2. **Waiting Stage**: The Campaign waits for the appropriate time to start the Campaign. The Protocol has a `WaitingPeriod` that is set on runtime, and all campaigns have to wait for that period to start. + +3. **Funding/Active Stage**: The Campaign can raise funds and sell their tokens to the public in this stage. If the `goal` is reached before the `period` to end the campaign, the campaign will be ended and the funds will be available for the public to claim and the raised funds for the Campaign Beneficiary or Creator to claim. + +### Campaign Ctegories + +#### A Successful Launchpad Campaign + +A successful Launchpad Campaign is one that has raised the `goal` and has sold their tokens to the public. Once the `goal` is reached, the Campaign is considered successful. + +#### A Failed Launchpad Campaign + +A failed Launchpad Campaign is one that has not raised the `goal` and has not sold their tokens to the public. Once the `goal` is not reached and the `period` to end the campaign has ended, the Campaign is considered failed and the campaign allocation of tokens is available for the Campaign Creator to claim refund and the raised funds are also available for the Crowd/Contributors/buyers to claim refunds all only before the `RetirementPeriod` of the campaign. diff --git a/blockchain/modules/edfis-launchpad/src/lib.rs b/blockchain/modules/edfis-launchpad/src/lib.rs new file mode 100644 index 00000000..430976f8 --- /dev/null +++ b/blockchain/modules/edfis-launchpad/src/lib.rs @@ -0,0 +1,915 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 . + +//! # Launchpad Crowdsales Pallet +//! +//! ## Overview +//! +//! Edfis Launchpad is a platform for projects to offer crowdsales (IDO) of their tokens +//! and raise funds on Edfis while listing their liquidity pool on the exchange. + +#![cfg_attr(not(feature = "std"), no_std)] +// Disable the following two lints since they originate from an external macro (namely decl_storage) +#![allow(clippy::string_lit_as_bytes)] +#![allow(clippy::unused_unit)] + +use frame_support::{ + pallet_prelude::*, transactional, PalletId, traits::Get, ensure +}; +use frame_system::{pallet_prelude::*, ensure_signed}; + +use orml_traits::{GetByKey, MultiCurrency, MultiLockableCurrency, LockIdentifier}; +use primitives::{Balance, CampaignId, CampaignInfo, CurrencyId}; +use support::{CampaignManager, Proposal}; + +use sp_std::{ + vec::Vec, +}; +use sp_runtime::{traits::{AccountIdConversion, Zero}, DispatchResult}; + +mod mock; +mod tests; +pub mod weights; + +pub use module::*; +pub use weights::WeightInfo; + +pub(crate) type BalanceOf = <::MultiCurrency as MultiCurrency<::AccountId>>::Balance; +pub(crate) type CurrencyIdOf = + <::MultiCurrency as MultiCurrency<::AccountId>>::CurrencyId; +pub(crate) type CampaignInfoOf = + CampaignInfo<::AccountId, BalanceOf, ::BlockNumber>; + +pub const LAUNCHPAD_LOCK_ID: LockIdentifier = *b"set/lpad"; + +#[frame_support::pallet] +pub mod module { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + + /// The Currency for managing assets related to the SERP (Setheum Elastic Reserve Protocol). + type MultiCurrency: MultiLockableCurrency; + + #[pallet::constant] + /// Native currency_id. + /// + type GetNativeCurrencyId: Get>; + + /// The Campaign Commission rate taken from successful campaigns + /// The Treasury Commission is transferred to the Network's Treasury account. + /// The first item of the tuple is the numerator of the commission rate, second + /// item is the denominator, fee_rate = numerator / denominator, + /// use (u32, u32) over another type to minimize internal division operation. + #[pallet::constant] + type GetCommission: Get<(u32, u32)>; + + /// The amount to be held on deposit by the owner of a crowdfund + /// - in HighEnd LaunchPad (HELP) currency id. (LaunchPad Token) + type SubmissionDeposit: Get>; + + /// The minimum amount that must be raised in a crowdsales campaign. + /// Campaign Goal must be at least this amount. + /// If this amount is not met, the proposal can be updated by the proposer or will be rejected. + type MinRaise: GetByKey, BalanceOf>; + + /// The minimum amount that may be contributed into a crowdfund - by currency_id. + /// Should almost certainly be at least ExistentialDeposit. + type MinContribution: GetByKey, BalanceOf>; + + /// The maximum number of proposals that could be running at any given time. + /// If set to 0, proposals are disabled and the Module will panic if a proposal is made. + type MaxProposalsCount: Get; + + /// The maximum number of campaigns that could be running at any given time. + /// If set to 0, campaigns are disabled and the Module will panic if a campaign is made. + type MaxCampaignsCount: Get; + + /// The maximum period of time (in blocks) that a crowdfund campaign clould be active. + /// If set to 0, active period is disabled and the Module will panic if a campaign is activated. + type MaxActivePeriod: Get; + + /// The period of time (number of blocks) a campaign is delayed after being Approved by governance. + type CampaignStartDelay: Get; + + /// The period of time (in blocks) after an unsuccessful crowdfund ending during which + /// contributors are able to withdraw their funds. After this period, their funds are lost. + type CampaignRetirementPeriod: Get; + + /// The period of time (in blocks) after a rejected crowdfund proposal during which + /// proposal creators's locked deposits are unlocked and the proposal is set to `is_rejected`. + /// After this period, their proposal is lost. + type ProposalRetirementPeriod: Get; + + /// The origin which may update, approve or reject campaign proposals. + type UpdateOrigin: EnsureOrigin; + + #[pallet::constant] + /// The Airdrop module pallet id, keeps airdrop funds. + type PalletId: Get; + + /// Weight information for the extrinsics in this module. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// The campaign funds raised already claimed by campaign creator or beneficiary + CampaignAlreadyClaimed, + /// The crowdfund's contribution period has ended; no more contributions will be accepted. + CampaignEnded, + /// Campaign has failed + CampaignFailed, + /// Campaign is not approved + CampaignNotApproved, + /// Campaign is not active + CampaignNotActive, + /// Campaign is not in the list of campaigns. + CampaignNotFound, + /// Campaign has not started + CampaignNotStarted, + /// Campaign is still active + CampaignStillActive, + /// Contributors balance is not enough to contribute + ContributionCurrencyNotEnough, + /// Contribution failed to transfer + ContributionFailedTransfer, + /// Contribution is not in the list of contributions. + ContributionNotFound, + /// Must contribute at least the minimum amount of funds. + ContributionTooSmall, + /// Contribution has duplicate account + DuplicateContribution, + /// Must contribute at least the minimum amount of funds. + GoalBelowMinimumRaise, + /// The Submission Deposit Funds are insufficient + InsufficientBalance, + /// Wrong Currency Type in use. + InvalidCurrencyType, + /// The fund index specified does not exist. + InvalidIndex, + /// The campaign is in waiting period + InWaitingPeriod, + /// Maximum number of simultaneous campaigns has been reached; + /// no more campaigns can be approved until one is closed. + MaxCampaignsExceeded, + /// Crowdsale period has exceeded the maximum active period. + MaxActivePeriodExceeded, + /// Maximum number of simultaneous proposals has been exceeded; + /// no more proposals can be made until one is approved or rejected. + MaxProposalsExceeded, + /// You cannot withdraw funds because you have not contributed any. + NoContribution, + /// Proposal is already approved. + ProposalAlreadyApproved, + /// Proposal is not in the list of proposals. + ProposalNotFound, + /// The origin is not correct + WrongOrigin, + /// Crowdfund period is too short. + ZeroPeriod, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + #[pallet::metadata(T::AccountId = "AccountId", BalanceOf = "Balance", CurrencyIdOf = "CurrencyId")] + pub enum Event { + /// Created Proposal \[currency_id\] + CreatedProposal(CurrencyIdOf, CampaignInfoOf), + /// Contributed to a campaign \[contributor, currency_id, amount\] + Contributed(T::AccountId, CurrencyIdOf, BalanceOf), + /// Claim contribution allocation \[contributor, currency_id, amount\] + ClaimedContributionAlloc(T::AccountId, CurrencyIdOf, BalanceOf), + /// Claimed Funds Raised \[claimant_account_id, currency_id, amount_claimed\] + ClaimedFundraise(T::AccountId, CurrencyIdOf, BalanceOf), + /// Rejected Proposal \[currency_id\] + RejectedProposal(CurrencyIdOf), + /// Approved Proposal \[currency_id\] + ApprovedProposal(CurrencyIdOf), + /// Activated Campaign \[currency_id\] + ActivatedCampaign(CurrencyIdOf), + /// Campaign Started \[currency_id\] + StartedCampaign(CurrencyIdOf), + /// Ended Campaign Successfully \[currency_id, campaign_info\] + EndedCampaignSuccessful(CurrencyIdOf), + /// Ended Campaign Unsuccessfully \[currency_id, campaign_info\] + EndedCampaignUnsuccessful(CurrencyIdOf), + /// Contributed to Campaign \[currency_id, contribution_amount\] + ContributedToCampaign(CurrencyIdOf, BalanceOf), + /// Claimed Contribution Allocation \[claimant_account_id, currency_id, allocation_claimed\] + ClaimedAllocation(T::AccountId, CurrencyIdOf, BalanceOf), + /// Dissolved Unclaimed Funds \[amount, currency_id, now\] + DissolvedFunds(BalanceOf, CurrencyIdOf, ::BlockNumber), + /// Dispensed Commissions \[amount, currency_id, now\] + DispensedCommissions(BalanceOf, CurrencyIdOf, ::BlockNumber), + } + + /// Info on all of the proposed campaigns. + /// + /// map CurrencyId => CampaignInfo + #[pallet::storage] + #[pallet::getter(fn proposals)] + pub type Proposals = StorageMap<_, Blake2_128Concat, CurrencyIdOf, CampaignInfoOf, OptionQuery>; + + /// Info on all of the approved campaigns. + /// + /// map CurrencyId => CampaignInfo + #[pallet::storage] + #[pallet::getter(fn campaigns)] + pub type Campaigns = StorageMap<_, Blake2_128Concat, CurrencyIdOf, CampaignInfoOf, OptionQuery>; + + // Track the next campaign id to be used. + #[pallet::storage] + #[pallet::getter(fn campaign_index)] + pub type CampaignsIndex = StorageValue<_, CampaignId, ValueQuery>; + + // Track the number of simultaneous Active Campaigns - ActiveCampaignsCount + + #[pallet::storage] + #[pallet::getter(fn active_campaigns_count)] + pub type ActiveCampaignsCount = StorageValue<_, u32, ValueQuery>; + + // Track the number of successful campaigns the protocol has achieved. + #[pallet::storage] + #[pallet::getter(fn successful_campaign_index)] + pub type SuccessfulCampaignsCount = StorageValue<_, u32, ValueQuery>; + + + /// Record of the total amount of funds raised in the protocol + /// under a specific currency_id. currency_id => total_raised + /// + /// TotalAmountRaised: map CurrencyIdOf => BalanceOf + #[pallet::storage] + #[pallet::getter(fn total_amount_raised)] + pub type TotalAmountRaised = StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks for Pallet { + // Call at the start of the block to eventuiate on_proposals and on_campaigns + // on_initialize is called at the start of the block. + fn on_initialize(now: T::BlockNumber) -> Weight { + + // Calls to eventuate proposals and campaigns. + + let mut count: Weight = 0; + count += 1; + + // Eventuate proposals and campaigns + + // If there are proposals, check if to remove rejected and retired proposals. + // Iterate over the proposals + for (id, campaign_info) in Proposals::::iter() { + // If the proposal is rejected, check if to remove it + if campaign_info.is_rejected && now >= campaign_info.proposal_retirement_period { + // Remove the proposal + Self::remove_proposal(id).unwrap(); + count += 1; + } + break; + } + // If there are campaigns, check if to start or end them + // Iterate over the campaigns + for (id, campaign_info) in Campaigns::::iter() { + // If the campaign is waiting, check if to start it + if campaign_info.is_waiting && campaign_info.campaign_start <= now { + // Activate Campaign + Self::activate_campaign(id).unwrap(); + count += 1; + } + // If the campaign is active, check if to end it + if campaign_info.is_active && !campaign_info.is_ended { + // If campaign is successfull, call on successful campaign + if campaign_info.raised >= campaign_info.goal { + Self::on_successful_campaign(now, id).unwrap(); + count += 1; + } else if campaign_info.campaign_end <= now && campaign_info.raised < campaign_info.goal { + // If campaign is failed, call on failed campaign + Self::on_failed_campaign(now, id).unwrap(); + count += 1; + } + } + // If the campaign reaches retirement period, call on retirement + if campaign_info.is_ended && &campaign_info.campaign_retirement_period <= &now { + Self::on_retire(id).unwrap(); + count += 1; + } + break; + } + T::WeightInfo::on_initialize(count as u32) + } + } + + #[pallet::call] + impl Pallet { + /// Make a new proposal + #[pallet::weight((T::WeightInfo::make_proposal(), DispatchClass::Operational))] + #[transactional] + pub fn make_proposal( + origin: OriginFor, + beneficiary: T::AccountId, + raise_currency: CurrencyIdOf, + sale_token: CurrencyIdOf, + token_price: BalanceOf, + crowd_allocation: BalanceOf, + goal: BalanceOf, + period: T::BlockNumber, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Ensure that the period is not zero + ensure!(period > T::BlockNumber::zero(), Error::::ZeroPeriod); + // Ensure that the period is not too long + ensure!(period <= T::MaxActivePeriod::get(), Error::::MaxActivePeriodExceeded); + // Ensure that the goal is not less than the Minimum Raise + ensure!(goal > T::MinRaise::get(&raise_currency), Error::::GoalBelowMinimumRaise); + + // Create proposal and add id. + Self::new_proposal( + who.clone(), + beneficiary, + raise_currency, + sale_token, + token_price, + crowd_allocation, + goal, + period, + )?; + Ok(()) + } + + // Make a contribution to an active campaign + #[pallet::weight((T::WeightInfo::contribute(), DispatchClass::Operational))] + #[transactional] + pub fn contribute( + origin: OriginFor, + id: CurrencyIdOf, + contribution_amount: BalanceOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::on_contribution( + who.clone(), + id, + contribution_amount + )?; + Self::deposit_event(Event::Contributed(who, id, contribution_amount)); + Ok(()) + } + + // Claim a contribution allocation + #[pallet::weight((T::WeightInfo::claim_contribution_allocation(), DispatchClass::Operational))] + #[transactional] + pub fn claim_contribution_allocation( + origin: OriginFor, + id: CurrencyIdOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::on_claim_allocation( + who.clone(), + id, + )?; + Ok(()) + } + + // Claim a campaign's raised funds + #[pallet::weight((T::WeightInfo::claim_campaign_fundraise(), DispatchClass::Operational))] + #[transactional] + pub fn claim_campaign_fundraise( + origin: OriginFor, + id: CurrencyIdOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::on_claim_campaign( + who.clone(), + id, + )?; + + let campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + Self::deposit_event(Event::ClaimedFundraise(who, id, campaign.raised)); + Ok(()) + } + + // Approve a proposal - origin must be `UpdateOrigin` + #[pallet::weight((T::WeightInfo::approve_proposal(), DispatchClass::Operational))] + #[transactional] + pub fn approve_proposal( + origin: OriginFor, + id: CurrencyIdOf, + ) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + Self::on_approve_proposal( + id, + )?; + + Self::deposit_event(Event::ApprovedProposal(id)); + Ok(()) + } + + // Reject a proposal - origin must be `UpdateOrigin` + #[pallet::weight((T::WeightInfo::reject_proposal(), DispatchClass::Operational))] + #[transactional] + pub fn reject_proposal( + origin: OriginFor, + id: CurrencyIdOf, + ) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + Self::on_reject_proposal( + id, + )?; + + Self::deposit_event(Event::RejectedProposal(id)); + Ok(()) + } + + // Activate a Waiting Campaign - origin must be `UpdateOrigin` + #[pallet::weight((T::WeightInfo::activate_waiting_campaign(), DispatchClass::Operational))] + #[transactional] + pub fn activate_waiting_campaign( + origin: OriginFor, + id: CurrencyIdOf, + ) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + Self::activate_campaign( + id, + )?; + + Self::deposit_event(Event::ActivatedCampaign(id)); + Ok(()) + } + } +} + + +impl Pallet { + /// Get the Launchpad's Treasury Account. + pub fn launchpad_treasury() -> T::AccountId { + T::PalletId::get().into_account() + } + + /// The account ID of the fund pot. + /// + pub fn campaign_pool(id: CampaignId) -> T::AccountId { + T::PalletId::get().into_sub_account(id) + } +} + +impl Proposal for Pallet { + type CurrencyId = CurrencyId; + + /// The Campaign Proposal info of `id` + fn proposal_info(id: Self::CurrencyId) -> Option> { + Self::proposals(id) + } + + /// Get all proposals + fn all_proposals() -> Vec> { + let proposals = Proposals::::iter().into_iter(); + let mut proposals_vec: Vec> = Vec::new(); + for (_, proposal) in proposals { + proposals_vec.push(proposal); + } + proposals_vec + } + + /// Create new Campaign Proposal with specific `CampaignInfo`, return the `id` of the Campaign + fn new_proposal( + origin: T::AccountId, + beneficiary: T::AccountId, + raise_currency: Self::CurrencyId, + sale_token: Self::CurrencyId, + token_price: BalanceOf, + crowd_allocation: BalanceOf, + goal: BalanceOf, + period: T::BlockNumber, + ) -> DispatchResult { + // Generate pool_id - overflow not managed + let pool_id = >::get() + 1; + >::put(pool_id); + + // Generate the CampaignInfo structure + let proposal = CampaignInfo { + id: sale_token, + origin: origin.clone(), + beneficiary: beneficiary, + pool: Self::campaign_pool(pool_id), + raise_currency: raise_currency, + sale_token: sale_token, + token_price: token_price, + crowd_allocation: crowd_allocation, + goal: goal, + raised: Zero::zero(), + contributors_count: Zero::zero(), + contributions: Vec::new(), + period: period, + campaign_start: Zero::zero(), + campaign_end: Zero::zero(), + campaign_retirement_period: Zero::zero(), + proposal_retirement_period: Zero::zero(), + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + + // try checks + let try_set_lock = T::MultiCurrency::set_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &origin, T::SubmissionDeposit::get()).is_ok(); + let try_make_transfer = T::MultiCurrency::transfer(sale_token, &origin, &Self::campaign_pool(pool_id), crowd_allocation).is_ok() ; + + if T::MultiCurrency::free_balance(T::GetNativeCurrencyId::get(), &origin) >= T::SubmissionDeposit::get() && + T::MultiCurrency::free_balance(sale_token, &origin) >= crowd_allocation { + if try_set_lock && try_make_transfer { + // set lock + T::MultiCurrency::set_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &origin, T::SubmissionDeposit::get()).unwrap(); + // make transfer + T::MultiCurrency::transfer(sale_token, &origin, &Self::campaign_pool(pool_id), crowd_allocation).unwrap(); + // insert proposal + >::insert(sale_token, proposal.clone()); + } + } else { + return Err(Error::::InsufficientBalance.into()); + } + + Self::deposit_event(Event::CreatedProposal(sale_token, proposal.clone())); + Ok(()) + } + + /// Approve Proposal by `id` at `now`. + fn on_approve_proposal(id: Self::CurrencyId)-> sp_std::result::Result<(), DispatchError> { + // Tag the proposal and ensure it is not already approved. + let mut proposal = Self::proposals(id).ok_or(Error::::ProposalNotFound)?; + ensure!(!proposal.is_approved, Error::::ProposalAlreadyApproved); + + // Approve the proposal in CampaignInfo and set it to waiting + proposal.is_approved = true; + proposal.is_waiting = true; + + // Set campaign start time + proposal.campaign_start = >::block_number() + T::CampaignStartDelay::get(); + + // Set campaign end time + proposal.campaign_end = >::block_number() + T::CampaignStartDelay::get() + proposal.period; + + // Remove from proposals and add to campaigns + >::remove(id); + >::insert(id, proposal); + // Active Campaigns count - overflow not managed + >::put(>::get() + 1); + Ok(()) + } + + /// Reject Proposal by `id` and remove from storage. + fn on_reject_proposal(id: Self::CurrencyId)-> sp_std::result::Result<(), DispatchError> { + // Check that the Proposal exists and tag it + let mut proposal = Self::proposals(id).ok_or(Error::::ProposalNotFound)?; + // Ensure that the proposal is not already approved + ensure!(!proposal.is_approved, Error::::ProposalAlreadyApproved); + + // Set the proposal to rejected + proposal.is_rejected = true; + proposal.proposal_retirement_period = >::block_number() + T::ProposalRetirementPeriod::get(); + // Update proposal storage + >::insert(id, proposal); + Ok(()) + } + + /// Remove proposal from storage by `id` + fn remove_proposal(id: Self::CurrencyId)-> sp_std::result::Result<(), DispatchError> { + // Check that the Proposal exists and tag it + let proposal = Self::proposals(id).ok_or(Error::::ProposalNotFound)?; + // Ensure that the proposal is not already approved + ensure!(!proposal.is_approved, Error::::ProposalAlreadyApproved); + ensure!(proposal.is_rejected, Error::::ProposalAlreadyApproved); + + let try_remove_lock = T::MultiCurrency::remove_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &proposal.origin).is_ok(); + let try_refund_transfer = T::MultiCurrency::transfer( proposal.sale_token, &proposal.pool, &proposal.origin, proposal.crowd_allocation).is_ok(); + // Unlock balances and remove the Proposal from the storage. + if try_remove_lock && try_refund_transfer { + // remove lock and refund proposal + T::MultiCurrency::remove_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &proposal.origin).unwrap(); + T::MultiCurrency::transfer( proposal.sale_token, &proposal.pool, &proposal.origin, proposal.crowd_allocation).unwrap(); + // Remove from proposals + >::remove(id); + }; + Ok(()) + } +} + +impl CampaignManager for Pallet { + type CurrencyId = CurrencyId; + + /// The Campaign info of `id` + fn campaign_info(id: Self::CurrencyId) -> Option> { + Self::campaigns(id) + } + + /// Get all campaigns + fn all_campaigns() -> Vec> { + let campaigns = Campaigns::::iter().into_iter(); + let mut campaigns_vec: Vec> = Vec::new(); + for (_, proposal) in campaigns { + campaigns_vec.push(proposal); + } + campaigns_vec + } + + /// Called when a contribution is received. + fn on_contribution( + who: T::AccountId, + id: Self::CurrencyId, + amount: BalanceOf, + ) -> DispatchResult { + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Ensure campaign is valid + ensure!(!campaign.is_failed, Error::::CampaignFailed); + ensure!(!campaign.is_ended, Error::::CampaignEnded); + ensure!(campaign.is_approved, Error::::CampaignNotApproved); + ensure!(campaign.is_active, Error::::CampaignNotActive); + + // Make assurances - minimum contribution & free balance + ensure!(amount >= T::MinContribution::get(&campaign.raise_currency), Error::::ContributionTooSmall); + ensure!(T::MultiCurrency::free_balance(campaign.raise_currency, &who) >= amount, Error::::ContributionCurrencyNotEnough); + + // Initiate the Contribution + let transfer_contribution = T::MultiCurrency::transfer(campaign.raise_currency, &who, &campaign.pool, amount).is_ok(); + if transfer_contribution { + // Transfer contribution and tag allocation + T::MultiCurrency::transfer(campaign.raise_currency, &who, &campaign.pool, amount).unwrap(); + let allocated = amount / campaign.token_price; + + // Check if contributor already exists in contributions list + let mut found = false; + // if campaign.contributions exists, check for who's contribution + + for (contributor, contribution, allocation, _) in campaign.contributions.iter_mut() { + if contributor == &who { + + found = true; + *contribution += amount; + *allocation += allocated; + campaign.raised += amount; + } + break; + } + if !found { + campaign.contributions.push((who, amount, allocated, false)); + campaign.raised += amount; + } + + // Tag contributors count + campaign.contributors_count = campaign.contributions.len() as u32; + + // Put campaign in campaigns storage + >::insert(id, campaign); + }; + Ok(()) + } + + /// Called when a contribution allocation is claimed + fn on_claim_allocation( + who: T::AccountId, + id: Self::CurrencyId, + ) -> DispatchResult { + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + let campaign_p = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Check if the contributor exists in the contributions of the campaign, if not return error + ensure!(campaign.contributions.iter().any(|(contributor, _, _, _)| *contributor == who), Error::::ContributionNotFound); + + // Ensure campaign is successfully ended + Self::ensure_successfully_ended_campaign(id)?; + + // Check if the contributor exists and transfer allocated from pool to contributor + for (contributor, _, allocation, claimed) in campaign.contributions.iter_mut() { + let transfer_allocation = T::MultiCurrency::transfer(campaign.sale_token, &campaign.pool, &who, *allocation).is_ok(); + + if contributor == &who && *claimed == false && transfer_allocation { + // set claimed to true - allocation claimed + *claimed = true; + // complete claim by adding campaign update to storage + >::insert(id, campaign); + + for (contributor_p, _, allocation_p, claimed_p) in campaign_p.contributions.iter() { + if contributor_p == &who && *claimed_p == false { + // transfer allocation + T::MultiCurrency::transfer(campaign_p.sale_token, &campaign_p.pool, &who, *allocation_p).unwrap(); + Self::deposit_event(Event::ClaimedContributionAlloc(who, id, *allocation_p)); + } + + break; + } + } + + break; + } + Ok(()) + } + + /// Called when a campaign's raised fund is claimed + fn on_claim_campaign( + who: T::AccountId, + id: Self::CurrencyId, + ) -> DispatchResult { + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Ensure origin is who created campaign proposal or beneficiary and its not claimed + ensure!(campaign.origin == who || campaign.beneficiary == who, Error::::WrongOrigin); + ensure!(!campaign.is_claimed, Error::::CampaignAlreadyClaimed); + + + if campaign.is_ended { + // Claim the campaign raised funds and transfer to the beneficiary + let transfer_claim = T::MultiCurrency::transfer( + campaign.raise_currency, + &campaign.pool, + &campaign.beneficiary, + campaign.raised + ) + .is_ok(); + + if campaign.is_successful && transfer_claim { + T::MultiCurrency::transfer( + campaign.raise_currency, + &campaign.pool, + &campaign.beneficiary, + campaign.raised + ).unwrap(); + // Campaign is claimed, update storage + campaign.is_claimed = true; + >::insert(id, campaign); + } + } + Ok(()) + } + + /// Called when a failed campaign is claimed by the proposer + fn on_claim_failed_campaign( + who: T::AccountId, + id: Self::CurrencyId, + ) -> DispatchResult { + let campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Ensure origin is who created campaign proposal + ensure!(campaign.origin == who || campaign.beneficiary == who, Error::::WrongOrigin); + + // Ensure campaign is valid and failed + ensure!(campaign.is_failed, Error::::CampaignFailed); + ensure!(campaign.is_ended, Error::::CampaignEnded); + + // Get the total amount of sale_token in the pool + let total_sale_token = T::MultiCurrency::total_balance(campaign.sale_token, &campaign.pool); + + let remove_lock = T::MultiCurrency::remove_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &campaign.origin).is_ok(); + let transfer_claim = T::MultiCurrency::transfer( campaign.sale_token, &campaign.pool, &who, total_sale_token).is_ok(); + // Unlock balances and remove the Proposal from the storage. + if remove_lock && transfer_claim { + T::MultiCurrency::remove_lock(LAUNCHPAD_LOCK_ID, T::GetNativeCurrencyId::get(), &campaign.origin).unwrap(); + T::MultiCurrency::transfer( campaign.sale_token, &campaign.pool, &who, total_sale_token).unwrap(); + // Update campaign in campaigns storage + >::insert(id, campaign); + }; + Ok(()) + } + + /// Activate a campaign by `id` + fn activate_campaign(id: Self::CurrencyId) -> DispatchResult { + // Ensure campaign exists + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Set campaign to active + campaign.is_waiting = false; + campaign.is_active = true; + // Update campaign storage + >::insert(id, campaign); + Ok(()) + } + + /// Ensure campaign is Valid and Successfully Ended + fn ensure_successfully_ended_campaign(id: Self::CurrencyId) -> DispatchResult { + let campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + ensure!(!campaign.is_failed, Error::::CampaignFailed); + ensure!(campaign.is_successful, Error::::CampaignFailed); + ensure!(campaign.is_ended, Error::::CampaignStillActive); + ensure!(campaign.is_approved, Error::::CampaignNotApproved); + + // ensure!(campaign.campaign_start <= >::block_number(), Error::::CampaignNotStarted); + Ok(()) + } + + /// Record Successful Campaign by `id` + fn on_successful_campaign(now: T::BlockNumber, id: Self::CurrencyId) -> DispatchResult { + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Set to successful and ended + campaign.is_successful = true; + campaign.is_ended = true; + + // Set retirement period + campaign.campaign_retirement_period = now + T::CampaignRetirementPeriod::get(); + + // Tag contributors count + campaign.contributors_count = campaign.contributions.len() as u32; + + + // Success count - overflow not managed + // Add to total successful campaigns + let success_count = >::get() + 1; + >::put(success_count); + + // Add to `TotalAmountRaised` in protocol + >::mutate(campaign.raise_currency, |total| *total += campaign.raised); + + // Update campaign storage + >::insert(id, campaign); + Ok(()) + } + + /// Record Failed Campaign by `id` + fn on_failed_campaign(now: T::BlockNumber, id: Self::CurrencyId) -> DispatchResult { + let mut campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + + // Set to failed and ended + campaign.is_failed = true; + campaign.is_ended = true; + + // Tag contributors count + campaign.contributors_count = campaign.contributions.len() as u32; + + // Set retirement period + campaign.campaign_retirement_period = now + T::CampaignRetirementPeriod::get(); + + // Update campaign storage + >::insert(id, campaign); + Ok(()) + } + + /// Called when pool is retired + /// Only unsuccessful pools are retired + fn on_retire(id: Self::CurrencyId) -> DispatchResult { + // Get campaign in tag + let campaign = Self::campaigns(id).ok_or(Error::::CampaignNotFound)?; + // Get accounts in tag + let treasury = Self::launchpad_treasury(); + + // Get the total amount of raise_currency in the pool + let total_raise_currency = T::MultiCurrency::total_balance(campaign.raise_currency, &campaign.pool); + // Get the total amount of sale_token in the pool + let total_sale_token = T::MultiCurrency::total_balance(campaign.sale_token, &campaign.pool); + + let transfer_allocation = T::MultiCurrency::transfer(campaign.raise_currency, &campaign.pool, &treasury, total_raise_currency).is_ok(); + let transfer_raise = T::MultiCurrency::transfer(campaign.sale_token, &campaign.pool, &treasury, total_sale_token).is_ok(); + // Dissolve unclaimed Fundraise + if transfer_allocation && transfer_raise { + T::MultiCurrency::transfer(campaign.raise_currency, &campaign.pool, &treasury, total_raise_currency).unwrap(); + T::MultiCurrency::transfer(campaign.sale_token, &campaign.pool, &treasury, total_sale_token).unwrap(); + // Remove campaign from campaigns storage + >::remove(id); + } + Ok(()) + } + + /// Get amount of contributors/contributions in a campaign + fn get_contributors_count(id: Self::CurrencyId) -> u32 { + let campaign = Self::campaigns(id).unwrap(); + campaign.contributions.len() as u32 + } + + /// Get the total_amounts_raised for all currencies from `TotalAmountRaised` + fn get_total_amounts_raised() -> Vec<(Self::CurrencyId, BalanceOf)> { + let total_amounts_raised: Vec<(Self::CurrencyId, BalanceOf)> = >::iter() + .into_iter() + .collect::)>>(); + total_amounts_raised + } +} diff --git a/blockchain/modules/edfis-launchpad/src/mock.rs b/blockchain/modules/edfis-launchpad/src/mock.rs new file mode 100644 index 00000000..ddbe1879 --- /dev/null +++ b/blockchain/modules/edfis-launchpad/src/mock.rs @@ -0,0 +1,223 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 the Edfis Launchpad module. + +#![cfg(test)] + +use super::*; +use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; +use frame_system::EnsureSignedBy; +use orml_traits::parameter_type_with_key; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::IdentityLookup, +}; +use primitives::{Amount, Balance, TokenSymbol}; + +pub type AccountId = u128; +pub type BlockNumber = u64; +// The network Treasury account. +pub const TREASURY: AccountId = 0; +// Mock accounts. +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const CHARLIE: AccountId = 3; + +pub const SEE: CurrencyId = CurrencyId::Token(TokenSymbol::SEE); +pub const USSD: CurrencyId = CurrencyId::Token(TokenSymbol::USSD); +pub const TEST: CurrencyId = CurrencyId::Token(TokenSymbol::SETR); +pub const EDF: CurrencyId = CurrencyId::Token(TokenSymbol::EDF); + +mod launchpad_crowdsales { + pub use super::super::*; +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = BlockNumber; + type Call = Call; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + Default::default() + }; +} + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + type MaxLocks = (); + type DustRemovalWhitelist = (); +} + +parameter_type_with_key! { + pub MinRaise: |currency_id: CurrencyId| -> Balance { + match currency_id { + &USSD => 100, + &SEE => 100, + &EDF => 100, + _ => 0, + } + }; +} + +parameter_type_with_key! { + pub MinContribution: |currency_id: CurrencyId| -> Balance { + match currency_id { + &USSD => 100, + &SEE => 100, + &EDF => 100, + _ => 0, + } + }; +} + +parameter_types! { + pub const GetNativeCurrencyId: CurrencyId = SEE; // Setheum native currency ticker is SEE/ + pub const GetCommission: (u32, u32) = (10, 100); // 10% + pub const SubmissionDeposit: Balance = 101; + pub const MaxProposalsCount: u32 = 3; + pub const MaxCampaignsCount: u32 = 3; + pub const MaxActivePeriod: BlockNumber = 20; + pub const CampaignStartDelay: BlockNumber = 20; + pub const RetirementPeriod: BlockNumber = 20; + pub const CrowdsalesPalletId: PalletId = PalletId(*b"set/help"); +} + +ord_parameter_types! { + pub const TreasuryAccount: AccountId = TREASURY; + pub const Eleven: AccountId = 11; +} +impl Config for Runtime { + type Event = Event; + type MultiCurrency = Tokens; + type GetNativeCurrencyId = GetNativeCurrencyId; + type GetCommission = GetCommission; + type SubmissionDeposit = SubmissionDeposit; + type MinRaise = MinRaise; + type MinContribution = MinContribution; + type MaxProposalsCount = MaxProposalsCount; + type MaxCampaignsCount = MaxCampaignsCount; + type MaxActivePeriod = MaxActivePeriod; + type CampaignStartDelay = CampaignStartDelay; + type CampaignRetirementPeriod = RetirementPeriod; + type ProposalRetirementPeriod = RetirementPeriod; + type UpdateOrigin = EnsureSignedBy; + type PalletId = CrowdsalesPalletId; + type WeightInfo = (); +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + LaunchPad: launchpad_crowdsales::{Pallet, Storage, Call, Event}, + Tokens: orml_tokens::{Pallet, Storage, Call, Event}, + } +); + +pub struct ExtBuilder { + balances: Vec<(AccountId, CurrencyId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { balances: vec![] } + } +} + +impl ExtBuilder { + pub fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn one_hundred_thousand_for_all(self) -> Self { + self.balances(vec![ + (ALICE, SEE, 100_000), + (ALICE, USSD, 100_000), + (ALICE, EDF, 100_000), + (ALICE, TEST, 100_000), + (BOB, SEE, 100_000), + (BOB, USSD, 100_000), + (BOB, EDF, 100_000), + (BOB, TEST, 100_000), + (CHARLIE, SEE, 100_000), + (CHARLIE, USSD, 100_000), + (CHARLIE, EDF, 100_000), + (CHARLIE, TEST, 100_000), + ]) + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + orml_tokens::GenesisConfig:: { + balances: self + .balances + .into_iter() + .collect::>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} diff --git a/blockchain/modules/edfis-launchpad/src/tests.rs b/blockchain/modules/edfis-launchpad/src/tests.rs new file mode 100644 index 00000000..881216bf --- /dev/null +++ b/blockchain/modules/edfis-launchpad/src/tests.rs @@ -0,0 +1,1144 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 the Edfis Launchpad module. + +#![cfg(test)] + +use super::*; +use frame_support::{assert_noop, assert_ok}; +use mock::*; + +#[test] +fn proposal_info_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_eq!( + LaunchPad::proposal_info(TEST), + Some(CampaignInfo { + id:TEST, + origin: ALICE, + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: vec![], + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }) + ) + }); +} + +#[test] +fn campaign_info_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 20, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + LaunchPad::on_initialize(23); + assert_eq!( + LaunchPad::campaign_info(TEST), + Some(CampaignInfo { + id:TEST, + origin: ALICE, + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: vec![], + period: 20, + campaign_start: 20, + campaign_end: 40, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: true, + is_rejected: false, + is_waiting: false, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }) + ) + }); +} + +#[test] +fn make_proposal_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + assert_ok!(LaunchPad::make_proposal( + Origin::signed(ALICE), + BOB, + USSD, + TEST, + 10, + 10_000, + 100_000, + 20 + )); + }); +} + +#[test] +fn make_proposal_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + assert_noop!( + LaunchPad::make_proposal( + Origin::signed(ALICE), + BOB, + USSD, + TEST, + 10, + 10_000, + 100_000, + 0 + ), + Error::::ZeroPeriod + ); + assert_noop!( + LaunchPad::make_proposal( + Origin::signed(ALICE), + BOB, + USSD, + TEST, + 10, + 10_000, + 100_000, + 21 + ), + Error::::MaxActivePeriodExceeded + ); + assert_noop!( + LaunchPad::make_proposal( + Origin::signed(ALICE), + BOB, + USSD, + TEST, + 10, + 10_000, + 99, + 20 + ), + Error::::GoalBelowMinimumRaise + ); + }); +} + +#[test] +fn contribute_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 10_000 + )); + assert_noop!( + LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10 + ), + Error::::ContributionTooSmall + ); + assert_noop!( + LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 100_001 + ), + Error::::ContributionCurrencyNotEnough + ); + }); +} + +#[test] +fn contribute_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_noop!( + LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10 + ), + Error::::CampaignNotFound + ); + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + assert_noop!( + LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + ), + Error::::CampaignNotActive + ); + }); +} + +#[test] +fn claim_contribution_allocation_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 50_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 50_000 + )); + + LaunchPad::on_initialize(41); + System::set_block_number(41); + assert_ok!(LaunchPad::claim_contribution_allocation( + Origin::signed(BOB), + TEST, + )); + }); +} + +#[test] +fn claim_contribution_allocation_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + LaunchPad::on_initialize(23); + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 10_000 + )); + + assert_noop!( + LaunchPad::claim_contribution_allocation( + Origin::signed(BOB), + TEST, + ), + Error::::CampaignFailed + ); + }); +} + +#[test] +fn claim_campaign_fundraise_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 50_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 50_000 + )); + + LaunchPad::on_initialize(41); + System::set_block_number(41); + assert_ok!(LaunchPad::claim_campaign_fundraise( + Origin::signed(ALICE), + TEST, + )); + }); +} + +#[test] +fn claim_campaign_fundraise_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + LaunchPad::on_initialize(23); + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 10_000 + )); + + LaunchPad::on_initialize(41); + System::set_block_number(41); + + assert_ok!(LaunchPad::claim_campaign_fundraise( + Origin::signed(ALICE), + TEST, + )); + + assert_noop!( + LaunchPad::claim_campaign_fundraise( + Origin::signed(CHARLIE), + TEST, + ), + Error::::WrongOrigin + ); + }); +} + +#[test] +fn claim_campaign_fundraise_does_not_work_already_claimed() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 0, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: false, + is_active: false, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: true, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + LaunchPad::on_initialize(23); + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 10_000 + )); + + LaunchPad::on_initialize(41); + System::set_block_number(41); + + assert_noop!( + LaunchPad::claim_campaign_fundraise( + Origin::signed(BOB), + TEST, + ), + Error::::CampaignAlreadyClaimed + ); + }); +} + +#[test] +fn approve_proposal_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + }); +} + +#[test] +fn approve_proposal_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: true, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_noop!( + LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + ), + Error::::ProposalAlreadyApproved + ); + assert_noop!( + LaunchPad::approve_proposal( + Origin::signed(11), + USSD, + ), + Error::::ProposalNotFound + ); + }); +} + +#[test] +fn reject_proposal_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::reject_proposal( + Origin::signed(11), + TEST, + )); + }); +} + +#[test] +fn reject_proposal_does_not_work() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: true, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_noop!( + LaunchPad::reject_proposal( + Origin::signed(11), + TEST, + ), + Error::::ProposalAlreadyApproved + ); + assert_noop!( + LaunchPad::reject_proposal( + Origin::signed(11), + USSD, + ), + Error::::ProposalNotFound + ); + }); +} + +#[test] +fn get_contributors_count_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + let proposal = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, proposal.clone()); + assert!( + >::contains_key(TEST), + "Proposal should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 10_000 + )); + assert_ok!(LaunchPad::contribute( + Origin::signed(BOB), + TEST, + 10_000 + )); + assert_ok!(LaunchPad::contribute( + Origin::signed(CHARLIE), + TEST, + 10_000 + )); + + assert_eq!(LaunchPad::get_contributors_count(TEST), 3); + }); +} + +#[test] +fn get_total_amounts_raised_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + assert_eq!( + LaunchPad::get_total_amounts_raised(), + vec![] + ); + let campaign = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, campaign.clone()); + assert!( + >::contains_key(TEST), + "Campaign should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 50_000 + )); + + LaunchPad::on_initialize(40); + + assert_ok!(LaunchPad::on_successful_campaign(>::block_number(), TEST)); + assert_eq!( + LaunchPad::get_total_amounts_raised(), + vec![ + (USSD, 50000), + ] + ); + }); +} + +#[test] +fn on_retire_works() { + ExtBuilder::default() + .one_hundred_thousand_for_all() + .build() + .execute_with(|| { + assert_eq!( + LaunchPad::get_total_amounts_raised(), + vec![] + ); + let campaign = CampaignInfo { + id: TEST, + origin: ALICE.clone(), + beneficiary: BOB, + pool: LaunchPad::campaign_pool(0), + raise_currency: USSD, + sale_token: TEST, + token_price: 10, + crowd_allocation: 10_000, + goal: 100_000, + raised: 0, + contributors_count: 0, + contributions: Vec::new(), + period: 20, + campaign_start: 21, + campaign_end: 0, + campaign_retirement_period: 0, + proposal_retirement_period: 0, + is_approved: false, + is_rejected: false, + is_waiting: true, + is_active: true, + is_successful: false, + is_failed: false, + is_ended: false, + is_claimed: false, + }; + >::insert(TEST, campaign.clone()); + assert!( + >::contains_key(TEST), + "Campaign should be in storage" + ); + + assert_ok!(LaunchPad::approve_proposal( + Origin::signed(11), + TEST, + )); + + LaunchPad::on_initialize(21); + + assert_ok!(LaunchPad::contribute( + Origin::signed(ALICE), + TEST, + 50_000 + )); + + LaunchPad::on_initialize(60); + + assert_ok!(LaunchPad::on_retire(TEST)); + }); +} diff --git a/blockchain/modules/edfis-launchpad/src/weights.rs b/blockchain/modules/edfis-launchpad/src/weights.rs new file mode 100644 index 00000000..22646f55 --- /dev/null +++ b/blockchain/modules/edfis-launchpad/src/weights.rs @@ -0,0 +1,149 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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_edfis_launchpad +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-04-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/setheum-node +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=module_edfis_launchpad +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./blockchain/modules/edfis-launchpad/src/weights.rs +// --template=.maintain/module-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_edfis_launchpad. +pub trait WeightInfo { + fn on_initialize(n: u32, ) -> Weight; + fn make_proposal() -> Weight; + fn contribute() -> Weight; + fn claim_contribution_allocation() -> Weight; + fn claim_campaign_fundraise() -> Weight; + fn approve_proposal() -> Weight; + fn reject_proposal() -> Weight; + fn activate_waiting_campaign() -> Weight; +} + +/// Weights for module_edfis_launchpad using the Setheum node and recommended hardware. +pub struct SetheumWeight(PhantomData); +impl WeightInfo for SetheumWeight { + fn on_initialize(n: u32, ) -> Weight { + Weight::from_parts(16_173_000, 0) + // Standard Error: 0 + .saturating_add((2_000 as u64).saturating_mul(n as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + } + fn make_proposal() -> Weight { + Weight::from_parts(180_406_000, 0) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(7 as u64)) + } + fn contribute() -> Weight { + Weight::from_parts(127_732_000, 0) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } + fn claim_contribution_allocation() -> Weight { + Weight::from_parts(121_893_000, 0) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + fn claim_campaign_fundraise() -> Weight { + Weight::from_parts(38_662_000, 0) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + } + fn approve_proposal() -> Weight { + Weight::from_parts(47_547_000, 0) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + fn reject_proposal() -> Weight { + Weight::from_parts(38_272_000, 0) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + fn activate_waiting_campaign() -> Weight { + Weight::from_parts(35_868_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 () { + fn on_initialize(n: u32, ) -> Weight { + Weight::from_parts(16_173_000, 0) + // Standard Error: 0 + .saturating_add((2_000 as u64).saturating_mul(n as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + } + fn make_proposal() -> Weight { + Weight::from_parts(180_406_000, 0) + .saturating_add(RocksDbWeight::get().reads(6 as u64)) + .saturating_add(RocksDbWeight::get().writes(7 as u64)) + } + fn contribute() -> Weight { + Weight::from_parts(127_732_000, 0) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) + } + fn claim_contribution_allocation() -> Weight { + Weight::from_parts(121_893_000, 0) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + fn claim_campaign_fundraise() -> Weight { + Weight::from_parts(38_662_000, 0) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + } + fn approve_proposal() -> Weight { + Weight::from_parts(47_547_000, 0) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + fn reject_proposal() -> Weight { + Weight::from_parts(38_272_000, 0) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + fn activate_waiting_campaign() -> Weight { + Weight::from_parts(35_868_000, 0) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} diff --git a/blockchain/modules/edfis-oracle/src/weights.rs b/blockchain/modules/edfis-oracle/src/weights.rs index e3d35e02..5651ebb6 100644 --- a/blockchain/modules/edfis-oracle/src/weights.rs +++ b/blockchain/modules/edfis-oracle/src/weights.rs @@ -26,7 +26,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/edfis-swap-legacy/src/weights.rs b/blockchain/modules/edfis-swap-legacy/src/weights.rs index 75b8bb18..9acf300a 100644 --- a/blockchain/modules/edfis-swap-legacy/src/weights.rs +++ b/blockchain/modules/edfis-swap-legacy/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/edfis-swap/src/weights.rs b/blockchain/modules/edfis-swap/src/weights.rs index efc167cd..82e2707d 100644 --- a/blockchain/modules/edfis-swap/src/weights.rs +++ b/blockchain/modules/edfis-swap/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/evm/src/weights.rs b/blockchain/modules/evm/src/weights.rs index fe0e560f..e6699e32 100644 --- a/blockchain/modules/evm/src/weights.rs +++ b/blockchain/modules/evm/src/weights.rs @@ -26,7 +26,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // pallet // --chain=dev diff --git a/blockchain/modules/nft/src/weights.rs b/blockchain/modules/nft/src/weights.rs index cb82347f..b04b7238 100644 --- a/blockchain/modules/nft/src/weights.rs +++ b/blockchain/modules/nft/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/prices/src/weights.rs b/blockchain/modules/prices/src/weights.rs index c02f9f62..a98bec52 100644 --- a/blockchain/modules/prices/src/weights.rs +++ b/blockchain/modules/prices/src/weights.rs @@ -26,7 +26,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/support/TODO.md b/blockchain/modules/support/TODO.md index 3e9cd565..9150f0e1 100644 --- a/blockchain/modules/support/TODO.md +++ b/blockchain/modules/support/TODO.md @@ -54,3 +54,5 @@ 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] - Use this as reference to upgrade the existing implementation of the Launchpad](src/lib.rs): Use this as reference to upgrade the existing implementation of the Launchpad. +- [ ] []() diff --git a/blockchain/modules/support/src/edfis_launchpad.rs b/blockchain/modules/support/src/edfis_launchpad.rs new file mode 100644 index 00000000..d5bd5a17 --- /dev/null +++ b/blockchain/modules/support/src/edfis_launchpad.rs @@ -0,0 +1,101 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 . + +//! Traits for the Launchpad Crowdsales Pallet. + +use codec::{Decode, Encode}; +use sp_runtime::{ + DispatchError, DispatchResult, +}; +use sp_std::{ + cmp::{Eq, PartialEq}, +}; + +/// Abstraction over th Launchpad Proposal system. +pub trait Proposal { + type CurrencyId; + + /// The Campaign Proposal info of `id` + fn proposal_info(id: Self::CurrencyId) -> Option>; + /// Get all proposals + fn all_proposals() -> Vec>; + /// Create new Campaign Proposal with specific `CampaignInfo`, return the `id` of the Campaign + fn new_proposal( + origin: AccountId, + beneficiary: AccountId, + raise_currency: CurrencyId, + sale_token: CurrencyId, + token_price: Balance, + crowd_allocation: Balance, + goal: Balance, + period: BlockNumber, + ) -> DispatchResult; + /// Approve Proposal by `id` at `now`. + fn on_approve_proposal(id: Self::CurrencyId) -> sp_std::result::Result<(), DispatchError>; + /// Reject Proposal by `id` and update storage + fn on_reject_proposal(id: Self::CurrencyId) -> sp_std::result::Result<(), DispatchError>; + /// Remove Proposal by `id` from storage + fn remove_proposal(id: Self::CurrencyId) -> sp_std::result::Result<(), DispatchError>; +} + +/// Abstraction over the Launchpad Campaign system. +pub trait CampaignManager { + type CurrencyId; + + /// The Campaign info of `id` + fn campaign_info(id: Self::CurrencyId) -> Option>; + /// Get all proposals + fn all_campaigns() -> Vec>; + /// Called when a contribution is received. + fn on_contribution( + who: AccountId, + id: Self::CurrencyId, + amount: Balance, + ) -> DispatchResult; + /// Called when a contribution allocation is claimed + fn on_claim_allocation( + who: AccountId, + id: Self::CurrencyId, + ) -> DispatchResult; + /// Called when a campaign's raised fund is claimed + fn on_claim_campaign( + who: AccountId, + id: Self::CurrencyId, + ) -> DispatchResult; + /// Called when a failed campaign is claimed by the proposer + fn on_claim_failed_campaign( + who: AccountId, + id: Self::CurrencyId, + ) -> DispatchResult; + /// Activate a campaign by `id` + fn activate_campaign(id: Self::CurrencyId) -> DispatchResult; + /// Ensure campaign is Valid and Successfully Ended + fn ensure_successfully_ended_campaign(id: Self::CurrencyId) -> DispatchResult; + /// Record Successful Campaign by `id` + fn on_successful_campaign(now: BlockNumber, id: Self::CurrencyId) -> DispatchResult ; + /// Record Failed Campaign by `id` + fn on_failed_campaign(now: BlockNumber, id: Self::CurrencyId) -> DispatchResult ; + /// Called when pool is retired + fn on_retire(id: Self::CurrencyId)-> DispatchResult; + /// Get amount of contributors in a campaign + fn get_contributors_count(id: Self::CurrencyId) -> u32; + /// Get the total amounts raised in protocol + fn get_total_amounts_raised() -> Vec<(CurrencyId, Balance)>; +} diff --git a/blockchain/modules/support/src/edfis.rs b/blockchain/modules/support/src/edfis_swap.rs similarity index 96% rename from blockchain/modules/support/src/edfis.rs rename to blockchain/modules/support/src/edfis_swap.rs index fc9206e8..525bbd26 100644 --- a/blockchain/modules/support/src/edfis.rs +++ b/blockchain/modules/support/src/edfis_swap.rs @@ -1,230 +1,230 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// 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 frame_support::{ensure, traits::Get}; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_core::H160; -use sp_runtime::{DispatchError, DispatchResult, RuntimeDebug}; -use sp_std::{cmp::PartialEq, prelude::*, result::Result}; - -#[derive(RuntimeDebug, Encode, Decode, Clone, Copy, PartialEq, Eq, TypeInfo)] -pub enum SwapLimit { - /// use exact amount supply amount to swap. (exact_supply_amount, minimum_target_amount) - ExactSupply(Balance, Balance), - /// swap to get exact amount target. (maximum_supply_amount, exact_target_amount) - ExactTarget(Balance, Balance), -} - -pub trait SwapManager { - fn get_liquidity_pool( - currency_id_a: CurrencyId, - currency_id_b: CurrencyId - ) -> (Balance, Balance); - - fn get_liquidity_token_address( - currency_id_a: CurrencyId, - currency_id_b: CurrencyId - ) -> Option; - - fn get_swap_amount( - path: &[CurrencyId], - limit: SwapLimit - ) -> Option<(Balance, Balance)>; - - fn get_best_price_swap_path( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - alternative_path_joint_list: Vec>, - ) -> Option<(Vec, Balance, Balance)>; - - fn swap_with_specific_path( - who: &AccountId, - path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; - - fn add_liquidity( - who: &AccountId, - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - max_amount_a: Balance, - max_amount_b: Balance, - min_share_increment: Balance, - ) -> Result<(Balance, Balance, Balance), DispatchError>; - - fn remove_liquidity( - who: &AccountId, - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - remove_share: Balance, - min_withdrawn_a: Balance, - min_withdrawn_b: Balance, - by_unstake: bool, - ) -> Result<(Balance, Balance), DispatchError>; -} - -pub trait Swap -where - CurrencyId: Clone, -{ - fn get_swap_amount( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Option<(Balance, Balance)>; - - fn swap( - who: &AccountId, - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; - - fn swap_by_path( - who: &AccountId, - swap_path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; -} - -#[derive(Eq, PartialEq, RuntimeDebug)] -pub enum SwapError { - CannotSwap, -} - -impl Into for SwapError { - fn into(self) -> DispatchError { - DispatchError::Other("Cannot swap") - } -} - -// Dex wrapper of Swap implementation -pub struct SpecificJointsSwap(sp_std::marker::PhantomData<(Dex, Joints)>); - -impl Swap - for SpecificJointsSwap -where - Dex: SwapManager, - Joints: Get>>, - Balance: Clone, - CurrencyId: Clone, -{ - fn get_swap_amount( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Option<(Balance, Balance)> { - >::get_best_price_swap_path( - supply_currency_id, - target_currency_id, - limit, - Joints::get(), - ) - .map(|(_, supply_amount, target_amount)| (supply_amount, target_amount)) - } - - fn swap( - who: &AccountId, - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> sp_std::result::Result<(Balance, Balance), DispatchError> { - let path = >::get_best_price_swap_path( - supply_currency_id, - target_currency_id, - limit.clone(), - Joints::get(), - ) - .ok_or_else(|| Into::::into(SwapError::CannotSwap))? - .0; - - >::swap_with_specific_path(who, &path, limit) - } - - fn swap_by_path( - who: &AccountId, - swap_path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError> { - >::swap_with_specific_path(who, swap_path, limit) - } -} - -#[cfg(feature = "std")] -impl SwapManager for () -where - Balance: Default, -{ - fn get_liquidity_pool(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { - Default::default() - } - - fn get_liquidity_token_address(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> Option { - Some(Default::default()) - } - - fn get_swap_amount(_path: &[CurrencyId], _limit: SwapLimit) -> Option<(Balance, Balance)> { - Some(Default::default()) - } - - fn get_best_price_swap_path( - _supply_currency_id: CurrencyId, - _target_currency_id: CurrencyId, - _limit: SwapLimit, - _alternative_path_joint_list: Vec>, - ) -> Option<(Vec, Balance, Balance)> { - Some(Default::default()) - } - - fn swap_with_specific_path( - _who: &AccountId, - _path: &[CurrencyId], - _limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError> { - Ok(Default::default()) - } - - fn add_liquidity( - _who: &AccountId, - _currency_id_a: CurrencyId, - _currency_id_b: CurrencyId, - _max_amount_a: Balance, - _max_amount_b: Balance, - _min_share_increment: Balance, - ) -> Result<(Balance, Balance, Balance), DispatchError> { - Ok(Default::default()) - } - - fn remove_liquidity( - _who: &AccountId, - _currency_id_a: CurrencyId, - _currency_id_b: CurrencyId, - _remove_share: Balance, - _min_withdrawn_a: Balance, - _min_withdrawn_b: Balance, - _by_unstake: bool, - ) -> Result<(Balance, Balance), DispatchError> { - Ok(Default::default()) - } -} +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 frame_support::{ensure, traits::Get}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_core::H160; +use sp_runtime::{DispatchError, DispatchResult, RuntimeDebug}; +use sp_std::{cmp::PartialEq, prelude::*, result::Result}; + +#[derive(RuntimeDebug, Encode, Decode, Clone, Copy, PartialEq, Eq, TypeInfo)] +pub enum SwapLimit { + /// use exact amount supply amount to swap. (exact_supply_amount, minimum_target_amount) + ExactSupply(Balance, Balance), + /// swap to get exact amount target. (maximum_supply_amount, exact_target_amount) + ExactTarget(Balance, Balance), +} + +pub trait SwapManager { + fn get_liquidity_pool( + currency_id_a: CurrencyId, + currency_id_b: CurrencyId + ) -> (Balance, Balance); + + fn get_liquidity_token_address( + currency_id_a: CurrencyId, + currency_id_b: CurrencyId + ) -> Option; + + fn get_swap_amount( + path: &[CurrencyId], + limit: SwapLimit + ) -> Option<(Balance, Balance)>; + + fn get_best_price_swap_path( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + alternative_path_joint_list: Vec>, + ) -> Option<(Vec, Balance, Balance)>; + + fn swap_with_specific_path( + who: &AccountId, + path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; + + fn add_liquidity( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + max_amount_a: Balance, + max_amount_b: Balance, + min_share_increment: Balance, + ) -> Result<(Balance, Balance, Balance), DispatchError>; + + fn remove_liquidity( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + remove_share: Balance, + min_withdrawn_a: Balance, + min_withdrawn_b: Balance, + by_unstake: bool, + ) -> Result<(Balance, Balance), DispatchError>; +} + +pub trait Swap +where + CurrencyId: Clone, +{ + fn get_swap_amount( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Option<(Balance, Balance)>; + + fn swap( + who: &AccountId, + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; + + fn swap_by_path( + who: &AccountId, + swap_path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; +} + +#[derive(Eq, PartialEq, RuntimeDebug)] +pub enum SwapError { + CannotSwap, +} + +impl Into for SwapError { + fn into(self) -> DispatchError { + DispatchError::Other("Cannot swap") + } +} + +// Dex wrapper of Swap implementation +pub struct SpecificJointsSwap(sp_std::marker::PhantomData<(Dex, Joints)>); + +impl Swap + for SpecificJointsSwap +where + Dex: SwapManager, + Joints: Get>>, + Balance: Clone, + CurrencyId: Clone, +{ + fn get_swap_amount( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Option<(Balance, Balance)> { + >::get_best_price_swap_path( + supply_currency_id, + target_currency_id, + limit, + Joints::get(), + ) + .map(|(_, supply_amount, target_amount)| (supply_amount, target_amount)) + } + + fn swap( + who: &AccountId, + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> sp_std::result::Result<(Balance, Balance), DispatchError> { + let path = >::get_best_price_swap_path( + supply_currency_id, + target_currency_id, + limit.clone(), + Joints::get(), + ) + .ok_or_else(|| Into::::into(SwapError::CannotSwap))? + .0; + + >::swap_with_specific_path(who, &path, limit) + } + + fn swap_by_path( + who: &AccountId, + swap_path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError> { + >::swap_with_specific_path(who, swap_path, limit) + } +} + +#[cfg(feature = "std")] +impl SwapManager for () +where + Balance: Default, +{ + fn get_liquidity_pool(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { + Default::default() + } + + fn get_liquidity_token_address(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> Option { + Some(Default::default()) + } + + fn get_swap_amount(_path: &[CurrencyId], _limit: SwapLimit) -> Option<(Balance, Balance)> { + Some(Default::default()) + } + + fn get_best_price_swap_path( + _supply_currency_id: CurrencyId, + _target_currency_id: CurrencyId, + _limit: SwapLimit, + _alternative_path_joint_list: Vec>, + ) -> Option<(Vec, Balance, Balance)> { + Some(Default::default()) + } + + fn swap_with_specific_path( + _who: &AccountId, + _path: &[CurrencyId], + _limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError> { + Ok(Default::default()) + } + + fn add_liquidity( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + _max_amount_a: Balance, + _max_amount_b: Balance, + _min_share_increment: Balance, + ) -> Result<(Balance, Balance, Balance), DispatchError> { + Ok(Default::default()) + } + + fn remove_liquidity( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + _remove_share: Balance, + _min_withdrawn_a: Balance, + _min_withdrawn_b: Balance, + _by_unstake: bool, + ) -> Result<(Balance, Balance), DispatchError> { + Ok(Default::default()) + } +} diff --git a/blockchain/modules/support/src/edfis_legacy.rs b/blockchain/modules/support/src/edfis_swap_legacy.rs similarity index 96% rename from blockchain/modules/support/src/edfis_legacy.rs rename to blockchain/modules/support/src/edfis_swap_legacy.rs index e9848cdc..aea21420 100644 --- a/blockchain/modules/support/src/edfis_legacy.rs +++ b/blockchain/modules/support/src/edfis_swap_legacy.rs @@ -1,223 +1,223 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// 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 frame_support::{ensure, traits::Get}; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_core::H160; -use sp_runtime::{DispatchError, RuntimeDebug}; -use sp_std::{cmp::PartialEq, prelude::*, result::Result}; - -#[derive(RuntimeDebug, Encode, Decode, Clone, Copy, PartialEq, Eq, TypeInfo)] -pub enum SwapLimit { - /// use exact amount supply amount to swap. (exact_supply_amount, minimum_target_amount) - ExactSupply(Balance, Balance), - /// swap to get exact amount target. (maximum_supply_amount, exact_target_amount) - ExactTarget(Balance, Balance), -} - -pub trait SwapManager { - fn get_liquidity_pool(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance); - - fn get_liquidity_token_address(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> Option; - - fn get_swap_amount(path: &[CurrencyId], limit: SwapLimit) -> Option<(Balance, Balance)>; - - fn get_best_price_swap_path( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - alternative_path_joint_list: Vec>, - ) -> Option<(Vec, Balance, Balance)>; - - fn swap_with_specific_path( - who: &AccountId, - path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; - - fn add_liquidity( - who: &AccountId, - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - max_amount_a: Balance, - max_amount_b: Balance, - min_share_increment: Balance, - stake_increment_share: bool, - ) -> Result<(Balance, Balance, Balance), DispatchError>; - - fn remove_liquidity( - who: &AccountId, - currency_id_a: CurrencyId, - currency_id_b: CurrencyId, - remove_share: Balance, - min_withdrawn_a: Balance, - min_withdrawn_b: Balance, - by_unstake: bool, - ) -> Result<(Balance, Balance), DispatchError>; -} - -pub trait Swap -where - CurrencyId: Clone, -{ - fn get_swap_amount( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Option<(Balance, Balance)>; - - fn swap( - who: &AccountId, - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; - - fn swap_by_path( - who: &AccountId, - swap_path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError>; -} - -#[derive(Eq, PartialEq, RuntimeDebug)] -pub enum SwapError { - CannotSwap, -} - -impl Into for SwapError { - fn into(self) -> DispatchError { - DispatchError::Other("Cannot swap") - } -} - -// Dex wrapper of Swap implementation -pub struct SpecificJointsSwap(sp_std::marker::PhantomData<(Dex, Joints)>); - -impl Swap - for SpecificJointsSwap -where - Dex: SwapManager, - Joints: Get>>, - Balance: Clone, - CurrencyId: Clone, -{ - fn get_swap_amount( - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> Option<(Balance, Balance)> { - >::get_best_price_swap_path( - supply_currency_id, - target_currency_id, - limit, - Joints::get(), - ) - .map(|(_, supply_amount, target_amount)| (supply_amount, target_amount)) - } - - fn swap( - who: &AccountId, - supply_currency_id: CurrencyId, - target_currency_id: CurrencyId, - limit: SwapLimit, - ) -> sp_std::result::Result<(Balance, Balance), DispatchError> { - let path = >::get_best_price_swap_path( - supply_currency_id, - target_currency_id, - limit.clone(), - Joints::get(), - ) - .ok_or_else(|| Into::::into(SwapError::CannotSwap))? - .0; - - >::swap_with_specific_path(who, &path, limit) - } - - fn swap_by_path( - who: &AccountId, - swap_path: &[CurrencyId], - limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError> { - >::swap_with_specific_path(who, swap_path, limit) - } -} - -#[cfg(feature = "std")] -impl SwapManager for () -where - Balance: Default, -{ - fn get_liquidity_pool(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { - Default::default() - } - - fn get_liquidity_token_address(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> Option { - Some(Default::default()) - } - - fn get_swap_amount(_path: &[CurrencyId], _limit: SwapLimit) -> Option<(Balance, Balance)> { - Some(Default::default()) - } - - fn get_best_price_swap_path( - _supply_currency_id: CurrencyId, - _target_currency_id: CurrencyId, - _limit: SwapLimit, - _alternative_path_joint_list: Vec>, - ) -> Option<(Vec, Balance, Balance)> { - Some(Default::default()) - } - - fn swap_with_specific_path( - _who: &AccountId, - _path: &[CurrencyId], - _limit: SwapLimit, - ) -> Result<(Balance, Balance), DispatchError> { - Ok(Default::default()) - } - - fn add_liquidity( - _who: &AccountId, - _currency_id_a: CurrencyId, - _currency_id_b: CurrencyId, - _max_amount_a: Balance, - _max_amount_b: Balance, - _min_share_increment: Balance, - _stake_increment_share: bool, - ) -> Result<(Balance, Balance, Balance), DispatchError> { - Ok(Default::default()) - } - - fn remove_liquidity( - _who: &AccountId, - _currency_id_a: CurrencyId, - _currency_id_b: CurrencyId, - _remove_share: Balance, - _min_withdrawn_a: Balance, - _min_withdrawn_b: Balance, - _by_unstake: bool, - ) -> Result<(Balance, Balance), DispatchError> { - Ok(Default::default()) - } -} +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 frame_support::{ensure, traits::Get}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_core::H160; +use sp_runtime::{DispatchError, RuntimeDebug}; +use sp_std::{cmp::PartialEq, prelude::*, result::Result}; + +#[derive(RuntimeDebug, Encode, Decode, Clone, Copy, PartialEq, Eq, TypeInfo)] +pub enum SwapLimit { + /// use exact amount supply amount to swap. (exact_supply_amount, minimum_target_amount) + ExactSupply(Balance, Balance), + /// swap to get exact amount target. (maximum_supply_amount, exact_target_amount) + ExactTarget(Balance, Balance), +} + +pub trait SwapManager { + fn get_liquidity_pool(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> (Balance, Balance); + + fn get_liquidity_token_address(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> Option; + + fn get_swap_amount(path: &[CurrencyId], limit: SwapLimit) -> Option<(Balance, Balance)>; + + fn get_best_price_swap_path( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + alternative_path_joint_list: Vec>, + ) -> Option<(Vec, Balance, Balance)>; + + fn swap_with_specific_path( + who: &AccountId, + path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; + + fn add_liquidity( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + max_amount_a: Balance, + max_amount_b: Balance, + min_share_increment: Balance, + stake_increment_share: bool, + ) -> Result<(Balance, Balance, Balance), DispatchError>; + + fn remove_liquidity( + who: &AccountId, + currency_id_a: CurrencyId, + currency_id_b: CurrencyId, + remove_share: Balance, + min_withdrawn_a: Balance, + min_withdrawn_b: Balance, + by_unstake: bool, + ) -> Result<(Balance, Balance), DispatchError>; +} + +pub trait Swap +where + CurrencyId: Clone, +{ + fn get_swap_amount( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Option<(Balance, Balance)>; + + fn swap( + who: &AccountId, + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; + + fn swap_by_path( + who: &AccountId, + swap_path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError>; +} + +#[derive(Eq, PartialEq, RuntimeDebug)] +pub enum SwapError { + CannotSwap, +} + +impl Into for SwapError { + fn into(self) -> DispatchError { + DispatchError::Other("Cannot swap") + } +} + +// Dex wrapper of Swap implementation +pub struct SpecificJointsSwap(sp_std::marker::PhantomData<(Dex, Joints)>); + +impl Swap + for SpecificJointsSwap +where + Dex: SwapManager, + Joints: Get>>, + Balance: Clone, + CurrencyId: Clone, +{ + fn get_swap_amount( + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> Option<(Balance, Balance)> { + >::get_best_price_swap_path( + supply_currency_id, + target_currency_id, + limit, + Joints::get(), + ) + .map(|(_, supply_amount, target_amount)| (supply_amount, target_amount)) + } + + fn swap( + who: &AccountId, + supply_currency_id: CurrencyId, + target_currency_id: CurrencyId, + limit: SwapLimit, + ) -> sp_std::result::Result<(Balance, Balance), DispatchError> { + let path = >::get_best_price_swap_path( + supply_currency_id, + target_currency_id, + limit.clone(), + Joints::get(), + ) + .ok_or_else(|| Into::::into(SwapError::CannotSwap))? + .0; + + >::swap_with_specific_path(who, &path, limit) + } + + fn swap_by_path( + who: &AccountId, + swap_path: &[CurrencyId], + limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError> { + >::swap_with_specific_path(who, swap_path, limit) + } +} + +#[cfg(feature = "std")] +impl SwapManager for () +where + Balance: Default, +{ + fn get_liquidity_pool(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> (Balance, Balance) { + Default::default() + } + + fn get_liquidity_token_address(_currency_id_a: CurrencyId, _currency_id_b: CurrencyId) -> Option { + Some(Default::default()) + } + + fn get_swap_amount(_path: &[CurrencyId], _limit: SwapLimit) -> Option<(Balance, Balance)> { + Some(Default::default()) + } + + fn get_best_price_swap_path( + _supply_currency_id: CurrencyId, + _target_currency_id: CurrencyId, + _limit: SwapLimit, + _alternative_path_joint_list: Vec>, + ) -> Option<(Vec, Balance, Balance)> { + Some(Default::default()) + } + + fn swap_with_specific_path( + _who: &AccountId, + _path: &[CurrencyId], + _limit: SwapLimit, + ) -> Result<(Balance, Balance), DispatchError> { + Ok(Default::default()) + } + + fn add_liquidity( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + _max_amount_a: Balance, + _max_amount_b: Balance, + _min_share_increment: Balance, + _stake_increment_share: bool, + ) -> Result<(Balance, Balance, Balance), DispatchError> { + Ok(Default::default()) + } + + fn remove_liquidity( + _who: &AccountId, + _currency_id_a: CurrencyId, + _currency_id_b: CurrencyId, + _remove_share: Balance, + _min_withdrawn_a: Balance, + _min_withdrawn_b: Balance, + _by_unstake: bool, + ) -> Result<(Balance, Balance), DispatchError> { + Ok(Default::default()) + } +} diff --git a/blockchain/modules/support/src/lib.rs b/blockchain/modules/support/src/lib.rs index ba2871a9..6b4ef3eb 100644 --- a/blockchain/modules/support/src/lib.rs +++ b/blockchain/modules/support/src/lib.rs @@ -32,8 +32,9 @@ use sp_std::{prelude::*, result::Result}; use xcm::prelude::*; pub mod bounded; -pub mod edfis; -pub mod edfis_legacy; +pub mod edfis_launchpad; +pub mod edfis_swap; +pub mod edfis_swap_legacy; pub mod evm; pub mod liquid_staking; pub mod ecdp; @@ -41,8 +42,9 @@ pub mod incentives; pub mod mocks; pub use crate::bounded::*; -pub use crate::edfis::*; -pub use crate::edfis_legacy::*; +pub use crate::edfis_launchpad::*; +pub use crate::edfis_swap::*; +pub use crate::edfis_swap_legacy::*; pub use crate::evm::*; pub use crate::liquid_staking::*; pub use crate::ecdp::*; @@ -186,138 +188,139 @@ pub trait BuyWeightRate { fn calculate_rate(location: MultiLocation) -> Option; } -/// The Structure of a Campaign info. -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode, Debug, Clone))] -pub struct CampaignInfo { - /// The Campaign Id - pub id: CampaignId, - /// Campaign Creator - pub origin: AccountId, - /// Project Name - pub project_name: Vec, - /// Project Logo - pub project_logo: Vec, - /// Project Description - pub project_description: Vec, - /// Project Website - pub project_website: Vec, - /// Campaign Beneficiary - pub beneficiary: AccountId, - /// Campaign Pool AccountId - pub pool: AccountId, - /// Currency type for the fundraise - pub raise_currency: CurrencyId, - /// Currency type (Token) for crowdsale - pub sale_token: CurrencyId, - /// Crowdsale Token Price - Amount of raise_currency per sale_token - pub token_price: Balance, - /// Crowdsale Token amount for sale - pub crowd_allocation: Balance, - /// The Fundraise Goal - HardCap - pub goal: Balance, - /// The Fundraise Amount raised - HardCap - pub raised: Balance, - /// The number of contributors to the campaign - pub contributors_count: u32, - /// The Campaign contributions - /// account_id, contribution, allocation, bool:claimed_allocation - pub contributions: Vec<(AccountId, Balance, Balance, bool)>, - /// The period that the campaign runs for. - pub period: BlockNumber, - /// The time when the campaign starts. - pub campaign_start: BlockNumber, - /// The time when the campaign ends. - pub campaign_end: BlockNumber, - /// The time when the campaign fund retires. - pub campaign_retirement_period: BlockNumber, - /// The time when a rejected proposal is removed from storage. - pub proposal_retirement_period: BlockNumber, - /// Is the campaign approved? - pub is_approved: bool, - /// Is the proposal rejected? - pub is_rejected: bool, - /// Is the campaign in waiting period? - pub is_waiting: bool, - /// Is the campaign active? - pub is_active: bool, - /// Is the campaign Successful? - pub is_successful: bool, - /// Is the campaign Failed? - pub is_failed: bool, - /// Is the campaign Ended? - pub is_ended: bool, - /// Is the campaign funds raised claimed - pub is_claimed: bool, -} +// TODO:[src/lib.rs:0] - Use this as reference to upgrade the existing implementation of the Launchpad +// /// The Structure of a Campaign info. +// #[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode, Debug, Clone))] +// pub struct CampaignInfo { +// /// The Campaign Id +// pub id: CampaignId, +// /// Campaign Creator +// pub origin: AccountId, +// /// Project Name +// pub project_name: Vec, +// /// Project Logo +// pub project_logo: Vec, +// /// Project Description +// pub project_description: Vec, +// /// Project Website +// pub project_website: Vec, +// /// Campaign Beneficiary +// pub beneficiary: AccountId, +// /// Campaign Pool AccountId +// pub pool: AccountId, +// /// Currency type for the fundraise +// pub raise_currency: CurrencyId, +// /// Currency type (Token) for crowdsale +// pub sale_token: CurrencyId, +// /// Crowdsale Token Price - Amount of raise_currency per sale_token +// pub token_price: Balance, +// /// Crowdsale Token amount for sale +// pub crowd_allocation: Balance, +// /// The Fundraise Goal - HardCap +// pub goal: Balance, +// /// The Fundraise Amount raised - HardCap +// pub raised: Balance, +// /// The number of contributors to the campaign +// pub contributors_count: u32, +// /// The Campaign contributions +// /// account_id, contribution, allocation, bool:claimed_allocation +// pub contributions: Vec<(AccountId, Balance, Balance, bool)>, +// /// The period that the campaign runs for. +// pub period: BlockNumber, +// /// The time when the campaign starts. +// pub campaign_start: BlockNumber, +// /// The time when the campaign ends. +// pub campaign_end: BlockNumber, +// /// The time when the campaign fund retires. +// pub campaign_retirement_period: BlockNumber, +// /// The time when a rejected proposal is removed from storage. +// pub proposal_retirement_period: BlockNumber, +// /// Is the campaign approved? +// pub is_approved: bool, +// /// Is the proposal rejected? +// pub is_rejected: bool, +// /// Is the campaign in waiting period? +// pub is_waiting: bool, +// /// Is the campaign active? +// pub is_active: bool, +// /// Is the campaign Successful? +// pub is_successful: bool, +// /// Is the campaign Failed? +// pub is_failed: bool, +// /// Is the campaign Ended? +// pub is_ended: bool, +// /// Is the campaign funds raised claimed +// pub is_claimed: bool, +// } -/// Abstraction over th Launchpad Proposal system. -pub trait Proposal { - /// Get all proposals - fn all_proposals() -> Vec>; - /// The Campaign Proposal info of `id` - fn proposal_info(id: CampaignId) -> Option>; - /// Create new Campaign Proposal with specific `CampaignInfo`, return the `id` of the Campaign - fn new_proposal( - origin: AccountId, - project_name: Vec, - project_logo: Vec, - project_description: Vec, - project_website: Vec, - beneficiary: AccountId, - raise_currency: CurrencyId, - sale_token: CurrencyId, - token_price: AsBalance, - crowd_allocation: AsBalance, - goal: AsBalance, - period: BlockNumber, - ) -> DispatchResult; - /// Approve Proposal by `id` at `now`. - fn on_approve_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; - /// Reject Proposal by `id` and update storage - fn on_reject_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; - /// Remove Proposal by `id` from storage - fn remove_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; -} +// /// Abstraction over th Launchpad Proposal system. +// pub trait Proposal { +// /// Get all proposals +// fn all_proposals() -> Vec>; +// /// The Campaign Proposal info of `id` +// fn proposal_info(id: CampaignId) -> Option>; +// /// Create new Campaign Proposal with specific `CampaignInfo`, return the `id` of the Campaign +// fn new_proposal( +// origin: AccountId, +// project_name: Vec, +// project_logo: Vec, +// project_description: Vec, +// project_website: Vec, +// beneficiary: AccountId, +// raise_currency: CurrencyId, +// sale_token: CurrencyId, +// token_price: AsBalance, +// crowd_allocation: AsBalance, +// goal: AsBalance, +// period: BlockNumber, +// ) -> DispatchResult; +// /// Approve Proposal by `id` at `now`. +// fn on_approve_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; +// /// Reject Proposal by `id` and update storage +// fn on_reject_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; +// /// Remove Proposal by `id` from storage +// fn remove_proposal(id: CampaignId) -> sp_std::result::Result<(), DispatchError>; +// } -/// Abstraction over the Launchpad Campaign system. -pub trait CampaignManager { - /// The Campaign info of `id` - fn campaign_info(id: CampaignId) -> Option>; - /// Get all proposals - fn all_campaigns() -> Vec>; - /// Called when a contribution is received. - fn on_contribution( - who: AccountId, - id: CampaignId, - amount: AsBalance, - ) -> DispatchResult; - /// Called when a contribution allocation is claimed - fn on_claim_allocation( - who: AccountId, - id: CampaignId, - ) -> DispatchResult; - /// Called when a campaign's raised fund is claimed - fn on_claim_campaign( - who: AccountId, - id: CampaignId, - ) -> DispatchResult; - /// Called when a failed campaign is claimed by the proposer - fn on_claim_failed_campaign( - who: AccountId, - id: CampaignId, - ) -> DispatchResult; - /// Activate a campaign by `id` - fn activate_campaign(id: CampaignId) -> DispatchResult; - /// Ensure campaign is Valid and Successfully Ended - fn ensure_successfully_ended_campaign(id: CampaignId) -> DispatchResult; - /// Record Successful Campaign by `id` - fn on_successful_campaign(now: BlockNumber, id: CampaignId) -> DispatchResult ; - /// Record Failed Campaign by `id` - fn on_failed_campaign(now: BlockNumber, id: CampaignId) -> DispatchResult ; - /// Called when pool is retired - fn on_retire(id: CampaignId)-> DispatchResult; - /// Get amount of contributors in a campaign - fn get_contributors_count(id: CampaignId) -> u32; - /// Get the total amounts raised in protocol - fn get_total_amounts_raised() -> Vec<(CurrencyId, AsBalance)>; -} +// /// Abstraction over the Launchpad Campaign system. +// pub trait CampaignManager { +// /// The Campaign info of `id` +// fn campaign_info(id: CampaignId) -> Option>; +// /// Get all proposals +// fn all_campaigns() -> Vec>; +// /// Called when a contribution is received. +// fn on_contribution( +// who: AccountId, +// id: CampaignId, +// amount: AsBalance, +// ) -> DispatchResult; +// /// Called when a contribution allocation is claimed +// fn on_claim_allocation( +// who: AccountId, +// id: CampaignId, +// ) -> DispatchResult; +// /// Called when a campaign's raised fund is claimed +// fn on_claim_campaign( +// who: AccountId, +// id: CampaignId, +// ) -> DispatchResult; +// /// Called when a failed campaign is claimed by the proposer +// fn on_claim_failed_campaign( +// who: AccountId, +// id: CampaignId, +// ) -> DispatchResult; +// /// Activate a campaign by `id` +// fn activate_campaign(id: CampaignId) -> DispatchResult; +// /// Ensure campaign is Valid and Successfully Ended +// fn ensure_successfully_ended_campaign(id: CampaignId) -> DispatchResult; +// /// Record Successful Campaign by `id` +// fn on_successful_campaign(now: BlockNumber, id: CampaignId) -> DispatchResult ; +// /// Record Failed Campaign by `id` +// fn on_failed_campaign(now: BlockNumber, id: CampaignId) -> DispatchResult ; +// /// Called when pool is retired +// fn on_retire(id: CampaignId)-> DispatchResult; +// /// Get amount of contributors in a campaign +// fn get_contributors_count(id: CampaignId) -> u32; +// /// Get the total amounts raised in protocol +// fn get_total_amounts_raised() -> Vec<(CurrencyId, AsBalance)>; +// } diff --git a/blockchain/modules/transaction-pause/src/weights.rs b/blockchain/modules/transaction-pause/src/weights.rs index fd3262f1..a85c7f3b 100644 --- a/blockchain/modules/transaction-pause/src/weights.rs +++ b/blockchain/modules/transaction-pause/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/transaction-payment/src/weights.rs b/blockchain/modules/transaction-payment/src/weights.rs index b9540f6d..95fa0154 100644 --- a/blockchain/modules/transaction-payment/src/weights.rs +++ b/blockchain/modules/transaction-payment/src/weights.rs @@ -25,7 +25,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/unified-accounts/src/weights.rs b/blockchain/modules/unified-accounts/src/weights.rs index 85bc3f9e..6d3a9d3e 100644 --- a/blockchain/modules/unified-accounts/src/weights.rs +++ b/blockchain/modules/unified-accounts/src/weights.rs @@ -26,7 +26,7 @@ //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/setheum +// target/release/setheum-node // benchmark // --chain=dev // --steps=50 diff --git a/blockchain/modules/vesting/src/weights.rs b/blockchain/modules/vesting/src/weights.rs index 251132c6..616257c7 100644 --- a/blockchain/modules/vesting/src/weights.rs +++ b/blockchain/modules/vesting/src/weights.rs @@ -1,60 +1,60 @@ -//! Autogenerated weights for orlm_vesting -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-05-04, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// /target/release/setheum -// benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=module_vesting -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./vesting/src/weights.rs -// --template -// ..maintain/orml-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 orml_vesting. -pub trait WeightInfo { - fn vested_transfer() -> Weight; - fn claim(i: u32, ) -> Weight; - fn update_vesting_schedules(i: u32, ) -> Weight; -} - -/// Default weights. -impl WeightInfo for () { - fn vested_transfer() -> Weight { - Weight::from_parts(69_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) - } - fn claim(i: u32, ) -> Weight { - Weight::from_parts(31_747_000, 0) - // Standard Error: 4_000 - .saturating_add(Weight::from_parts(63_000, 0).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - fn update_vesting_schedules(i: u32, ) -> Weight { - Weight::from_parts(29_457_000, 0) - // Standard Error: 4_000 - .saturating_add(Weight::from_parts(117_000, 0).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } -} +//! Autogenerated weights for orlm_vesting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-05-04, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// /target/release/setheum-node +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=module_vesting +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./vesting/src/weights.rs +// --template +// ..maintain/orml-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 orml_vesting. +pub trait WeightInfo { + fn vested_transfer() -> Weight; + fn claim(i: u32, ) -> Weight; + fn update_vesting_schedules(i: u32, ) -> Weight; +} + +/// Default weights. +impl WeightInfo for () { + fn vested_transfer() -> Weight { + Weight::from_parts(69_000_000, 0) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) + } + fn claim(i: u32, ) -> Weight { + Weight::from_parts(31_747_000, 0) + // Standard Error: 4_000 + .saturating_add(Weight::from_parts(63_000, 0).saturating_mul(i as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn update_vesting_schedules(i: u32, ) -> Weight { + Weight::from_parts(29_457_000, 0) + // Standard Error: 4_000 + .saturating_add(Weight::from_parts(117_000, 0).saturating_mul(i as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } +} diff --git a/blockchain/primitives/src/edfis_launchpad.rs b/blockchain/primitives/src/edfis_launchpad.rs new file mode 100644 index 00000000..87c15428 --- /dev/null +++ b/blockchain/primitives/src/edfis_launchpad.rs @@ -0,0 +1,90 @@ +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 . + +//! Primitives for the Edfis Launchpad module. + +use codec::{Decode, Encode}; +use sp_runtime::{ + DispatchError, DispatchResult, RuntimeDebug, +}; +use sp_std::{ + cmp::{Eq, PartialEq}, +}; + +/// Launchpad Campaign ID +pub type CampaignId = u32; + +/// The Structure of a Campaign info. +// #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct CampaignInfo { + /// The Campaign Id + pub id: CurrencyId, + /// Campaign Creator + pub origin: AccountId, + /// Campaign Beneficiary + pub beneficiary: AccountId, + /// Campaign Pool AccountId + pub pool: AccountId, + /// Currency type for the fundraise + pub raise_currency: CurrencyId, + /// Currency type (Token) for crowdsale + pub sale_token: CurrencyId, + /// Crowdsale Token Price - Amount of raise_currency per sale_token + pub token_price: Balance, + /// Crowdsale Token amount for sale + pub crowd_allocation: Balance, + /// The Fundraise Goal - HardCap + pub goal: Balance, + /// The Fundraise Amount raised - HardCap + pub raised: Balance, + /// The number of contributors to the campaign + pub contributors_count: u32, + /// The Campaign contributions + /// account_id, contribution, allocation, bool:claimed_allocation + pub contributions: Vec<(AccountId, Balance, Balance, bool)>, + /// The period that the campaign runs for. + pub period: BlockNumber, + /// The time when the campaign starts. + pub campaign_start: BlockNumber, + /// The time when the campaign ends. + pub campaign_end: BlockNumber, + /// The time when the campaign fund retires. + pub campaign_retirement_period: BlockNumber, + /// The time when a rejected proposal is removed from storage. + pub proposal_retirement_period: BlockNumber, + /// Is the campaign approved? + pub is_approved: bool, + /// Is the proposal rejected? + pub is_rejected: bool, + /// Is the campaign in waiting period? + pub is_waiting: bool, + /// Is the campaign active? + pub is_active: bool, + /// Is the campaign Successful? + pub is_successful: bool, + /// Is the campaign Failed? + pub is_failed: bool, + /// Is the campaign Ended? + pub is_ended: bool, + /// Is the campaign funds raised claimed + pub is_claimed: bool, +} diff --git a/blockchain/primitives/src/lib.rs b/blockchain/primitives/src/lib.rs index 60b87042..14a973b1 100644 --- a/blockchain/primitives/src/lib.rs +++ b/blockchain/primitives/src/lib.rs @@ -1,212 +1,211 @@ -// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم - -// 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 . - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::unnecessary_cast)] -#![allow(clippy::upper_case_acronyms)] - -pub mod bonding; -pub mod currency; -pub mod evm; -pub mod nft; -pub mod signature; -pub mod task; -pub mod testing; -pub mod unchecked_extrinsic; - -pub use testing::*; - -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_core::U256; -use sp_runtime::{ - generic, - traits::{BlakeTwo256, IdentifyAccount, Verify}, - FixedU128, RuntimeDebug, -}; -use sp_std::prelude::*; - -pub use currency::{CurrencyId, DexShare, TokenSymbol}; -pub use evm::{convert_decimals_from_evm, convert_decimals_to_evm}; - -#[cfg(test)] -mod tests; - -/// An index to a block. -pub type BlockNumber = u32; - -/// Alias to 512-bit hash when used in the context of a transaction signature on -/// the chain. -pub type Signature = signature::SetheumMultiSignature; - -/// Alias to the public key used for this chain, actually a `MultiSigner`. Like -/// the signature, this also isn't a fixed size when encoded, as different -/// cryptos have different size public keys. -pub type AccountPublic = ::Signer; - -/// Alias to the opaque account ID type for this chain, actually a -/// `AccountId32`. This is always 32 bytes. -pub type AccountId = ::AccountId; - -/// The type for looking up accounts. We don't expect more than 4 billion of -/// them. -pub type AccountIndex = u32; - -/// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; - -/// Index of a transaction in the chain. 32-bit should be plenty. -pub type Nonce = u32; - -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; - -/// An instant or duration in time. -pub type Moment = u64; - -/// Counter for the number of eras that have passed. -pub type EraIndex = u32; - -/// Balance of an account. -pub type Balance = u128; - -/// Signed version of Balance -pub type Amount = i128; - -/// Fees type primarily for Edfis `ExchangeFee` and `TradingFee`. -pub type Fees = u128; - -/// Auction ID -pub type AuctionId = u32; - -/// Launchpad Campaign ID -pub type CampaignId = u32; - -/// Share type -pub type Share = u128; - -/// Header type. -pub type Header = generic::Header; - -/// Block type. -pub type Block = generic::Block; - -/// Block ID. -pub type BlockId = generic::BlockId; - -/// Opaque, encoded, unchecked extrinsic. -pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - -/// Fee multiplier. -pub type Multiplier = FixedU128; - -#[derive( - Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, Serialize, Deserialize, -)] -pub enum AuthoritysOriginId { - Root, - Treasury, - LiquidSeeStakingTreasury, - LiquidEdfStakingTreasury, - SetterEcdpTreasury, - SlickUsdEcdpTreasury, - TreasuryReserve, -} - -#[derive( - Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, Serialize, Deserialize, -)] -pub enum DataProviderId { - Aggregated = 0, - Setheum = 1, -} - -#[derive( - Encode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, MaxEncodedLen, Serialize, Deserialize, -)] -pub struct TradingPair(CurrencyId, CurrencyId); - -impl TradingPair { - pub fn from_currency_ids(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> Option { - if currency_id_a.is_trading_pair_currency_id() - && currency_id_b.is_trading_pair_currency_id() - && currency_id_a != currency_id_b - { - if currency_id_a > currency_id_b { - Some(TradingPair(currency_id_b, currency_id_a)) - } else { - Some(TradingPair(currency_id_a, currency_id_b)) - } - } else { - None - } - } - - pub fn first(&self) -> CurrencyId { - self.0 - } - - pub fn second(&self) -> CurrencyId { - self.1 - } - - pub fn dex_share_currency_id(&self) -> CurrencyId { - CurrencyId::join_dex_share_currency_id(self.first(), self.second()) - .expect("shouldn't be invalid! guaranteed by construction") - } -} - -impl Decode for TradingPair { - fn decode(input: &mut I) -> sp_std::result::Result { - let (first, second): (CurrencyId, CurrencyId) = Decode::decode(input)?; - TradingPair::from_currency_ids(first, second) - .ok_or_else(|| parity_scale_codec::Error::from("invalid currency id")) - } -} - -#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)] -pub struct ECDPPosition { - /// The amount of collateral. - pub collateral: Balance, - /// The amount of debit. - pub debit: Balance, -} - -#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, MaxEncodedLen, TypeInfo)] -#[repr(u8)] -pub enum ReserveIdentifier { - CollatorSelection, - EvmStorageDeposit, - EvmDeveloperDeposit, - SetterEcdp, - SlickUsdEcdp, - Nft, - TransactionPayment, - TransactionPaymentDeposit, - - // always the last, indicate number of variants - Count, -} - -/// Convert any type that implements Into into byte representation ([u8, 32]) -pub fn to_bytes>(value: T) -> [u8; 32] { - Into::<[u8; 32]>::into(value.into()) -} +// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم + +// 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 . + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::upper_case_acronyms)] + +pub mod bonding; +pub mod currency; +pub mod edfis_launchpad; +pub mod evm; +pub mod nft; +pub mod signature; +pub mod task; +pub mod testing; +pub mod unchecked_extrinsic; + +pub use testing::*; +pub use edfis_launchpad::*; + +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::U256; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + FixedU128, RuntimeDebug, +}; +use sp_std::prelude::*; + +pub use currency::{CurrencyId, DexShare, TokenSymbol}; +pub use evm::{convert_decimals_from_evm, convert_decimals_to_evm}; + +#[cfg(test)] +mod tests; + +/// An index to a block. +pub type BlockNumber = u32; + +/// Alias to 512-bit hash when used in the context of a transaction signature on +/// the chain. +pub type Signature = signature::SetheumMultiSignature; + +/// Alias to the public key used for this chain, actually a `MultiSigner`. Like +/// the signature, this also isn't a fixed size when encoded, as different +/// cryptos have different size public keys. +pub type AccountPublic = ::Signer; + +/// Alias to the opaque account ID type for this chain, actually a +/// `AccountId32`. This is always 32 bytes. +pub type AccountId = ::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of +/// them. +pub type AccountIndex = u32; + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; + +/// Index of a transaction in the chain. 32-bit should be plenty. +pub type Nonce = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// An instant or duration in time. +pub type Moment = u64; + +/// Counter for the number of eras that have passed. +pub type EraIndex = u32; + +/// Balance of an account. +pub type Balance = u128; + +/// Signed version of Balance +pub type Amount = i128; + +/// Fees type primarily for Edfis `ExchangeFee` and `TradingFee`. +pub type Fees = u128; + +/// Auction ID +pub type AuctionId = u32; + +/// Share type +pub type Share = u128; + +/// Header type. +pub type Header = generic::Header; + +/// Block type. +pub type Block = generic::Block; + +/// Block ID. +pub type BlockId = generic::BlockId; + +/// Opaque, encoded, unchecked extrinsic. +pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + +/// Fee multiplier. +pub type Multiplier = FixedU128; + +#[derive( + Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, Serialize, Deserialize, +)] +pub enum AuthoritysOriginId { + Root, + Treasury, + LiquidSeeStakingTreasury, + LiquidEdfStakingTreasury, + SetterEcdpTreasury, + SlickUsdEcdpTreasury, + TreasuryReserve, +} + +#[derive( + Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, Serialize, Deserialize, +)] +pub enum DataProviderId { + Aggregated = 0, + Setheum = 1, +} + +#[derive( + Encode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo, MaxEncodedLen, Serialize, Deserialize, +)] +pub struct TradingPair(CurrencyId, CurrencyId); + +impl TradingPair { + pub fn from_currency_ids(currency_id_a: CurrencyId, currency_id_b: CurrencyId) -> Option { + if currency_id_a.is_trading_pair_currency_id() + && currency_id_b.is_trading_pair_currency_id() + && currency_id_a != currency_id_b + { + if currency_id_a > currency_id_b { + Some(TradingPair(currency_id_b, currency_id_a)) + } else { + Some(TradingPair(currency_id_a, currency_id_b)) + } + } else { + None + } + } + + pub fn first(&self) -> CurrencyId { + self.0 + } + + pub fn second(&self) -> CurrencyId { + self.1 + } + + pub fn dex_share_currency_id(&self) -> CurrencyId { + CurrencyId::join_dex_share_currency_id(self.first(), self.second()) + .expect("shouldn't be invalid! guaranteed by construction") + } +} + +impl Decode for TradingPair { + fn decode(input: &mut I) -> sp_std::result::Result { + let (first, second): (CurrencyId, CurrencyId) = Decode::decode(input)?; + TradingPair::from_currency_ids(first, second) + .ok_or_else(|| parity_scale_codec::Error::from("invalid currency id")) + } +} + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)] +pub struct ECDPPosition { + /// The amount of collateral. + pub collateral: Balance, + /// The amount of debit. + pub debit: Balance, +} + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, MaxEncodedLen, TypeInfo)] +#[repr(u8)] +pub enum ReserveIdentifier { + CollatorSelection, + EvmStorageDeposit, + EvmDeveloperDeposit, + SetterEcdp, + SlickUsdEcdp, + Nft, + TransactionPayment, + TransactionPaymentDeposit, + + // always the last, indicate number of variants + Count, +} + +/// Convert any type that implements Into into byte representation ([u8, 32]) +pub fn to_bytes>(value: T) -> [u8; 32] { + Into::<[u8; 32]>::into(value.into()) +} diff --git a/blockchain/runtime/src/weights/dex_oracle.rs b/blockchain/runtime/src/weights/dex_oracle.rs index 81462a85..ef91ae5e 100644 --- a/blockchain/runtime/src/weights/dex_oracle.rs +++ b/blockchain/runtime/src/weights/dex_oracle.rs @@ -1,85 +1,85 @@ -// This file is part of Setheum. - -// Copyright (C) 2020-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 dex_oracle -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-01-21, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// target/release/setheum -// benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=dex_oracle -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --template=.maintain/runtime-weight-template.hbs -// --output=./runtime/src/weights/ - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; - -/// Weight functions for dex_oracle. -pub struct WeightInfo(PhantomData); -impl dex_oracle::WeightInfo for WeightInfo { - // Storage: DexOracle AveragePrices (r:1 w:0) - // Storage: Timestamp Now (r:0 w:1) - // Storage: Dex LiquidityPool (r:1 w:0) - // Storage: DexOracle Cumulatives (r:1 w:1) - fn on_initialize_with_update_average_prices(n: u32, u: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 162_000 - .saturating_add((32_749_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 162_000 - .saturating_add((22_671_000 as Weight).saturating_mul(u as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(u as Weight))) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(u as Weight))) - } - // Storage: DexOracle AveragePrices (r:1 w:1) - // Storage: Dex LiquidityPool (r:1 w:0) - // Storage: Timestamp Now (r:1 w:0) - // Storage: DexOracle Cumulatives (r:0 w:1) - fn enable_average_price() -> Weight { - (24_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - // Storage: DexOracle AveragePrices (r:1 w:1) - // Storage: DexOracle Cumulatives (r:0 w:1) - fn disable_average_price() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - // Storage: DexOracle AveragePrices (r:1 w:1) - fn update_average_price_interval() -> Weight { - (12_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } -} +// This file is part of Setheum. + +// Copyright (C) 2020-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 dex_oracle +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-01-21, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/setheum-node +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=dex_oracle +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=.maintain/runtime-weight-template.hbs +// --output=./runtime/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for dex_oracle. +pub struct WeightInfo(PhantomData); +impl dex_oracle::WeightInfo for WeightInfo { + // Storage: DexOracle AveragePrices (r:1 w:0) + // Storage: Timestamp Now (r:0 w:1) + // Storage: Dex LiquidityPool (r:1 w:0) + // Storage: DexOracle Cumulatives (r:1 w:1) + fn on_initialize_with_update_average_prices(n: u32, u: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 162_000 + .saturating_add((32_749_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 162_000 + .saturating_add((22_671_000 as Weight).saturating_mul(u as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(u as Weight))) + .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(u as Weight))) + } + // Storage: DexOracle AveragePrices (r:1 w:1) + // Storage: Dex LiquidityPool (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: DexOracle Cumulatives (r:0 w:1) + fn enable_average_price() -> Weight { + (24_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + // Storage: DexOracle AveragePrices (r:1 w:1) + // Storage: DexOracle Cumulatives (r:0 w:1) + fn disable_average_price() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + // Storage: DexOracle AveragePrices (r:1 w:1) + fn update_average_price_interval() -> Weight { + (12_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +}