Farms supports the release of multiple rewarders; each rewarder has an emission speed represented by emission_per_second and distributes rewards among multiple clmmpools based on the pool's pool_allocate_point.
The allocation of a single pool rewarder to staked clmm positions is determined by the position share, which is calculated at the time of staking into the pool.
Package: @cetuspackages/farming
Or using CLI:
mvr add @cetuspackages/farmingIf you prefer to use git tags, you can find all available versions in the GitHub releases or use:
# Check GitHub for the latest version tag
StableFarming = { git = "https://github.com/CetusProtocol/cetus-clmm-interface.git", subdir = "sui/stable_farming", rev = "[LATEST_TAG]", override = true }Replace [LATEST_TAG] with the latest tag from the releases page.
Note: Git tags may not always reflect the latest published versions. For the most up-to-date integration, Move Registry is recommended.
> **Note**: For the latest published addresses, visit [Move Registry](https://www.moveregistry.com/package/@cetuspackages/farming)
## 1. Key Structures
- **RewarderManager**: Singleton that creates a global object when the contract is deployed, holds the rewarder balance, and manages multiple `Rewarder` information through LinkedTable.
```move
struct RewarderManager has key, store {
id: UID,
vault: Bag,
pool_shares: LinkedTable<ID, u128>,
rewarders: LinkedTable<TypeName, Rewarder>
}
-
Rewarder
struct Rewarder has store { reward_coin: TypeName, total_allocate_point: u64, emission_per_second: u128, last_reward_time: u64, total_reward_released: u128, total_reward_harvested: u64, pools: LinkedTable<ID, PoolRewarderInfo> } struct PoolRewarderInfo has store { allocate_point: u64, acc_per_share: u128, last_reward_time: u64, reward_released: u128, reward_harvested: u64 }
-
Pool
struct Pool has key, store { id: UID, clmm_pool_id: ID, effective_tick_lower: I32, effective_tick_upper: I32, sqrt_price: u128, total_share: u128, rewarders: vector<TypeName>, positions: LinkedTable<ID, WrappedPositionInfo> } struct WrappedPositionNFT has key, store { id: UID, pool_id: ID, clmm_postion: Position, url: String } /// Clmm Position struct Position has key, store { id: UID, pool: ID, // clmm pool id index: u64, coin_type_a: TypeName, coin_type_b: TypeName, name: String, description: String, url: String, tick_lower_index: I32, tick_upper_index: I32, liquidity: u128, }
acl.move: The ACL module
config.move: Global Config module
pool.move: Pool associated with clmmpool to record information about farming
rewarder.move: Global RewarderManager to manage multiple Rewarders
router.move: All Entry functions
-
Deposit Clmm Position into Farming Pool. When the clmmpool is staked, a
WrappedPositionNFTis sent to the user.public entry fun deposit( global_config: &GlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_position: CLMMPosition, clk: &Clock, ctx: &mut TxContext )
-
Withdraw Clmm Position from Farms Pool. All rewarders should be harvested before withdrawal. This instruction should work with the harvest instruction; the harvest move call should be organized before the withdrawal move call in a single transaction. If the clmmpool of this position has two rewarders, two move calls of harvest should be included in the transaction.
public entry fun withdraw( global_config: &GlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, wrapped_position: WrappedPositionNFT, clk: &Clock, ctx: &TxContext )
-
Harvest farm rewarders
public entry fun harvest<RewardCoin>( global_config: &GlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, wrapped_position: &WrappedPositionNFT, clk: &Clock, ctx: &mut TxContext )
-
Add liquidity into the clmmpool position staked in Farms pool
public entry fun add_liquidity<CoinA, CoinB>( global_config: &GlobalConfig, clmm_global_config: &CLMMGlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_pool: &mut CLMMPool<CoinA, CoinB>, wrapped_position: &mut WrappedPositionNFT, coin_a: Coin<CoinA>, coin_b: Coin<CoinB>, amount_limit_a: u64, amount_limit_b: u64, delta_liquidity: u128, clk: &Clock, ctx: &mut TxContext, )
-
Add fixed coin liquidity into the clmmpool position staked in Farms pool
public entry fun add_liquidity_fix_coin<CoinA, CoinB>( global_config: &GlobalConfig, clmm_global_config: &CLMMGlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_pool: &mut CLMMPool<CoinA, CoinB>, wrapped_position: &mut WrappedPositionNFT, coin_a: Coin<CoinA>, coin_b: Coin<CoinB>, amount_a: u64, amount_b: u64, fix_amount_a: bool, clk: &Clock, ctx: &mut TxContext )
-
Remove liquidity from the clmmpool position staked in Farms pool
public entry fun remove_liquidity<CoinA, CoinB>( global_config: &GlobalConfig, clmm_global_config: &CLMMGlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_pool: &mut CLMMPool<CoinA, CoinB>, wrapped_position: &mut WrappedPositionNFT, delta_liquidity: u128, min_amount_a: u64, min_amount_b: u64, clk: &Clock, ctx: &mut TxContext )
-
Collect clmm fee using
WrappedPositionNFTpublic entry fun collect_fee<CoinA, CoinB>( global_config: &GlobalConfig, clmm_global_config: &CLMMGlobalConfig, clmm_pool: &mut CLMMPool<CoinA, CoinB>, wrapped_position: &WrappedPositionNFT, ctx: &mut TxContext )
-
Collect clmm rewarder using
WrappedPositionNFTpublic entry fun collect_clmm_reward<RewardCoin, CoinA, CoinB>( global_config: &GlobalConfig, clmm_global_config: &CLMMGlobalConfig, clmm_pool: &mut CLMMPool<CoinA, CoinB>, warped_position: &WrappedPositionNFT, vault: &mut RewarderGlobalVault, reward_coin: Coin<RewardCoin>, clock: &Clock, ctx: &mut TxContext )
-
Close clmm position Before calling this instruction to close the clmm position, the possible existing multiple clmm rewarders should be collected, and possible multiple Farms rewarders should be harvested. So the
collect_clmm_rewardandharvestmove calls should be organized withclose_positioninto a single transaction.public fun close_position<CoinA, CoinB>( global_config: &GlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_global_config: &CLMMGlobalConfig, clmm_pool: &mut CLMMPool<CoinA, CoinB>, wrapped_position: WrappedPositionNFT, min_amount_a: u64, min_amount_b: u64, clk: &Clock, ctx: &mut TxContext )
-
pool::deposit_v2 pool::deposit and router::deposit are deprecated
public fun deposit_v2<CoinA, CoinB>( global_config: &GlobalConfig, rewarder_manager: &mut RewarderManager, pool: &mut Pool, clmm_pool: &CLMMPool<CoinA, CoinB>, clmm_position: CLMMPosition, clk: &Clock, ctx: &mut TxContext ): WrappedPositionNFT -
pool::close_v2 pool::close_position and router::close_position are deprecated
public fun close_position_v2<CoinA, CoinB>(
global_config: &GlobalConfig,
rewarder_manager: &mut RewarderManager,
pool: &mut Pool,
clmm_global_config: &CLMMGlobalConfig,
clmm_pool: &mut CLMMPool<CoinA, CoinB>,
wrapped_position: WrappedPositionNFT,
min_amount_a: u64,
min_amount_b: u64,
clk: &Clock,
ctx: &TxContext,
): (Balance<CoinA>, Balance<CoinB>) {}
-
Testnet
package_id: 0xcc38686ca84d1dca949b6966dcdb66b698b58a4bba247d8db4d6a3a1dbeca26e published_at: 0x3c4582ee27a09f7e6c091022d0d279fdc8e54c1f782916bf135a71a8e8006aa5 RewardManager: 0x960c7800e301fd1e47b79037927b426db57b643bd2934f7069d81c2dae092230 GlobalConfig: 0x499132a4baf342a0fe9528a3666a77b2aece3be129f4a3ada469fef4b9c34fb4 CLmmGlobalConfig: 0x9774e359588ead122af1c7e7f64e14ade261cfeecdb5d0eb4a5b3b4c8ab8bd3e -
Mainnet
package_id: 0x11ea791d82b5742cc8cab0bf7946035c97d9001d7c3803a93f119753da66f526 published_at: 0x7e4ca066f06a1132ab0499c8c0b87f847a0d90684afa902e52501a44dbd81992 RewardManager: 0xe0e155a88c77025056da08db5b1701a91b79edb6167462f768e387c3ed6614d5 GlobalConfig: 0x21215f2f6de04b57dd87d9be7bb4e15499aec935e36078e2488f36436d64996e ClmmGlobalConfig: 0xdaa46292632c3c4d8f31f23ea0f9b36a28ff3677e9684980e4438403a67a3d8f
struct CreatePoolEvent has drop, copy {
pool_id: ID,
clmm_pool_id: ID,
sqrt_price: u128,
effective_tick_lower: I32,
effective_tick_upper: I32,
}Query all events of type "{package_id}::pool::CreatePoolEvent", and all related Farms pool_id and clmm_pool_id will be returned.
Simulate the accumulated_position_rewards instruction, which will emit the AccumulatedPositionRewardsEvent. Parsing the event will get multiple rewarders that the WrappedPositionNFT gets at the current time.
public fun accumulated_position_rewards(
global_config: &GlobalConfig,
rewarder_manager: &mut RewarderManager,
pool: &mut Pool,
wrapped_position_id: ID,
clk: &Clock,
) {
emit struct AccumulatedPositionRewardsEvent has drop, copy {
pool_id: ID,
wrapped_position_id: ID,
clmm_position_id: ID,
rewards: vec_map::VecMap<TypeName, u64>
}
}7. Open Liquidity Position, Add Liquidity if Needed, and Deposit into Farms Pool in a Single Transaction
Organize open position, add liquidity, and deposit move calls into a single transaction.
/// clmmpool::pool::open_position and get Clmm LP.
public fun open_position<CoinTypeA, CoinTypeB>(
config: &GlobalConfig,
pool: &mut Pool<CoinTypeA, CoinTypeB>,
tick_lower: u32,
tick_upper: u32,
ctx: &mut TxContext
): Position;
/// integrate::pool_v2::add_liquidity_by_fix_coin.
public entry fun add_liquidity_by_fix_coin<CoinTypeA, CoinTypeB>(
config: &GlobalConfig,
pool: &mut Pool<CoinTypeA, CoinTypeB>,
position_nft: &mut Position,
coin_a: Coin<CoinTypeA>,
coin_b: Coin<CoinTypeB>,
amount_a: u64,
amount_b: u64,
fix_amount_a: bool,
clock: &Clock,
ctx: &mut TxContext
);
/// call farms::pool::deposit to deposit LP into Farms pool and get WrappedPositionNFT
public fun deposit(
global_config: &GlobalConfig,
rewarder_manager: &mut RewarderManager,
pool: &mut Pool,
clmm_position: CLMMPosition,
clk: &Clock,
ctx: &mut TxContext
): WrappedPositionNFT