Skip to content

Commit

Permalink
Fix liquidity incentive (#3217)
Browse files Browse the repository at this point in the history
* fix: add end time for liquidity incentive

* fix: remove unuse import

* fix: withdraw incentive

* fix: modify endtime when update reward coin
  • Loading branch information
mx819812523 authored Jan 21, 2025
1 parent 0b43579 commit 48ce3d2
Showing 1 changed file with 51 additions and 38 deletions.
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

0 comments on commit 48ce3d2

Please sign in to comment.