@@ -23,6 +23,7 @@ module suilend::lending_market {
23
23
use suilend::liquidity_mining::{Self };
24
24
use sui::package;
25
25
use sui::sui::SUI ;
26
+ use sui::dynamic_field::{Self };
26
27
27
28
// === Errors ===
28
29
const EIncorrectVersion : u64 = 1 ;
@@ -32,9 +33,10 @@ module suilend::lending_market {
32
33
const ERewardPeriodNotOver : u64 = 5 ;
33
34
const ECannotClaimReward : u64 = 6 ;
34
35
const EInvalidObligationId : u64 = 7 ;
36
+ const EInvalidFeeReceivers : u64 = 8 ;
35
37
36
38
// === Constants ===
37
- const CURRENT_VERSION : u64 = 6 ;
39
+ const CURRENT_VERSION : u64 = 7 ;
38
40
const U64_MAX : u64 = 18_446_744_073_709_551_615 ;
39
41
40
42
// === One time Witness ===
@@ -54,7 +56,7 @@ module suilend::lending_market {
54
56
55
57
// window duration is in seconds
56
58
rate_limiter: RateLimiter ,
57
- fee_receiver: address ,
59
+ fee_receiver: address , // deprecated
58
60
59
61
/// unused
60
62
bad_debt_usd: Decimal ,
@@ -72,6 +74,15 @@ module suilend::lending_market {
72
74
obligation_id: ID
73
75
}
74
76
77
+ // === Dynamic Fields ===
78
+ public struct FeeReceiversKey has copy , drop , store { }
79
+
80
+ public struct FeeReceivers has store {
81
+ receivers: vector <address >,
82
+ weights: vector <u64 >,
83
+ total_weight: u64 ,
84
+ }
85
+
75
86
// cTokens redemptions and borrows are rate limited to mitigate exploits. however,
76
87
// on a liquidation we don't want to rate limit redemptions because we don't want liquidators to
77
88
// get stuck holding cTokens. So the liquidate function issues this exemption
@@ -168,7 +179,7 @@ module suilend::lending_market {
168
179
LendingMarketOwnerCap <P >,
169
180
LendingMarket <P >
170
181
) {
171
- let lending_market = LendingMarket <P > {
182
+ let mut lending_market = LendingMarket <P > {
172
183
id: object::new (ctx),
173
184
version: CURRENT_VERSION ,
174
185
reserves: vector ::empty (),
@@ -178,12 +189,19 @@ module suilend::lending_market {
178
189
bad_debt_usd: decimal::from (0 ),
179
190
bad_debt_limit_usd: decimal::from (0 ),
180
191
};
181
-
192
+
182
193
let owner_cap = LendingMarketOwnerCap <P > {
183
194
id: object::new (ctx),
184
195
lending_market_id: object::id (&lending_market)
185
196
};
186
197
198
+ set_fee_receivers (
199
+ &owner_cap,
200
+ &mut lending_market,
201
+ vector [tx_context::sender (ctx)],
202
+ vector [100 ]
203
+ );
204
+
187
205
(owner_cap, lending_market)
188
206
}
189
207
@@ -689,8 +707,8 @@ module suilend::lending_market {
689
707
);
690
708
};
691
709
710
+ let deposit_reserve = vector ::borrow_mut (&mut lending_market.reserves, deposit_reserve_id);
692
711
let expected_ctokens = {
693
- let deposit_reserve = vector ::borrow (&lending_market.reserves, deposit_reserve_id);
694
712
assert !(reserve::coin_type (deposit_reserve) == type_name::get <RewardType >(), EWrongType );
695
713
696
714
floor (
@@ -702,7 +720,7 @@ module suilend::lending_market {
702
720
};
703
721
704
722
if (expected_ctokens == 0 ) {
705
- transfer:: public_transfer (rewards, lending_market.fee_receiver );
723
+ reserve:: join_fees < P , RewardType >(deposit_reserve, coin:: into_balance (rewards) );
706
724
}
707
725
else {
708
726
let ctokens = deposit_liquidity_and_mint_ctokens <P , RewardType >(
@@ -1038,6 +1056,34 @@ module suilend::lending_market {
1038
1056
lending_market.rate_limiter = rate_limiter::new (config, clock::timestamp_ms (clock) / 1000 );
1039
1057
}
1040
1058
1059
+ public fun set_fee_receivers <P >(
1060
+ _: &LendingMarketOwnerCap <P >,
1061
+ lending_market: &mut LendingMarket <P >,
1062
+ receivers: vector <address >,
1063
+ weights: vector <u64 >,
1064
+ ) {
1065
+ assert !(lending_market.version == CURRENT_VERSION , EIncorrectVersion );
1066
+
1067
+ assert !(vector ::length (&receivers) == vector ::length (&weights), EInvalidFeeReceivers );
1068
+ assert !(vector ::length (&receivers) > 0 , EInvalidFeeReceivers );
1069
+
1070
+ let total_weight = vector ::fold !(weights, 0 , |acc, weight| acc + weight);
1071
+ assert !(total_weight > 0 , EInvalidFeeReceivers );
1072
+
1073
+ if (dynamic_field::exists_ (&lending_market.id, FeeReceiversKey {})) {
1074
+ let FeeReceivers { .. } = dynamic_field::remove <FeeReceiversKey , FeeReceivers >(
1075
+ &mut lending_market.id,
1076
+ FeeReceiversKey {}
1077
+ );
1078
+ };
1079
+
1080
+ dynamic_field::add (
1081
+ &mut lending_market.id,
1082
+ FeeReceiversKey {},
1083
+ FeeReceivers { receivers, weights, total_weight }
1084
+ );
1085
+ }
1086
+
1041
1087
entry fun claim_fees <P , T >(
1042
1088
lending_market: &mut LendingMarket <P >,
1043
1089
reserve_array_index: u64 ,
@@ -1047,10 +1093,44 @@ module suilend::lending_market {
1047
1093
1048
1094
let reserve = vector ::borrow_mut (&mut lending_market.reserves, reserve_array_index);
1049
1095
assert !(reserve::coin_type (reserve) == type_name::get <T >(), EWrongType );
1050
- let (ctoken_fees, fees) = reserve::claim_fees <P , T >(reserve);
1051
1096
1052
- transfer::public_transfer (coin::from_balance (ctoken_fees, ctx), lending_market.fee_receiver);
1053
- transfer::public_transfer (coin::from_balance (fees, ctx), lending_market.fee_receiver);
1097
+ let (mut ctoken_fees, mut fees) = reserve::claim_fees <P , T >(reserve);
1098
+ let total_ctoken_fees = balance::value (&ctoken_fees);
1099
+ let total_fees = balance::value (&fees);
1100
+
1101
+ let fee_receivers: &FeeReceivers = dynamic_field::borrow (&lending_market.id, FeeReceiversKey {});
1102
+ let num_fee_receivers = vector ::length (&fee_receivers.weights);
1103
+
1104
+ num_fee_receivers.do !(|i| {
1105
+ let fee_amount = (total_fees as u128 ) * (fee_receivers.weights[i] as u128 ) / (fee_receivers.total_weight as u128 );
1106
+ let fee = if (i == num_fee_receivers - 1 ) {
1107
+ balance::withdraw_all (&mut fees)
1108
+ } else {
1109
+ balance::split (&mut fees, fee_amount as u64 )
1110
+ };
1111
+
1112
+ if (balance::value (&fee) > 0 ) {
1113
+ transfer::public_transfer (coin::from_balance (fee, ctx), fee_receivers.receivers[i]);
1114
+ } else {
1115
+ balance::destroy_zero (fee);
1116
+ };
1117
+
1118
+ let ctoken_fee_amount = (total_ctoken_fees as u128 ) * (fee_receivers.weights[i] as u128 ) / (fee_receivers.total_weight as u128 );
1119
+ let ctoken_fee = if (i == num_fee_receivers - 1 ) {
1120
+ balance::withdraw_all (&mut ctoken_fees)
1121
+ } else {
1122
+ balance::split (&mut ctoken_fees, ctoken_fee_amount as u64 )
1123
+ };
1124
+
1125
+ if (balance::value (&ctoken_fee) > 0 ) {
1126
+ transfer::public_transfer (coin::from_balance (ctoken_fee, ctx), fee_receivers.receivers[i]);
1127
+ } else {
1128
+ balance::destroy_zero (ctoken_fee);
1129
+ };
1130
+ });
1131
+
1132
+ balance::destroy_zero (fees);
1133
+ balance::destroy_zero (ctoken_fees);
1054
1134
}
1055
1135
1056
1136
public fun new_obligation_owner_cap <P >(
0 commit comments