Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

CU-1p8545r dutch auction #410

Merged
merged 11 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 2 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 5 additions & 88 deletions frame/composable-traits/src/auction.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use crate::{
dex::Orderbook,
loans::{DurationSeconds, PriceStructure, Timestamp},
};
use crate::loans::DurationSeconds;
use frame_support::pallet_prelude::*;
use scale_info::TypeInfo;
use sp_runtime::Permill;

#[derive(Decode, Encode, Clone, TypeInfo)]
#[derive(Decode, Encode, Clone, TypeInfo, Debug, PartialEq)]
pub enum AuctionStepFunction {
/// default - direct pass through to dex without steps, just to satisfy defaults and reasonably
/// for testing
Expand All @@ -20,97 +17,17 @@ impl Default for AuctionStepFunction {
}
}

#[derive(Decode, Encode, Clone, PartialEq, TypeInfo)]
pub enum AuctionState<DexOrderId> {
AuctionStarted,
AuctionOnDex(DexOrderId),
AuctionEndedSuccessfully,
/// like DEX does not support asset now or halted
AuctionFatalFailed,
/// so if for some reason system loop is not properly set, than will get timeout
AuctionTimeFailed,
}

impl<DexOrderId> Default for AuctionState<DexOrderId> {
fn default() -> Self {
Self::AuctionStarted
}
}

/// Auction is done via dexes which act each block. Each block decide if intention was satisfied or
/// not. That information is provided via event subscribes which callback into auction.
/// Assuming liquidity providers to be off our local chain, it means that it is high latency
/// external loop.
pub enum AuctionExchangeCallback {
/// success transfer of funds
Success,
/// some technical fail of transaction, can issue new one
RetryFail,
/// cannot retry within current state of system, like assets are not supported
FatalFail,
}

#[derive(Default, Decode, Encode, Clone, TypeInfo)]
#[derive(Default, Decode, Encode, Clone, TypeInfo, Debug, PartialEq)]
pub struct LinearDecrease {
/// The number of seconds until the price reach zero.
/// Seconds after auction start when the price reaches zero
pub total: DurationSeconds,
dzmitry-lahoda marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Default, Decode, Encode, Clone, TypeInfo)]
#[derive(Default, Decode, Encode, Clone, TypeInfo, Debug, PartialEq)]
pub struct StairstepExponentialDecrease {
// Length of time between price drops
pub step: DurationSeconds,
// Per-step multiplicative factor, usually more than 50%, mostly closer to 100%, but not 100%.
// Drop per unit of `step`.
pub cut: Permill,
}

/// An object from which we can initiate a dutch auction.
// see example of it in clip.sol of makerdao
pub trait DutchAuction {
type OrderId;
type Orderbook: Orderbook;
type AccountId;
type AssetId;
type Balance;
type Order;
type GroupId;

/// Transfer the asset from the provided account to the auction account.
/// The caller is responsible for checking the price at which the auction executed (not known in
/// advance of course).
///
/// Description.
///
/// * `account_id`: the order owner.
/// * `source_account`: the account from which we extract the `amount` of `source_asset_id`
/// from.
/// * `source_asset_id`: the asset we are interested to trade for `target_asset_id`.
/// * `target_account`: the beneficiary of the order.
/// * `total_amount`: the amount of `source_asset_id`.
/// * `price`: the initial price for `total_amount` and some rules.
#[allow(clippy::too_many_arguments)]
fn start(
account_id: &Self::AccountId,
source_asset_id: Self::AssetId,
source_account: &Self::AccountId,
target_asset_id: Self::AssetId,
target_account: &Self::AccountId,
total_amount: Self::Balance,
price: PriceStructure<Self::GroupId, Self::Balance>,
function: AuctionStepFunction,
) -> Result<Self::OrderId, DispatchError>;

/// run existing auctions
/// if some auctions completed, transfer amount to target account
/// `now` current time.
fn off_chain_run_auctions(now: Timestamp) -> DispatchResult;

fn get_auction_state(order: &Self::OrderId) -> Option<Self::Order>;

/// called back from DEX
fn intention_updated(
order: &Self::OrderId,
action_event: AuctionExchangeCallback,
) -> DispatchResult;
}
159 changes: 159 additions & 0 deletions frame/composable-traits/src/defi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//! Common codes for defi pallets

use codec::{Codec, Decode, Encode, FullCodec};
use frame_support::{pallet_prelude::MaybeSerializeDeserialize, Parameter};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{CheckedAdd, CheckedMul, CheckedSub, Zero},
DispatchError, FixedPointOperand,
};

use crate::{
currency::{AssetIdLike, BalanceLike},
math::LiftedFixedBalance,
};

#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq)]
pub struct Take<Balance> {
/// amount of `base`
pub amount: Balance,
/// direction depends on referenced order type
/// either minimal or maximal amount of `quote` for given unit of `base`
pub limit: Balance,
}

impl<Balance: PartialOrd + Zero> Take<Balance> {
pub fn is_valid(&self) -> bool {
self.amount > Balance::zero() && self.limit > Balance::zero()
}
}

/// take `quote` currency and give `base` currency
#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq)]
pub struct Sell<AssetId, Balance> {
pub pair: CurrencyPair<AssetId>,
pub take: Take<Balance>,
}

impl<AssetId: PartialEq, Balance: PartialOrd + Zero> Sell<AssetId, Balance> {
pub fn is_valid(&self) -> bool {
self.take.is_valid() && self.pair.is_valid()
}
}

/// given `base`, how much `quote` needed for unit
/// see [currency pair](https://www.investopedia.com/terms/c/currencypair.asp)
#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq)]
pub struct CurrencyPair<AssetId> {
/// See [Base Currency](https://www.investopedia.com/terms/b/basecurrency.asp)
pub base: AssetId,
/// counter currency
pub quote: AssetId,
}

impl<AssetId: PartialEq> CurrencyPair<AssetId> {
pub fn is_valid(&self) -> bool {
self.base != self.quote
}
}

/// type parameters for traits in pure defi area
pub trait DeFiEngine {
/// The asset ID type
type AssetId: AssetIdLike;
/// The balance type of an account
type Balance: BalanceLike;
/// The user account identifier type for the runtime
type AccountId;
}

/// take nothing
impl<Balance: Default> Default for Take<Balance> {
fn default() -> Self {
Self { amount: Default::default(), limit: Default::default() }
}
}

impl<AssetId: Default> Default for CurrencyPair<AssetId> {
fn default() -> Self {
Self { base: Default::default(), quote: Default::default() }
}
}

impl<AssetId, Balance> Sell<AssetId, Balance> {
pub fn new(base: AssetId, quote: AssetId, base_amount: Balance, quote_limit: Balance) -> Self {
Self {
take: Take { amount: base_amount, limit: quote_limit },
pair: CurrencyPair { base, quote },
}
}
}

/// default sale is no sale and invalid sale
impl<AssetId: Default, Balance: Default> Default for Sell<AssetId, Balance> {
fn default() -> Self {
Self { pair: Default::default(), take: Default::default() }
}
}

/// order is something that lives some some time until taken
pub trait OrderIdLike:
FullCodec + Copy + Eq + PartialEq + sp_std::fmt::Debug + TypeInfo + sp_std::hash::Hash + Default
{
}
impl<
T: FullCodec
+ Copy
+ Eq
+ PartialEq
+ sp_std::fmt::Debug
+ TypeInfo
+ sp_std::hash::Hash
+ Default,
> OrderIdLike for T
{
}

pub trait SellEngine<Configuration>: DeFiEngine {
type OrderId: OrderIdLike;
/// sell base asset for price given or higher
/// - `from_to` - account requesting sell
fn ask(
from_to: &Self::AccountId,
order: Sell<Self::AssetId, Self::Balance>,
configuration: Configuration,
) -> Result<Self::OrderId, DispatchError>;
/// take order. get not found error if order never existed or was removed.
/// - `take.limit` - for `sell` order it is maximal value are you to pay for `base` in `quote`
/// asset, for `buy`
/// order it is minimal value you are eager to accept for `base`
/// - `take.amount` - amount of
/// `base` you are ready to exchange for this order
fn take(
from_to: &Self::AccountId,
order_id: Self::OrderId,
take: Take<Self::Balance>,
) -> Result<(), DispatchError>;
}

pub trait DeFiComposableConfig: frame_system::Config {
// what.
type AssetId: AssetIdLike + MaybeSerializeDeserialize + From<u128> + Default;

type Balance: BalanceLike
+ Default
+ Parameter
+ Codec
+ Copy
+ Ord
+ CheckedAdd
+ CheckedSub
+ CheckedMul
+ CheckedSub
+ From<u64> // at least 64 bit
+ Zero
+ FixedPointOperand
+ Into<LiftedFixedBalance> // integer part not more than bits in this
+ Into<u128>; // cannot do From<u128>, until LiftedFixedBalance integer part is larger than 128
// bit
}