🎁Farm Manager
The Farm Manager is a monolithic contract that handles all the farms-related logic for the pools.
How it works
The Farm Manager has two main concepts; a Farm
, containing a reward to be distributed and a Position
, defined as a user's liquidity in a pool locked in the contract.
Users of the Liquidity Hub, when providing liquidity, can opt to lock their LP shares which will in turn send them to the Farm Manager until they are unlocked.
Farms
Creating farms is permissionless, and farms can be perpetual. This means they can be expanded forever. Anyone can create an farm by calling the ManageFarm
message with the FarmAction::Fill
action and paying the farm creation fee, which is sent to the Fee Collector.
Users can decide to provide an identifier, which they can later use to top up or close the farm. If no identifier is provided, the contract will generate one.
Topping up a Farm
To top up a farm, the owner of the farm must call ManageFarm
with the FarmAction::Fill
action. The user must provide the same identifier as the original farm. The farm can only be topped up with the same token as the original reward, and the amount must be a multiple of the original reward's amount.
Closing a Farm
To close a farm, the owner of the farm or the owner of the contract must call ManageFarm
with the FarmAction::Close
action with the identifier of the farm to be closed. The farm will be closed, and the remaining tokens will be sent to the owner of the farm.
Reward Distribution
Farm rewards are distributed every epoch, which is created by the Epoch Manager. Whenever an epoch is created; the Farm Manager gets called via the EpochChangedHook
hook, alerting the contract that a new epoch has been created. The contract will then take snapshots for every LP token in the contract and save it in the LP_WEIGHT_HISTORY
map for the current epoch. That helps to calculate the rewards when users claim them.
The maximum number of concurrent farms for a given LP denom is defined when the contract is instantiated, and it is stored in the config as max_concurrent_farms
.
Positions
Positions can be created, expanded (topped up), or withdrawn. This is done via the ManagePosition
message, followed by the desired action, i.e. PositionAction::Fill
, PositionAction::Close
or PositionAction::Withdraw
.
When a user creates a position, it must provide an unlocking duration. The longer the unlocking duration, the higher the weight of the LP position, which equates to higher rewards.
The unlocking duration is the time it takes in seconds to unlock the position, which is necessary to withdraw the LP tokens from the contract.
Topping up a Position
When a user creates a position, the LP tokens are locked in the contract. The user can't withdraw them until the unlocking duration is complete. To expand a position, the user must call ManagePosition
with the PositionAction::Fill
action using the same position identifier as the original position. In this case, since it's considered to be the same position, any changes in the unlocking duration parameter passed along with the PositionAction::Fill
action will be ignored. Instead, the one in the original position will be used.
If a user doesn't provide an identifier when creating a position, the contract will generate one.
The minimum unlocking duration is 1 day, and the maximum is 365 days.
Closing a Position
Closing a position is done by calling ManagePosition
with the PositionAction::Close
action. The user must provide the identifier of the position to be closed. Once this action is triggered, the Position.open
state is set to false, and expiring_at
is set to the block height after which the position will be able to be withdrawn.
Withdrawing a Position
Once the unlocking duration is complete, the user can withdraw the LP tokens from the contract by calling the ManagePosition
with the PositionAction::Withdraw
action. Alternatively, if the user doesn't want to wait for the unlocking duration to complete, it is possible to do an emergency withdrawal by passing true
on the emergency_unlock
parameter. This will unlock and withdraw the position immediately, but the user will pay a penalty fee that will go the Fee Collector.
Once the user closes and withdraws the position, they receive their LP tokens back.
Claiming Farm Rewards
Users can claim farm rewards from active farms for their LP tokens, only if they have a position in the contract. Users can only claim rewards for future epochs, i.e. after the epoch in which the position was created.
Farm rewards are distributed based on the user's share of the total LP tokens in the contract. So if there's a total of 100 LP tokens in the contract, and a user has 10 LP tokens, the user will receive 10% of the rewards for that epoch, for that given farm.
To claim rewards, the user must call the Claim
message. Once that's done, the contract will save the epoch in which the claim was made in LAST_CLAIMED_EPOCH
, and will sync the user's LP weight history saved in LP_WEIGHT_HISTORY
. This helps computing the rewards for the user.
Instantiate
Instantiates an instance of the farm manager contract
Key | Type | Description |
---|---|---|
| String | The owner of the contract. |
| String | The epoch manager address, where the epochs are managed. |
| String | The address of the fee collector, where protocol fees go. |
| Coin | The fee that must be paid to create a farm. |
| u32 | The maximum amount of farms that can exist for a single LP token at a time. |
| u32 | New farms are allowed to start up to |
| u64 | The minimum amount of time that a user can lock their tokens for. In seconds. |
| u64 | The maximum amount of time that a user can lock their tokens for. In seconds. |
| Decimal | The penalty for unlocking a position before the unlocking duration finishes. In percentage. |
ExecuteMsg
ManageFarm
Manages a farm based on the action, which can be:
Fill: Fills a farm. If the farm doesn't exist, it creates a new one. If it exists already, it expands it given the sender created the original farm and the params are correct.
Close: Closes a farm with the given identifier. If the farm has expired, anyone can close it. Otherwise, only the farm creator or the owner of the contract can close a farm.
Once created, farms are stored in the FARMS
map.
Key | Type | Description |
---|---|---|
| String | The LP asset denom to create the farm for. |
| Option<u64> | The epoch at which the farm will start. If unspecified, it will start at the current epoch. |
| Option<u64> | The epoch at which the farm should preliminarily end (if it's not expanded). If unspecified, the farm will default to end at 14 epochs from the current one. |
| Option<Curve> | The type of distribution curve. If unspecified, the distribution will be linear. |
| Coin | The asset to be distributed in this farm. |
| Option<String> | If set, it will be used to identify the farm. |
ManagePosition
Manages a position based on the action, which can be:
Fill: Fills a position. If the position doesn't exist, it opens it. If it exists already, it expands it given the sender opened the original position and the params are correct.
Close: Closes an existing position. The position stops earning farm rewards.
Withdraw: Withdraws the LP tokens from a position after the position has been closed and the unlocking duration has passed.
Positions are stored in the POSITIONS
map.
Key | Type | Description |
---|---|---|
| Option<String> | The identifier of the position. |
| u64 | The time it takes in seconds to unlock this position. |
| Option<String> | The receiver for the position. If left empty, defaults to the message sender. |
Claim
Claims the rewards for the user.
UpdateConfig
Updates the contract configuration.
Key | Type | Description |
---|---|---|
| Option<String> | The address to of the fee collector, to send fees to. |
| Option<String> | The epoch manager address, where the epochs are managed. |
| Option<Coin> | The fee that must be paid to create a farm. |
| Option<u32> | The maximum amount of farms that can exist for a single LP token at a time. |
| Option<u32> | The maximum amount of epochs in the future a new farm is allowed to start in. |
| Option<u64> | The minimum amount of time that a user can lock their tokens for. In seconds. |
| Option<u64> | The maximum amount of time that a user can lock their tokens for. In seconds. |
| Option<Decimal> | The penalty for unlocking a position before the unlocking duration finishes. In percentage. |
EpochChangedHook
Gets triggered by the epoch manager when a new epoch is created.
Key | Type | Description |
---|---|---|
| Epoch | The current epoch. |
UpdateOwnership(::cw_ownable::Action)
Implements cw_ownable
. Updates the contract's ownership. ::cw_ownable::Action
can be TransferOwnership
, AcceptOwnership
and RenounceOwnership
.
Note: This is a cw_ownable
message.
Propose to transfer the contract's ownership to another account, optionally with an expiry time. Can only be called by the contract's current owner. Any existing pending ownership transfer is overwritten.
Key | Type | Description |
---|---|---|
| String | The new owner proposed, |
| Option<Expiration> | Optional expiration time parameter. |
QueryMsg
Config
Returns the configuration of the contract.
Farms
Retrieves the configuration of the manager.
Key | Type | Description |
---|---|---|
| Option<FarmsBy> | An optional parameter specifying what to filter farms by. Can be either the farm identifier, lp denom or the farm asset. |
| Option<String> | An optional parameter specifying what farm (identifier) to start searching after. |
| Option<u32> | The amount of farms to return. If unspecified, will default to a value specified by the contract. |
Positions
Retrieves the positions for an address.
Key | Type | Description |
---|---|---|
| String | The address to get positions for. |
| Option<bool> | An optional parameter specifying to return only positions that match the given open state. if true, it will return open positions. If false, it will return closed positions. |
Rewards
Retrieves the rewards for an address.
Key | Type | Description |
---|---|---|
| String | The address to get all the farm rewards for. |
LPWeight
Retrieves the total LP weight in the contract for a given denom on a given epoch.
Key | Type | Description |
---|---|---|
| String | The address to get the LP weight for. |
| String | The denom to get the total LP weight for. |
| u64 | The epoch id to get the LP weight for. |
Ownership
Returns the ownership of the contract.
Note: This is a cw_ownable
query.
MigrateMsg
Message to migrate the contract to a new code ID.
Last updated