🎁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

{
  "owner": "mantra1...",
  "epoch_manager_addr": "mantra1...",
  "fee_collector_addr": "mantra1...",
  "create_farm_fee": {
    "denom": "uom",
    "amount": "1000000000"
  },
  "max_concurrent_farms": 7,
  "max_farm_epoch_buffer": 14,
  "min_unlocking_duration": 86400,
  "max_unlocking_duration": 31536000,
  "emergency_unlock_penalty": "0.01"
}
KeyTypeDescription

owner

String

The owner of the contract.

epoch_manager_addr

String

The epoch manager address, where the epochs are managed.

fee_collector_addr

String

The address of the fee collector, where protocol fees go.

create_farm_fee

Coin

The fee that must be paid to create a farm.

max_concurrent_farms

u32

The maximum amount of farms that can exist for a single LP token at a time.

max_farm_epoch_buffer

u32

New farms are allowed to start up to current_epoch + start_epoch_buffer into the future.

min_unlocking_duration

u64

The minimum amount of time that a user can lock their tokens for. In seconds.

max_unlocking_duration

u64

The maximum amount of time that a user can lock their tokens for. In seconds.

emergency_unlock_penalty

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.

{
  "manage_farm": {
    "action": {
      "fill": {
        "params": {
          "lp_denom": "factory/mantra1.../LP",
          "start_epoch": 10,
          "preliminary_end_epoch": 24,
          "curve": "linear",
          "farm_asset": {
            "denom": "uom",
            "amount": "1000000000"
          },
          "farm_identifier": "farm_identifier"
        }
      }
    }
  }
}
KeyTypeDescription

lp_denom

String

The LP asset denom to create the farm for.

start_epoch

Option<u64>

The epoch at which the farm will start. If unspecified, it will start at the current epoch.

preliminary_end_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.

curve

Option<Curve>

The type of distribution curve. If unspecified, the distribution will be linear.

farm_asset

Coin

The asset to be distributed in this farm.

farm_identifier

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.

{
  "manage_position": {
    "action": {
      "fill": {
        "identifier": "position_identifier",
        "unlocking_duration": 86400,
        "receiver": "mantra1..."
      }
    }
  }
}
KeyTypeDescription

identifier

Option<String>

The identifier of the position.

unlocking_duration

u64

The time it takes in seconds to unlock this position.

receiver

Option<String>

The receiver for the position. If left empty, defaults to the message sender.

Claim

Claims the rewards for the user.

{
  "claim": {}
}

UpdateConfig

Updates the contract configuration.

{
  "update_config": {
    "fee_collector_addr": "mantra1...",
    "epoch_manager_addr": "mantra1...",
    "create_farm_fee": {
      "denom": "uom",
      "amount": "1000000000"
    },
    "max_concurrent_farms": 7,
    "max_farm_epoch_buffer": 14,
    "min_unlocking_duration": 86400,
    "max_unlocking_duration": 31536000,
    "emergency_unlock_penalty": "0.01"
  }
}
KeyTypeDescription

fee_collector_addr

Option<String>

The address to of the fee collector, to send fees to.

epoch_manager_addr

Option<String>

The epoch manager address, where the epochs are managed.

create_farm_fee

Option<Coin>

The fee that must be paid to create a farm.

max_concurrent_farms

Option<u32>

The maximum amount of farms that can exist for a single LP token at a time.

max_farm_epoch_buffer

Option<u32>

The maximum amount of epochs in the future a new farm is allowed to start in.

min_unlocking_duration

Option<u64>

The minimum amount of time that a user can lock their tokens for. In seconds.

max_unlocking_duration

Option<u64>

The maximum amount of time that a user can lock their tokens for. In seconds.

emergency_unlock_penalty

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.

{
  "epoch_changed_hook": {
    "current_epoch": {
      "id": 23,
      "start_time": "1571797419879305533"
    }
  }
}
KeyTypeDescription

current_epoch

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.

{
  "update_ownership": {
    "transfer_ownership": {
      "new_owner": "mantra1...",
      "expiry": {
        "at_height": "424242424242"
      }
    }
  }
}
KeyTypeDescription

new_owner

String

The new owner proposed,

expiry

Option<Expiration>

Optional expiration time parameter.

QueryMsg

Config

Returns the configuration of the contract.

{
  "config": {}
}

Farms

Retrieves the configuration of the manager.

{
  "farms": {
    "filter_by": {
      "identifier": "farm_identifier"
    },
    "start_after": "identifier_123",
    "limit": 30
  }
}
KeyTypeDescription

filter_by

Option<FarmsBy>

An optional parameter specifying what to filter farms by. Can be either the farm identifier, lp denom or the farm asset.

start_after

Option<String>

An optional parameter specifying what farm (identifier) to start searching after.

limit

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.

{
  "positions": {
    "address": "mantra1...",
    "open_state": true
  }
}
KeyTypeDescription

address

String

The address to get positions for.

open_state

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.

{
  "rewards": {
    "address": "mantra1..."
  }
}
KeyTypeDescription

address

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.

{
  "lp_weight": {
    "address": "mantra1...",
    "denom": "uom",
    "epoch_id": 50
  }
}
KeyTypeDescription

address

String

The address to get the LP weight for.

denom

String

The denom to get the total LP weight for.

epoch_id

u64

The epoch id to get the LP weight for.

Ownership

Returns the ownership of the contract.

Note: This is a cw_ownable query.

{
  "ownership": {}
}

MigrateMsg

Message to migrate the contract to a new code ID.

{}

Last updated