From c934153b7f029ddad05aa60b09069184802c9eed Mon Sep 17 00:00:00 2001 From: andrewsource147 <31321699+andrewsource147@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:23:39 +0700 Subject: [PATCH] Migrate farming rate (#60) * migrate farming rate * add migrate farming rate to cli --- .gitignore | 6 +- cli/farming-cli/src/args.rs | 3 + cli/farming-cli/src/main.rs | 48 + programs/farming/Cargo.toml | 2 +- programs/farming/src/lib.rs | 86 +- programs/farming/src/pool.rs | 43 +- target/types/farming.ts | 2131 ++++++++++++++++++++++++++++++++++ 7 files changed, 2274 insertions(+), 45 deletions(-) create mode 100644 target/types/farming.ts diff --git a/.gitignore b/.gitignore index f90b4eb..d32ec71 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ .anchor .DS_Store -target +target/* +!target/types test-ledger **/*.rs.bk /keys/ @@ -14,4 +15,5 @@ soteria-linux-develop/ testing/ deployment dist -.env \ No newline at end of file +.env + diff --git a/cli/farming-cli/src/args.rs b/cli/farming-cli/src/args.rs index b739cbe..cf142c9 100644 --- a/cli/farming-cli/src/args.rs +++ b/cli/farming-cli/src/args.rs @@ -146,6 +146,9 @@ pub enum CliCommand { #[clap(long)] pool: Pubkey, }, + + CheckFunderAllPool {}, + MigrateFarmingRate {}, } #[derive(Parser, Debug)] diff --git a/cli/farming-cli/src/main.rs b/cli/farming-cli/src/main.rs index 77b56af..46ca17c 100644 --- a/cli/farming-cli/src/main.rs +++ b/cli/farming-cli/src/main.rs @@ -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; @@ -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(()) @@ -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::(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::(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(()) +} diff --git a/programs/farming/Cargo.toml b/programs/farming/Cargo.toml index 6ffe218..6ab99ae 100644 --- a/programs/farming/Cargo.toml +++ b/programs/farming/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "farming" -version = "0.2.0" +version = "0.2.1" description = "Created with Anchor" edition = "2018" diff --git a/programs/farming/src/lib.rs b/programs/farming/src/lib.rs index dfec984..88b72cd 100644 --- a/programs/farming/src/lib.rs +++ b/programs/farming/src/lib.rs @@ -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; @@ -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; @@ -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 { @@ -437,6 +443,18 @@ pub mod farming { Ok(()) } + /// anyone can call this + pub fn migrate_farming_rate(ctx: Context) -> 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) -> Result<()> { let pool = &ctx.accounts.pool; @@ -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>, +} + /// Accounts for [ClosePool](/dual_farming/instruction/struct.ClosePool.html) instruction #[derive(Accounts)] pub struct ClosePool<'info> { @@ -926,6 +952,7 @@ pub struct ClosePool<'info> { /// Pool account wrapper #[account] +#[derive(Debug)] pub struct Pool { /// Privileged account. pub authority: Pubkey, // 32 @@ -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. @@ -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)] @@ -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> { diff --git a/programs/farming/src/pool.rs b/programs/farming/src/pool.rs index 19487d2..2ea17d2 100644 --- a/programs/farming/src/pool.rs +++ b/programs/farming/src/pool.rs @@ -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 { +fn calculate_reward_rate(funding_amount: u64, reward_duration: u64) -> Option { 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 @@ -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() @@ -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() @@ -61,7 +61,7 @@ pub fn rate_after_funding( pool: &mut Account, funding_amount_a: u64, funding_amount_b: u64, -) -> Result<(u64, u64)> { +) -> Result<(u128, u128)> { let current_time = clock::Clock::get() .unwrap() .unix_timestamp @@ -69,23 +69,21 @@ pub fn rate_after_funding( .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() @@ -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 @@ -134,3 +131,17 @@ pub fn user_earned_amount(pool: &Account, user: &Account) -> (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() + ); + } +} diff --git a/target/types/farming.ts b/target/types/farming.ts new file mode 100644 index 0000000..a741ca9 --- /dev/null +++ b/target/types/farming.ts @@ -0,0 +1,2131 @@ +export type Farming = { + "version": "0.2.1", + "name": "farming", + "docs": [ + "Dual farming program" + ], + "instructions": [ + { + "name": "initializePool", + "docs": [ + "Initializes a new pool. Able to create pool with single reward by passing the same Mint account for reward_a_mint and reward_a_mint" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Staking mint" + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "rewardAMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Reward A mint" + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A vault PDA" + ] + }, + { + "name": "rewardBMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Reward B mint" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B vault PDA" + ] + }, + { + "name": "authority", + "isMut": true, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + }, + { + "name": "base", + "isMut": false, + "isSigner": true, + "docs": [ + "Base" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "SPL Token program" + ] + }, + { + "name": "rent", + "isMut": false, + "isSigner": false, + "docs": [ + "Rent" + ] + } + ], + "args": [ + { + "name": "rewardDuration", + "type": "u64" + } + ] + }, + { + "name": "createUser", + "docs": [ + "Initialize a user staking account" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Authority of user account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [] + }, + { + "name": "pause", + "docs": [ + "Pause the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [] + }, + { + "name": "unpause", + "docs": [ + "Unpauses a previously paused pool. Allowing for funding." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [] + }, + { + "name": "deposit", + "docs": [ + "User deposit tokens in the pool." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the deposit/withdraw instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "stakeFromAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User staking ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdraw", + "docs": [ + "User withdraw tokens in the pool." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the deposit/withdraw instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "stakeFromAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User staking ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "sptAmount", + "type": "u64" + } + ] + }, + { + "name": "authorizeFunder", + "docs": [ + "Authorize additional funders for the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [ + { + "name": "funderToAdd", + "type": "publicKey" + } + ] + }, + { + "name": "deauthorizeFunder", + "docs": [ + "Deauthorize funders for the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [ + { + "name": "funderToRemove", + "type": "publicKey" + } + ] + }, + { + "name": "fund", + "docs": [ + "Fund the pool with rewards. This resets the clock on the end date, pushing it out to the set duration. And, linearly redistributes remaining rewards." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A Vault PDA" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B Vault PDA" + ] + }, + { + "name": "funder", + "isMut": false, + "isSigner": true, + "docs": [ + "Funder" + ] + }, + { + "name": "fromA", + "isMut": true, + "isSigner": false, + "docs": [ + "Funder reward A ATA" + ] + }, + { + "name": "fromB", + "isMut": true, + "isSigner": false, + "docs": [ + "Funder reward B ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "amountA", + "type": "u64" + }, + { + "name": "amountB", + "type": "u64" + } + ] + }, + { + "name": "claim", + "docs": [ + "User claim rewards" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A Vault PDA" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B Vault PDA" + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "rewardAAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User's Reward A ATA" + ] + }, + { + "name": "rewardBAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User's Reward B ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "withdrawExtraToken", + "docs": [ + "Withdraw token that mistakenly deposited to staking_vault" + ], + "accounts": [ + { + "name": "pool", + "isMut": false, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "withdrawToAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "Token account to receive mistakenly deposited token" + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the staking instance" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [] + }, + { + "name": "closeUser", + "docs": [ + "Closes a users stake account. Validation is done to ensure this is only allowed when the user has nothing staked and no rewards pending." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "migrateFarmingRate", + "docs": [ + "anyone can call this" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "closePool", + "docs": [ + "Closes a pool account. Only able to be done when there are no users staked." + ], + "accounts": [ + { + "name": "refundee", + "isMut": true, + "isSigner": false + }, + { + "name": "stakingRefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardARefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardBRefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "pool", + "docs": [ + "Pool account wrapper" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "docs": [ + "Privileged account." + ], + "type": "publicKey" + }, + { + "name": "paused", + "docs": [ + "Paused state of the program" + ], + "type": "bool" + }, + { + "name": "stakingMint", + "docs": [ + "Mint of the token that can be staked." + ], + "type": "publicKey" + }, + { + "name": "stakingVault", + "docs": [ + "Vault to store staked tokens." + ], + "type": "publicKey" + }, + { + "name": "rewardAMint", + "docs": [ + "Mint of the reward A token." + ], + "type": "publicKey" + }, + { + "name": "rewardAVault", + "docs": [ + "Vault to store reward A tokens." + ], + "type": "publicKey" + }, + { + "name": "rewardBMint", + "docs": [ + "Mint of the reward B token." + ], + "type": "publicKey" + }, + { + "name": "rewardBVault", + "docs": [ + "Vault to store reward B tokens." + ], + "type": "publicKey" + }, + { + "name": "baseKey", + "docs": [ + "Base key" + ], + "type": "publicKey" + }, + { + "name": "rewardDuration", + "docs": [ + "The period which rewards are linearly distributed." + ], + "type": "u64" + }, + { + "name": "rewardDurationEnd", + "docs": [ + "The timestamp at which the current reward period ends." + ], + "type": "u64" + }, + { + "name": "lastUpdateTime", + "docs": [ + "The last time reward states were updated." + ], + "type": "u64" + }, + { + "name": "rewardARate", + "docs": [ + "deprecated field" + ], + "type": "u64" + }, + { + "name": "rewardBRate", + "docs": [ + "deprecated field" + ], + "type": "u64" + }, + { + "name": "rewardAPerTokenStored", + "docs": [ + "Last calculated reward A per pool token." + ], + "type": "u128" + }, + { + "name": "rewardBPerTokenStored", + "docs": [ + "Last calculated reward B per pool token." + ], + "type": "u128" + }, + { + "name": "userStakeCount", + "docs": [ + "Users staked" + ], + "type": "u32" + }, + { + "name": "funders", + "docs": [ + "authorized funders", + "[] because short size, fixed account size, and ease of use on", + "client due to auto generated account size property" + ], + "type": { + "array": [ + "publicKey", + 3 + ] + } + }, + { + "name": "rewardARateU128", + "docs": [ + "reward_a_rate in u128 form" + ], + "type": "u128" + }, + { + "name": "rewardBRateU128", + "docs": [ + "reward_b_rate in u128 form" + ], + "type": "u128" + }, + { + "name": "poolBump", + "docs": [ + "Pool bump" + ], + "type": "u8" + }, + { + "name": "totalStaked", + "docs": [ + "Total staked amount" + ], + "type": "u64" + } + ] + } + }, + { + "name": "user", + "docs": [ + "Farming user account" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool", + "docs": [ + "Pool the this user belongs to." + ], + "type": "publicKey" + }, + { + "name": "owner", + "docs": [ + "The owner of this account." + ], + "type": "publicKey" + }, + { + "name": "rewardAPerTokenComplete", + "docs": [ + "The amount of token A claimed." + ], + "type": "u128" + }, + { + "name": "rewardBPerTokenComplete", + "docs": [ + "The amount of token B claimed." + ], + "type": "u128" + }, + { + "name": "rewardAPerTokenPending", + "docs": [ + "The amount of token A pending claim." + ], + "type": "u64" + }, + { + "name": "rewardBPerTokenPending", + "docs": [ + "The amount of token B pending claim." + ], + "type": "u64" + }, + { + "name": "balanceStaked", + "docs": [ + "The amount staked." + ], + "type": "u64" + }, + { + "name": "nonce", + "docs": [ + "Signer nonce." + ], + "type": "u8" + } + ] + } + } + ], + "events": [ + { + "name": "EventDeposit", + "fields": [ + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventWithdraw", + "fields": [ + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventFund", + "fields": [ + { + "name": "amountA", + "type": "u64", + "index": false + }, + { + "name": "amountB", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventClaim", + "fields": [ + { + "name": "amountA", + "type": "u64", + "index": false + }, + { + "name": "amountB", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventAuthorizeFunder", + "fields": [ + { + "name": "newFunder", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "EventUnauthorizeFunder", + "fields": [ + { + "name": "funder", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "InsufficientFundWithdraw", + "msg": "Insufficient funds to withdraw." + }, + { + "code": 6001, + "name": "AmountMustBeGreaterThanZero", + "msg": "Amount must be greater than zero." + }, + { + "code": 6002, + "name": "SingleDepositTokenBCannotBeFunded", + "msg": "Reward B cannot be funded - pool is single deposit." + }, + { + "code": 6003, + "name": "PoolPaused", + "msg": "Pool is paused." + }, + { + "code": 6004, + "name": "DurationTooShort", + "msg": "Duration cannot be shorter than one day." + }, + { + "code": 6005, + "name": "FunderAlreadyAuthorized", + "msg": "Provided funder is already authorized to fund." + }, + { + "code": 6006, + "name": "MaxFunders", + "msg": "Maximum funders already authorized." + }, + { + "code": 6007, + "name": "CannotDeauthorizePoolAuthority", + "msg": "Cannot deauthorize the primary pool authority." + }, + { + "code": 6008, + "name": "CannotDeauthorizeMissingAuthority", + "msg": "Authority not found for deauthorization." + }, + { + "code": 6009, + "name": "MathOverflow", + "msg": "Math operation overflow" + } + ] +}; + +export const IDL: Farming = { + "version": "0.2.1", + "name": "farming", + "docs": [ + "Dual farming program" + ], + "instructions": [ + { + "name": "initializePool", + "docs": [ + "Initializes a new pool. Able to create pool with single reward by passing the same Mint account for reward_a_mint and reward_a_mint" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Staking mint" + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "rewardAMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Reward A mint" + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A vault PDA" + ] + }, + { + "name": "rewardBMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Reward B mint" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B vault PDA" + ] + }, + { + "name": "authority", + "isMut": true, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + }, + { + "name": "base", + "isMut": false, + "isSigner": true, + "docs": [ + "Base" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "SPL Token program" + ] + }, + { + "name": "rent", + "isMut": false, + "isSigner": false, + "docs": [ + "Rent" + ] + } + ], + "args": [ + { + "name": "rewardDuration", + "type": "u64" + } + ] + }, + { + "name": "createUser", + "docs": [ + "Initialize a user staking account" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Authority of user account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [] + }, + { + "name": "pause", + "docs": [ + "Pause the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [] + }, + { + "name": "unpause", + "docs": [ + "Unpauses a previously paused pool. Allowing for funding." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [] + }, + { + "name": "deposit", + "docs": [ + "User deposit tokens in the pool." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the deposit/withdraw instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "stakeFromAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User staking ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdraw", + "docs": [ + "User withdraw tokens in the pool." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the deposit/withdraw instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "stakeFromAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User staking ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "sptAmount", + "type": "u64" + } + ] + }, + { + "name": "authorizeFunder", + "docs": [ + "Authorize additional funders for the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [ + { + "name": "funderToAdd", + "type": "publicKey" + } + ] + }, + { + "name": "deauthorizeFunder", + "docs": [ + "Deauthorize funders for the pool" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the pool" + ] + } + ], + "args": [ + { + "name": "funderToRemove", + "type": "publicKey" + } + ] + }, + { + "name": "fund", + "docs": [ + "Fund the pool with rewards. This resets the clock on the end date, pushing it out to the set duration. And, linearly redistributes remaining rewards." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A Vault PDA" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B Vault PDA" + ] + }, + { + "name": "funder", + "isMut": false, + "isSigner": true, + "docs": [ + "Funder" + ] + }, + { + "name": "fromA", + "isMut": true, + "isSigner": false, + "docs": [ + "Funder reward A ATA" + ] + }, + { + "name": "fromB", + "isMut": true, + "isSigner": false, + "docs": [ + "Funder reward B ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [ + { + "name": "amountA", + "type": "u64" + }, + { + "name": "amountB", + "type": "u64" + } + ] + }, + { + "name": "claim", + "docs": [ + "User claim rewards" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA." + ] + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward A Vault PDA" + ] + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Reward B Vault PDA" + ] + }, + { + "name": "user", + "isMut": true, + "isSigner": false, + "docs": [ + "User." + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of user" + ] + }, + { + "name": "rewardAAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User's Reward A ATA" + ] + }, + { + "name": "rewardBAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "User's Reward B ATA" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "withdrawExtraToken", + "docs": [ + "Withdraw token that mistakenly deposited to staking_vault" + ], + "accounts": [ + { + "name": "pool", + "isMut": false, + "isSigner": false, + "docs": [ + "Global accounts for the staking instance." + ] + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Staking vault PDA" + ] + }, + { + "name": "withdrawToAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "Token account to receive mistakenly deposited token" + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "docs": [ + "Authority of the staking instance" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Misc." + ] + } + ], + "args": [] + }, + { + "name": "closeUser", + "docs": [ + "Closes a users stake account. Validation is done to ensure this is only allowed when the user has nothing staked and no rewards pending." + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "migrateFarmingRate", + "docs": [ + "anyone can call this" + ], + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "closePool", + "docs": [ + "Closes a pool account. Only able to be done when there are no users staked." + ], + "accounts": [ + { + "name": "refundee", + "isMut": true, + "isSigner": false + }, + { + "name": "stakingRefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardARefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardBRefundee", + "isMut": true, + "isSigner": false + }, + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "stakingVault", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardAVault", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardBVault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "pool", + "docs": [ + "Pool account wrapper" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "docs": [ + "Privileged account." + ], + "type": "publicKey" + }, + { + "name": "paused", + "docs": [ + "Paused state of the program" + ], + "type": "bool" + }, + { + "name": "stakingMint", + "docs": [ + "Mint of the token that can be staked." + ], + "type": "publicKey" + }, + { + "name": "stakingVault", + "docs": [ + "Vault to store staked tokens." + ], + "type": "publicKey" + }, + { + "name": "rewardAMint", + "docs": [ + "Mint of the reward A token." + ], + "type": "publicKey" + }, + { + "name": "rewardAVault", + "docs": [ + "Vault to store reward A tokens." + ], + "type": "publicKey" + }, + { + "name": "rewardBMint", + "docs": [ + "Mint of the reward B token." + ], + "type": "publicKey" + }, + { + "name": "rewardBVault", + "docs": [ + "Vault to store reward B tokens." + ], + "type": "publicKey" + }, + { + "name": "baseKey", + "docs": [ + "Base key" + ], + "type": "publicKey" + }, + { + "name": "rewardDuration", + "docs": [ + "The period which rewards are linearly distributed." + ], + "type": "u64" + }, + { + "name": "rewardDurationEnd", + "docs": [ + "The timestamp at which the current reward period ends." + ], + "type": "u64" + }, + { + "name": "lastUpdateTime", + "docs": [ + "The last time reward states were updated." + ], + "type": "u64" + }, + { + "name": "rewardARate", + "docs": [ + "deprecated field" + ], + "type": "u64" + }, + { + "name": "rewardBRate", + "docs": [ + "deprecated field" + ], + "type": "u64" + }, + { + "name": "rewardAPerTokenStored", + "docs": [ + "Last calculated reward A per pool token." + ], + "type": "u128" + }, + { + "name": "rewardBPerTokenStored", + "docs": [ + "Last calculated reward B per pool token." + ], + "type": "u128" + }, + { + "name": "userStakeCount", + "docs": [ + "Users staked" + ], + "type": "u32" + }, + { + "name": "funders", + "docs": [ + "authorized funders", + "[] because short size, fixed account size, and ease of use on", + "client due to auto generated account size property" + ], + "type": { + "array": [ + "publicKey", + 3 + ] + } + }, + { + "name": "rewardARateU128", + "docs": [ + "reward_a_rate in u128 form" + ], + "type": "u128" + }, + { + "name": "rewardBRateU128", + "docs": [ + "reward_b_rate in u128 form" + ], + "type": "u128" + }, + { + "name": "poolBump", + "docs": [ + "Pool bump" + ], + "type": "u8" + }, + { + "name": "totalStaked", + "docs": [ + "Total staked amount" + ], + "type": "u64" + } + ] + } + }, + { + "name": "user", + "docs": [ + "Farming user account" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool", + "docs": [ + "Pool the this user belongs to." + ], + "type": "publicKey" + }, + { + "name": "owner", + "docs": [ + "The owner of this account." + ], + "type": "publicKey" + }, + { + "name": "rewardAPerTokenComplete", + "docs": [ + "The amount of token A claimed." + ], + "type": "u128" + }, + { + "name": "rewardBPerTokenComplete", + "docs": [ + "The amount of token B claimed." + ], + "type": "u128" + }, + { + "name": "rewardAPerTokenPending", + "docs": [ + "The amount of token A pending claim." + ], + "type": "u64" + }, + { + "name": "rewardBPerTokenPending", + "docs": [ + "The amount of token B pending claim." + ], + "type": "u64" + }, + { + "name": "balanceStaked", + "docs": [ + "The amount staked." + ], + "type": "u64" + }, + { + "name": "nonce", + "docs": [ + "Signer nonce." + ], + "type": "u8" + } + ] + } + } + ], + "events": [ + { + "name": "EventDeposit", + "fields": [ + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventWithdraw", + "fields": [ + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventFund", + "fields": [ + { + "name": "amountA", + "type": "u64", + "index": false + }, + { + "name": "amountB", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventClaim", + "fields": [ + { + "name": "amountA", + "type": "u64", + "index": false + }, + { + "name": "amountB", + "type": "u64", + "index": false + } + ] + }, + { + "name": "EventAuthorizeFunder", + "fields": [ + { + "name": "newFunder", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "EventUnauthorizeFunder", + "fields": [ + { + "name": "funder", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "InsufficientFundWithdraw", + "msg": "Insufficient funds to withdraw." + }, + { + "code": 6001, + "name": "AmountMustBeGreaterThanZero", + "msg": "Amount must be greater than zero." + }, + { + "code": 6002, + "name": "SingleDepositTokenBCannotBeFunded", + "msg": "Reward B cannot be funded - pool is single deposit." + }, + { + "code": 6003, + "name": "PoolPaused", + "msg": "Pool is paused." + }, + { + "code": 6004, + "name": "DurationTooShort", + "msg": "Duration cannot be shorter than one day." + }, + { + "code": 6005, + "name": "FunderAlreadyAuthorized", + "msg": "Provided funder is already authorized to fund." + }, + { + "code": 6006, + "name": "MaxFunders", + "msg": "Maximum funders already authorized." + }, + { + "code": 6007, + "name": "CannotDeauthorizePoolAuthority", + "msg": "Cannot deauthorize the primary pool authority." + }, + { + "code": 6008, + "name": "CannotDeauthorizeMissingAuthority", + "msg": "Authority not found for deauthorization." + }, + { + "code": 6009, + "name": "MathOverflow", + "msg": "Math operation overflow" + } + ] +};