Farming
TokenFarm is designed to implement a smart contract-based farming distribution incentive system. Its primary functions include user staking and unstaking of Yield Tokens, and accessing system rewards.
Contract Functions:
Users can stake
Yield Token
(YT, ERC20 Token) acquired by providing liquidity to the fund pool to participate in mining and receive rewards.Users can deposit multiple mining orders, with each staking order calculating earnings separately. Different terms will have different earnings multipliers.
After each day, earnings continue to accrue for each order until the staked assets are withdrawn voluntarily.
Earnings are divided into two parts: the first part comes from block rewards, and the second part comes from earnings generated by the Position Token business.
Main Variables:
TOKEN:
Address of the token used for earnings distribution.lpToken:
Contract address of the staked asset.tokenPerBlock:
Number of tokens produced per block on average.proTotal:
Total number of product orders.startBlock:
Block at which mining begins.lastRewardBlock:
Last updated block for rewards.accTokenPerShare:
Accumulated token rewards per share.accDividendPerShare:
Accumulated dividend rewards per share.totalPower:
Total computing power.totalDividend:
Accumulated dividend funds.
Structures:
Product:
Defines the product cycle duration and multiplier.Order:
Defines the ID, participation amount, computing power, and order time.UserInfo:
Defines the user's computing power, staked amount, earned rewards, pending rewards, reward debts, and other information.
Events:
Deposit:
Triggered when a user stakes their assets.Withdraw:
Triggered when a user withdraws their earnings.
Functions:
initialize()
:
Initialize the basic parameters of the contract.pendingToken()
:
Check the pending rewards waiting to be claimed by the user.updatePool()
:
Update the total hash power and the accumulated rewards per share.deposit()
:
Allow users to deposit staked tokens for mining participation.withdraw()
:
Allow users to withdraw their earnings.harvestOrderPrincipal()
:
Allow users to withdraw their principal amount.getAllProducts()
:
Retrieve information about all products within the system.argInfo()
:
Retrieve aggregated information such as a user's staked assets, authorized limits, pending rewards, etc.getUserOrders()
:
Retrieve a user's current and historical orders.intoFee()
:
Increase Farming2 dividend rewards for the "market contract" address.getToken()
:
Return the address of the reward token.getLpToken()
:
Return the address of the LP token used for mining.getUserInfo()
:
Return detailed information about a specified user.getMultiplier()
:
Calculate the reward multiplier based on the block range.
Security and Upgradability:
Use the
SafeMath library
to prevent integer overflow.Use the
SafeERC20 library
to safely handle ERC20 tokens.
Contract behaviors
Hashrate
Hashrate is a critical concept that determines a user's share in mining activities and the amount of rewards they qualify to receive. Hashrate can be understood as a weighted representation of the assets a user stakes, influencing the distribution ratio of rewards. In the contract logic, each order receives a separate hashrate value based on the periodic hashrate multiplier. The sum of hashrates from all orders represents the user's effective hashrate.
Concept of Hashrate:
Hashrate represents the contribution of a user to a mining pool.
The more assets a user stakes, the higher their hashrate, which corresponds to a larger share of rewards they can receive.
Hashrate is also associated with specific multipliers, meaning different products and deposit options offer varying hashrate multipliers.
Hashrate decreases only when a user releases their staked orders.
The role of hashrate in business logic:
Reward Calculation: Hashrate is used to calculate the amount of rewards a user should receive. The contract allocates rewards based on the proportion of a user's hashrate to the total hashrate.
Updating Total Hashrate:
Reward Debt Management: Each user's UserInfo structure includes a rewardDebt field, which tracks the rewards accumulated by the user since the last update. Hashrate plays a crucial role in calculating this rewardDebt for the user.
Here's the translation for the concepts you provided:
Order Hashrate: In the Order structure, the power field represents the hashrate of each order.
User Hashrate:
In the UserInfo structure, the power field represents the current total hashrate of the user.Reward Debt Update:
In the deposit and _withdraw functions, the user's rewardDebt is updated based on their hashrate and the accumulated rewards per share (accTokenPerShare).Reward Distribution:
In the pendingToken function, the user's pending rewards are calculated based on their hashrate and the accumulated rewards per share.Total Hashrate Maintenance:
In the deposit and harvestOrderPrincipal functions, the total hashrate (totalPower) increases or decreases accordingly as users deposit or withdraw assets.
User Deposit Process
When a user initiates a deposit operation in the smart contract, the following sequence of events occurs:
Calling the deposit function: The user begins the deposit process by calling the deposit function and providing the
product ID (_pid)
and theamount (_amount)
they wish to stake.Updating pool rewards: The contract first calls the updatePool function to ensure all reward variables are up to date, which affects the calculation of user rewards.
Checking user's existing balance: If the user had a previous balance, the contract calculates and transfers their pending rewards to their earnings balance.
Transferring user's staked tokens: The user's YT tokens are transferred from their account to the contract's address. This is achieved by calling
IERC20(lpToken)
.safeTransferFrom.Creating a new order: A new order is created for the user, including the product ID, staked amount, calculated hashrate, and the current block timestamp.
Updating user's hashrate and reward debt: The user's total hashrate is updated based on the hashrate of the new order, and their reward
debt (rewardDebt)
is updated. This debt is the product of the user's hashrate and the accumulated rewards per share.Updating total hashrate and user's stake amount: The contract updates the total hashrate
(totalPower)
and the user's stakeamount (amount)
based on the hashrate and staked amount of the new order.Triggering Deposit event: Finally, the contract triggers a Deposit event, recording the user's address, product ID, and staked amount for on-chain tracking.
Profit Calculation
In the TokenFarm smart contract, calculating a user's profits involves several key steps and variables. Here is the detailed process of calculating user profits:
Basis of Profit Calculation
Accumulated Token
Per Share (accTokenPerShare)
: This is a dynamically changing value that represents the accumulated token rewards per share (or per unit of LP tokens staked) since the start of mining. This value is updated with each new block and is linked to the total hashrate (totalPower).User Hashrate (power)
The user's hashrate is calculated based on the amount of LP tokens they stake and the hashrate multiplier associated with the specific product. Hashrate determines the basis for calculating rewards for the user.
User Reward Debt (rewardDebt)
Each user's UserInfo structure includes a rewardDebt field, which records the user's accrued reward debt since the last update. This value is the product of the user's staked amount and accTokenPerShare.
rewardDebt
rewardDebt is influenced by the user's staked hashrate and the duration of staking. When users stake funds in the Farming pool, their account records a rewardDebt. This value increases over time as rewards accumulate. When users remove funds from the pool or claim earnings, their rewardDebt is calculated against the current amount of reward tokens per liquidity unit in the pool to determine the amount of rewards they should receive.
Specifically, rewardDebt is a snapshot value recorded when the user stakes, representing the portion of reward tokens each liquidity token should receive. When users wish to withdraw their rewards, the system calculates the difference between the current reward token amount per hashrate share (tracked by accTokenPerShare) and the user's
rewardDebt
. This difference is the user's unclaimedreward
.Calculating Pending Rewards (pendingToken)
The user's pending rewards are calculated using the pendingToken function, which considers the user's hashrate and the changes in accTokenPerShare since the last update.
Updating and Claiming Rewards
When users
deposit (deposit)
or claimrewards (_withdraw)
, the contract calls the updatePool function to update accTokenPerShare and lastRewardBlock.The user's rewardDebt is recalculated based on their current hashrate and the updated accTokenPerShare.
Specific Calculation Steps
Updating Pool Rewards:
multiplier = getMultiplier(lastRewardBlock, block.number); tokenReward = multiplier × tokenPerBlock; accTokenPerShare = accTokenPerShare + (tokenReward × 1e12 / totalPower)
;Calculating User Pending Rewards:
pending = (user.power × accTokenPerShare / 1e12) - user.rewardDebt
;Updating User Reward Debt:
user.rewardDebt = user.power × accTokenPerShare / 1e12
;Claiming Rewards: Users claim rewards by calling the withdraw function, which triggers the _withdraw internal function. Their earnings balance increases by their pending
rewards (pending).
Their rewardDebt is updated to reflect the new reward status.
Example of Hashrate and Profit Calculation in the Contract
When the smart contract needs to calculate the reward generated per block, it updates accTokenPerShare
using the following formula::
Where:
tokenReward
is the token reward generated per block.totalPower
is the total hashrate of LP tokens staked by all users.1e12
is a precision factor used to maintain accuracy in calculations. It ensures that there is enough precision even when large amounts of tokens are distributed, and prevents exceeding the maximum value of theuint256
type.
At any point in time, the pending rewards for a user can be calculated as follows:
Where:
userPower
is the current hashrate of the user.accTokenPerShare
is the accumulated reward per share.userRewardDebt
is the reward debt recorded for the user at the last reward claim.
Interface
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.12;
pragma experimental ABIEncoderV2;
interface TokenFarm {
// Product information structure
struct Product {
uint256 cycle; // Cycle timestamp
uint256 multiplier; // Multiplier, 1 represents 1/1000
}
// Order information structure
struct Order {
uint256 id; // Order ID
uint256 amount; // Participation amount
uint256 power; // Order power
uint256 orderTime; // Order time
}
// User information structure
struct UserInfo {
uint256 power; // User power
uint256 amount; // User staked amount
uint256 received; // User rewards received
uint256 earnings; // User pending rewards
uint256 rewardDebt; // User cumulative reward withdrawals
uint256 rewardDividendDebt; // User cumulative dividend reward withdrawals
Order[] orders; // User's current orders
Order[] history; // User's historical orders
}
// Deposit event
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
// Withdraw event
event Withdraw(address indexed user, uint256 poolId, uint256 amount);
// Initialization function
function initialize(
address _token,
address _lpToken,
uint256 _tokenPerBlock,
uint256 _startBlock
) external;
// Get reward token address
function getToken() external view returns (address);
// Get mining token address
function getLpToken() external view returns (address);
// Get reward start block
function getStartBlock() external view returns (uint256);
// Last reward update block
function lastRewardBlock() external view returns (uint256);
// Accumulated token per share
function accTokenPerShare() external view returns (uint256);
// Accumulated dividend per share
function accDividendPerShare() external view returns (uint256);
// Last dividend reward update block
function lastDividendRewardBlock() external view returns (uint256);
// Total power
function totalPower() external view returns (uint256);
// Total accumulated dividend funds
function totalDividend() external view returns (uint256);
// Token amount per block
function getTokenPerBlock() external view returns (uint256);
// Get user information
function getUserInfo(address _user) external view returns (UserInfo memory);
// Get weighted reward multiplier
function getMultiplier(
uint256 _from,
uint256 _to
) external pure returns (uint256);
// View pending token rewards
// 0: Pool 1, 1: Pool 2
function pendingToken(
address _user
) external view returns (uint256, uint256);
// Update pool rewards variables, update power
function updatePool() external;
/**
* @dev Deposit LP tokens into the pool to earn rewards
* @param _pid Product ID
* @param _amount Amount of YT to participate
*/
function deposit(uint256 _pid, uint256 _amount) external;
/**
* @dev User claims their rewards
* @param _poolId Pool ID, 1 represents Pool 1, 2 represents Pool 2
*/
function withdraw(uint256 _poolId) external;
/**
* @dev User claims their principal
* @param _orderId Specific order ID, after claiming, the order is moved to the history
*/
function harvestOrderPrincipal(uint256 _orderId) external;
// Set the number of rewards per block
function setTokenPerBlock(uint256 _tokenPerBlock) external;
// Pause the contract
function pause() external;
// Resume the contract
function unpause() external;
// Add a new product, only callable by admin
function addPro(uint256 _id, uint256 _cycle, uint256 _multiplier) external;
// Get all products in the system
function getAllProducts() external view returns (Product[] memory _products);
/**
* @dev Get aggregate information
* @param _user User address
* @return _balance User's LP assets
* @return _allowance User's authorized amount for the farm contract
* @return _pending1 Farm 1 earnings
* @return _pending2 Farm 2 earnings (correcting the original comment)
* @return _userPower User's power
* @return _userAmount User's staked amount
* @return _totalPower Total pool power
* @return _totalAmount Total pool staked amount
*/
function argInfo(
address _user
) external view returns (
uint256 _balance,
uint256 _allowance,
uint256 _pending1,
uint256 _pending2, // Correcting the original comment error from _penfing2 to _pending2
uint256 _userPower,
uint256 _userAmount,
uint256 _totalPower,
uint256 _totalAmount
);
/**
* @dev Get user's order information
* @param _user User address
* @return _orders User's current orders
* @return _history User's historical orders
*/
function getUserOrders(
address _user
) external view returns (Order[] memory _orders, Order[] memory _history);
/**
* @dev Admin address, airdrop dividend rewards or external reward injections, directly distributed
* This income is distributed in real-time
*/
function intoFee(uint256 _amount) external;
}
Last updated