Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix liquidity incentive #3217

Merged
merged 4 commits into from
Jan 21, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 51 additions & 38 deletions apps/rooch_dex/sources/liquidity_incentive.move
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
module rooch_dex::liquidity_incentive {

use std::signer::address_of;
use app_admin::admin::AdminCap;
use std::u64;
use moveos_std::table;
use rooch_framework::coin;
use moveos_std::table::{Table, new};
Expand Down Expand Up @@ -42,6 +42,7 @@ module rooch_dex::liquidity_incentive {
const ErrorFarmingNotAlive: u64 = 7;
const ErrorFarmingAliveStateInvalid: u64 = 8;
const ErrorFarmingNotStake: u64 = 9;
const ErrorNotCreator: u64 = 10;

const EXP_MAX_SCALE: u128 = 9;

Expand Down Expand Up @@ -117,13 +118,15 @@ module rooch_dex::liquidity_incentive {


struct FarmingAsset<phantom X: key+store, phantom Y: key+ store, phantom RewardToken: key+store> has key, store {
creator: address,
asset_total_weight: u128,
harvest_index: u128,
last_update_timestamp: u64,
// Release count per seconds
release_per_second: u128,
// Start time, by seconds, user can operate stake only after this timestamp
start_time: u64,
end_time: u64,
coin_store: Object<CoinStore<RewardToken>>,
stake_info: Table<address, Stake<X, Y>>,
// Representing the pool is alive, false: not alive, true: alive.
Expand All @@ -143,41 +146,72 @@ module rooch_dex::liquidity_incentive {
release_per_second: u128,
coin_amount: u256,
start_time: u64,
admin: &mut Object<AdminCap>
){
let reward_coin = account_coin_store::withdraw<RewardToken>(account, coin_amount);
create_pool_with_coin<X, Y, RewardToken>(release_per_second, reward_coin, start_time, admin)
create_pool_with_coin<X, Y, RewardToken>(account, release_per_second, reward_coin, start_time)
}

public entry fun add_incentive<X: key+store, Y: key+store, RewardToken: key+store>(
account: &signer,
farming_asset_obj: &mut Object<FarmingAsset<X, Y, RewardToken>>,
coin_amount: u256,
){
let reward_coin = account_coin_store::withdraw<RewardToken>(account, coin_amount);
let farming_asset = object::borrow_mut(farming_asset_obj);
coin_store::deposit(&mut farming_asset.coin_store, reward_coin);
assert!(farming_asset.end_time >= now_seconds(), ErrorFarmingNotAlive);
let end_time = (coin_amount / (farming_asset.release_per_second as u256) as u64);
farming_asset.end_time = farming_asset.end_time + end_time
}

public entry fun withdraw_incentive<X: key+store, Y: key+store, RewardToken: key+store>(
account: &signer,
farming_asset_obj: &mut Object<FarmingAsset<X, Y, RewardToken>>,
coin_amount: u256,
){

let farming_asset = object::borrow_mut(farming_asset_obj);
assert!(address_of(account) == farming_asset.creator, ErrorNotCreator);
let coin = coin_store::withdraw(&mut farming_asset.coin_store, coin_amount);
account_coin_store::deposit(farming_asset.creator, coin);
let end_time = (coin_amount / (farming_asset.release_per_second as u256) as u64);
farming_asset.end_time = farming_asset.end_time - end_time
}


/// Add asset pools
public fun create_pool_with_coin<X: key+store, Y: key+store, RewardToken: key+store>(
account: &signer,
release_per_second: u128,
coin: Coin<RewardToken>,
start_time: u64,
_admin: &mut Object<AdminCap>
) {
let end_time = (coin::value(&coin) / (release_per_second as u256) as u64);
let coin_store = coin_store::create_coin_store<RewardToken>();
coin_store::deposit(&mut coin_store, coin);
if (swap_utils::sort_token_type<X, Y>()) {
let farming_asset = object::new(FarmingAsset<X, Y, RewardToken> {
creator: address_of(account),
asset_total_weight: 0,
harvest_index: 0,
last_update_timestamp: start_time,
release_per_second,
start_time,
end_time,
coin_store,
stake_info: new(),
alive: true
});
object::to_shared(farming_asset)
}else {
let farming_asset = object::new(FarmingAsset<Y, X, RewardToken> {
creator: address_of(account),
asset_total_weight: 0,
harvest_index: 0,
last_update_timestamp: start_time,
release_per_second,
start_time,
end_time,
coin_store,
stake_info: new(),
alive: true
Expand All @@ -186,32 +220,6 @@ module rooch_dex::liquidity_incentive {
};
}

public fun modify_parameter<X: key+store, Y: key+store, RewardToken: key+store>(
release_per_second: u128,
alive: bool,
farming_asset_obj: &mut Object<FarmingAsset<X, Y, RewardToken>>,
_admin: &mut Object<AdminCap>
) {
// Not support to shuttingdown alive state.
assert!(alive, ErrorFarmingAliveStateInvalid);

let farming_asset = object::borrow_mut<FarmingAsset<X,Y,RewardToken>>(farming_asset_obj);

let now_seconds = now_seconds();

farming_asset.release_per_second = release_per_second;
farming_asset.last_update_timestamp = now_seconds;

// if the pool is alive, then update index
if (farming_asset.alive) {
farming_asset.harvest_index = calculate_harvest_index_with_asset(
farming_asset,
now_seconds
);
};
farming_asset.alive = alive;
}

/// Call by stake user, staking amount of asset in order to get yield farming token
public entry fun stake<X: key+store, Y: key+store, RewardToken: key+store>(
signer: &signer,
Expand All @@ -236,6 +244,7 @@ module rooch_dex::liquidity_incentive {
// Check locking time
let now_seconds = now_seconds();
assert!(farming_asset.start_time <= now_seconds, ErrorFarmingNotStillFreeze);
assert!(farming_asset.end_time > now_seconds, ErrorFarmingNotStillFreeze);

let time_period = now_seconds - farming_asset.last_update_timestamp;

Expand Down Expand Up @@ -298,7 +307,7 @@ module rooch_dex::liquidity_incentive {
let Stake { last_harvest_index, asset_weight, asset, gain } =
table::remove(&mut farming_asset.stake_info, signer::address_of(signer));

let now_seconds = now_seconds();
let now_seconds = u64::min(now_seconds(), farming_asset.end_time);
let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds);

let period_gain = calculate_withdraw_amount(new_harvest_index, last_harvest_index, asset_weight);
Expand All @@ -309,6 +318,7 @@ module rooch_dex::liquidity_incentive {

// Update farm asset
farming_asset.asset_total_weight = farming_asset.asset_total_weight - asset_weight;

farming_asset.last_update_timestamp = now_seconds;

if (farming_asset.alive) {
Expand All @@ -333,7 +343,7 @@ module rooch_dex::liquidity_incentive {
): Coin<RewardToken> {
let farming_asset = object::borrow_mut(farming_asset_obj);
assert!(table::contains(&farming_asset.stake_info, signer::address_of(signer)), ErrorFarmingNotStake);
let now_seconds = now_seconds();
let now_seconds = u64::min(now_seconds(), farming_asset.end_time);
let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds);
let stake = table::borrow_mut(&mut farming_asset.stake_info, signer::address_of(signer));
let period_gain = calculate_withdraw_amount(
Expand All @@ -357,7 +367,7 @@ module rooch_dex::liquidity_incentive {
}

/// The user can quering all yield farming amount in any time and scene
public fun query_gov_token_amount<X: key+store, Y: key+store, RewardToken: key+store>(
public fun query_harvest_token_amount<X: key+store, Y: key+store, RewardToken: key+store>(
account: address,
farming_asset_obj: &Object<FarmingAsset<X, Y, RewardToken>>
): u128 {
Expand All @@ -366,7 +376,7 @@ module rooch_dex::liquidity_incentive {
return 0
};
let stake = table::borrow(&farming_asset.stake_info, account);
let now_seconds = now_seconds();
let now_seconds = u64::min(now_seconds(), farming_asset.end_time);

let new_harvest_index = calculate_harvest_index_with_asset(
farming_asset,
Expand Down Expand Up @@ -452,7 +462,8 @@ module rooch_dex::liquidity_incentive {
asset_total_weight: u128,
last_update_timestamp: u64,
now_seconds: u64,
release_per_second: u128): u128 {
release_per_second: u128
): u128 {
assert!(asset_total_weight > 0, ErrorFarmingTotalWeightIsZero);
assert!(last_update_timestamp <= now_seconds, ErrorFarmingTimestampInvalid);

Expand Down Expand Up @@ -494,27 +505,29 @@ module rooch_dex::liquidity_incentive {
coin_store::deposit(&mut coin_store, lp_reward_coin);
let farming_asset_obj = object::new(
FarmingAsset<TestCoinX, TestCoinY, TestRewardCoin> {
creator: address_of(&sender),
asset_total_weight: 0,
harvest_index: 0,
last_update_timestamp: now_seconds(),
release_per_second: 100,
start_time:now_seconds(),
end_time: now_seconds() + 10000,
coin_store,
stake_info: new(),
alive: true
});
stake(&sender, 100, &mut farming_asset_obj);
let seconds = 100;
timestamp::fast_forward_seconds_for_test(seconds);
let reward_a = query_gov_token_amount(address_of(&sender), &farming_asset_obj);
let reward_a = query_harvest_token_amount(address_of(&sender), &farming_asset_obj);
stake(&account_b, 100, &mut farming_asset_obj);
let total_weight = query_total_stake(&farming_asset_obj);
assert!(total_weight == 200, 1);
assert!(reward_a == 10000, 2);
timestamp::fast_forward_seconds_for_test(seconds);
reward_a = query_gov_token_amount(address_of(&sender), &farming_asset_obj);
reward_a = query_harvest_token_amount(address_of(&sender), &farming_asset_obj);
assert!(reward_a == 15000, 3);
let reward_b = query_gov_token_amount(address_of(&account_b), &farming_asset_obj);
let reward_b = query_harvest_token_amount(address_of(&account_b), &farming_asset_obj);
assert!(reward_b == 5000, 4);
let reward_coin = do_harvest(&sender, &mut farming_asset_obj);
assert!(coin::value(&reward_coin) == 15000, 5);
Expand Down
Loading