文件 1 的 1:LPMiningV1.sol
pragma solidity ^0.6.12;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
interface IAward {
function addFreeAward(address _user, uint256 _amount) external;
function addAward(address _user, uint256 _amount) external;
function withdraw(uint256 _amount) external;
function destroy(uint256 amount) external;
}
interface IERC20Token is IERC20 {
function maxSupply() external view returns (uint256);
function issue(address account, uint256 amount) external returns (bool);
function burn(uint256 amount) external returns (bool);
}
interface ILPMining {
function add(address pool, uint256 index, uint256 allocP) external;
function set(uint256 pid, uint256 allocPoint) external;
function updateReferenceToken(uint256 pid, uint256 rIndex) external;
function batchSharePools() external;
function onTransferLiquidity(address from, address to, uint256 lpAmount) external;
function claimUserShares(uint pid, address user) external;
function claimLiquidityShares(address user, address[] calldata tokens, uint256[] calldata balances, uint256[] calldata weights, uint256 amount, bool _add) external;
}
interface IOracle {
function requestTokenPrice(address token) external returns(uint8 decimal, uint256 price);
}
interface IPool is IERC20 {
function getCurrentTokens() external view returns (address[] memory);
function getNormalizedWeight(address token) external view returns (uint);
function getBalance(address token) external view returns (uint);
}
contract LPMiningV1 is ILPMining, Ownable {
using SafeMath for uint256;
struct ShareInfo {
uint256 tvl;
uint256 accPoolPerShare;
uint256 lastRewardBlock;
}
struct PoolInfo {
IPool mPool;
uint256 poolIndex;
uint256 referIndex;
uint256 allocPoint;
uint256 lastTvl;
uint256 accTokenPerShare;
uint256 rewardDebt;
address[] tokens;
uint256[] balances;
uint256[] weights;
}
struct UserInfo {
uint256 rewardDebt;
}
address[] public pools;
mapping(address => PoolInfo) public poolInfo;
mapping(uint256 => mapping(address => UserInfo)) public userInfo;
ShareInfo public shareInfo;
mapping(address => bool) private governors;
modifier onlyGovernor{
require(governors[_msgSender()], "LPMiningV1: caller is not the governor");
_;
}
IOracle public priceOracle;
uint256 immutable public startBlock;
uint256 immutable public endTripleBlock;
uint256 public endBlock;
uint256 public tokenPerBlock = 15 * 10 ** 17;
IAward public award;
event Initialization(address award, uint256 _tokenPerBlock, uint256 startBlock, uint256 endTripleBlock, uint256 endBlock, address oracle);
event Add(address mPool, uint256 index, uint256 allocP);
event Set(uint256 pid, uint256 allocPoint);
event ReferCurrencyChanged(address pool, uint256 oldIndex, uint256 newIndex);
event ClaimLiquidityShares(address pool, address user, uint256 rewards);
event ClaimUserShares(uint pid, address user, uint256 rewards);
event OnTransferLiquidity(address from, address to, uint256 lpAmount, uint256 fromAwards, uint256 toAwards);
constructor(
address _award,
uint256 _tokenPerBlock,
uint256 _startBlock,
uint256 _endTripleBlock,
uint256 _endBlock,
address _oracle
) public {
require(_award != address(0), "LPMiningV1: award invalid");
require(_startBlock < _endTripleBlock && _endTripleBlock < _endBlock, "LPMiningV1: blocks range invalid");
require(_oracle != address(0), "LPMiningV1: oracle invalid");
award = IAward(_award);
tokenPerBlock = _tokenPerBlock;
endTripleBlock = _endTripleBlock;
governors[_msgSender()] = true;
startBlock = _startBlock;
endBlock = _endBlock;
shareInfo.lastRewardBlock = block.number > _startBlock ? block.number : _startBlock;
priceOracle = IOracle(_oracle);
emit Initialization(_award, _tokenPerBlock, _startBlock, _endTripleBlock, _endBlock, _oracle);
}
function poolIn(address pool) view public returns (bool){
if (address(poolInfo[pool].mPool) == address(0)) {
return false;
}
return true;
}
function indexOfPool(address pool) view public returns (uint256){
if (poolIn(pool)) {
return poolInfo[pool].poolIndex;
}
return uint256(- 1);
}
function setEndBlock(uint256 _endBlock) onlyOwner external{
endBlock = _endBlock;
}
function add(address pool, uint256 index, uint256 allocP) override onlyGovernor external {
require(!poolIn(pool), "LPMiningV1: pool duplicate");
require(pools.length.add(1) < uint256(- 1), "LPMiningV1: pools list overflow");
IPool pPool = IPool(pool);
require(index < pPool.getCurrentTokens().length, "LPMiningV1: reference token not exist");
address[] memory tokens = pPool.getCurrentTokens();
uint256[] memory _balances = new uint256[](tokens.length);
uint256[] memory _weights = new uint256[](tokens.length);
for (uint i = 0; i < tokens.length; i++) {
_balances[i] = pPool.getBalance(tokens[i]);
_weights[i] = pPool.getNormalizedWeight(tokens[i]);
}
poolInfo[pool] = PoolInfo({
mPool : pPool,
poolIndex : pools.length,
referIndex : index,
lastTvl : 0,
allocPoint : allocP,
accTokenPerShare : 0,
rewardDebt : 0,
tokens : tokens,
balances : _balances,
weights : _weights
});
pools.push(pool);
updateRewards();
sharePoolRewards(poolInfo[pool]);
emit Add(pool, index, allocP);
}
function set(uint256 pid, uint256 allocPoint) override onlyGovernor external {
require(pid < pools.length, "LPMiningV1: pool not exist");
PoolInfo storage info = poolInfo[pools[pid]];
poolInfo[pools[pid]].allocPoint = allocPoint;
updateRewards();
sharePoolRewards(info);
emit Set(pid, allocPoint);
}
function addGovernor(address governor) onlyOwner external {
governors[governor] = true;
}
function removeGovernor(address governor) onlyOwner external {
governors[governor] = false;
}
function updateOracle(address oracle) onlyGovernor external {
require(oracle != address(0), "LPMiningV1: oracle invalid");
priceOracle = IOracle(oracle);
}
function batchSharePools() override external {
updateRewards();
for (uint i = 0; i < pools.length; i = i.add(1)) {
sharePoolRewards(poolInfo[pools[i]]);
}
}
function updateAward(address _award) external onlyOwner {
require(_award != address(0), "LPMiningV1: award invalid");
award = IAward(_award);
}
function updateReferenceToken(uint256 pid, uint256 rIndex) override onlyGovernor external {
require(pid < pools.length, "LPMiningV1: pool not exist");
address pool = pools[pid];
require(rIndex < IPool(pool).getCurrentTokens().length, "LPMiningV1: reference token not exist");
PoolInfo storage info = poolInfo[pool];
uint256 old = info.referIndex;
info.referIndex = rIndex;
updateRewards();
sharePoolRewards(info);
emit ReferCurrencyChanged(pool, old, rIndex);
}
function claimUserShares(uint pid, address user) override external {
require(pid < pools.length, "LPMiningV1: pool not exist");
uint256 rewards = calUserRewards(pid, user, 0, true);
if (rewards > 0) {
award.addAward(user, rewards);
}
emit ClaimUserShares(pid, user, rewards);
}
function claimLiquidityShares(address user, address[] calldata tokens, uint256[] calldata balances, uint256[] calldata weights, uint256 amount, bool _add) override external {
uint256 pid = indexOfPool(msg.sender);
if (pid != uint256(- 1)) {
PoolInfo storage pool = poolInfo[msg.sender];
pool.tokens = tokens;
pool.balances = balances;
pool.weights = weights;
uint256 rewards = calUserRewards(pid, user, amount, _add);
if (rewards > 0) {
award.addAward(user, rewards);
}
emit ClaimLiquidityShares(msg.sender, user, rewards);
}
}
function pendingShares(uint256 pid, address user) public view returns (uint256) {
PoolInfo memory info = poolInfo[pools[pid]];
UserInfo memory uinfo = userInfo[pid][user];
if (info.mPool.totalSupply() == 0 || shareInfo.tvl == 0 || shareInfo.lastRewardBlock >= block.number) {
return 0;
}
uint256 accPoolPerShare = shareInfo.accPoolPerShare;
uint256 multiplier = getMultiplier(shareInfo.lastRewardBlock, block.number);
uint256 rewards = multiplier.mul(tokenPerBlock).mul(1e18).div(shareInfo.tvl);
accPoolPerShare = accPoolPerShare.add(rewards);
uint256 accTokenPerShare = info.accTokenPerShare;
rewards = accPoolPerShare.mul(info.lastTvl).sub(info.rewardDebt).div(info.mPool.totalSupply());
accTokenPerShare = accTokenPerShare.add(rewards);
return accTokenPerShare.mul(info.mPool.balanceOf(user)).sub(uinfo.rewardDebt).div(1e18);
}
function onTransferLiquidity(address from, address to, uint256 lpAmount) override external {
uint256 pid = indexOfPool(msg.sender);
if (pid != uint256(- 1)) {
uint256 fromAwards = calUserRewards(pid, from, lpAmount, false);
uint256 toAwards = calUserRewards(pid, to, lpAmount, true);
if (fromAwards > 0) {
if (Address.isContract(from)) {
award.destroy(fromAwards);
} else {
award.addAward(from, fromAwards);
}
}
if (toAwards > 0) {
if (Address.isContract(to)) {
award.destroy(toAwards);
} else {
award.addAward(to, toAwards);
}
}
emit OnTransferLiquidity(from, to, lpAmount, fromAwards, toAwards);
}
}
function updateRewards() private {
if (shareInfo.tvl > 0 && block.number > shareInfo.lastRewardBlock) {
uint256 multiplier = getMultiplier(shareInfo.lastRewardBlock, block.number);
uint256 rewards = multiplier.mul(tokenPerBlock).mul(1e18).div(shareInfo.tvl);
shareInfo.accPoolPerShare = shareInfo.accPoolPerShare.add(rewards);
}
shareInfo.lastRewardBlock = block.number > startBlock ? block.number : startBlock;
}
function sharePoolRewards(PoolInfo storage info) private {
_sharePoolRewards(info, 0, true);
}
function _sharePoolRewards(PoolInfo storage info, uint256 lpAmount, bool _add) private {
uint newTotalLiquidity = info.mPool.totalSupply();
uint lastTotalLiquidity = _add ? newTotalLiquidity.sub(lpAmount) : newTotalLiquidity.add(lpAmount);
if (lastTotalLiquidity > 0) {
uint256 rewards = shareInfo.accPoolPerShare.mul(info.lastTvl).sub(info.rewardDebt).div(lastTotalLiquidity);
info.accTokenPerShare = info.accTokenPerShare.add(rewards);
}
uint256 newTvl = getPoolTvl(info);
info.rewardDebt = shareInfo.accPoolPerShare.mul(newTvl);
shareInfo.tvl = shareInfo.tvl.add(newTvl).sub(info.lastTvl);
info.lastTvl = newTvl;
}
function calUserRewards(uint256 pid, address user, uint256 lpAmount, bool _add) private returns (uint256){
updateRewards();
PoolInfo storage info = poolInfo[pools[pid]];
_sharePoolRewards(info, lpAmount, _add);
UserInfo storage user_info = userInfo[pid][user];
uint256 newAmount = info.mPool.balanceOf(user);
uint256 lastAmount = _add ? newAmount.sub(lpAmount) : newAmount.add(lpAmount);
uint256 shares = info.accTokenPerShare.mul(lastAmount).sub(user_info.rewardDebt).div(1e18);
user_info.rewardDebt = newAmount.mul(info.accTokenPerShare);
return shares;
}
function getPoolTvl(PoolInfo memory info) private returns (uint256){
address baseToken = info.tokens[info.referIndex];
uint256 balance = info.balances[info.referIndex];
uint256 nw = info.weights[info.referIndex];
uint256 totalBalance = bdiv(balance, nw);
(uint8 decimal, uint256 price) = priceOracle.requestTokenPrice(baseToken);
require(price > 0, "LPMiningV1: token price invalid");
uint256 divisor = 10 ** uint256(decimal);
return totalBalance.mul(price).mul(info.allocPoint).div(divisor);
}
function getMultiplier(uint256 _from, uint256 _to) internal view returns (uint256) {
if (_to <= endBlock) {
if (_to <= endTripleBlock) {
return _to.sub(_from).mul(3);
} else if (_from < endTripleBlock) {
return endTripleBlock.sub(_from).mul(3).add(_to.sub(endTripleBlock));
}
return _to.sub(_from);
} else if (_from >= endBlock) {
return 0;
} else {
if (_from < endTripleBlock) {
return endTripleBlock.sub(_from).mul(3).add(endBlock.sub(endTripleBlock));
}
return endBlock.sub(_from);
}
}
function bdiv(uint a, uint b) internal pure returns (uint){
require(b != 0, "ERR_DIV_ZERO");
uint c0 = a * 1e18;
require(a == 0 || c0 / a == 1e18, "ERR_DIV_INTERNAL");
uint c1 = c0 + (b / 2);
require(c1 >= c0, "ERR_DIV_INTERNAL");
uint c2 = c1 / b;
return c2;
}
}