Skip to main content
Deno 2 is finally here 🎉️
Learn more

Farming Lenfi

Insert intro…

State Diagram

stateDiagram
    direction LR

    state "Idle" as idle
    state "Depositing" as depositing
    state "Locked" as locked
    state "Withdrawing" as withdrawing

    [*] --> idle
    [*] --> depositing
    idle --> depositing
    depositing --> locked
    locked --> withdrawing
    withdrawing --> [*]

Genesis

    flowchart LR

        %% Vertices
        creator_utxo["Farm \n Creator \n UTxO"]
        validator["Farm \n Multi-validator \n (Mint)"]
        farm_utxo["Farm UTxO"]
        creation_tx[["Creation TX"]]
        
        %% Links
        creator_utxo --> creation_tx
        validator --> creation_tx
        creation_tx --> farm_utxo

Idle

Despositing

    flowchart LR

        %% Vertices
        deposit_utxo_in["Depositor \n UTxO"]
        deposit_utxo_out[Depositor \n LP Tokens \n UTxO]
        validator["Farm \n Multi-validator \n (Mint)"]
        farm_utxo_in["Farm UTxO"]
        farm_utxo_out["Farm UTxO"]
        deposit_tx[["Depositing TX"]]
        
        %% Links
        deposit_utxo_in --> deposit_tx
        farm_utxo_in --> deposit_tx
        validator --> deposit_tx
        deposit_tx --> deposit_utxo_out
        deposit_tx --> farm_utxo_out

Locked

Withdrawing

    flowchart LR

        %% Vertices
        deposit_utxo["Depositor \n UTxO"]
        validator["Farm \n Multi-validator \n (Mint)"]
        farm_utxo_in["Farm UTxO"]
        farm_utxo_out["Farm UTxO"]
        change_utxo[Reward \n UTxO]
        withdrawing_tx[["Withdrawing TX"]]
        
        %% Links
        deposit_utxo --> withdrawing_tx
        farm_utxo_in --> withdrawing_tx
        validator --> withdrawing_tx
        withdrawing_tx --> change_utxo
        withdrawing_tx --> farm_utxo_out

Further Details

Farm rewards are the same as the asset locked by the pool of the FARM token. This can be validated in the minting transaction by referencing the Lenfi pool UTxO.

Our price algorithm calculates proportionally FARM minted such that the min value is gained per FARM, max value is capped (so leftover can be collected), and the earlier you come, the better. You get less FARM as more tokens are deposited, and all FARM are equal in the withdrawal stage i.e you mint more FARM if you deposit sooner than others.

Farm_pool, farming token, share a policy (and similar token_name), but farm_pool begins with 0, FARM with 1. Therefore, locking an FARM to create fake farm_pool is never valid (must begin with 0, FARM begin with 1).

Timing conditions are obvious- genesis, idle, depositing, locked and withdrawing are all mutually exclusive periods. Genesis does not need it’s own timing as it is a mint event.

Off-chain will abstractly manage each of these transaction types: genesis
depositing
withdrawing
and each of these queries (likely using blockfrost) with json response:
list of farms
farm details

No serving backend is required. No frontend integration is required. Off-chain should be a deno library, in clean typescript, using lucid, so that it can easily be used for both those purposes.

Testing: we will write simple tests

Farm Orders

Instead of sending funds directly to the farm script where the transaction has an increased chance failing due to a race condition caused by the pricing algorithm, the user can send funds to an order script which temporarily locks funds while a batcher submits a transaction on the user’s behalf. The idea is that if the batcher’s transaction fails, they create a new transaction later in the future in an attempt to transfer user funds from the order script to the farm script.

A transaction sending funds to the order script does not have to be valid for the farm script, meaning an ‘order’ can be created which describes a transaction involving the farm script in the distant future.

A single UTxO is sent to the address that placed the order when the order is fulfilled.

Spending Funds to the Order Script

To send funds to the order script, the datum for the order script must be constructed:

type Order {
    order: OrderType,
    partial_output: PartialOutput,
    farm_id: ByteArray,
    cancelling_credential: Option<CancellationCredential>,
}

OrderType determines the type of order being placed (i.e. depositing, withdrawing, collecting change), PartialOutput specifies the construction of the output to be created by the batcher from the order. The farm_id is the id of the farm that the resulting output of the order script is sent to and the optional cancelling_credential specifies how an order can be cancelled, if at all.

type OrderType {
    Deposit {
        deposit_amount: Int,        // amount being deposited into the farm
        minimum_lp_return: Int,
    }
    Withdraw
    CollectChange
}

type PartialOutput {
  address: Address,
  value: Value,
  datum: FarmDatum,
}

Depositing

A deposit order can be spent to the order script to temporarily lock funds that are to be deposited to the farm script in the future. Depositing is specified by using the Deposit constructor of OrderType in the order field in Order. The deposit off-chain enforces:

  1. The deposit_amount must be less then or equal to the value spent to the order script.
  2. minimum_lp_return must be a positive value.
  3. value must contain only two fields, ADA and the reward token.
  4. The amount of reward token in value must be equal to deposit_amount.
  5. The farm that the order is being placed for (specified by farm_id) exists.

The deposit on-chain enforces:

  1. Order script funds are being spent to another script i.e. the farm script. This is inferred by having a datum.
  2. The script being spent to has a farm_id equal to the farm_id or the order.
  3. The value being spent to the farm script equals deposit_amount.
  4. The amount of LP token minted and spent to the address seen in PartialOutput is greater than or equal to minimum_lp_return.

address in PartialOutput should be the address that will deposit into the farm and value is how much is being spent in the depositing transaction. The difference between the funds initially sent to the order script and value is the fee collected by the batcher for holding the funds and the difference between value and deposit_amount is the fee collected by the batcher for processing the order (i.e. spending to the farm script).

Withdrawing and Collecting Change

If a user wishes to withdraw funds from the farm automatically, they can place a withdraw order. It is possible to place a withdraw order before the withdrawal period of the farm commences. Off-chain it is verified that:

  1. The farm that the order is being placed for (specified by farm_id) exists.

On-chain it is verified that:

  1. The script being spent to has a farm_id equal to the farm_id or the order.

In the PartialOutput, value contains the LP tokens that will be spent to the farm in order to recieve the reward tokens and address is the address in which the reward tokens are sent to.

Cancelling an Order

Current Issues

  1. How to verify whether a farmId exists. (off-chain)
  2. How to verify whether an address provided in an order exists. (off-chain)
  3. How to collect order fees.