// SPDX-License-Identifier: UNLICENSED// Audit on 5-Jan-2021 by Keno and BoringCrypto// P1 - P3: OKpragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;// solhint-disable avoid-low-level-callsimport"./libraries/BoringERC20.sol";
// T1 - T4: OKcontractBaseBoringBatchable{
function_getRevertMsg(bytesmemory _returnData) internalpurereturns (stringmemory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)if (_returnData.length<68) return"Transaction reverted silently";
assembly {
// Slice the sighash.
_returnData :=add(_returnData, 0x04)
}
returnabi.decode(_returnData, (string)); // All that remains is the revert string
}
// F3 - F9: OK// F1: External is ok here because this is the batch function, adding it to a batch makes no sense// F2: Calls in the batch may be payable, delegatecall operates in the same context, so each call in the batch has access to msg.value// C1 - C21: OK// C3: The length of the loop is fully under user control, so can't be exploited// C7: Delegatecall is only used on the same contract, so it's safefunctionbatch(bytes[] calldata calls, bool revertOnFail) externalpayablereturns(bool[] memory successes, bytes[] memory results) {
// Interactions
successes =newbool[](calls.length);
results =newbytes[](calls.length);
for (uint256 i =0; i < calls.length; i++) {
(bool success, bytesmemory result) =address(this).delegatecall(calls[i]);
require(success ||!revertOnFail, _getRevertMsg(result));
successes[i] = success;
results[i] = result;
}
}
}
// T1 - T4: OKcontractBoringBatchableisBaseBoringBatchable{
// F1 - F9: OK// F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)// if part of a batch this could be used to grief once as the second call would not need the permit// C1 - C21: OKfunctionpermitToken(IERC20 token, addressfrom, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public{
// Interactions// X1 - X5
token.permit(from, to, amount, deadline, v, r, s);
}
}
// SPDX-License-Identifier: MITpragmasolidity 0.6.12;// a library for performing overflow-safe math, updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math)libraryBoringMath{
functionadd(uint256 a, uint256 b) internalpurereturns (uint256 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
functionsub(uint256 a, uint256 b) internalpurereturns (uint256 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
functionmul(uint256 a, uint256 b) internalpurereturns (uint256 c) {require(b ==0|| (c = a * b)/b == a, "BoringMath: Mul Overflow");}
functionto128(uint256 a) internalpurereturns (uint128 c) {
require(a <=uint128(-1), "BoringMath: uint128 Overflow");
c =uint128(a);
}
functionto64(uint256 a) internalpurereturns (uint64 c) {
require(a <=uint64(-1), "BoringMath: uint64 Overflow");
c =uint64(a);
}
functionto32(uint256 a) internalpurereturns (uint32 c) {
require(a <=uint32(-1), "BoringMath: uint32 Overflow");
c =uint32(a);
}
}
libraryBoringMath128{
functionadd(uint128 a, uint128 b) internalpurereturns (uint128 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
functionsub(uint128 a, uint128 b) internalpurereturns (uint128 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
libraryBoringMath64{
functionadd(uint64 a, uint64 b) internalpurereturns (uint64 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
functionsub(uint64 a, uint64 b) internalpurereturns (uint64 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
libraryBoringMath32{
functionadd(uint32 a, uint32 b) internalpurereturns (uint32 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
functionsub(uint32 a, uint32 b) internalpurereturns (uint32 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
// SPDX-License-Identifier: MITpragmasolidity 0.6.12;librarySignedSafeMath{
int256constantprivate _INT256_MIN =-2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(int256 a, int256 b) internalpurereturns (int256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) {
return0;
}
require(!(a ==-1&& b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two signed integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(int256 a, int256 b) internalpurereturns (int256) {
require(b !=0, "SignedSafeMath: division by zero");
require(!(b ==-1&& a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(int256 a, int256 b) internalpurereturns (int256) {
int256 c = a - b;
require((b >=0&& c <= a) || (b <0&& c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(int256 a, int256 b) internalpurereturns (int256) {
int256 c = a + b;
require((b >=0&& c >= a) || (b <0&& c < a), "SignedSafeMath: addition overflow");
return c;
}
functiontoUInt256(int256 a) internalpurereturns (uint256) {
require(a >=0, "Integer < 0");
returnuint256(a);
}
}
Contract Source Code
File 7 of 7: ichiFarmV2.sol
// SPDX-License-Identifier: MITpragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;import"@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import"@boringcrypto/boring-solidity/contracts/BoringBatchable.sol";
import"@boringcrypto/boring-solidity/contracts/BoringOwnable.sol";
import"./lib/SignedSafeMath.sol";
contractichiFarmV2isBoringOwnable, BoringBatchable{
usingBoringMathforuint256;
usingBoringMath128foruint128;
usingBoringERC20forIERC20;
usingSignedSafeMathforint256;
/// @notice Info of each IFV2 user./// `amount` LP token amount the user has provided./// `rewardDebt` The amount of ICHI entitled to the user.structUserInfo {
uint256 amount;
int256 rewardDebt;
}
/// @notice Info of each IFV2 pool./// `allocPoint` The amount of allocation points assigned to the pool./// Also known as the amount of ICHI to distribute per block.structPoolInfo {
uint128 accIchiPerShare;
uint64 lastRewardBlock;
uint64 allocPoint;
}
/// @dev Address of ICHI contract.
IERC20 privateimmutable ICHI;
/// @notice Info of each IFV2 pool.
PoolInfo[] public poolInfo;
/// @notice Address of the LP token for each IFV2 pool.
IERC20[] public lpToken;
/// @dev List of all added LP tokens.mapping (address=>bool) private addedLPs;
/// @notice Info of each user that stakes LP tokens.mapping (uint256=>mapping (address=> UserInfo)) public userInfo;
/// @notice Total allocation points. Must be the sum of all allocation points in all pools.uint256public totalAllocPoint;
/// @notice ICHI tokens created per block.uint256public ichiPerBlock;
/// @dev Extra decimals for pool's accIchiPerShare attribute. Needed in order to accomodate different types of LPs.uint256privateconstant ACC_ICHI_PRECISION =1e18;
/// @dev nonReentrant flag used to secure functions with external calls.boolprivate nonReentrant;
eventDeposit(addressindexed user, uint256indexed pid, uint256 amount, addressindexed to);
eventWithdraw(addressindexed user, uint256indexed pid, uint256 amount, addressindexed to);
eventEmergencyWithdraw(addressindexed user, uint256indexed pid, uint256 amount, addressindexed to);
eventHarvest(addressindexed user, uint256indexed pid, uint256 amount);
eventLogPoolAddition(uint256indexed pid, uint256 allocPoint, IERC20 indexed lpToken);
eventLogSetPool(uint256indexed pid, uint256 allocPoint);
eventLogUpdatePool(uint256indexed pid, uint64 lastRewardBlock, uint256 lpSupply, uint256 accIchiPerShare);
eventSetIchiPerBlock(uint256 ichiPerBlock, bool withUpdate);
/// @param _ichi The ICHI token contract address./// @param _ichiPerBlock ICHI tokens created per block.constructor(IERC20 _ichi, uint256 _ichiPerBlock) public{
ICHI = _ichi;
ichiPerBlock = _ichiPerBlock;
totalAllocPoint =0;
}
/// @notice Update number of ICHI tokens created per block. Can only be called by the owner./// @param _ichiPerBlock ICHI tokens created per block./// @param _withUpdate true if massUpdatePools should be triggered as well.functionsetIchiPerBlock(uint256 _ichiPerBlock, bool _withUpdate) externalonlyOwner{
if (_withUpdate) {
massUpdateAllPools();
}
ichiPerBlock = _ichiPerBlock;
emit SetIchiPerBlock(_ichiPerBlock, _withUpdate);
}
/// @notice Set the nonReentrant flag. Could be used to pause/resume the farm operations. Can only be called by the owner./// @param _val nonReentrant flag value to be set.functionsetNonReentrant(bool _val) externalonlyOwnerreturns (bool) {
nonReentrant = _val;
return nonReentrant;
}
/// @notice Returns the number of IFV2 pools.functionpoolLength() externalviewreturns (uint256 pools) {
pools = poolInfo.length;
}
/// @notice Returns the ICHI reward value for a specific pool.functionpoolIchiReward(uint256 _pid) externalviewreturns (uint256) {
if (totalAllocPoint ==0)
return0;
return ichiPerBlock.mul(poolInfo[_pid].allocPoint) / totalAllocPoint;
}
/// @notice Returns the total number of LPs staked in the farm.functiongetLPSupply(uint256 _pid) externalviewreturns (uint256) {
uint256 lpSupply = lpToken[_pid].balanceOf(address(this));
return lpSupply;
}
/// @notice Add a new LP to the pool. Can only be called by the owner./// DO NOT add the same LP token more than once. Rewards will be messed up if you do./// @param allocPoint AP of the new pool./// @param _lpToken Address of the LP ERC-20 token.functionadd(uint256 allocPoint, IERC20 _lpToken) externalonlyOwner{
require(!addedLPs[address(_lpToken)], "ichiFarmV2::there is already a pool with this LP");
uint256 lastRewardBlock =block.number;
totalAllocPoint = totalAllocPoint.add(allocPoint);
lpToken.push(_lpToken);
addedLPs[address(_lpToken)] =true;
poolInfo.push(PoolInfo({
allocPoint: allocPoint.to64(),
lastRewardBlock: lastRewardBlock.to64(),
accIchiPerShare: 0
}));
emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken);
}
/// @notice Update the given pool's ICHI allocation point. Can only be called by the owner./// @param _pid The index of the pool. See `poolInfo`./// @param _allocPoint New AP of the pool.functionset(uint256 _pid, uint256 _allocPoint) externalonlyOwner{
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
poolInfo[_pid].allocPoint = _allocPoint.to64();
emit LogSetPool(_pid, _allocPoint);
}
/// @notice View function to see pending ICHI on frontend./// @param _pid The index of the pool. See `poolInfo`./// @param _user Address of user./// @return pending ICHI reward for a given user.functionpendingIchi(uint256 _pid, address _user) externalviewreturns (uint256 pending) {
PoolInfo memory pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
uint256 accIchiPerShare = pool.accIchiPerShare;
uint256 lpSupply = lpToken[_pid].balanceOf(address(this));
if (block.number> pool.lastRewardBlock && lpSupply >0&& totalAllocPoint >0) {
uint256 blocks =block.number.sub(pool.lastRewardBlock);
accIchiPerShare = accIchiPerShare.add(
(blocks.mul(ichiPerBlock).mul(pool.allocPoint).mul(ACC_ICHI_PRECISION) / totalAllocPoint) / lpSupply);
}
pending =int256(user.amount.mul(accIchiPerShare) / ACC_ICHI_PRECISION).sub(user.rewardDebt).toUInt256();
}
/// @notice Update reward variables for all pools. Be careful of gas spending!functionmassUpdateAllPools() public{
uint256 len = poolInfo.length;
for (uint256 pid =0; pid < len; ++pid) {
updatePool(pid);
}
}
/// @notice Update reward variables for specified pools. Be careful of gas spending!/// @param pids Pool IDs of all to be updated. Make sure to update all active pools.functionmassUpdatePools(uint256[] calldata pids) external{
uint256 len = pids.length;
for (uint256 i =0; i < len; ++i) {
updatePool(pids[i]);
}
}
/// @notice Update reward variables of the given pool./// @param pid The index of the pool. See `poolInfo`./// @return pool Returns the pool that was updated.functionupdatePool(uint256 pid) publicreturns (PoolInfo memory pool) {
pool = poolInfo[pid];
if (block.number> pool.lastRewardBlock) {
uint256 lpSupply = lpToken[pid].balanceOf(address(this));
if (lpSupply >0&& totalAllocPoint >0) {
uint256 blocks =block.number.sub(pool.lastRewardBlock);
pool.accIchiPerShare = pool.accIchiPerShare.add(
((blocks.mul(ichiPerBlock).mul(pool.allocPoint).mul(ACC_ICHI_PRECISION) / totalAllocPoint) / lpSupply).to128());
}
pool.lastRewardBlock =block.number.to64();
poolInfo[pid] = pool;
emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accIchiPerShare);
}
}
/// @notice Deposit LP tokens to IFV2 for ICHI allocation./// @param pid The index of the pool. See `poolInfo`./// @param amount LP token amount to deposit./// @param to The receiver of `amount` deposit benefit.functiondeposit(uint256 pid, uint256 amount, address to) external{
require(!nonReentrant, "ichiFarmV2::nonReentrant - try again");
nonReentrant =true;
PoolInfo memory pool = updatePool(pid);
UserInfo storage user = userInfo[pid][to];
// Effects
user.amount = user.amount.add(amount);
user.rewardDebt = user.rewardDebt.add(int256(amount.mul(pool.accIchiPerShare) / ACC_ICHI_PRECISION));
// Interactions
lpToken[pid].safeTransferFrom(msg.sender, address(this), amount);
emit Deposit(msg.sender, pid, amount, to);
nonReentrant =false;
}
/// @notice Withdraw LP tokens from IFV2./// @param pid The index of the pool. See `poolInfo`./// @param amount LP token amount to withdraw./// @param to Receiver of the LP tokens.functionwithdraw(uint256 pid, uint256 amount, address to) external{
require(!nonReentrant, "ichiFarmV2::nonReentrant - try again");
nonReentrant =true;
PoolInfo memory pool = updatePool(pid);
UserInfo storage user = userInfo[pid][msg.sender];
// Effects
user.rewardDebt = user.rewardDebt.sub(int256(amount.mul(pool.accIchiPerShare) / ACC_ICHI_PRECISION));
user.amount = user.amount.sub(amount);
// Interactions
lpToken[pid].safeTransfer(to, amount);
emit Withdraw(msg.sender, pid, amount, to);
nonReentrant =false;
}
/// @notice Harvest proceeds for transaction sender to `to`./// @param pid The index of the pool. See `poolInfo`./// @param to Receiver of ICHI rewards.functionharvest(uint256 pid, address to) external{
require(!nonReentrant, "ichiFarmV2::nonReentrant - try again");
nonReentrant =true;
PoolInfo memory pool = updatePool(pid);
UserInfo storage user = userInfo[pid][msg.sender];
int256 accumulatedIchi =int256(user.amount.mul(pool.accIchiPerShare) / ACC_ICHI_PRECISION);
uint256 _pendingIchi = accumulatedIchi.sub(user.rewardDebt).toUInt256();
// Effects
user.rewardDebt = accumulatedIchi;
// Interactionsif (_pendingIchi >0) {
ICHI.safeTransfer(to, _pendingIchi);
}
emit Harvest(msg.sender, pid, _pendingIchi);
nonReentrant =false;
}
/// @notice Withdraw without caring about rewards. EMERGENCY ONLY./// @param pid The index of the pool. See `poolInfo`./// @param to Receiver of the LP tokens.functionemergencyWithdraw(uint256 pid, address to) public{
require(address(0) != to, "ichiFarmV2::can't withdraw to address zero");
UserInfo storage user = userInfo[pid][msg.sender];
uint256 amount = user.amount;
user.amount =0;
user.rewardDebt =0;
// Note: transfer can fail or succeed if `amount` is zero.
lpToken[pid].safeTransfer(to, amount);
emit EmergencyWithdraw(msg.sender, pid, amount, to);
}
}