@@ -30,6 +30,7 @@ use solana_program::{
30
30
Sysvar ,
31
31
} ,
32
32
} ;
33
+ use solend_sdk:: state:: { RateLimiter , RateLimiterConfig } ;
33
34
use solend_sdk:: { switchboard_v2_devnet, switchboard_v2_mainnet} ;
34
35
use spl_token:: state:: Mint ;
35
36
use std:: { cmp:: min, result:: Result } ;
@@ -53,9 +54,17 @@ pub fn process_instruction(
53
54
msg ! ( "Instruction: Init Lending Market" ) ;
54
55
process_init_lending_market ( program_id, owner, quote_currency, accounts)
55
56
}
56
- LendingInstruction :: SetLendingMarketOwner { new_owner } => {
57
+ LendingInstruction :: SetLendingMarketOwnerAndConfig {
58
+ new_owner,
59
+ rate_limiter_config,
60
+ } => {
57
61
msg ! ( "Instruction: Set Lending Market Owner" ) ;
58
- process_set_lending_market_owner ( program_id, new_owner, accounts)
62
+ process_set_lending_market_owner_and_config (
63
+ program_id,
64
+ new_owner,
65
+ rate_limiter_config,
66
+ accounts,
67
+ )
59
68
}
60
69
LendingInstruction :: InitReserve {
61
70
liquidity_amount,
@@ -128,9 +137,12 @@ pub fn process_instruction(
128
137
accounts,
129
138
)
130
139
}
131
- LendingInstruction :: UpdateReserveConfig { config } => {
140
+ LendingInstruction :: UpdateReserveConfig {
141
+ config,
142
+ rate_limiter_config,
143
+ } => {
132
144
msg ! ( "Instruction: UpdateReserveConfig" ) ;
133
- process_update_reserve_config ( program_id, config, accounts)
145
+ process_update_reserve_config ( program_id, config, rate_limiter_config , accounts)
134
146
}
135
147
LendingInstruction :: LiquidateObligationAndRedeemReserveCollateral { liquidity_amount } => {
136
148
msg ! ( "Instruction: Liquidate Obligation and Redeem Reserve Collateral" ) ;
@@ -190,16 +202,18 @@ fn process_init_lending_market(
190
202
token_program_id : * token_program_id. key ,
191
203
oracle_program_id : * oracle_program_id. key ,
192
204
switchboard_oracle_program_id : * switchboard_oracle_program_id. key ,
205
+ current_slot : Clock :: get ( ) ?. slot ,
193
206
} ) ;
194
207
LendingMarket :: pack ( lending_market, & mut lending_market_info. data . borrow_mut ( ) ) ?;
195
208
196
209
Ok ( ( ) )
197
210
}
198
211
199
212
#[ inline( never) ] // avoid stack frame limit
200
- fn process_set_lending_market_owner (
213
+ fn process_set_lending_market_owner_and_config (
201
214
program_id : & Pubkey ,
202
215
new_owner : Pubkey ,
216
+ rate_limiter_config : RateLimiterConfig ,
203
217
accounts : & [ AccountInfo ] ,
204
218
) -> ProgramResult {
205
219
let account_info_iter = & mut accounts. iter ( ) ;
@@ -221,6 +235,11 @@ fn process_set_lending_market_owner(
221
235
}
222
236
223
237
lending_market. owner = new_owner;
238
+
239
+ if rate_limiter_config != lending_market. rate_limiter . config {
240
+ lending_market. rate_limiter = RateLimiter :: new ( rate_limiter_config, Clock :: get ( ) ?. slot ) ;
241
+ }
242
+
224
243
LendingMarket :: pack ( lending_market, & mut lending_market_info. data . borrow_mut ( ) ) ?;
225
244
226
245
Ok ( ( ) )
@@ -347,6 +366,7 @@ fn process_init_reserve(
347
366
supply_pubkey : * reserve_collateral_supply_info. key ,
348
367
} ) ,
349
368
config,
369
+ rate_limiter_config : RateLimiterConfig :: default ( ) ,
350
370
} ) ;
351
371
352
372
let collateral_amount = reserve. deposit_liquidity ( liquidity_amount) ?;
@@ -659,7 +679,6 @@ fn process_redeem_reserve_collateral(
659
679
}
660
680
let token_program_id = next_account_info ( account_info_iter) ?;
661
681
662
- _refresh_reserve_interest ( program_id, reserve_info, clock) ?;
663
682
_redeem_reserve_collateral (
664
683
program_id,
665
684
collateral_amount,
@@ -673,6 +692,7 @@ fn process_redeem_reserve_collateral(
673
692
user_transfer_authority_info,
674
693
clock,
675
694
token_program_id,
695
+ true ,
676
696
) ?;
677
697
let mut reserve = Reserve :: unpack ( & reserve_info. data . borrow ( ) ) ?;
678
698
reserve. last_update . mark_stale ( ) ;
@@ -695,8 +715,9 @@ fn _redeem_reserve_collateral<'a>(
695
715
user_transfer_authority_info : & AccountInfo < ' a > ,
696
716
clock : & Clock ,
697
717
token_program_id : & AccountInfo < ' a > ,
718
+ check_rate_limits : bool ,
698
719
) -> Result < u64 , ProgramError > {
699
- let lending_market = LendingMarket :: unpack ( & lending_market_info. data . borrow ( ) ) ?;
720
+ let mut lending_market = LendingMarket :: unpack ( & lending_market_info. data . borrow ( ) ) ?;
700
721
if lending_market_info. owner != program_id {
701
722
msg ! ( "Lending market provided is not owned by the lending program" ) ;
702
723
return Err ( LendingError :: InvalidAccountOwner . into ( ) ) ;
@@ -750,8 +771,31 @@ fn _redeem_reserve_collateral<'a>(
750
771
}
751
772
752
773
let liquidity_amount = reserve. redeem_collateral ( collateral_amount) ?;
774
+
775
+ if check_rate_limits {
776
+ lending_market
777
+ . rate_limiter
778
+ . update (
779
+ clock. slot ,
780
+ reserve. market_value ( Decimal :: from ( liquidity_amount) ) ?,
781
+ )
782
+ . map_err ( |err| {
783
+ msg ! ( "Market outflow limit exceeded! Please try again later." ) ;
784
+ err
785
+ } ) ?;
786
+
787
+ reserve
788
+ . rate_limiter
789
+ . update ( clock. slot , Decimal :: from ( liquidity_amount) )
790
+ . map_err ( |err| {
791
+ msg ! ( "Reserve outflow limit exceeded! Please try again later." ) ;
792
+ err
793
+ } ) ?;
794
+ }
795
+
753
796
reserve. last_update . mark_stale ( ) ;
754
797
Reserve :: pack ( reserve, & mut reserve_info. data . borrow_mut ( ) ) ?;
798
+ LendingMarket :: pack ( lending_market, & mut lending_market_info. data . borrow_mut ( ) ) ?;
755
799
756
800
spl_token_burn ( TokenBurnParams {
757
801
mint : reserve_collateral_mint_info. clone ( ) ,
@@ -1359,7 +1403,7 @@ fn process_borrow_obligation_liquidity(
1359
1403
}
1360
1404
let token_program_id = next_account_info ( account_info_iter) ?;
1361
1405
1362
- let lending_market = LendingMarket :: unpack ( & lending_market_info. data . borrow ( ) ) ?;
1406
+ let mut lending_market = LendingMarket :: unpack ( & lending_market_info. data . borrow ( ) ) ?;
1363
1407
if lending_market_info. owner != program_id {
1364
1408
msg ! ( "Lending market provided is not owned by the lending program" ) ;
1365
1409
return Err ( LendingError :: InvalidAccountOwner . into ( ) ) ;
@@ -1477,6 +1521,27 @@ fn process_borrow_obligation_liquidity(
1477
1521
1478
1522
let cumulative_borrow_rate_wads = borrow_reserve. liquidity . cumulative_borrow_rate_wads ;
1479
1523
1524
+ // check outflow rate limits
1525
+ {
1526
+ lending_market
1527
+ . rate_limiter
1528
+ . update ( clock. slot , borrow_reserve. market_value ( borrow_amount) ?)
1529
+ . map_err ( |err| {
1530
+ msg ! ( "Market outflow limit exceeded! Please try again later." ) ;
1531
+ err
1532
+ } ) ?;
1533
+
1534
+ borrow_reserve
1535
+ . rate_limiter
1536
+ . update ( clock. slot , borrow_amount)
1537
+ . map_err ( |err| {
1538
+ msg ! ( "Reserve outflow limit exceeded! Please try again later" ) ;
1539
+ err
1540
+ } ) ?;
1541
+ }
1542
+
1543
+ LendingMarket :: pack ( lending_market, & mut lending_market_info. data . borrow_mut ( ) ) ?;
1544
+
1480
1545
borrow_reserve. liquidity . borrow ( borrow_amount) ?;
1481
1546
borrow_reserve. last_update . mark_stale ( ) ;
1482
1547
Reserve :: pack ( borrow_reserve, & mut borrow_reserve_info. data . borrow_mut ( ) ) ?;
@@ -1885,6 +1950,7 @@ fn process_liquidate_obligation_and_redeem_reserve_collateral(
1885
1950
user_transfer_authority_info,
1886
1951
clock,
1887
1952
token_program_id,
1953
+ false ,
1888
1954
) ?;
1889
1955
let withdraw_reserve = Reserve :: unpack ( & withdraw_reserve_info. data . borrow ( ) ) ?;
1890
1956
if & withdraw_reserve. config . fee_receiver != withdraw_reserve_liquidity_fee_receiver_info. key
@@ -1959,6 +2025,7 @@ fn process_withdraw_obligation_collateral_and_redeem_reserve_liquidity(
1959
2025
user_transfer_authority_info,
1960
2026
clock,
1961
2027
token_program_id,
2028
+ true ,
1962
2029
) ?;
1963
2030
Ok ( ( ) )
1964
2031
}
@@ -1967,6 +2034,7 @@ fn process_withdraw_obligation_collateral_and_redeem_reserve_liquidity(
1967
2034
fn process_update_reserve_config (
1968
2035
program_id : & Pubkey ,
1969
2036
config : ReserveConfig ,
2037
+ rate_limiter_config : RateLimiterConfig ,
1970
2038
accounts : & [ AccountInfo ] ,
1971
2039
) -> ProgramResult {
1972
2040
validate_reserve_config ( config) ?;
@@ -2024,6 +2092,11 @@ fn process_update_reserve_config(
2024
2092
return Err ( LendingError :: InvalidMarketAuthority . into ( ) ) ;
2025
2093
}
2026
2094
2095
+ // if window duration and max outflow are different, then create a new rate limiter instance.
2096
+ if rate_limiter_config != reserve. rate_limiter . config {
2097
+ reserve. rate_limiter = RateLimiter :: new ( rate_limiter_config, Clock :: get ( ) ?. slot ) ;
2098
+ }
2099
+
2027
2100
if * pyth_price_info. key != reserve. liquidity . pyth_oracle_pubkey {
2028
2101
validate_pyth_keys ( & lending_market, pyth_product_info, pyth_price_info) ?;
2029
2102
reserve. liquidity . pyth_oracle_pubkey = * pyth_price_info. key ;
0 commit comments