Skip to content

Commit 1e7a7c7

Browse files
feat: collateral tracking (#192)
* feat: collateral tracking * fix: update only snapshot and info in market struct * chore: remove comments and dead code * cargo: fmt * chore: some small naming + comments * test: add comprehensive tests for snapshot * cargo: fmt * cargo: clippy * chore: small change on (500) to 500 * chore: increase test precision * chore: make crate public fields in snapshot for use in common module * test: improve testing time boundaries of snapshots * cargo: fmt * test: increase divisor and sleeps on multiple_snapshots_show_progression * test: try other method of finding snapshot state for test --------- Co-authored-by: peer2 <peer2@templarprotocol.com>
1 parent bbc8540 commit 1e7a7c7

File tree

8 files changed

+690
-47
lines changed

8 files changed

+690
-47
lines changed

common/src/borrow.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ impl<'a> BorrowPositionGuard<'a> {
378378
.increase_collateral_asset_deposit(amount)
379379
.unwrap_or_else(|| env::panic_str("Borrow position collateral asset overflow"));
380380

381+
asset_op!(self.market.collateral_asset_deposited += amount);
382+
381383
MarketEvent::CollateralDeposited {
382384
account_id: self.account_id.clone(),
383385
collateral_asset_amount: amount,
@@ -394,6 +396,8 @@ impl<'a> BorrowPositionGuard<'a> {
394396
.decrease_collateral_asset_deposit(amount)
395397
.unwrap_or_else(|| env::panic_str("Borrow position collateral asset underflow"));
396398

399+
asset_op!(self.market.collateral_asset_deposited -= amount);
400+
397401
MarketEvent::CollateralWithdrawn {
398402
account_id: self.account_id.clone(),
399403
collateral_asset_amount: amount,

common/src/market/impl.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,19 @@ use near_sdk::{
66
};
77

88
use crate::{
9-
asset::BorrowAssetAmount,
9+
asset::{BorrowAssetAmount, CollateralAssetAmount},
1010
asset_op,
1111
borrow::{BorrowPosition, BorrowPositionGuard, BorrowPositionRef},
1212
chunked_append_only_list::ChunkedAppendOnlyList,
1313
event::MarketEvent,
14-
market::MarketConfiguration,
14+
market::{MarketConfiguration, WithdrawalResolution},
1515
number::Decimal,
1616
snapshot::Snapshot,
1717
static_yield::StaticYieldRecord,
1818
supply::{SupplyPosition, SupplyPositionGuard, SupplyPositionRef},
1919
withdrawal_queue::{error::WithdrawalQueueLockError, WithdrawalQueue},
2020
};
2121

22-
use super::WithdrawalResolution;
23-
2422
#[derive(BorshStorageKey)]
2523
#[near]
2624
enum StorageKey {
@@ -35,10 +33,20 @@ enum StorageKey {
3533
pub struct Market {
3634
prefix: Vec<u8>,
3735
pub configuration: MarketConfiguration,
36+
/// Total amount of borrow asset earning interest in the market.
3837
pub borrow_asset_deposited_active: BorrowAssetAmount,
38+
/// Mapping of upcoming snapshot indices to amounts of borrow asset that will be activated.
3939
pub borrow_asset_deposited_incoming: HashMap<u32, BorrowAssetAmount>,
40+
/// Sending borrow asset out, because if somebody sends the contract borrow asset, it's ok for the
41+
/// contract to attempt to fulfill withdrawal request, even if the market thinks it doesn't have
42+
/// enough to fulfill.
4043
pub borrow_asset_in_flight: BorrowAssetAmount,
44+
/// Amount of borrow asset that has been withdrawn (is in use by) by borrowers.
45+
///
46+
/// `borrow_asset_deposited_active - borrow_asset_borrowed >= 0` should always be true.
4147
pub borrow_asset_borrowed: BorrowAssetAmount,
48+
/// Market-wide collateral asset deposit tracking.
49+
pub collateral_asset_deposited: CollateralAssetAmount,
4250
pub(crate) supply_positions: UnorderedMap<AccountId, SupplyPosition>,
4351
pub(crate) borrow_positions: UnorderedMap<AccountId, BorrowPosition>,
4452
pub current_snapshot: Snapshot,
@@ -66,7 +74,7 @@ impl Market {
6674

6775
let first_snapshot = Snapshot::new(configuration.time_chunk_configuration.previous());
6876
let mut current_snapshot = first_snapshot.clone();
69-
current_snapshot.time_chunk = configuration.time_chunk_configuration.now();
77+
current_snapshot.set_time_chunk(configuration.time_chunk_configuration.now());
7078

7179
let mut self_ = Self {
7280
prefix: prefix.clone(),
@@ -75,6 +83,7 @@ impl Market {
7583
borrow_asset_deposited_incoming: HashMap::new(),
7684
borrow_asset_in_flight: 0.into(),
7785
borrow_asset_borrowed: 0.into(),
86+
collateral_asset_deposited: 0.into(),
7887
supply_positions: UnorderedMap::new(key!(SupplyPositions)),
7988
borrow_positions: UnorderedMap::new(key!(BorrowPositions)),
8089
current_snapshot,
@@ -117,13 +126,16 @@ impl Market {
117126
self.current_snapshot.update_active(
118127
self.borrow_asset_deposited_active,
119128
self.borrow_asset_borrowed,
129+
self.collateral_asset_deposited,
120130
&self.configuration.borrow_interest_rate_strategy,
121131
);
122132
self.current_snapshot.add_yield(yield_distribution);
123-
self.current_snapshot.deposited_incoming = *self
124-
.borrow_asset_deposited_incoming
125-
.get(&self.finalized_snapshots.len())
126-
.unwrap_or(&0.into());
133+
self.current_snapshot.set_borrow_asset_deposited_incoming(
134+
*self
135+
.borrow_asset_deposited_incoming
136+
.get(&self.finalized_snapshots.len())
137+
.unwrap_or(&0.into()),
138+
);
127139
} else {
128140
// Otherwise, finalize the current snapshot and create a new one.
129141
let deposited_incoming = self
@@ -132,11 +144,12 @@ impl Market {
132144
.unwrap_or(0.into());
133145
asset_op!(self.borrow_asset_deposited_active += deposited_incoming);
134146
let mut snapshot = Snapshot::new(time_chunk);
135-
snapshot.yield_distribution = yield_distribution;
136-
snapshot.deposited_incoming = deposited_incoming;
147+
snapshot.set_yield_distribution(yield_distribution);
148+
snapshot.set_borrow_asset_deposited_incoming(deposited_incoming);
137149
snapshot.update_active(
138150
self.borrow_asset_deposited_active,
139151
self.borrow_asset_borrowed,
152+
self.collateral_asset_deposited,
140153
&self.configuration.borrow_interest_rate_strategy,
141154
);
142155
std::mem::swap(&mut snapshot, &mut self.current_snapshot);

common/src/snapshot.rs

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
use near_sdk::{env, json_types::U64, near};
22

33
use crate::{
4-
asset::BorrowAssetAmount, asset_op, interest_rate_strategy::InterestRateStrategy,
5-
number::Decimal, time_chunk::TimeChunk,
4+
asset::{BorrowAssetAmount, CollateralAssetAmount},
5+
asset_op,
6+
interest_rate_strategy::InterestRateStrategy,
7+
number::Decimal,
8+
time_chunk::TimeChunk,
69
};
710

811
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
912
#[near(serializers = [borsh, json])]
1013
pub struct Snapshot {
11-
pub time_chunk: TimeChunk,
12-
pub end_timestamp_ms: U64,
13-
deposited_active: BorrowAssetAmount,
14-
pub deposited_incoming: BorrowAssetAmount,
15-
borrowed: BorrowAssetAmount,
16-
pub yield_distribution: BorrowAssetAmount,
14+
pub(crate) time_chunk: TimeChunk,
15+
pub(crate) end_timestamp_ms: U64,
16+
pub(crate) borrow_asset_deposited_active: BorrowAssetAmount,
17+
borrow_asset_deposited_incoming: BorrowAssetAmount,
18+
borrow_asset_borrowed: BorrowAssetAmount,
19+
collateral_asset_deposited: CollateralAssetAmount,
20+
yield_distribution: BorrowAssetAmount,
1721
interest_rate: Decimal,
1822
}
1923

2024
impl Snapshot {
2125
pub fn new(time_chunk: TimeChunk) -> Self {
2226
Self {
2327
time_chunk,
24-
end_timestamp_ms: near_sdk::env::block_timestamp_ms().into(),
25-
deposited_active: 0.into(),
26-
deposited_incoming: 0.into(),
27-
borrowed: 0.into(),
28+
end_timestamp_ms: env::block_timestamp_ms().into(),
29+
borrow_asset_deposited_active: 0.into(),
30+
borrow_asset_deposited_incoming: 0.into(),
31+
borrow_asset_borrowed: 0.into(),
32+
collateral_asset_deposited: 0.into(),
2833
yield_distribution: BorrowAssetAmount::zero(),
2934
interest_rate: Decimal::ZERO,
3035
}
@@ -36,35 +41,70 @@ impl Snapshot {
3641

3742
pub fn update_active(
3843
&mut self,
39-
deposited_active: BorrowAssetAmount,
40-
borrowed: BorrowAssetAmount,
44+
borrow_asset_deposited_active: BorrowAssetAmount,
45+
borrow_asset_borrowed: BorrowAssetAmount,
46+
collateral_asset_deposited: CollateralAssetAmount,
4147
interest_rate_strategy: &InterestRateStrategy,
4248
) {
4349
self.end_timestamp_ms = env::block_timestamp_ms().into();
44-
self.deposited_active = deposited_active;
45-
self.borrowed = borrowed;
50+
self.borrow_asset_deposited_active = borrow_asset_deposited_active;
51+
self.borrow_asset_borrowed = borrow_asset_borrowed;
52+
self.collateral_asset_deposited = collateral_asset_deposited;
4653
self.interest_rate = interest_rate_strategy.at(self.usage_ratio());
4754
}
4855

4956
pub fn usage_ratio(&self) -> Decimal {
50-
if self.deposited_active.is_zero() || self.borrowed.is_zero() {
57+
if self.borrow_asset_deposited_active.is_zero() || self.borrow_asset_borrowed.is_zero() {
5158
Decimal::ZERO
52-
} else if self.borrowed >= self.deposited_active {
59+
} else if self.borrow_asset_borrowed >= self.borrow_asset_deposited_active {
5360
Decimal::ONE
5461
} else {
55-
Decimal::from(self.borrowed) / Decimal::from(self.deposited_active)
62+
Decimal::from(self.borrow_asset_borrowed)
63+
/ Decimal::from(self.borrow_asset_deposited_active)
5664
}
5765
}
5866

67+
pub fn set_time_chunk(&mut self, time_chunk: TimeChunk) {
68+
self.time_chunk = time_chunk;
69+
}
70+
71+
pub fn set_borrow_asset_deposited_incoming(&mut self, amount: BorrowAssetAmount) {
72+
self.borrow_asset_deposited_incoming = amount;
73+
}
74+
75+
pub fn set_yield_distribution(&mut self, amount: BorrowAssetAmount) {
76+
self.yield_distribution = amount;
77+
}
78+
79+
pub fn time_chunk(&self) -> &TimeChunk {
80+
&self.time_chunk
81+
}
82+
83+
pub fn end_timestamp_ms(&self) -> U64 {
84+
self.end_timestamp_ms
85+
}
86+
87+
pub fn borrow_asset_deposited_incoming(&self) -> BorrowAssetAmount {
88+
self.borrow_asset_deposited_incoming
89+
}
90+
91+
pub fn collateral_asset_deposited(&self) -> CollateralAssetAmount {
92+
self.collateral_asset_deposited
93+
}
94+
95+
pub fn yield_distribution(&self) -> BorrowAssetAmount {
96+
self.yield_distribution
97+
}
98+
5999
pub fn interest_rate(&self) -> Decimal {
60100
self.interest_rate
61101
}
62102

63-
pub fn deposited_active(&self) -> BorrowAssetAmount {
64-
self.deposited_active
103+
pub fn borrow_asset_deposited_active(&self) -> BorrowAssetAmount {
104+
self.borrow_asset_deposited_active
65105
}
66106

67-
pub fn borrowed(&self) -> BorrowAssetAmount {
68-
self.borrowed
107+
pub fn borrow_asset_borrowed(&self) -> BorrowAssetAmount {
108+
self.borrow_asset_borrowed
69109
}
70110
}

common/src/supply.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ impl<M: Deref<Target = Market>> SupplyPositionRef<M> {
162162
amount += u128::from(incoming.amount);
163163
}
164164

165-
if !snapshot.deposited_active().is_zero() {
166-
accumulated += amount * Decimal::from(snapshot.yield_distribution)
167-
/ Decimal::from(snapshot.deposited_active());
165+
if !snapshot.borrow_asset_deposited_active.is_zero() {
166+
accumulated += amount * Decimal::from(snapshot.yield_distribution())
167+
/ Decimal::from(snapshot.borrow_asset_deposited_active);
168168
}
169169

170170
next_snapshot_index = i as u32 + 1;

contract/market/src/impl_market_external.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,11 @@ impl MarketExternalInterface for Contract {
292292
}
293293

294294
fn get_last_yield_rate(&self) -> Decimal {
295-
let deposited: Decimal = self.current_snapshot.deposited_active().into();
295+
let deposited: Decimal = self.current_snapshot.borrow_asset_deposited_active().into();
296296
if deposited.is_zero() {
297297
return Decimal::ZERO;
298298
}
299-
let borrowed: Decimal = self.current_snapshot.borrowed().into();
299+
let borrowed: Decimal = self.current_snapshot.borrow_asset_borrowed().into();
300300
let supply_weight: Decimal = self.configuration.yield_weights.supply.get().into();
301301
let total_weight: Decimal = self.configuration.yield_weights.total_weight().get().into();
302302

contract/market/tests/happy_path.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ async fn test_happy(#[case] borrow_mt: bool, #[case] collateral_mt: bool) {
8181

8282
let snapshots = c.list_finalized_snapshots(None, None).await;
8383
assert_eq!(snapshots.len(), 1);
84-
assert!(snapshots[0].yield_distribution.is_zero());
85-
assert!(snapshots[0].deposited_active().is_zero());
86-
assert!(snapshots[0].borrowed().is_zero());
84+
assert!(snapshots[0].yield_distribution().is_zero());
85+
assert!(snapshots[0].borrow_asset_deposited_active().is_zero());
86+
assert!(snapshots[0].borrow_asset_borrowed().is_zero());
8787

8888
// Step 1: Supply user sends tokens to contract to use for borrows.
8989
c.supply(&supply_user, 1100).await;

0 commit comments

Comments
 (0)