Skip to content

Commit

Permalink
Use Loan pallet as NAV source for investor pool
Browse files Browse the repository at this point in the history
  • Loading branch information
Branan Riley committed Dec 1, 2021
1 parent 828dc60 commit 5b09f5e
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 95 deletions.
82 changes: 8 additions & 74 deletions pallets/tinlake-investor-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod tests;
mod benchmarking;

use codec::HasCompact;
use common_traits::PoolReserve;
use common_traits::{PoolNAV, PoolReserve};
use core::{convert::TryFrom, ops::AddAssign};
use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::UnixTime};
use frame_system::pallet_prelude::*;
Expand Down Expand Up @@ -67,8 +67,6 @@ pub struct PoolDetails<AccountId, CurrencyId, EpochId, Balance> {
pub max_reserve: Balance,
pub available_reserve: Balance,
pub total_reserve: Balance,

pub fake_nav: Balance,
}

/// Per-tranche and per-user order details.
Expand Down Expand Up @@ -172,6 +170,9 @@ pub mod pallet {
CurrencyId = Self::CurrencyId,
>;

type LoanAmount: Into<Self::Balance>;
type NAV: PoolNAV<Self::PoolId, Self::LoanAmount>;

/// A conversion from a tranche ID to a CurrencyId
type TrancheToken: TrancheToken<Self>;
type Time: UnixTime;
Expand Down Expand Up @@ -259,6 +260,8 @@ pub mod pallet {
InsufficientReserve,
/// Subordination Ratio validation failed
SubordinationRatioViolated,
/// The NAV was not available
NoNAV,
/// Generic error for invalid input data provided
InvalidData,
}
Expand Down Expand Up @@ -322,7 +325,6 @@ pub mod pallet {
max_reserve,
available_reserve: Zero::zero(),
total_reserve: Zero::zero(),
fake_nav: Zero::zero(),
},
);
Self::deposit_event(Event::PoolCreated(pool_id, owner));
Expand Down Expand Up @@ -438,9 +440,7 @@ pub mod pallet {
pool.last_epoch_closed = current_epoch_end;
pool.available_reserve = Zero::zero();
let epoch_reserve = pool.total_reserve;

// TODO: get NAV
let nav = pool.fake_nav;
let nav = T::NAV::nav(pool_id).ok_or(Error::<T>::NoNAV)?.0.into();

if pool
.tranches
Expand Down Expand Up @@ -561,64 +561,6 @@ pub mod pallet {
Ok(())
})
}

// Reserve Operations

#[pallet::weight(100)]
pub fn test_payback(
origin: OriginFor<T>,
pool_id: T::PoolId,
amount: T::Balance,
) -> DispatchResult {
// Internal/pvt call from Coordinator, so no need to check origin on final implementation
let who = ensure_signed(origin)?;
Self::do_payback(who, pool_id, amount)
}

#[pallet::weight(100)]
pub fn test_borrow(
origin: OriginFor<T>,
pool_id: T::PoolId,
amount: T::Balance,
) -> DispatchResult {
// Internal/pvt call from Coordinator, so no need to check origin on final implementation
let who = ensure_signed(origin)?;
Self::do_borrow(who, pool_id, amount)
}

#[pallet::weight(100)]
pub fn test_nav_up(
origin: OriginFor<T>,
pool_id: T::PoolId,
amount: T::Balance,
) -> DispatchResult {
let _ = ensure_signed(origin)?;
Pool::<T>::try_mutate(pool_id, |pool| {
let pool = pool.as_mut().ok_or(Error::<T>::NoSuchPool)?;
pool.fake_nav = pool
.fake_nav
.checked_add(&amount)
.ok_or(Error::<T>::Overflow)?;
Ok(())
})
}

#[pallet::weight(100)]
pub fn test_nav_down(
origin: OriginFor<T>,
pool_id: T::PoolId,
amount: T::Balance,
) -> DispatchResult {
let _ = ensure_signed(origin)?;
Pool::<T>::try_mutate(pool_id, |pool| {
let pool = pool.as_mut().ok_or(Error::<T>::NoSuchPool)?;
pool.fake_nav = pool
.fake_nav
.checked_sub(&amount)
.ok_or(Error::<T>::Overflow)?;
Ok(())
})
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -921,7 +863,7 @@ pub mod pallet {
.ok_or(Error::<T>::Overflow)?;

// Rebalance tranches based on the new tranche asset values and ratios
let nav = pool.fake_nav;
let nav = T::NAV::nav(pool_id).ok_or(Error::<T>::NoNAV)?.0.into();
let mut remaining_nav = nav;
let mut remaining_reserve = pool.total_reserve;
let last_tranche = pool.tranches.len() - 1;
Expand Down Expand Up @@ -1005,10 +947,6 @@ pub mod pallet {
.total_reserve
.checked_add(&amount)
.ok_or(Error::<T>::Overflow)?;
pool.fake_nav = pool
.fake_nav
.checked_sub(&amount)
.ok_or(Error::<T>::Overflow)?;
let mut remaining_amount = amount;
for tranche in &mut pool.tranches {
Self::update_tranche_debt(tranche).ok_or(Error::<T>::Overflow)?;
Expand Down Expand Up @@ -1051,10 +989,6 @@ pub mod pallet {
.available_reserve
.checked_sub(&amount)
.ok_or(Error::<T>::Overflow)?;
pool.fake_nav = pool
.fake_nav
.checked_add(&amount)
.ok_or(Error::<T>::Overflow)?;
let mut remaining_amount = amount;
for tranche in &mut pool.tranches {
Self::update_tranche_debt(tranche).ok_or(Error::<T>::Overflow)?;
Expand Down
70 changes: 69 additions & 1 deletion pallets/tinlake-investor-pool/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{self as pallet_tinlake_investor_pool, Config};
use crate::{self as pallet_tinlake_investor_pool, Config, DispatchResult};
use frame_support::{
parameter_types,
traits::{GenesisBuild, Hooks},
Expand All @@ -17,6 +17,49 @@ primitives_tokens::impl_tranche_token!();
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

mod fake_nav {
use super::Balance;
use codec::HasCompact;
use frame_support::pallet_prelude::*;
pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
use super::*;

#[pallet::config]
pub trait Config: frame_system::Config {
type PoolId: Member + Parameter + Default + Copy + HasCompact + MaxEncodedLen;
}

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);

#[pallet::storage]
pub type Nav<T: Config> = StorageMap<_, Blake2_128Concat, T::PoolId, Balance>;

impl<T: Config> Pallet<T> {
pub fn value(pool_id: T::PoolId) -> Balance {
Nav::<T>::get(pool_id).unwrap_or(0)
}

pub fn update(pool_id: T::PoolId, balance: Balance) {
Nav::<T>::insert(pool_id, balance);
}
}
}

impl<T: Config> common_traits::PoolNAV<T::PoolId, Balance> for Pallet<T> {
fn nav(pool_id: T::PoolId) -> Option<(Balance, u64)> {
Some((Self::value(pool_id), 0))
}
fn update_nav(pool_id: T::PoolId) -> Result<Balance, DispatchError> {
Ok(Self::value(pool_id))
}
}
}

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test where
Expand All @@ -28,6 +71,7 @@ frame_support::construct_runtime!(
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
Tokens: orml_tokens::{Pallet, Storage, Event<T>, Config<T>},
TinlakeInvestorPool: pallet_tinlake_investor_pool::{Pallet, Call, Storage, Event<T>},
FakeNav: fake_nav::{Pallet, Storage},
}
);

Expand Down Expand Up @@ -101,10 +145,16 @@ impl Config for Test {
type EpochId = u32;
type CurrencyId = CurrencyId;
type Tokens = Tokens;
type LoanAmount = Balance;
type NAV = FakeNav;
type TrancheToken = TrancheToken<Test>;
type Time = Timestamp;
}

impl fake_nav::Config for Test {
type PoolId = u32;
}

pub const CURRENCY: Balance = 1_000_000_000_000_000_000;

// Build genesis storage according to the mock runtime.
Expand Down Expand Up @@ -143,3 +193,21 @@ pub fn next_block_after(seconds: u64) {
Timestamp::on_initialize(System::block_number());
Timestamp::set(Origin::none(), Timestamp::now() + seconds).unwrap();
}

pub fn test_borrow(borrower: u64, pool_id: u32, amount: Balance) -> DispatchResult {
test_nav_up(pool_id, amount);
TinlakeInvestorPool::do_borrow(borrower, pool_id, amount)
}

pub fn test_payback(borrower: u64, pool_id: u32, amount: Balance) -> DispatchResult {
test_nav_down(pool_id, amount);
TinlakeInvestorPool::do_payback(borrower, pool_id, amount)
}

pub fn test_nav_up(pool_id: u32, amount: Balance) {
FakeNav::update(pool_id, FakeNav::value(pool_id) + amount);
}

pub fn test_nav_down(pool_id: u32, amount: Balance) {
FakeNav::update(pool_id, FakeNav::value(pool_id) - amount);
}
24 changes: 4 additions & 20 deletions pallets/tinlake-investor-pool/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ fn core_constraints_currency_available_cant_cover_redemptions() {
max_reserve: 40,
available_reserve: Zero::zero(),
total_reserve: 39,
fake_nav: 0,
};

let epoch = EpochExecutionInfo {
Expand Down Expand Up @@ -105,7 +104,6 @@ fn pool_constraints_pool_reserve_above_max_reserve() {
max_reserve: 5,
available_reserve: Zero::zero(),
total_reserve: 40,
fake_nav: 90,
};

let epoch = EpochExecutionInfo {
Expand Down Expand Up @@ -178,7 +176,6 @@ fn pool_constraints_tranche_violates_sub_ratio() {
max_reserve: 150,
available_reserve: Zero::zero(),
total_reserve: 50,
fake_nav: 0,
};

let epoch = EpochExecutionInfo {
Expand Down Expand Up @@ -251,7 +248,6 @@ fn pool_constraints_pass() {
max_reserve: 150,
available_reserve: Zero::zero(),
total_reserve: 50,
fake_nav: 145,
};

let epoch = EpochExecutionInfo {
Expand Down Expand Up @@ -280,7 +276,7 @@ fn epoch() {
let tin_investor = Origin::signed(0);
let drop_investor = Origin::signed(1);
let pool_owner = Origin::signed(2);
let borrower = Origin::signed(3);
let borrower = 3;
let pool_account = Origin::signed(PoolLocator { pool_id: 0 }.into_account());

// Initialize pool with initial investments
Expand Down Expand Up @@ -332,11 +328,7 @@ fn epoch() {

// Borrow some money
next_block();
assert_ok!(TinlakeInvestorPool::test_borrow(
borrower.clone(),
0,
500 * CURRENCY
));
assert_ok!(test_borrow(borrower.clone(), 0, 500 * CURRENCY));

let pool = TinlakeInvestorPool::pool(0).unwrap();
assert_eq!(pool.tranches[0].debt, 250 * CURRENCY);
Expand All @@ -348,16 +340,8 @@ fn epoch() {

// Repay (with made up interest) after a month.
next_block_after(60 * 60 * 24 * 30);
assert_ok!(TinlakeInvestorPool::test_nav_up(
borrower.clone(),
0,
10 * CURRENCY
));
assert_ok!(TinlakeInvestorPool::test_payback(
borrower.clone(),
0,
510 * CURRENCY
));
test_nav_up(0, 10 * CURRENCY);
assert_ok!(test_payback(borrower.clone(), 0, 510 * CURRENCY));

let pool = TinlakeInvestorPool::pool(0).unwrap();
assert_eq!(pool.tranches[0].debt, 0);
Expand Down
2 changes: 2 additions & 0 deletions runtime/development/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ impl pallet_tinlake_investor_pool::Config for Runtime {
type EpochId = u32;
type CurrencyId = CurrencyId;
type Tokens = Tokens;
type LoanAmount = Amount;
type NAV = Loan;
type TrancheToken = TrancheToken<Runtime>;
type Time = Timestamp;
}
Expand Down

0 comments on commit 5b09f5e

Please sign in to comment.