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

Migrate farming rate #60

Merged
merged 2 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
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
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

.anchor
.DS_Store
target
target/*
!target/types
test-ledger
**/*.rs.bk
/keys/
Expand All @@ -14,4 +15,5 @@ soteria-linux-develop/
testing/
deployment
dist
.env
.env

3 changes: 3 additions & 0 deletions cli/farming-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ pub enum CliCommand {
#[clap(long)]
pool: Pubkey,
},

CheckFunderAllPool {},
MigrateFarmingRate {},
}

#[derive(Parser, Debug)]
Expand Down
48 changes: 48 additions & 0 deletions cli/farming-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ mod utils;

use crate::args::*;
use crate::utils::*;
use anyhow::Ok;
use anyhow::Result;

use anchor_client::solana_client::rpc_filter::RpcFilterType;
use anchor_client::solana_sdk::commitment_config::CommitmentConfig;
use anchor_client::solana_sdk::pubkey::Pubkey;
use anchor_client::solana_sdk::signer::keypair::*;
use anchor_client::solana_sdk::signer::Signer;
use anchor_client::{Client, Program};
use farming::Pool;
use std::rc::Rc;
use std::str::FromStr;

Expand Down Expand Up @@ -93,6 +96,12 @@ fn main() -> Result<()> {
CliCommand::StakeInfo { pool } => {
stake_info(&program, &pool, &payer.pubkey())?;
}
CliCommand::CheckFunderAllPool {} => {
check_funder_all_pool(&program)?;
}
CliCommand::MigrateFarmingRate {} => {
migrate_farming_rate(&program)?;
}
}

Ok(())
Expand Down Expand Up @@ -430,3 +439,42 @@ pub fn stake_info(program: &Program, pool_pda: &Pubkey, user: &Pubkey) -> Result
);
Ok(())
}

fn check_funder_all_pool(program: &Program) -> Result<()> {
let pools: Vec<(Pubkey, Pool)> = program.accounts::<Pool>(vec![]).unwrap();

println!("len pool {}", pools.len());

for pool in pools.iter() {
assert_eq!(pool.1.reward_a_rate_u128, 0);
assert_eq!(pool.1.reward_b_rate_u128, 0);
}
Ok(())
}

fn migrate_farming_rate(program: &Program) -> Result<()> {
let pools: Vec<(Pubkey, Pool)> = program.accounts::<Pool>(vec![]).unwrap();

println!("len pool {}", pools.len());

for pool in pools.iter() {
let pool_state = pool.1.clone();
let mut should_migrate = false;
if pool_state.reward_a_rate_u128 == 0 && pool_state._reward_a_rate != 0 {
should_migrate = true;
}
if pool_state.reward_b_rate_u128 == 0 && pool_state._reward_b_rate != 0 {
should_migrate = true;
}

if should_migrate {
let builder = program
.request()
.accounts(farming::accounts::MigrateFarmingRate { pool: pool.0 })
.args(farming::instruction::MigrateFarmingRate {});
let signature = builder.send()?;
println!("Migrate pool {} signature {:?}", pool.0, signature);
}
}
Ok(())
}
2 changes: 1 addition & 1 deletion programs/farming/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "farming"
version = "0.2.0"
version = "0.2.1"
description = "Created with Anchor"
edition = "2018"

Expand Down
86 changes: 60 additions & 26 deletions programs/farming/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use std::convert::Into;
use std::convert::TryInto;
use std::fmt::Debug;

use crate::pool::*;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::{clock, sysvar};
use anchor_spl::token::{self, Mint, Token, TokenAccount};

use crate::pool::*;
use std::convert::TryFrom;

/// Export for pool implementation
pub mod pool;
Expand Down Expand Up @@ -88,8 +88,6 @@ pub mod farming {
pool.total_staked = 0;
pool.reward_duration_end = 0;
pool.last_update_time = 0;
pool.reward_a_rate = 0;
pool.reward_b_rate = 0;
pool.reward_a_per_token_stored = 0;
pool.reward_b_per_token_stored = 0;
pool.user_stake_count = 0;
Expand Down Expand Up @@ -275,8 +273,16 @@ pub mod farming {
update_rewards(pool, None, pool.total_staked).unwrap();

let (reward_a_rate, reward_b_rate) = rate_after_funding(pool, amount_a, amount_b)?;
pool.reward_a_rate = reward_a_rate;
pool.reward_b_rate = reward_b_rate;
pool.reward_a_rate_u128 = reward_a_rate;
pool.reward_b_rate_u128 = reward_b_rate;

// this is to avoid breaking old integrator
if let Ok(reward_rate) = u64::try_from(reward_a_rate) {
pool._reward_a_rate = reward_rate;
}
if let Ok(reward_rate) = u64::try_from(reward_b_rate) {
pool._reward_b_rate = reward_rate;
}

// Transfer reward A tokens into the A vault.
if amount_a > 0 {
Expand Down Expand Up @@ -437,6 +443,18 @@ pub mod farming {
Ok(())
}

/// anyone can call this
pub fn migrate_farming_rate(ctx: Context<MigrateFarmingRate>) -> Result<()> {
let pool = &mut ctx.accounts.pool;
if pool.reward_a_rate_u128 == 0 && pool._reward_a_rate != 0 {
pool.reward_a_rate_u128 = pool._reward_a_rate.into();
}
if pool.reward_b_rate_u128 == 0 && pool._reward_b_rate != 0 {
pool.reward_b_rate_u128 = pool._reward_b_rate.into();
}
Ok(())
}

/// Closes a pool account. Only able to be done when there are no users staked.
pub fn close_pool(ctx: Context<ClosePool>) -> Result<()> {
let pool = &ctx.accounts.pool;
Expand Down Expand Up @@ -887,6 +905,14 @@ pub struct CloseUser<'info> {
#[account(mut)]
owner: Signer<'info>,
}

/// Accounts for [MigrateFarmingRate](/dual_farming/instruction/struct.MigrateFarmingRate.html) instruction
#[derive(Accounts)]
pub struct MigrateFarmingRate<'info> {
#[account(mut)]
pool: Box<Account<'info, Pool>>,
}

/// Accounts for [ClosePool](/dual_farming/instruction/struct.ClosePool.html) instruction
#[derive(Accounts)]
pub struct ClosePool<'info> {
Expand Down Expand Up @@ -926,6 +952,7 @@ pub struct ClosePool<'info> {

/// Pool account wrapper
#[account]
#[derive(Debug)]
pub struct Pool {
/// Privileged account.
pub authority: Pubkey, // 32
Expand All @@ -951,10 +978,10 @@ pub struct Pool {
pub reward_duration_end: u64, // 8
/// The last time reward states were updated.
pub last_update_time: u64, // 8
/// Rate of reward A distribution.
pub reward_a_rate: u64, // 8
/// Rate of reward B distribution.
pub reward_b_rate: u64, // 8
/// deprecated field
pub _reward_a_rate: u64, // 8
/// deprecated field
pub _reward_b_rate: u64, // 8
/// Last calculated reward A per pool token.
pub reward_a_per_token_stored: u128, // 16
/// Last calculated reward B per pool token.
Expand All @@ -964,13 +991,35 @@ pub struct Pool {
/// authorized funders
/// [] because short size, fixed account size, and ease of use on
/// client due to auto generated account size property
pub funders: [Pubkey; 4], // 32 * 4 = 128
pub funders: [Pubkey; 3], // 32 * 4 = 128
/// reward_a_rate in u128 form
pub reward_a_rate_u128: u128,
/// reward_b_rate in u128 form
pub reward_b_rate_u128: u128,
/// Pool bump
pub pool_bump: u8, // 1
/// Total staked amount
pub total_staked: u64,
}

impl Pool {
/// return reward a rate
pub fn get_reward_a_rate(&self) -> u128 {
if self.reward_a_rate_u128 == 0 {
return self._reward_a_rate.into();
}
return self.reward_a_rate_u128;
}

/// return reward b rate
pub fn get_reward_b_rate(&self) -> u128 {
if self.reward_b_rate_u128 == 0 {
return self._reward_b_rate.into();
}
return self.reward_b_rate_u128;
}
}

/// Farming user account
#[account]
#[derive(Default)]
Expand Down Expand Up @@ -1066,21 +1115,6 @@ pub enum ErrorCode {
MathOverflow,
}

impl Debug for Pool {
/// writes a subset of pool fields for debugging
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "paused: {} reward_duration: {} reward_duration_end: {} reward_a_rate: {} reward_b_rate: {} reward_a_per_token_stored {} reward_b_per_token_stored {}",
self.paused,
self.reward_duration,
self.reward_duration_end,
self.reward_a_rate,
self.reward_b_rate,
self.reward_a_per_token_stored,
self.reward_b_per_token_stored,
)
}
}

impl Debug for User {
/// writes a subset of user fields for debugging
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
Expand Down
43 changes: 27 additions & 16 deletions programs/farming/src/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ pub use crate::*;
use spl_math::uint::U192;

/// Rate by funding
fn calculate_reward_rate(funding_amount: u64, reward_duration: u64) -> Option<u64> {
fn calculate_reward_rate(funding_amount: u64, reward_duration: u64) -> Option<u128> {
let funding_amount: u128 = funding_amount.into();
let reward_duration: u128 = reward_duration.into();
let reward_rate = funding_amount
.checked_mul(PRECISION)?
.checked_div(reward_duration)?;
reward_rate.try_into().ok()
Some(reward_rate)
}

/// Calculate reward per token
Expand All @@ -31,7 +31,7 @@ pub fn reward_per_token(
.reward_a_per_token_stored
.checked_add(
time_period
.checked_mul(pool.reward_a_rate.into())
.checked_mul(pool.get_reward_a_rate().into())
.unwrap()
.checked_div(total_staked.into())
.unwrap()
Expand All @@ -44,7 +44,7 @@ pub fn reward_per_token(
.reward_b_per_token_stored
.checked_add(
time_period
.checked_mul(pool.reward_b_rate.into())
.checked_mul(pool.get_reward_b_rate().into())
.unwrap()
.checked_div(total_staked.into())
.unwrap()
Expand All @@ -61,31 +61,29 @@ pub fn rate_after_funding(
pool: &mut Account<Pool>,
funding_amount_a: u64,
funding_amount_b: u64,
) -> Result<(u64, u64)> {
) -> Result<(u128, u128)> {
let current_time = clock::Clock::get()
.unwrap()
.unix_timestamp
.try_into()
.unwrap();
let reward_period_end = pool.reward_duration_end;

let a: u64;
let b: u64;

if current_time >= reward_period_end {
a = calculate_reward_rate(funding_amount_a, pool.reward_duration).unwrap();
b = calculate_reward_rate(funding_amount_b, pool.reward_duration).unwrap();
let a = calculate_reward_rate(funding_amount_a, pool.reward_duration).unwrap();
let b = calculate_reward_rate(funding_amount_b, pool.reward_duration).unwrap();
Ok((a, b))
} else {
let remaining_seconds = reward_period_end.checked_sub(current_time).unwrap();
let leftover_a: u64 = (remaining_seconds as u128)
.checked_mul(pool.reward_a_rate.into())
.checked_mul(pool.get_reward_a_rate())
.unwrap()
.checked_div(PRECISION)
.unwrap()
.try_into()
.unwrap(); //back to u64
let leftover_b: u64 = (remaining_seconds as u128)
.checked_mul(pool.reward_b_rate.into())
.checked_mul(pool.get_reward_b_rate())
.unwrap()
.checked_div(PRECISION)
.unwrap()
Expand All @@ -95,11 +93,10 @@ pub fn rate_after_funding(
let total_a = leftover_a.checked_add(funding_amount_a).unwrap();
let total_b = leftover_b.checked_add(funding_amount_b).unwrap();

a = calculate_reward_rate(total_a, pool.reward_duration).unwrap();
b = calculate_reward_rate(total_b, pool.reward_duration).unwrap();
let a = calculate_reward_rate(total_a, pool.reward_duration).unwrap();
let b = calculate_reward_rate(total_b, pool.reward_duration).unwrap();
Ok((a, b))
}

Ok((a, b))
}

/// Calculate earned reward amount of staking user
Expand Down Expand Up @@ -134,3 +131,17 @@ pub fn user_earned_amount(pool: &Account<Pool>, user: &Account<User>) -> (u64, u

(a, b)
}

#[cfg(test)]
mod overflow_test {
use super::*;
#[test]
fn test_overflow() {
let reward_duration = 1u64;
let funding_amount = u64::MAX;
println!(
"reward rate {}",
calculate_reward_rate(funding_amount, reward_duration).unwrap()
);
}
}
Loading
Loading