# WHEAT Protocol ## Table of Contents 1. [Summary](#Summary) 2. [Architecture](#Architecture) 1. [Distribution Layer](#Distribution-Layer) 2. [Vault System](#Vault-System) 3. [Components](#Components) 1. [Strategy](#Strategy) 2. [Collector](#Collector) 3. [Buyback](#Buyback) 4. [Adapter](#Adapter) 5. [Exchange](#Exchange) 4. [Platforms](#Platforms) 1. [PancakeSwap](#PancakeSwap) 2. [AutoFarm](#AutoFarm) 3. [PantherSwap](#PantherSwap) 5. [Security Notes](#Security-Notes) # Summary [WHEAT](https://bscscan.com/address/0x3ab63309F85df5D4c3351ff8EACb87980E05Da4E) is a reward token similar to CAKE in the sense that it rewards users that stake their funds into a [MasterChef](https://github.com/pancakeswap/pancake-farm/blob/master/contracts/MasterChef.sol)-based contract. Users farm WHEAT in exchange for the liquidity brought into the protocol. In that regard, the main diference from PancakeSwap's is that WHEAT's MasterChef is designed to allow staking of not only LP shares, but also strategy shares. Strategy shares are regular ERC-20 tokens minted whenever users deposit assets into one of WHEAT's strategy contracts, also known as vaults. These are interest-bearing contracts for which shares appreciate over time and profits are compounded. Therefore, by staking funds on WHEAT, users not only harvest the WHEAT token itself, but also see their assets grow while deposited in one of its vaults. The goal of this document is to provide an overview of the WHEAT protocol and its overall organization. # Architecture The WHEAT architecture can be separated into two layers: - **WHEAT Distribution Layer**: basically a MasterChef-based contract for minting and distributing the WHEAT token - **WHEAT Vault System**: a set of contracts that implement, not only interest-bearing strategies, but also a mechanism for using performance fees to buy back WHEAT and burn it; in order to counterpoint the token sell presure ## Distribution Layer The WHEAT distribution layer is a clone of the PancakeSwap [MasterChef](https://github.com/pancakeswap/pancake-farm/blob/master/contracts/MasterChef.sol) system for the issuance and distribution of CAKE. Like PancakeSwap's MasterChef, the [WHEAT's MasterChef](https://bscscan.com/address/0x95fABAe2E9Fb0A269cE307550cAC3093A3cdB448) allows the distribution of a fixed-per-block amount of WHEAT across multiple farming pools. For each pool users can stake funds and harvest WHEAT proportionally to their share and according to the pool multiplier (allocPoint). The most notable difference of the WHEAT MasterChef, when compared with PancakeSwap's, is the use of WHEAT strategy shares for staking. For instance, a given PancakeSwap pool for BNB/BUSD requires staking LP shares into MasterChef to harvest CAKE. The similar case for WHEAT would require users to first deposit their BNB/BUSD LP shares into a WHEAT strategy contract, in exchange for strategy shares; then the strategy shares are staked into WHEAT's MasterChef and from thereon WHEAT can be harvested. ## Vault System The WHEAT Vault System is a set of smart contracts. There are basically 3 types of contracts: strategies (a.k.a. vaults), fee collectors, and buybacks. Users interact with strategies contracts. They deposit assets into the strategy contract and receive shares in exchange for the liquidity provided. Those shares can then be staked into WHEAT's MasterChef to harvest WHEAT. WHEAT strategies are interest-bearing. This means that the issued shares appreciate over time, which allows users to withdraw more funds than initially deposited. From the profits a performance fee is deducted and sent to a fee collector contract. The fee collector contracts receive the performance fee from strategy contracts and use a similar mechanism, as the one used by strategy contracts, to generate profits from it. These profits in turn are sent to a buyback contract. Note that the funds accumulated from the performance fees into fee collectors belong to the protocol treasury but remain allocated to these contracts. The buyback contracts simply receive profits from fee collectors and use these funds to buy and burn WHEAT (and also GRO). The purpose of buybacks is to counterpoint the sell presure of WHEAT. We can now summarize the keys aspects of the WHEAT vault system: - Strategies allow users to invest their capital gaining profits - Part of the profits is deducted as performance fee and is reinvested - Profits from the performance fee reinvestment are used to buy back WHEAT and burn it # Components This session describe the components of the WHEAT vault system. Note that each component is implemented on its own smart contract. ## Strategy The strategy contracts, also known as vaults, allow users to deposit funds and receive their equivalent in shares. These strategy shares are interest-bearing instruments, their price appreciate over time. The appreciation comes from staking the funds into other platforms that provide farming incentives. The strategy contract compounds the profits by selling the farmed assets for more of the invested asset and reinvesting. Here is the basic interface of a strategy contract: ```solidity interface IStrategy is IERC20 { function rewardToken() external view returns (address); function routingToken() external view returns (address); function reserveToken() external view returns (address); function totalReserve() public view returns (uint256); function totalSupply() public view returns (uint256); function calcSharesFromAmount(uint256 _amount) external view returns (uint256); function calcAmountFromShares(uint256 _shares) external view returns (uint256); function pendingReward() external view returns (uint256); function deposit(uint256 _amount, uint256 _minShares) external; function withdraw(uint256 _shares, uint256 _minAmount) external; function gulp(uint256 _minRewardAmount) external; } ``` The `rewardToken` is the token being farmed by the strategy. For instance, a strategy based on PancakeSwap will farm CAKE. The `reserveToken` is the token being invested. It is usually a LP share for PancakeSwap, but can potentially be any ERC-20 asset. The `routingToken` is either the same as the `reserveToken`, for non-LP tokens, or one of the tokens that compose the pair, for LP tokens. For instance, if the `reserveToken` is BNB/BUSD, the routing token could be either BNB or BUSD. The `routingToken` is the token to which the `rewardToken` is converted to before adding liqudity to the LP. This is needed in order to mint more LP shares for strategy reinvestment. The `totalReserve` and `totalSupply` is the total amount of `reserveToken` deposited into the contract and the total supply of shares minted for the contract, respectively. The ratio between `totalReserve` and `totalSupply` gives the strategy share price. The `calcSharesFromAmount` and `calcAmountFromShares` provides a way to estimate how many shares are received for a deposit or, conversely, how much of the `reserveToken` will be paid for a withdrawal. These estimations can be used to compute the second parameter of `deposit` and `withdraw`, which controls slippage. The `gulp` function performs compounding of profits. It will harvest the accumulated `rewardToken` and convert it into more of the `reserveToken`. A slippage parameter is available for gulp, that value can be estimated using the `pendingReward` function. Regarding fees, `deposit` and `withdraw` are possibly subject to deposit and withdrawal fees. On the other hand, `gulp` is subject to a performance fee (amounted in terms of the `rewardToken`) which is sent to the associated fee collector. ## Collector The fee collector contract receives the performance fees from one or many strategy contracts and stakes these funds, similarly to strategies, in order to farm incentives. The fee collector profits from staking funds are sent to a buyback contract. ```solidity interface ICollector { function rewardToken() external view returns (address); function routingToken() external view returns (address); function reserveToken() external view returns (address); function pendingDeposit() external view returns (uint256); function gulp(uint256 _minDepositAmount) external; // priviledged functions function announceMigration(address _migrationRecipient) external; function cancelMigration() external; function migrate(address _migrationRecipient, bool _emergency) external; } ``` The `reserveToken` of a fee collector is the asset used to stake into the underlying platform in order to farm the `rewardToken`. The `routingToken` is either the same as the `reserveToken`, for non-LP tokens, or one of the tokens that compose the pair, for LP tokens. Similar to strategy contracts, fee collector contracts provide a `gulp` function that stakes any `reserveToken` funds received by the contract, and harvest the `rewardToken`, which gets transfered to the associated buyback contract. The `pendingDeposit` function can be used to estimate the slippage parameter of `gulp`. The funds staked by a fee collector belong to the WHEAT treasury. These funds can be migrated upon need. For this reason, the fee collector contract provides 3 priviledged functions to implement the migration procedure. The `announceMigration` function annouces the migration which has a wait perior of 24 hours. Once 24 hours are completed, the `migrate` function can be called to move the fee collector funds to a new address, which is available for another 24 hours. In order to cancel the procedure, `cancelMigration` can be called. ## Buyback The buyback contract receives assets from other contracts and uses the accumulated balance to buy two other assets (notably WHEAT and GRO) and burn them. ```solidity interface IBuyback { function rewardToken() external view returns (address); function buybackToken1() external view returns (address); function buybackToken2() external view returns (address); function pendingBurning() external view returns (uint256, uint256); function gulp(uint256 _minBurning1, uint256 _minBurning2) external; } ``` The `rewardToken` is the asset that gets deposited (i.e. transfered) into the buyback contract from other contracts. The `buybackToken1` and `buybackToken2` are the two assets that are bought back from the `rewardToken` balance, and burned. Similar to strategy and collector contracts, buyback contracts provide a `gulp` function that converts the `rewardToken` into `buybackToken1` and `buybackToken2` and then burn them. The `pendingBurning` function can be used to estimate the slippage parameters of `gulp`. ## Adapter Adapter contracts are glue contracts that allow interoperability between contracts that operate with direferent reserve/reward tokens. The adapter basically receives a given asset, converts it to another asset, and then send the funds to another contract. There are two type of adapter contracts: collector adapters and buyback adapters. As an example, we could couple an AUTO-based strategy contract with a CAKE-based collector by using a AUTO-to-CAKE collector adapter. ```solidity interface IAdapter { function sourceToken() external view returns (address); function targetToken() external view returns (address); function pendingTarget() external view returns (uint256); function gulp(uint256 _minTotalTarget) external; } ``` The `sourceToken` is the token to be received by the adapter contract. The `targetToken` is the token to be converted to by the adapter contract. The `gulp` functions converts the `sourceToken` balance of the contract into the `targetToken` and sends it to the underlying contract. The `pendingTarget` estimates the amount of the `targetToken` pending after conversion and is used as slippage parameter for `gulp`. ## Exchange The exchange contract is a helper contract that provides a exchange abstraction. It is used by all other contracts of the system which delegate to it most of the work in asset conversion. Note that the exchange contract provides token to token conversions via a decentralized exchange (concretely via an Uniswap V2 compatible router). It also provides token to LP shares conversions, which actually happens by providing liquidity to the LP using a single asset. ```solidity interface IExchange { function calcConversionFromInput(address _from, address _to, uint256 _inputAmount) external view returns (uint256 _outputAmount); function calcJoinPoolFromInput(address _pool, address _token, uint256 _inputAmount) external view returns (uint256 _outputShares); function convertFundsFromInput(address _from, address _to, uint256 _inputAmount, uint256 _minOutputAmount) external returns (uint256 _outputAmount); function joinPoolFromInput(address _pool, address _token, uint256 _inputAmount, uint256 _minOutputShares) external returns (uint256 _outputShares); } ``` # Platforms As of writing, WHEAT support strategies for 3 platforms: PancakeSwap, AutoFarm, and PantherSwap. ## PancakeSwap The PancakeSwap strategy contract receives assets from users and deposit them directly into the [PancakeSwap's MasterChef](https://bscscan.com/address/0x73feaa1eE314F8c655E354234017bE2193C9E24E) contract. From time to time it performs the harvest, sells accumulated CAKE for more of the underlying asset, and deposits it back into PancakeSwap MasterChef. The PancakeSwap strategy contract supports 2 types of reserve assets for which CAKE is converted into when harvested, in order to implement the compounding: - CAKE itself: this is the simple case where the reserve asset is CAKE and there is no conversion work. This is for PID 0. - Uniswap V2 compatible LP shares: in this case one of the tokens of the LP pair must be the routing token. Therefore the harvest CAKE is converted to the routing token, which in turn is used to add liquidity to the associated Uniswap V2 LP using a single asset technique. This is for PID > 0. ```solidity interface MasterChef { function pendingCake(uint256 _pid, address _user) external view returns (uint256); function poolInfo(uint256 _pid) external view returns (address _lpToken, uint256 _allocPoint, uint256 _lastRewardBlock, uint256 _accCakePerShare); function userInfo(uint256 _pid, address _user) external view returns (uint256 _amount, uint256 _rewardDebt); function deposit(uint256 _pid, uint256 _amount) external; function enterStaking(uint256 _amount) external; function leaveStaking(uint256 _amount) external; function withdraw(uint256 _pid, uint256 _amount) external; } ``` ## AutoFarm The AutoFarm compounding strategy is very similar to the PancakeSwap compounding strategy. However it is based on the [AutoFarm](https://bscscan.com/address/0x0895196562C7868C5Be92459FaE7f877ED450452) staking contract which is a modified version of PancakeSwap's MasterChef. It rewards with [AUTO](https://bscscan.com/address/0xa184088a740c695E156F91f5cC086a06bb78b827) and has its own characteristics. ```solidity interface AutoFarm { function pendingAUTO(uint256 _pid, address _user) external view returns (uint256); function poolInfo(uint256 _pid) external view returns (address _token, uint256 _allocPoint, uint256 _lastRewardBlock, uint256 _accAutoPerShare, address _strategy); function stakedWantTokens(uint256 _pid, address _user) external view returns (uint256 _amount); function userInfo(uint256 _pid, address _user) external view returns (uint256 _shares, uint256 _rewardDebt); function deposit(uint256 _pid, uint256 _amount) external; function withdraw(uint256 _pid, uint256 _amount) external; } ``` Most notably, AutoFarm has a builtin strategy coupling such that funds do not remain deposited in the contract itself, but are rather forwarded to an AutoFarm strategy associated with a given PID index. As a consumer of the AutoFarm contract interface, the most notable adjustment we need to make into the corresponding WHEAT strategy is to account for deposit/withdrawal fees collected by the AutoFarm underlying strategy. These are available as part of the AutoFarm strategy interface: ```solidity interface AutoFarmStrategy { function entranceFeeFactor() external view returns (uint256); function entranceFeeFactorMax() external view returns (uint256); function withdrawFeeFactor() external view returns (uint256); function withdrawFeeFactorMax() external view returns (uint256); } ``` The deposit/withdrawal fee itself is calculated percentually using the formula: ```1 - (feeFactor / feeFactorMax)``` The AutoFarm strategy contract supports 4 types of reserve assets for which AUTO is converted into when harvested (in order to implement the compounding): - Regular tokens: this is the simple case where the reserve asset is a regular asset for which there is liquidity on the market for the exchange. Therefore AUTO is converted to the reserve asset. - Uniswap V2 compatible LP shares: in this case one of the tokens of the LP pair must be the routing token. Therefore the harvest AUTO is converted to the routing token, which in turn is used to add liquidity to the associated Uniswap V2 LP using a single asset technique. - Belt tokens: there is usually no liquidity for Belt tokens on the exchange. To handle this case, AUTO is converted into the underlying asset of the Belt token (e.g. BUSD for beltBUSD) using the exchange and then funds are deposited into the associated Belt contract, minting the required Belt token. ```solidity interface BeltStrategyToken { function amountToShares(uint256 _amount) external view returns (uint256); function deposit(uint256 _amount, uint256 _minShares) external; } ``` - 4-Belt LP shares: we also support LP shares of the 4-Belt LP. The 4-Belt LP is a Curve clone liquidity pool that is composed of beltDAI, beltUSDC, beltUSDT, and beltBUSD. In this case, AUTO is converted into one of the underlying tokens (i.e. DAI, USDC, and so forth), the funds are then used to mint the corresponding Belt token, and, finally, the Belt token amount is used to provide liquidity to the pool. ```solidity interface BeltStrategyPool { function calc_token_amount(uint256[4] calldata _amounts, bool _deposit) external view returns (uint256); function add_liquidity(uint256[4] calldata _amounts, uint256 _minTokenAmount) external; } ``` ## PantherSwap The PantherSwap strategy is also similar to PancakeSwap's strategy. It is based on farming using the [Panther MasterChef](https://bscscan.com/address/0x058451C62B96c594aD984370eDA8B6FD7197bbd4) for staking. However, the PANTHER token, and its farming, have additional restrictions that must be taken care of: - There is a max amount of PANTHER that can be transfered in a single operation. - There is a transfer tax deducted from the receiver of every PANTHER transfer. - There is a minimum amount of time required between consecutive harvesting for the same user. ```solidity interface IPantherToken is IERC20 { function maxTransferAmount() external view returns (uint256); function transferTaxRate() external view returns (uint16); } ``` In order to deal with the transfer tax and transfer limit, the Panther contracts need to cap transfer and conversion amounts; as well as increase the slippage margins when calling `gulp`, as PantherSwap router estimate functions seem not to account for the transfer tax. Moreover the exchange contract needs to use the [special router function](https://github.com/Uniswap/uniswap-v2-periphery/blob/dda62473e2da448bc9cb8f4514dadda4aeede5f4/contracts/UniswapV2Router02.sol#L339) `swapExactTokensForTokensSupportingFeeOnTransferTokens` for tokens that implement a transfer fee. ```solidity interface PantherMasterChef { function pendingPanther(uint256 _pid, address _user) external view returns (uint256); function canHarvest(uint256 _pid, address _user) external view returns (bool _canHarvest); function poolInfo(uint256 _pid) external view returns (address _lpToken, uint256 _allocPoint, uint256 _lastRewardBlock, uint256 _accPantherPerShare, uint16 _depositFeeBP, uint256 _harvestInterval); function userInfo(uint256 _pid, address _user) external view returns (uint256 _amount, uint256 _rewardDebt, uint256 _rewardLockedUp, uint256 _nextHarvestUntil); function deposit(uint256 _pid, uint256 _amount, address _referrer) external; function withdraw(uint256 _pid, uint256 _amount) external; } ``` # Security Notes In the design of WHEAT contracts we have added pervasive precautions that we believe makes the system more robust: - We have restricted all open/public functions to EOA (External-Only-Accounts) or addresses in a whitelist. This means that only end-user wallets or contracts previously whitelisted can call non-priviledged functions; - We have placed a reentrancy guard to every open/public function available; - We have provided a slippage parameter to all functions that perform token conversions; namely `deposit`, `withdrawal` and `gulp` functions; - We have added a function to recover lost funds for all assets that do not play a role in the normal operation of contracts. This function is priviledged and sends the full balance to the protocol treasury multisig.