Skip to content

Commit 802d92f

Browse files
authored
Supply referral initial module (#519)
* initial module * rename to protocol fees * referral * comment * fix unit test * fix get share amount bug * minor
1 parent 35adc43 commit 802d92f

9 files changed

Lines changed: 298 additions & 71 deletions

File tree

packages/margin_trading/sources/helper/margin_constants.move

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const MIN_LEVERAGE: u64 = 1_000_000_000; // 1x
1111
const MAX_LEVERAGE: u64 = 20_000_000_000; // 20x
1212
const YEAR_MS: u64 = 365 * 24 * 60 * 60 * 1000;
1313
const MIN_MIN_BORROW: u64 = 1000;
14+
const DEFAULT_REFERRAL: address = @0x0;
1415

1516
public fun margin_version(): u64 {
1617
MARGIN_VERSION
@@ -43,3 +44,7 @@ public fun year_ms(): u64 {
4344
public fun min_min_borrow(): u64 {
4445
MIN_MIN_BORROW
4546
}
47+
48+
public fun default_referral(): address {
49+
DEFAULT_REFERRAL
50+
}

packages/margin_trading/sources/margin_pool.move

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use margin_trading::{
88
margin_registry::{MarginRegistry, MaintainerCap, MarginPoolCap},
99
margin_state::{Self, State},
1010
position_manager::{Self, PositionManager},
11-
protocol_config::{InterestConfig, MarginPoolConfig, ProtocolConfig}
11+
protocol_config::{InterestConfig, MarginPoolConfig, ProtocolConfig},
12+
protocol_fees::{Self, ProtocolFees, Referral}
1213
};
1314
use std::type_name::{Self, TypeName};
1415
use sui::{balance::{Self, Balance}, clock::Clock, coin::Coin, event, vec_set::{Self, VecSet}};
@@ -29,6 +30,7 @@ public struct MarginPool<phantom Asset> has key, store {
2930
vault: Balance<Asset>,
3031
state: State,
3132
config: ProtocolConfig,
33+
protocol_fees: ProtocolFees,
3234
positions: PositionManager,
3335
allowed_deepbook_pools: VecSet<ID>,
3436
}
@@ -99,6 +101,7 @@ public fun create_margin_pool<Asset>(
99101
vault: balance::zero<Asset>(),
100102
state: margin_state::default(clock),
101103
config,
104+
protocol_fees: protocol_fees::default_protocol_fees(ctx, clock),
102105
positions: position_manager::create_position_manager(ctx),
103106
allowed_deepbook_pools: vec_set::empty(),
104107
};
@@ -204,20 +207,26 @@ public fun update_margin_pool_config<Asset>(
204207
}
205208

206209
// === Public Functions * LENDING * ===
207-
/// Allows anyone to supply the margin pool. Returns the new user supply amount.
210+
/// Supply to the margin pool. Returns the new user supply amount.
208211
public fun supply<Asset>(
209212
self: &mut MarginPool<Asset>,
210213
registry: &MarginRegistry,
211214
coin: Coin<Asset>,
215+
referral: Option<address>,
212216
clock: &Clock,
213217
ctx: &TxContext,
214-
) {
218+
): u64 {
215219
registry.load_inner();
216-
let supplier = ctx.sender();
217220
let supply_amount = coin.value();
218-
219-
let supply_shares = self.state.increase_supply(&self.config, supply_amount, clock);
220-
self.positions.increase_user_supply(supplier, supply_shares);
221+
let (supply_shares, protocol_fees) = self
222+
.state
223+
.increase_supply(&self.config, supply_amount, clock);
224+
self.protocol_fees.increase_fees_per_share(self.state.supply_shares(), protocol_fees);
225+
let (total_user_supply, previous_referral) = self
226+
.positions
227+
.increase_user_supply(referral, supply_shares, ctx);
228+
self.protocol_fees.decrease_shares(previous_referral, total_user_supply - supply_shares, clock);
229+
self.protocol_fees.increase_shares(referral, total_user_supply, clock);
221230

222231
let balance = coin.into_balance();
223232
self.vault.join(balance);
@@ -227,14 +236,16 @@ public fun supply<Asset>(
227236
event::emit(AssetSupplied {
228237
margin_pool_id: self.id(),
229238
asset_type: type_name::with_defining_ids<Asset>(),
230-
supplier,
239+
supplier: ctx.sender(),
231240
supply_amount,
232241
supply_shares,
233242
timestamp: clock.timestamp_ms(),
234243
});
244+
245+
total_user_supply
235246
}
236247

237-
/// Allows withdrawal from the margin pool. Returns the withdrawn coin.
248+
/// Withdraw from the margin pool. Returns the withdrawn coin.
238249
public fun withdraw<Asset>(
239250
self: &mut MarginPool<Asset>,
240251
registry: &MarginRegistry,
@@ -243,20 +254,25 @@ public fun withdraw<Asset>(
243254
ctx: &mut TxContext,
244255
): Coin<Asset> {
245256
registry.load_inner();
246-
let supplier = ctx.sender();
247-
let supplied_shares = self.positions.user_supply_shares(supplier);
257+
let supplied_shares = self.positions.user_supply_shares(ctx);
248258
let supplied_amount = self.state.supply_shares_to_amount(supplied_shares, &self.config, clock);
249259
let withdraw_amount = amount.destroy_with_default(supplied_amount);
250260
let withdraw_shares = math::mul(supplied_shares, math::div(withdraw_amount, supplied_amount));
251261

252-
self.positions.decrease_user_supply(supplier, withdraw_shares);
262+
let (_, protocol_fees) = self
263+
.state
264+
.decrease_supply_shares(&self.config, withdraw_shares, clock);
265+
self.protocol_fees.increase_fees_per_share(self.state.supply_shares(), protocol_fees);
266+
267+
let (_, previous_referral) = self.positions.decrease_user_supply(withdraw_shares, ctx);
268+
self.protocol_fees.decrease_shares(previous_referral, withdraw_shares, clock);
253269
assert!(withdraw_amount <= self.vault.value(), ENotEnoughAssetInPool);
254270
let coin = self.vault.split(withdraw_amount).into_coin(ctx);
255271

256272
event::emit(AssetWithdrawn {
257273
margin_pool_id: self.id(),
258274
asset_type: type_name::with_defining_ids<Asset>(),
259-
supplier,
275+
supplier: ctx.sender(),
260276
withdraw_amount,
261277
withdraw_shares,
262278
timestamp: clock.timestamp_ms(),
@@ -265,6 +281,19 @@ public fun withdraw<Asset>(
265281
coin
266282
}
267283

284+
/// Withdraw the referral fees.
285+
public fun withdraw_referral_fees<Asset>(
286+
self: &mut MarginPool<Asset>,
287+
referral: &mut Referral,
288+
clock: &Clock,
289+
ctx: &mut TxContext,
290+
): Coin<Asset> {
291+
let referral_fees = self.protocol_fees.calculate_and_claim(referral, clock);
292+
let coin = self.vault.split(referral_fees).into_coin(ctx);
293+
294+
coin
295+
}
296+
268297
// === Public-View Functions ===
269298
public fun deepbook_pool_allowed<Asset>(self: &MarginPool<Asset>, deepbook_pool_id: ID): bool {
270299
self.allowed_deepbook_pools.contains(&deepbook_pool_id)
@@ -280,9 +309,10 @@ public(package) fun borrow<Asset>(
280309
): (Coin<Asset>, u64, u64) {
281310
assert!(amount <= self.vault.value(), ENotEnoughAssetInPool);
282311
assert!(amount >= self.config.min_borrow(), EBorrowAmountTooLow);
283-
let (total_borrow, total_borrow_shares) = self
312+
let (total_borrow, total_borrow_shares, protocol_fees) = self
284313
.state
285314
.increase_borrow(&self.config, amount, clock);
315+
self.protocol_fees.increase_fees_per_share(self.state.supply_shares(), protocol_fees);
286316
assert!(
287317
self.state.utilization_rate() <= self.config.max_utilization_rate(),
288318
EMaxPoolBorrowPercentageExceeded,
@@ -297,7 +327,8 @@ public(package) fun repay<Asset>(
297327
coin: Coin<Asset>,
298328
clock: &Clock,
299329
) {
300-
let amount = self.state.decrease_borrow_shares(&self.config, shares, clock);
330+
let (amount, protocol_fees) = self.state.decrease_borrow_shares(&self.config, shares, clock);
331+
self.protocol_fees.increase_fees_per_share(self.state.supply_shares(), protocol_fees);
301332
assert!(coin.value() == amount, EInvalidRepayQuantity);
302333
self.vault.join(coin.into_balance());
303334
}
@@ -311,7 +342,8 @@ public(package) fun repay_liquidation<Asset>(
311342
coin: Coin<Asset>,
312343
clock: &Clock,
313344
): (u64, u64, u64) {
314-
let amount = self.state.decrease_borrow_shares(&self.config, shares, clock); // decreased 48.545 shares, 97.087 USDC
345+
let (amount, protocol_fees) = self.state.decrease_borrow_shares(&self.config, shares, clock); // decreased 48.545 shares, 97.087 USDC
346+
self.protocol_fees.increase_fees_per_share(self.state.supply_shares(), protocol_fees);
315347
let coin_value = coin.value(); // 100 USDC
316348
let (reward, default) = if (coin_value > amount) {
317349
self.state.increase_supply_absolute(coin_value - amount);

packages/margin_trading/sources/margin_pool/margin_state.move

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@ public(package) fun increase_supply(
2929
config: &ProtocolConfig,
3030
amount: u64,
3131
clock: &Clock,
32-
): u64 {
33-
self.update(config, clock);
32+
): (u64, u64) {
33+
let protocol_fees = self.update(config, clock);
3434
let ratio = self.supply_ratio();
3535
let shares = math::div(amount, ratio);
3636
self.supply_shares = self.supply_shares + shares;
3737
self.supply = self.supply + amount;
3838

39-
shares
39+
(shares, protocol_fees)
4040
}
4141

4242
public(package) fun decrease_supply_shares(
4343
self: &mut State,
4444
config: &ProtocolConfig,
4545
shares: u64,
4646
clock: &Clock,
47-
): u64 {
48-
self.update(config, clock);
47+
): (u64, u64) {
48+
let protocol_fees = self.update(config, clock);
4949
let ratio = self.supply_ratio();
5050
let amount = math::mul(shares, ratio);
5151
self.supply_shares = self.supply_shares - shares;
5252
self.supply = self.supply - amount;
5353

54-
amount
54+
(amount, protocol_fees)
5555
}
5656

5757
public(package) fun increase_supply_absolute(self: &mut State, amount: u64) {
@@ -67,14 +67,14 @@ public(package) fun increase_borrow(
6767
config: &ProtocolConfig,
6868
amount: u64,
6969
clock: &Clock,
70-
): (u64, u64) {
71-
self.update(config, clock);
70+
): (u64, u64, u64) {
71+
let protocol_fees = self.update(config, clock);
7272
let ratio = self.borrow_ratio();
7373
let shares = math::div(amount, ratio);
7474
self.borrow_shares = self.borrow_shares + shares;
7575
self.borrow = self.borrow + amount;
7676

77-
(self.borrow, self.borrow_shares)
77+
(self.borrow, self.borrow_shares, protocol_fees)
7878
}
7979

8080
/// Decrease borrowed shares and return the corresponding amount
@@ -83,14 +83,14 @@ public(package) fun decrease_borrow_shares(
8383
config: &ProtocolConfig,
8484
shares: u64,
8585
clock: &Clock,
86-
): u64 {
87-
self.update(config, clock);
86+
): (u64, u64) {
87+
let protocol_fees = self.update(config, clock);
8888
let ratio = self.borrow_ratio();
8989
let amount = math::mul(shares, ratio);
9090
self.borrow_shares = self.borrow_shares - shares;
9191
self.borrow = self.borrow - amount;
9292

93-
amount
93+
(amount, protocol_fees)
9494
}
9595

9696
public(package) fun utilization_rate(self: &State): u64 {
@@ -105,6 +105,10 @@ public(package) fun supply(self: &State): u64 {
105105
self.supply
106106
}
107107

108+
public(package) fun supply_shares(self: &State): u64 {
109+
self.supply_shares
110+
}
111+
108112
public(package) fun borrow_shares_to_amount(
109113
self: &State,
110114
shares: u64,
@@ -115,7 +119,8 @@ public(package) fun borrow_shares_to_amount(
115119
let elapsed = now - self.last_update_timestamp;
116120

117121
let time_adjusted_rate = config.time_adjusted_rate(self.utilization_rate(), elapsed);
118-
let borrow = self.borrow + math::mul(self.borrow, time_adjusted_rate);
122+
let interest = math::mul(self.borrow, time_adjusted_rate);
123+
let borrow = self.borrow + interest;
119124
let ratio = if (self.borrow_shares == 0) {
120125
constants::float_scaling()
121126
} else {
@@ -135,7 +140,9 @@ public(package) fun supply_shares_to_amount(
135140
let elapsed = now - self.last_update_timestamp;
136141

137142
let time_adjusted_rate = config.time_adjusted_rate(self.utilization_rate(), elapsed);
138-
let supply = self.supply + math::mul(self.borrow, time_adjusted_rate);
143+
let interest = math::mul(self.borrow, time_adjusted_rate);
144+
let protocol_fees = math::mul(interest, config.protocol_spread());
145+
let supply = self.supply + interest - protocol_fees;
139146
let ratio = if (self.supply_shares == 0) {
140147
constants::float_scaling()
141148
} else {
@@ -145,14 +152,18 @@ public(package) fun supply_shares_to_amount(
145152
math::div(shares, ratio)
146153
}
147154

148-
fun update(self: &mut State, config: &ProtocolConfig, clock: &Clock) {
155+
fun update(self: &mut State, config: &ProtocolConfig, clock: &Clock): u64 {
149156
let now = clock.timestamp_ms();
150157
let elapsed = now - self.last_update_timestamp;
151158

152159
let time_adjusted_rate = config.time_adjusted_rate(self.utilization_rate(), elapsed);
153-
self.supply = self.supply + math::mul(self.borrow, time_adjusted_rate);
154-
self.borrow = self.borrow + math::mul(self.borrow, time_adjusted_rate);
160+
let interest = math::mul(self.borrow, time_adjusted_rate);
161+
let protocol_fees = math::mul(interest, config.protocol_spread());
162+
self.supply = self.supply + interest - protocol_fees;
163+
self.borrow = self.borrow + interest;
155164
self.last_update_timestamp = now;
165+
166+
protocol_fees
156167
}
157168

158169
fun supply_ratio(self: &State): u64 {

packages/margin_trading/sources/margin_pool/position_manager.move

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,73 @@ module margin_trading::position_manager;
88
use sui::table::{Self, Table};
99

1010
public struct PositionManager has store {
11-
supply_shares: Table<address, u64>,
11+
positions: Table<address, Position>,
12+
}
13+
14+
public struct Position has store {
15+
shares: u64,
16+
referral: Option<address>,
1217
}
1318

1419
public(package) fun create_position_manager(ctx: &mut TxContext): PositionManager {
1520
PositionManager {
16-
supply_shares: table::new(ctx),
21+
positions: table::new(ctx),
1722
}
1823
}
1924

2025
/// Increase the supply shares of the user and return outstanding supply shares.
2126
public(package) fun increase_user_supply(
2227
self: &mut PositionManager,
23-
user: address,
28+
referral: Option<address>,
2429
supply_shares: u64,
25-
): u64 {
26-
self.add_supply_entry(user);
27-
let user_supply_shares = self.supply_shares.borrow_mut(user);
28-
*user_supply_shares = *user_supply_shares + supply_shares;
30+
ctx: &TxContext,
31+
): (u64, Option<address>) {
32+
let user = ctx.sender();
33+
self.add_supply_entry(referral, ctx);
34+
let user_position = self.positions.borrow_mut(user);
35+
let current_referral = user_position.referral;
36+
user_position.shares = user_position.shares + supply_shares;
37+
user_position.referral = referral;
2938

30-
*user_supply_shares
39+
(user_position.shares, current_referral)
3140
}
3241

3342
/// Decrease the supply shares of the user and return outstanding supply shares.
3443
public(package) fun decrease_user_supply(
3544
self: &mut PositionManager,
36-
user: address,
3745
supply_shares: u64,
38-
): u64 {
39-
let user_supply_shares = self.supply_shares.borrow_mut(user);
40-
*user_supply_shares = *user_supply_shares - supply_shares;
46+
ctx: &TxContext,
47+
): (u64, Option<address>) {
48+
let user = ctx.sender();
49+
let user_position = self.positions.borrow_mut(user);
50+
user_position.shares = user_position.shares - supply_shares;
4151

42-
*user_supply_shares
52+
(user_position.shares, user_position.referral)
4353
}
4454

45-
public(package) fun add_supply_entry(self: &mut PositionManager, user: address) {
46-
if (!self.supply_shares.contains(user)) {
55+
public(package) fun add_supply_entry(
56+
self: &mut PositionManager,
57+
referral: Option<address>,
58+
ctx: &TxContext,
59+
) {
60+
let user = ctx.sender();
61+
if (!self.positions.contains(user)) {
4762
self
48-
.supply_shares
63+
.positions
4964
.add(
5065
user,
51-
0,
66+
Position {
67+
shares: 0,
68+
referral,
69+
},
5270
);
5371
}
5472
}
5573

56-
public(package) fun user_supply_shares(self: &PositionManager, user: address): u64 {
57-
if (self.supply_shares.contains(user)) {
58-
*self.supply_shares.borrow(user)
74+
public(package) fun user_supply_shares(self: &PositionManager, ctx: &TxContext): u64 {
75+
let user = ctx.sender();
76+
if (self.positions.contains(user)) {
77+
self.positions.borrow(user).shares
5978
} else {
6079
0
6180
}

0 commit comments

Comments
 (0)