Skip to content

Commit

Permalink
finalizing lending feautes
Browse files Browse the repository at this point in the history
Signed-off-by: Dzmitry Lahoda <dzmitry@lahoda.pro>
  • Loading branch information
dzmitry-lahoda committed Jan 23, 2022
1 parent ff39c92 commit 7eea5a7
Show file tree
Hide file tree
Showing 24 changed files with 723 additions and 413 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/composablefi/composable)](https://github.com/composablefi/composable/tags) [![Twitter](https://img.shields.io/badge/Twitter-gray?logo=twitter)](https://twitter.com/ComposableFin) [![Discord](https://img.shields.io/badge/Discord-gray?logo=discord)](https://discord.gg/pFZn2GCn65) [![Telegram](https://img.shields.io/badge/Telegram-gray?logo=telegram)](https://t.me/ComposableFinanceAnnouncements) [![Medium](https://img.shields.io/badge/Medium-gray?logo=medium)](https://composablefi.medium.com/)


Picasso is our custom built kusama parachain, based on the substrate framework.
Picasso is our custom built Kusama parachain, based on the substrate framework.




## Install
## Install

For linux, FreeBSD, OpenBSD and macOS:

Expand All @@ -26,7 +23,7 @@ git clone https://github.com/composableFi/composable
cd composable/
sh scripts/init.sh
cargo build --release
```
```

or you can simply install it with this one liner:

Expand Down
18 changes: 2 additions & 16 deletions frame/bonded-finance/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
# Bonded Finance Pallet
# Overview

## Overview

A simple pallet providing means of submitting bond offers.

## Interface

This pallet implements the `BondedFinance` trait from `composable-traits`.

## Dispatchable Functions

- `offer` ― Register a new bond offer, allowing use to later bond it.
- `bond` ― Bond to an offer, the user should provide the number of contracts a user is willing
to buy. On offer completion (a.k.a. no more contract on the offer), the `stake` put by the creator is refunded.
- `cancel_offer` ― Cancel a running offer, blocking further bond but not cancelling the
currently vested rewards. The `stake` put by the creator is refunded.
A simple pallet providing means of submitting bond offers.
34 changes: 20 additions & 14 deletions frame/bonded-finance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,21 @@
//!
//! A simple pallet providing means of submitting bond offers.
//!
//! ## Interface
//! Alice offers some bond priced in specific assets per bond as some amount of shares.
//! At same time she provides reward asset which will be vested into account which takes bond
//! offers. She locks some native currency to make the offer registered.
//!
//! Bob bonds parts of shares from offer by transfer some asset amount desired by Alice.
//! Bob will be vested part of reward amount during maturity period from that moment.
//! It the end of some period Bob may or may be not be return with amount he provided to Alice -
//! depends on how offer was setup.
//!
//! This pallet implements the `BondedFinance` trait from `composable-traits`.
//! Alice may cancel offer and prevent new bonds on offer, she gets her native tokens back.
//! All existing vesting periods continue to be executed.
//!
//! ## Dispatchable Functions
//! ## Interface
//!
//! - `offer` - Register a new bond offer, allowing use to later bond it.
//! - `bond` - Bond to an offer, the user should provide the number of nb_of_bonds a user is willing
//! to buy.
//! - `cancel_offer` - Cancel a running offer, blocking further bond but not cancelling the
//! currently vested rewards.
//! This pallet implements the `composable_traits::bonded_finance::BondedFinance`.
mod benchmarks;
mod mock;
Expand Down Expand Up @@ -194,7 +198,7 @@ pub mod pallet {

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Create a new offer.
/// Create a new offer. So later can `bond` it.
///
/// The dispatch origin for this call must be _Signed_ and the sender must have the
/// appropriate funds.
Expand All @@ -208,6 +212,9 @@ pub mod pallet {
}

/// Bond to an offer.
/// And user should provide the number of contracts she is willing to buy.
/// On offer completion (a.k.a. no more contract on the offer), the `stake` put by the
/// creator is refunded.
///
/// The dispatch origin for this call must be _Signed_ and the sender must have the
/// appropriate funds.
Expand All @@ -225,12 +232,12 @@ pub mod pallet {
Ok(())
}

/// Cancel an offer.
///
/// Cancel a running offer. Blocking further bond but not cancelling the
/// currently vested rewards. The `stake` put by the creator is refunded.
/// The dispatch origin for this call must be _Signed_ and the sender must be `AdminOrigin`
///
/// Emits a `OfferCancelled`.
#[pallet::weight(10_000)]
#[pallet::weight(10_000)] // TODO: add weights
#[transactional]
pub fn cancel(origin: OriginFor<T>, offer_id: T::BondOfferId) -> DispatchResult {
let (issuer, offer) = Self::get_offer(offer_id)?;
Expand Down Expand Up @@ -315,7 +322,7 @@ pub mod pallet {
nb_of_bonds <= offer.nb_of_bonds,
Error::<T>::InvalidNumberOfBonds
);
// NOTE(hussein-aitlahcen): can't overflow, subsumed by `offer.valid()` in
// can't overflow, subsumed by `offer.valid()` in
// `do_offer`
let value = nb_of_bonds * offer.bond_price;
let reward_share = T::Convert::convert(
Expand Down Expand Up @@ -355,7 +362,6 @@ pub mod pallet {
)?;
},
BondDuration::Infinite => {
// NOTE(hussein-aitlahcen): in the case of an inifite duration for
// the offer, the liquidity is never returned to the bonder, meaning
// that the protocol is now owning the funds.
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::{
slice::SliceIndex,
};
use frame_support::{traits::Get, BoundedVec};
use sp_std::{convert::TryFrom, fmt, marker::PhantomData, prelude::*};
use sp_std::{convert::TryFrom, marker::PhantomData, prelude::*};

/// A bounded, sorted vector.
///
Expand Down Expand Up @@ -137,12 +137,12 @@ impl<T: Ord, S> Default for BoundedSortedVec<T, S> {
}

#[cfg(feature = "std")]
impl<T: Ord, S> fmt::Debug for BoundedSortedVec<T, S>
impl<T: Ord, S> sp_std::fmt::Debug for BoundedSortedVec<T, S>
where
T: fmt::Debug,
T: sp_std::fmt::Debug,
S: Get<u32>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
f.debug_tuple("BoundedVec").field(&self.0).field(&Self::bound()).finish()
}
}
Expand Down
72 changes: 64 additions & 8 deletions frame/composable-support/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::{marker::PhantomData, ops::Deref};
use scale_info::TypeInfo;
use sp_runtime::DispatchError;

/// Black box that embbed the validated value.
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, TypeInfo)]
Expand All @@ -8,9 +9,9 @@ pub struct Validated<T, U> {
_marker: PhantomData<U>,
}

impl<T: Copy, U> Validated<T, U> {
impl<T, U> Validated<T, U> {
#[inline(always)]
pub fn value(&self) -> T {
pub fn value(self) -> T {
self.value
}
}
Expand All @@ -30,14 +31,29 @@ impl<T, U> AsRef<T> for Validated<T, U> {
}
}

pub trait ValidateDispatch<U>: Sized {
fn validate(self) -> Result<Self, DispatchError>;
}

pub trait Validate<U>: Sized {
// use string here because in serde layer there is not dispatch
fn validate(self) -> Result<Self, &'static str>;
}

#[derive(Debug, Eq, PartialEq)]
pub struct QED;
pub enum Valid {}

#[derive(Debug, Eq, PartialEq)]
pub enum Invalid {}

impl<T> Validate<Invalid> for T {
#[inline(always)]
fn validate(self) -> Result<Self, &'static str> {
Err("not valid")
}
}

impl<T> Validate<QED> for T {
impl<T> Validate<Valid> for T {
#[inline(always)]
fn validate(self) -> Result<Self, &'static str> {
Ok(self)
Expand All @@ -53,7 +69,20 @@ impl<T: Validate<U> + Validate<V>, U, V> Validate<(U, V)> for T {
}
}

impl<T: codec::Decode + Validate<(U, V)>, U, V> codec::Decode for Validated<T, (U, V)> {
// as per substrate pattern and existing macroses for similar purposes, they tend to make things
// flat like `#[impl_trait_for_tuples::impl_for_tuples(30)]`
// so if we will need more than 3, can consider it
impl<T: Validate<U> + Validate<V> + Validate<W>, U, V, W> Validate<(U, V, W)> for T {
#[inline(always)]
fn validate(self) -> Result<Self, &'static str> {
let value = Validate::<U>::validate(self)?;
let value = Validate::<V>::validate(value)?;
let value = Validate::<W>::validate(value)?;
Ok(value)
}
}

impl<T: codec::Decode + Validate<U>, U> codec::Decode for Validated<T, U> {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let value = Validate::validate(T::decode(input)?).map_err(Into::<codec::Error>::into)?;
Ok(Validated { value, _marker: PhantomData })
Expand All @@ -78,9 +107,9 @@ mod test {
#[derive(Debug, Eq, PartialEq)]
struct ValidBRange;

type CheckARange = (ValidARange, QED);
type CheckBRange = (ValidBRange, QED);
type CheckABRange = (ValidARange, (ValidBRange, QED));
type CheckARange = (ValidARange, Valid);
type CheckBRange = (ValidBRange, Valid);
type CheckABRange = (ValidARange, (ValidBRange, Valid));

#[derive(Debug, Eq, PartialEq, codec::Encode, codec::Decode)]
struct X {
Expand Down Expand Up @@ -125,6 +154,16 @@ mod test {
assert!(Validated::<X, CheckARange>::decode(&mut &bytes[..]).is_err());
}

#[test]
fn encode_decode_validated_encode_decode() {
let original = X { a: 0xDEADC0DE, b: 0xCAFEBABE };
let bytes = original.encode();
let wrapped = Validated::<X, Valid>::decode(&mut &bytes[..]).unwrap();
let bytes = wrapped.encode();
let reencoded = X::decode(&mut &bytes[..]).unwrap();
assert_eq!(reencoded, original);
}

#[test]
fn test_valid_b() {
let valid = X { a: 0xCAFEBABE, b: 10 };
Expand Down Expand Up @@ -172,4 +211,21 @@ mod test {
let bytes = invalid.encode();
assert!(Validated::<X, CheckABRange>::decode(&mut &bytes[..]).is_err());
}

#[test]
fn valid_triple() {
let value = X { a: 10, b: 0xDEADC0DE };
let bytes = value.encode();
assert_eq!(
Ok(Validated { value, _marker: PhantomData }),
Validated::<X, (Valid, Valid, Valid)>::decode(&mut &bytes[..])
);
}

#[test]
fn valid_invalid_valid() {
let value = X { a: 10, b: 0xDEADC0DE };
let bytes = value.encode();
assert!(Validated::<X, (Valid, Invalid, Valid)>::decode(&mut &bytes[..]).is_err());
}
}
1 change: 1 addition & 0 deletions frame/composable-traits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sp-arithmetic = { default-features = false, git = "https://github.com/paritytech
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" }
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" }

composable-support = {default-features = false, path = "../composable-support"}
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
serde = { version = '1', optional = true }
plotters = {version = "0.3.1", optional = true}
Expand Down
5 changes: 3 additions & 2 deletions frame/composable-traits/src/bonded_finance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ pub struct BondOffer<AccountId, AssetId, Balance, BlockNumber> {
/// The account that will receive the locked assets.
pub beneficiary: AccountId,
/// Asset to be locked. Unlockable after `duration`.
/// Asset which `beneficiary` wants to get for his offer.
pub asset: AssetId,
/// Price of a bond.
/// Price of a bond unit in `asset`.
pub bond_price: Balance,
/// Number of bonds. We use the Balance type for the sake of simplicity.
pub nb_of_bonds: Balance,
Expand All @@ -51,7 +52,7 @@ pub struct BondOffer<AccountId, AssetId, Balance, BlockNumber> {
pub reward: BondOfferReward<AssetId, Balance, BlockNumber>,
}

/// The Bond reward.
/// The Bond reward. Asset and rules reward will be given.
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct BondOfferReward<AssetId, Balance, BlockNumber> {
/// The actual reward asset.
Expand Down
8 changes: 8 additions & 0 deletions frame/composable-traits/src/defi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ pub struct CurrencyPair<AssetId> {
pub quote: AssetId,
}

impl<AssetId> From<(AssetId, AssetId)> for CurrencyPair<AssetId> {
fn from(other: (AssetId, AssetId)) -> Self {
Self { base: other.0, quote: other.1 }
}
}

/// `AssetId` is Copy, than consider pair to be Copy
impl<AssetId: Copy> Copy for CurrencyPair<AssetId> {}

Expand Down Expand Up @@ -234,6 +240,8 @@ pub type Ratio = FixedU128;

#[cfg(test)]
mod tests {
use crate::defi::LiftedFixedBalance;

use super::{Ratio, Take};
use sp_runtime::FixedPointNumber;

Expand Down
Loading

0 comments on commit 7eea5a7

Please sign in to comment.