Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


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

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

159 changes: 114 additions & 45 deletions pallets/bancor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> = <<T as Config>::MultiCurrency as MultiCurrency<AccountIdOf<T>>>::Balance;

const BILLION: u128 = 1_000_000_000;
// These time units are defined in number of blocks.
const BLOCKS_PER_DAY: u32 = 60 / 12 * 60 * 24;

#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct BancorPool<Balance> {
Expand All @@ -63,6 +65,9 @@ pub mod pallet {
#[pallet::constant]
type InterventionPercentage: Get<Percent>;

#[pallet::constant]
type DailyReleasePercentage: Get<Percent>;

/// Set default weight.
type WeightInfo: WeightInfo;
}
Expand Down Expand Up @@ -97,6 +102,11 @@ pub mod pallet {
#[pallet::getter(fn get_bancor_pool)]
pub type BancorPools<T> = StorageMap<_, Blake2_128Concat, CurrencyId, BancorPool<BalanceOf<T>>>;

/// Reserve for releasing Tokens to the bancor pool
#[pallet::storage]
#[pallet::getter(fn get_bancor_reserve)]
pub type BancorReserve<T> = StorageMap<_, Blake2_128Concat, CurrencyId, BalanceOf<T>>;

#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub bancor_pools: Vec<(CurrencyId, BalanceOf<T>)>,
Expand All @@ -122,7 +132,8 @@ pub mod pallet {
vstoken_base_supply: *base_balance,
};

BancorPools::<T>::insert(currency_id, pool);
BancorPools::<T>::insert(currency_id.clone(), pool);
BancorReserve::<T>::insert(currency_id.clone(), BalanceOf::<T>::from(0u32));
}
}
}
Expand All @@ -131,7 +142,91 @@ pub mod pallet {
pub struct Pallet<T>(PhantomData<T>);

#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {
// check whether the price of vstoken (token/vstoken) is lower than 75%. if yes, then half
// of this newly released token should be used to buy vstoken, so that the price of vstoken
// will increase. Meanwhile, the other half will be put on the ceiling variable to indicate
// exchange availability. If not, all the newly release token should be put aside to the
// ceiling to not to impact the pool price.
fn on_initialize(_: T::BlockNumber) -> Weight {
// for each bancor pool currency_id, release 5% of reserve tokens to the pool
for (currency_id, reserve_amount) in BancorReserve::<T>::iter() {
let token_amount = reserve_amount /
T::DailyReleasePercentage::get()
.saturating_reciprocal_mul_floor(BalanceOf::<T>::from(BLOCKS_PER_DAY));

if token_amount > Zero::zero() {
// get the current price of vstoken
// let (nominator, denominator) = Self::get_instant_vstoken_price(currency_id);
if let Ok((nominator, denominator)) =
Self::get_instant_vstoken_price(currency_id)
{
let amount_kept: BalanceOf<T>;
// if vstoken price is lower than 0.75 token
if T::InterventionPercentage::get()
.saturating_reciprocal_mul_floor(nominator) <=
denominator
{
amount_kept = token_amount / BalanceOf::<T>::saturated_from(2u128);
} else {
amount_kept = token_amount;
}

let sell_amount = token_amount.saturating_sub(amount_kept);
// deal with ceiling variable
if amount_kept != Zero::zero() {
if let Err(_) =
Self::increase_bancor_pool_ceiling(currency_id, amount_kept)
{
continue;
}
}
// deal with exchange transaction
if sell_amount != Zero::zero() {
// make changes in the bancor pool
if let Ok(vstoken_amount) =
Self::calculate_price_for_vstoken(currency_id, sell_amount)
{
let sell_result = Self::revise_bancor_pool_token_buy_vstoken(
currency_id,
sell_amount,
vstoken_amount,
);
// if somehow not able to sell token, then add the amount to
// ceiling.
if let Err(err_msg) = sell_result {
match err_msg {
Error::<T>::BancorPoolNotExist => (),
_ => {
if let Err(_) = Self::increase_bancor_pool_ceiling(
currency_id,
sell_amount,
) {
continue;
}
},
};
}
}
}

// deduct token_amount from BancorReserve
BancorReserve::<T>::mutate(currency_id, |reserve_option| {
match reserve_option {
Some(reserve) => {
*reserve = reserve.saturating_sub(token_amount);
},
_ => (),
}
});
}
}
}

// TODO: Estimate weight for this function
1_000
}
}

#[pallet::call]
impl<T: Config> Pallet<T> {
Expand All @@ -141,9 +236,13 @@ pub mod pallet {
currency_id: CurrencyId,
token_amount: BalanceOf<T>,
) -> DispatchResult {
ensure_root(origin)?;
let adder = ensure_signed(origin)?;
ensure!(currency_id.is_token(), Error::<T>::NotSupportTokenType);

let token_balance = T::MultiCurrency::free_balance(currency_id, &adder);
ensure!(token_balance >= token_amount, Error::<T>::NotEnoughBalance);

T::MultiCurrency::withdraw(currency_id, &adder, token_amount)?;
Self::add_token(currency_id, token_amount)?;

Ok(())
Expand Down Expand Up @@ -426,49 +525,19 @@ impl<T: Config> Pallet<T> {
}

impl<T: Config> BancorHandler<BalanceOf<T>> for Pallet<T> {
// check whether the price of vstoken (token/vstoken) is lower than 75%. if yes, then half of
// this newly released token should be used to buy vstoken, so that the price of vstoken will
// increase. Meanwhile, the other half will be put on the ceiling variable to indicate exchange
// availability. If not, all the newly release token should be put aside to the ceiling to not
// to impact the pool price.
fn add_token(currency_id: CurrencyId, token_amount: BalanceOf<T>) -> Result<(), DispatchError> {
// get the current price of vstoken
let (nominator, denominator) = Self::get_instant_vstoken_price(currency_id)?;

let amount_kept: BalanceOf<T>;
// if vstoken price is lower than 0.75 token
if T::InterventionPercentage::get().saturating_reciprocal_mul_floor(nominator) <=
denominator
{
amount_kept = token_amount / BalanceOf::<T>::saturated_from(2u128);
} else {
amount_kept = token_amount;
}

let sell_amount = token_amount.saturating_sub(amount_kept);

// deal with ceiling variable
if amount_kept != Zero::zero() {
Self::increase_bancor_pool_ceiling(currency_id, amount_kept)?;
}

// deal with exchange transaction
if sell_amount != Zero::zero() {
// make changes in the bancor pool
let vstoken_amount = Self::calculate_price_for_vstoken(currency_id, sell_amount)?;
let sell_result = Self::revise_bancor_pool_token_buy_vstoken(
currency_id,
sell_amount,
vstoken_amount,
);

// if somehow not able to sell token, then add the amount to ceiling.
if let Err(err_msg) = sell_result {
match err_msg {
Error::<T>::BancorPoolNotExist => Err(Error::<T>::BancorPoolNotExist),
_ => Self::increase_bancor_pool_ceiling(currency_id, sell_amount),
}?;
}
ensure!(token_amount >= Zero::zero(), Error::<T>::AmountNotGreaterThanZero);

if token_amount != Zero::zero() {
BancorReserve::<T>::mutate(currency_id, |reserve_option| -> Result<(), Error<T>> {
match reserve_option {
Some(reserve) => {
*reserve = reserve.saturating_add(token_amount);
Ok(())
},
_ => Err(Error::<T>::BancorPoolNotExist),
}
})?;
}

Ok(())
Expand Down
36 changes: 26 additions & 10 deletions pallets/bancor/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

#![cfg(test)]

use frame_support::{construct_runtime, parameter_types, traits::GenesisBuild};
use frame_support::{
construct_runtime, parameter_types,
traits::{GenesisBuild, OnFinalize, OnInitialize},
};
pub use node_primitives::{Balance, CurrencyId, TokenSymbol};
use sp_core::H256;
use sp_runtime::{
Expand Down Expand Up @@ -109,11 +112,13 @@ impl orml_tokens::Config for Test {

parameter_types! {
pub const InterventionPercentage: Percent = Percent::from_percent(75);
pub const DailyReleasePercentage: Percent = Percent::from_percent(5);
}

impl bancor::Config for Test {
type Event = Event;
type InterventionPercentage = InterventionPercentage;
type DailyReleasePercentage = DailyReleasePercentage;
type MultiCurrency = Tokens;
type WeightInfo = ();
}
Expand Down Expand Up @@ -147,16 +152,16 @@ impl ExtBuilder {
])
}

pub fn hundred_thousand_for_alice_n_bob(self) -> Self {
pub fn thousand_thousand_for_alice_n_bob(self) -> Self {
self.balances(vec![
(ALICE, KSM, 100_000),
(ALICE, DOT, 100_000),
(ALICE, VSKSM, 100_000),
(ALICE, VSDOT, 100_000),
(BOB, KSM, 100_000),
(BOB, DOT, 100_000),
(BOB, VSKSM, 100_000),
(BOB, VSDOT, 100_000),
(ALICE, KSM, 1_000_000),
(ALICE, DOT, 1_000_000),
(ALICE, VSKSM, 1_000_000),
(ALICE, VSDOT, 1_000_000),
(BOB, KSM, 1_000_000),
(BOB, DOT, 1_000_000),
(BOB, VSKSM, 1_000_000),
(BOB, VSDOT, 1_000_000),
])
}

Expand All @@ -179,3 +184,14 @@ impl ExtBuilder {
t.into()
}
}

// simulate block production
pub(crate) fn run_to_block(n: u64) {
while System::block_number() < n {
Bancor::on_finalize(System::block_number());
System::on_finalize(System::block_number());
System::set_block_number(System::block_number() + 1);
System::on_initialize(System::block_number());
Bancor::on_initialize(System::block_number());
}
}
Loading