Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
36 changes: 0 additions & 36 deletions frame/bonded-finance/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,42 +179,6 @@ prop_compose! {
}
}

#[test]
fn bond_offer_cc_e005bce405358673d3249e53d51e474d4b135bdf8f2b0b82f94f525c0efbdbf8() {
ExtBuilder::build()
.execute_with(|| {
let offer = BondOffer {
beneficiary: 1,
asset: MockCurrencyId::BTC,
bond_price: 1000000,
nb_of_bonds: 381,
maturity: BondDuration::Infinite,
reward: BondOfferReward {
asset: MockCurrencyId::ETH,
amount: 381000000,
maturity: 1,
},
};
prop_assert_ok!(Tokens::mint_into(NATIVE_CURRENCY_ID, &ALICE, Stake::get()));
prop_assert_ok!(Tokens::mint_into(offer.reward.asset, &ALICE, offer.reward.amount));
let half_nb_of_bonds = offer.nb_of_bonds / 2;
let half_reward = offer.reward.amount / 2;
prop_assert_ok!(Tokens::mint_into(
offer.asset,
&BOB,
half_nb_of_bonds * offer.bond_price
));

let offer_id = BondedFinance::do_offer(&ALICE, offer.clone()).unwrap();

let bob_reward = BondedFinance::do_bond(offer_id, &BOB, half_nb_of_bonds).unwrap();
prop_assert_acceptable_computation_error!(bob_reward, half_reward, 3);

Ok(())
})
.unwrap();
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(10_000))]

Expand Down
32 changes: 29 additions & 3 deletions frame/composable-support/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ impl<T: Validate<U> + Validate<V> + Validate<W>, U, V, W> Validate<(U, V, W)> fo
}
}

impl<T: Validate<U> + Validate<V> + Validate<W> + Validate<Z>, U, V, W, Z> Validate<(U, V, W, Z)>
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)?;
let value = Validate::<Z>::validate(value)?;
Ok(value)
}
}

impl<T: Validate<U>, U> Validated<T, U> {
pub fn value(self) -> T {
self.value
Expand Down Expand Up @@ -119,14 +132,13 @@ mod test {
type CheckARangeTag = (ValidARange, Valid);
type CheckBRangeTag = (ValidBRange, Valid);
type CheckABRangeTag = (ValidARange, (ValidBRange, Valid));
type ManyValidatorsTags = (ValidARange, (ValidBRange, (Invalid, Valid)));
// note: next seems is not supported yet
// type NestedValidated = (Validated<X, Valid>, Validated<Y, Valid>);
// #[derive(Debug, Eq, PartialEq, codec::Encode, codec::Decode, Default)]
// struct Y {
// }

#[derive(Debug, Eq, PartialEq, codec::Encode, codec::Decode, Default)]
#[derive(Debug, Eq, PartialEq, codec::Encode, codec::Decode, Default, Clone)]
struct X {
a: u32,
b: u32,
Expand Down Expand Up @@ -155,7 +167,21 @@ mod test {
#[test]
fn nested_validator() {
let valid = X { a: 10, b: 0xCAFEBABE };
assert!(Validate::<ManyValidatorsTags>::validate(valid).is_err());

type ManyValidatorsTagsNested = (ValidARange, (ValidBRange, (Invalid, Valid)));

assert!(Validate::<ManyValidatorsTagsNested>::validate(valid).is_err());
}

#[test]
fn either_nested_or_flat() {
let valid = X { a: 10, b: 0xCAFEBABE };
type ManyValidatorsTagsNested = (ValidARange, (ValidBRange, (Invalid, Valid)));
type ManyValidatorsTagsFlat = (ValidARange, ValidBRange, Invalid, Valid);
assert_eq!(
Validate::<ManyValidatorsTagsNested>::validate(valid.clone()),
Validate::<ManyValidatorsTagsFlat>::validate(valid)
);
}

#[test]
Expand Down
93 changes: 47 additions & 46 deletions frame/lending/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ pub mod pallet {
/// We normalized price over all markets and protect from spam all possible pairs equally.
/// Locking borrow amount ensures manager can create market wit borrow assets, and we force
/// him to really create it.
///
/// This solution forces to have amount before creating market.
/// Vault can take that amount if reconfigured so, but that may be changed during runtime
/// upgrades.
#[pallet::constant]
type MarketCreationStake: Get<Self::Balance>;

Expand Down Expand Up @@ -971,17 +975,21 @@ pub mod pallet {
debt_owner: &T::AccountId,
amount_to_borrow: T::Balance,
) -> Result<FixedU128, DispatchError> {
let debt_asset_id = DebtMarkets::<T>::get(market_id);

let market_index =
BorrowIndex::<T>::try_get(market_id).map_err(|_| Error::<T>::MarketDoesNotExist)?;

let account_interest_index =
DebtIndex::<T>::get(market_id, debt_owner).unwrap_or_else(ZeroToOneFixedU128::zero);
let debt_asset_id = DebtMarkets::<T>::get(market_id);
let existing_borrow_amount =
<T as Config>::MultiCurrency::balance(debt_asset_id, debt_owner);

// TODO: split out math from update, make update last step
<T as Config>::MultiCurrency::mint_into(debt_asset_id, debt_owner, amount_to_borrow)?;
// TODO: decide if need to split out dept teacking vs debt token
// TODO: decide if need to split out dept tracking vs debt token
<T as Config>::MultiCurrency::hold(debt_asset_id, debt_owner, amount_to_borrow)?;

let total_borrow_amount = existing_borrow_amount.safe_add(&amount_to_borrow)?;
let existing_borrow_share =
Percent::from_rational(existing_borrow_amount, total_borrow_amount);
Expand Down Expand Up @@ -1249,6 +1257,7 @@ pub mod pallet {
Ok(())
}

/// must be called in transaction
fn repay_borrow(
market_id: &Self::MarketId,
from: &Self::AccountId,
Expand Down Expand Up @@ -1277,64 +1286,56 @@ pub mod pallet {

let mut remaining_borrow_amount =
<T as Config>::MultiCurrency::balance(debt_asset_id, &market_account);
if total_repay_amount <= burn_amount {
// only repay interest
<T as Config>::MultiCurrency::release(
debt_asset_id,
beneficiary,
total_repay_amount,
true,
)
.expect("can always release held debt balance");
<T as Config>::MultiCurrency::burn_from(
debt_asset_id,
beneficiary,
total_repay_amount,
)
.expect("can always burn debt balance");

// BUG: so each time we repay, we must burn from market and from account, evidently
// this is not case now NOTE: we do not ++ borrow on each user, but on market total,
// so that there gas burn too much, so real borrow is borrow * (market index /
// borrower index) TODO: cover relation with test and fix it
let debt_to_release = if total_repay_amount <= burn_amount {
total_repay_amount
} else {
let repay_borrow_amount = total_repay_amount - burn_amount;

remaining_borrow_amount -= repay_borrow_amount;
<T as Config>::MultiCurrency::burn_from(debt_asset_id, &market_account, repay_borrow_amount).expect(
"debt balance of market must be of parts of debts of borrowers and can reduce it",
);
<T as Config>::MultiCurrency::release(
debt_asset_id,
beneficiary,
burn_amount,
true,
)
.expect("can always release held debt balance");
<T as Config>::MultiCurrency::burn_from(
debt_asset_id,
beneficiary,
burn_amount,
)
.expect("can always burn debt balance");
}
// TODO: fuzzing is must to uncover cases when sum != total
&market_account,
repay_borrow_amount,
)?;
burn_amount
};

// release_and_burn
<T as Config>::MultiCurrency::release(
debt_asset_id,
beneficiary,
debt_to_release,
true,
)?;
<T as Config>::MultiCurrency::burn_from(
debt_asset_id,
beneficiary,
debt_to_release,
)?;

<T as Config>::MultiCurrency::transfer(
borrow_asset_id,
from,
&market_account,
total_repay_amount,
debt_to_release,
false,
)
.expect("must be able to transfer because of above checks");
)?;

if remaining_borrow_amount == T::Balance::zero() {
BorrowTimestamp::<T>::remove(market_id, beneficiary);
DebtIndex::<T>::remove(market_id, beneficiary);
let rent = BorrowRent::<T>::get(market_id, beneficiary)
.expect("protocol maintains rent if there is debt");
<T as Config>::NativeCurrency::transfer(
&market_account,
beneficiary,
rent,
false,
)
.expect("there is enough rent to transfer back by protocol");
if let Some(rent) = BorrowRent::<T>::get(market_id, beneficiary) {
<T as Config>::NativeCurrency::transfer(
&market_account,
beneficiary,
rent,
false,
)?;
}
}
}

Expand Down