Skip to content

Commit

Permalink
Stake unused sui (#38)
Browse files Browse the repository at this point in the history
* stake unused sui (WIP)

* refactor borrow, redeem so that they expose the LiquidityRequest type

* refactor staker module a little bit

* remove the target util stuff

* adding borrow_sui, redeem_sui functions

* add staker functions

* new idea

* slightly nicer way of doing stuff

* fixes

* keep public funcsigs the same

* review fixes

* test edge case

* testing more edge cases

* add test for borrow_request

* Staker fees (#40)

* claim fees when rebalancing staker

* leave 1 sui of staker fees in the lst just in case

* add more stuff to event

* version checks on the new functions

* test behavior changes a bit after liquid staking bugfix

* Type fixes (#41)
  • Loading branch information
0xripleys authored Nov 17, 2024
1 parent adae8a9 commit c2153ed
Show file tree
Hide file tree
Showing 10 changed files with 1,109 additions and 46 deletions.
14 changes: 14 additions & 0 deletions contracts/sprungsui/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "sprungsui"
edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }

[addresses]
sprungsui = "0x0"

[dev-dependencies]

[dev-addresses]

20 changes: 20 additions & 0 deletions contracts/sprungsui/sources/sprungsui.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module sprungsui::sprungsui {
use sui::coin::{Self};

public struct SPRUNGSUI has drop {}

fun init(witness: SPRUNGSUI, ctx: &mut TxContext) {
let (treasury, metadata) = coin::create_currency(
witness,
9,
b"",
b"Staked SUI",
b"",
option::none(),
ctx
);

transfer::public_share_object(metadata);
transfer::public_transfer(treasury, ctx.sender())
}
}
23 changes: 21 additions & 2 deletions contracts/suilend/Move.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

[move]
version = 3
manifest_digest = "97E92C3AE2671D15B98EDF2F75D00F01F060C660AA87BCA5FB95A6792D62C242"
deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600"
manifest_digest = "35F92FCDECB247F47BD4304AA29525DA91F7CF4EBCB8777527B825341BB52706"
deps_digest = "060AD7E57DFB13104F21BE5F5C3759D03F0553FC3229247D9A7A6B45F50D03A3"
dependencies = [
{ id = "Pyth", name = "Pyth" },
{ id = "Sui", name = "Sui" },
{ id = "liquid_staking", name = "liquid_staking" },
]

[[move.package]]
Expand All @@ -30,6 +31,15 @@ dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
]

[[move.package]]
id = "SuiSystem"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/mainnet", subdir = "crates/sui-framework/packages/sui-system" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
]

[[move.package]]
id = "Wormhole"
source = { git = "https://github.com/solendprotocol/wormhole.git", rev = "e1698d3c72b15cdddd7da98ad43e151f83b72a0a", subdir = "sui/wormhole" }
Expand All @@ -38,6 +48,15 @@ dependencies = [
{ id = "Sui", name = "Sui" },
]

[[move.package]]
id = "liquid_staking"
source = { git = "https://github.com/solendprotocol/liquid-staking.git", rev = "main", subdir = "contracts" }

dependencies = [
{ id = "Sui", name = "Sui" },
{ id = "SuiSystem", name = "SuiSystem" },
]

[move.toolchain-version]
compiler-version = "1.35.3"
edition = "2024.beta"
Expand Down
8 changes: 8 additions & 0 deletions contracts/suilend/Move.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ git = "https://github.com/solendprotocol/pyth-crosschain.git"
subdir = "target_chains/sui/contracts"
rev = "98e218c64bb75cf1350eb7b021e1ffcc3aedfd62"

[dependencies.liquid_staking]
git = "https://github.com/solendprotocol/liquid-staking.git"
subdir = "contracts"
rev = "main"

[dependencies.sprungsui]
local = "../sprungsui"

[addresses]
sui = "0x2"
# suilend = "0x0"
Expand Down
164 changes: 148 additions & 16 deletions contracts/suilend/sources/lending_market.move
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module suilend::lending_market {
// === Imports ===
use sui::object::{Self, ID, UID};
use sui_system::sui_system::{SuiSystemState};
use suilend::rate_limiter::{Self, RateLimiter, RateLimiterConfig};
use std::ascii::{Self};
use sui::event::{Self};
Expand All @@ -10,17 +11,18 @@ module suilend::lending_market {
use sui::clock::{Self, Clock};
use sui::tx_context::{Self, TxContext};
use sui::transfer;
use suilend::reserve::{Self, Reserve, CToken};
use suilend::reserve::{Self, Reserve, CToken, LiquidityRequest};
use suilend::reserve_config::{ReserveConfig, borrow_fee};
use suilend::obligation::{Self, Obligation};
use sui::coin::{Self, Coin, CoinMetadata};
use sui::coin::{Self, Coin, CoinMetadata, TreasuryCap};
use sui::balance::{Self};
use pyth::price_info::{PriceInfoObject};
use std::type_name::{Self, TypeName};
use std::vector::{Self};
use std::option::{Self, Option};
use suilend::liquidity_mining::{Self};
use sui::package;
use sui::sui::SUI;

// === Errors ===
const EIncorrectVersion: u64 = 1;
Expand Down Expand Up @@ -258,6 +260,26 @@ module suilend::lending_market {
mut rate_limiter_exemption: Option<RateLimiterExemption<P, T>>,
ctx: &mut TxContext
): Coin<T> {
let liquidity_request = redeem_ctokens_and_withdraw_liquidity_request(
lending_market,
reserve_array_index,
clock,
ctokens,
rate_limiter_exemption,
ctx
);

fulfill_liquidity_request(lending_market, reserve_array_index, liquidity_request, ctx)
}

public fun redeem_ctokens_and_withdraw_liquidity_request<P, T>(
lending_market: &mut LendingMarket<P>,
reserve_array_index: u64,
clock: &Clock,
ctokens: Coin<CToken<P, T>>,
mut rate_limiter_exemption: Option<RateLimiterExemption<P, T>>,
ctx: &mut TxContext
): LiquidityRequest<P, T> {
let lending_market_id = object::id_address(lending_market);
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);
assert!(coin::value(&ctokens) > 0, ETooSmall);
Expand Down Expand Up @@ -285,25 +307,24 @@ module suilend::lending_market {
);
};

let liquidity = reserve::redeem_ctokens<P, T>(
let liquidity_request = reserve::redeem_ctokens<P, T>(
reserve,
coin::into_balance(ctokens)
);

assert!(balance::value(&liquidity) > 0, ETooSmall);
assert!(reserve::liquidity_request_amount(&liquidity_request) > 0, ETooSmall);

event::emit(RedeemEvent {
lending_market_id,
coin_type: type_name::get<T>(),
reserve_id: object::id_address(reserve),
ctoken_amount,
liquidity_amount: balance::value(&liquidity),
liquidity_amount: reserve::liquidity_request_amount(&liquidity_request),
});

coin::from_balance(liquidity, ctx)
liquidity_request
}


public fun deposit_ctokens_into_obligation<P, T>(
lending_market: &mut LendingMarket<P>,
reserve_array_index: u64,
Expand All @@ -323,7 +344,6 @@ module suilend::lending_market {
)
}


/// Borrow tokens of type T. A fee is charged.
public fun borrow<P, T>(
lending_market: &mut LendingMarket<P>,
Expand All @@ -333,6 +353,25 @@ module suilend::lending_market {
mut amount: u64,
ctx: &mut TxContext
): Coin<T> {
let liquidity_request = borrow_request<P, T>(
lending_market,
reserve_array_index,
obligation_owner_cap,
clock,
amount
);

fulfill_liquidity_request(lending_market, reserve_array_index, liquidity_request, ctx)
}

/// Borrow tokens of type T. A fee is charged.
public fun borrow_request<P, T>(
lending_market: &mut LendingMarket<P>,
reserve_array_index: u64,
obligation_owner_cap: &ObligationOwnerCap<P>,
clock: &Clock,
mut amount: u64,
): LiquidityRequest<P, T> {
let lending_market_id = object::id_address(lending_market);
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);
assert!(amount > 0, ETooSmall);
Expand All @@ -354,11 +393,15 @@ module suilend::lending_market {
assert!(amount > 0, ETooSmall);
};

let (receive_balance, borrow_amount_with_fees) = reserve::borrow_liquidity<P, T>(reserve, amount);
let origination_fee_amount = borrow_amount_with_fees - balance::value(&receive_balance);
obligation::borrow<P>(obligation, reserve, clock, borrow_amount_with_fees);
let liquidity_request = reserve::borrow_liquidity<P, T>(reserve, amount);
obligation::borrow<P>(
obligation,
reserve,
clock,
reserve::liquidity_request_amount(&liquidity_request)
);

let borrow_value = reserve::market_value_upper_bound(reserve, decimal::from(borrow_amount_with_fees));
let borrow_value = reserve::market_value_upper_bound(reserve, decimal::from(reserve::liquidity_request_amount(&liquidity_request)));
rate_limiter::process_qty(
&mut lending_market.rate_limiter,
clock::timestamp_ms(clock) / 1000,
Expand All @@ -370,12 +413,29 @@ module suilend::lending_market {
coin_type: type_name::get<T>(),
reserve_id: object::id_address(reserve),
obligation_id: object::id_address(obligation),
liquidity_amount: borrow_amount_with_fees,
origination_fee_amount,
liquidity_amount: reserve::liquidity_request_amount(&liquidity_request),
origination_fee_amount: reserve::liquidity_request_fee(&liquidity_request),
});

obligation::zero_out_rewards_if_looped(obligation, &mut lending_market.reserves, clock);
coin::from_balance(receive_balance, ctx)
liquidity_request
}

public fun fulfill_liquidity_request<P, T>(
lending_market: &mut LendingMarket<P>,
reserve_array_index: u64,
liquidity_request: LiquidityRequest<P, T>,
ctx: &mut TxContext
): Coin<T> {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);

let reserve = vector::borrow_mut(&mut lending_market.reserves, reserve_array_index);
assert!(reserve::coin_type(reserve) == type_name::get<T>(), EWrongType);

coin::from_balance(
reserve::fulfill_liquidity_request(reserve, liquidity_request),
ctx
)
}

public fun withdraw_ctokens<P, T>(
Expand Down Expand Up @@ -664,6 +724,52 @@ module suilend::lending_market {
}
}

/* Staker operations */
public fun init_staker<P, S: drop>(
lending_market: &mut LendingMarket<P>,
_: &LendingMarketOwnerCap<P>,
sui_reserve_array_index: u64,
treasury_cap: TreasuryCap<S>,
ctx: &mut TxContext
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);

let reserve = vector::borrow_mut(&mut lending_market.reserves, sui_reserve_array_index);
assert!(reserve::coin_type(reserve) == type_name::get<SUI>(), EWrongType);

reserve::init_staker<P, S>(reserve, treasury_cap, ctx);
}

public fun rebalance_staker<P>(
lending_market: &mut LendingMarket<P>,
sui_reserve_array_index: u64,
system_state: &mut SuiSystemState,
ctx: &mut TxContext
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);

let reserve = vector::borrow_mut(&mut lending_market.reserves, sui_reserve_array_index);
assert!(reserve::coin_type(reserve) == type_name::get<SUI>(), EWrongType);

reserve::rebalance_staker<P>(reserve, system_state, ctx);
}

public fun unstake_sui_from_staker<P>(
lending_market: &mut LendingMarket<P>,
sui_reserve_array_index: u64,
liquidity_request: &LiquidityRequest<P, SUI>,
system_state: &mut SuiSystemState,
ctx: &mut TxContext
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);

let reserve = vector::borrow_mut(&mut lending_market.reserves, sui_reserve_array_index);
if (reserve::coin_type(reserve) != type_name::get<SUI>()) {
return;
};

reserve::unstake_sui_from_staker<P>(reserve, liquidity_request, system_state, ctx);
}

// === Public-View Functions ===
fun max_borrow_amount<P>(
Expand Down Expand Up @@ -803,7 +909,7 @@ module suilend::lending_market {
object::id(lending_market),
config,
vector::length(&lending_market.reserves),
coin_metadata,
coin::get_decimals(coin_metadata),
price_info,
clock,
ctx
Expand Down Expand Up @@ -1080,4 +1186,30 @@ module suilend::lending_market {
let LendingMarketOwnerCap { id, lending_market_id: _ } = lending_market_owner_cap;
object::delete(id);
}

#[test_only]
public fun add_reserve_for_testing<P, T>(
_: &LendingMarketOwnerCap<P>,
lending_market: &mut LendingMarket<P>,
price_info: &PriceInfoObject,
config: ReserveConfig,
mint_decimals: u8,
clock: &Clock,
ctx: &mut TxContext
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);
assert!(reserve_array_index<P, T>(lending_market) == vector::length(&lending_market.reserves), EDuplicateReserve);

let reserve = reserve::create_reserve<P, T>(
object::id(lending_market),
config,
vector::length(&lending_market.reserves),
mint_decimals,
price_info,
clock,
ctx
);

vector::push_back(&mut lending_market.reserves, reserve);
}
}
Loading

0 comments on commit c2153ed

Please sign in to comment.