UNPKG

@polycity/core

Version:

Core contracts for the PolyCityDex protocol

38 lines 61.7 kB
{ "language": "Solidity", "sources": { "contracts/interfaces/IMasterChef.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol\";\n\ninterface IMasterChef {\n using BoringERC20 for IERC20;\n struct UserInfo {\n uint256 amount; // How many LP tokens the user has provided.\n uint256 rewardDebt; // Reward debt. See explanation below.\n }\n\n struct PoolInfo {\n IERC20 lpToken; // Address of LP token contract.\n uint256 allocPoint; // How many allocation points assigned to this pool. PICHI to distribute per block.\n uint256 lastRewardBlock; // Last block number that PICHI distribution occurs.\n uint256 accPichiPerShare; // Accumulated PICHI per share, times 1e12. See below.\n }\n\n function poolInfo(uint256 pid) external view returns (IMasterChef.PoolInfo memory);\n\n function totalAllocPoint() external view returns (uint256);\n\n function deposit(uint256 _pid, uint256 _amount) external;\n}\n" }, "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol": { "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.6.12;\n\nimport \"../interfaces/IERC20.sol\";\n\nlibrary BoringERC20 {\n function safeSymbol(IERC20 token) internal view returns(string memory) {\n (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x95d89b41));\n return success && data.length > 0 ? abi.decode(data, (string)) : \"???\";\n }\n\n function safeName(IERC20 token) internal view returns(string memory) {\n (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x06fdde03));\n return success && data.length > 0 ? abi.decode(data, (string)) : \"???\";\n }\n\n function safeDecimals(IERC20 token) internal view returns (uint8) {\n (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x313ce567));\n return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;\n }\n\n function safeTransfer(IERC20 token, address to, uint256 amount) internal {\n (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0xa9059cbb, to, amount));\n require(success && (data.length == 0 || abi.decode(data, (bool))), \"BoringERC20: Transfer failed\");\n }\n\n function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {\n (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0x23b872dd, from, to, amount));\n require(success && (data.length == 0 || abi.decode(data, (bool))), \"BoringERC20: TransferFrom failed\");\n }\n}" }, "@boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n\ninterface IERC20 {\n function totalSupply() external view returns (uint256);\n function balanceOf(address account) external view returns (uint256);\n function allowance(address owner, address spender) external view returns (uint256);\n function approve(address spender, uint256 amount) external returns (bool);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n // EIP 2612\n function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;\n}" }, "contracts/MiniChefV2.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/BoringBatchable.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/BoringOwnable.sol\";\nimport \"./libraries/SignedSafeMath.sol\";\nimport \"./interfaces/IRewarder.sol\";\nimport \"./interfaces/IMasterChef.sol\";\n\ninterface IMigratorChef {\n // Take the current LP token address and return the new LP token address.\n // Migrator should have full access to the caller's LP token.\n function migrate(IERC20 token) external returns (IERC20);\n}\n\n/// @dev The (older) MasterChef contract gives out a constant number of PICHI tokens per block.\n/// It is the only address with minting rights for PICHI.\n/// The idea for this MasterChef V2 (MCV2) contract is therefore to be the owner of a dummy token\n/// that is deposited into the MasterChef V1 (MCV1) contract.\n/// The allocation point for this pool on MCV1 is the total allocation point for all pools that receive double incentives.\ncontract MiniChefV2 is BoringOwnable, BoringBatchable {\n using BoringMath for uint256;\n using BoringMath128 for uint128;\n using BoringERC20 for IERC20;\n using SignedSafeMath for int256;\n\n /// @dev Info of each MCV2 user.\n /// `amount` LP token amount the user has provided.\n /// `rewardDebt` The amount of PICHI entitled to the user.\n struct UserInfo {\n uint256 amount;\n int256 rewardDebt;\n }\n\n /// @dev Info of each MCV2 pool.\n /// `allocPoint` The amount of allocation points assigned to the pool.\n /// Also known as the amount of PICHI to distribute per block.\n struct PoolInfo {\n uint128 accPichiPerShare;\n uint64 lastRewardTime;\n uint64 allocPoint;\n }\n\n /// @dev Address of PICHI contract.\n IERC20 public immutable PICHI;\n // @dev The migrator contract. It has a lot of power. Can only be set through governance (owner).\n IMigratorChef public migrator;\n\n /// @dev Info of each MCV2 pool.\n PoolInfo[] public poolInfo;\n /// @dev Address of the LP token for each MCV2 pool.\n IERC20[] public lpToken;\n /// @dev Address of each `IRewarder` contract in MCV2.\n IRewarder[] public rewarder;\n\n /// @dev Info of each user that stakes LP tokens.\n mapping (uint256 => mapping (address => UserInfo)) public userInfo;\n /// @dev Total allocation points. Must be the sum of all allocation points in all pools.\n uint256 public totalAllocPoint;\n\n uint256 public pichiPerSecond;\n uint256 private constant ACC_PICHI_PRECISION = 1e12;\n\n event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event Harvest(address indexed user, uint256 indexed pid, uint256 amount);\n event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken, IRewarder indexed rewarder);\n event LogSetPool(uint256 indexed pid, uint256 allocPoint, IRewarder indexed rewarder, bool overwrite);\n event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accPichiPerShare);\n event LogPichiPerSecond(uint256 pichiPerSecond);\n\n /// @param _pichi The PICHI token contract address.\n constructor(IERC20 _pichi) public {\n PICHI = _pichi;\n }\n\n /// @dev Returns the number of MCV2 pools.\n function poolLength() public view returns (uint256 pools) {\n pools = poolInfo.length;\n }\n\n /// @dev Add a new LP to the pool. Can only be called by the owner.\n /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.\n /// @param allocPoint AP of the new pool.\n /// @param _lpToken Address of the LP ERC-20 token.\n /// @param _rewarder Address of the rewarder delegate.\n function add(uint256 allocPoint, IERC20 _lpToken, IRewarder _rewarder) public onlyOwner {\n totalAllocPoint = totalAllocPoint.add(allocPoint);\n lpToken.push(_lpToken);\n rewarder.push(_rewarder);\n\n poolInfo.push(PoolInfo({\n allocPoint: allocPoint.to64(),\n lastRewardTime: block.timestamp.to64(),\n accPichiPerShare: 0\n }));\n emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken, _rewarder);\n }\n\n /// @dev Update the given pool's PICHI allocation point and `IRewarder` contract. Can only be called by the owner.\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _allocPoint New AP of the pool.\n /// @param _rewarder Address of the rewarder delegate.\n /// @param overwrite True if _rewarder should be `set`. Otherwise `_rewarder` is ignored.\n function set(uint256 _pid, uint256 _allocPoint, IRewarder _rewarder, bool overwrite) public onlyOwner {\n totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);\n poolInfo[_pid].allocPoint = _allocPoint.to64();\n if (overwrite) { rewarder[_pid] = _rewarder; }\n emit LogSetPool(_pid, _allocPoint, overwrite ? _rewarder : rewarder[_pid], overwrite);\n }\n\n /// @dev Sets the pichi per second to be distributed. Can only be called by the owner.\n /// @param _pichiPerSecond The amount of Pichi to be distributed per second.\n function setPichiPerSecond(uint256 _pichiPerSecond) public onlyOwner {\n pichiPerSecond = _pichiPerSecond;\n emit LogPichiPerSecond(_pichiPerSecond);\n }\n\n /// @dev Set the `migrator` contract. Can only be called by the owner.\n /// @param _migrator The contract address to set.\n function setMigrator(IMigratorChef _migrator) public onlyOwner {\n migrator = _migrator;\n }\n\n /// @dev Migrate LP token to another LP contract through the `migrator` contract.\n /// @param _pid The index of the pool. See `poolInfo`.\n function migrate(uint256 _pid) public {\n require(address(migrator) != address(0), \"MasterChefV2: no migrator set\");\n IERC20 _lpToken = lpToken[_pid];\n uint256 bal = _lpToken.balanceOf(address(this));\n _lpToken.approve(address(migrator), bal);\n IERC20 newLpToken = migrator.migrate(_lpToken);\n require(bal == newLpToken.balanceOf(address(this)), \"MasterChefV2: migrated balance must match\");\n lpToken[_pid] = newLpToken;\n }\n\n /// @dev View function to see pending PICHI on frontend.\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _user Address of user.\n /// @return pending PICHI reward for a given user.\n function pendingPichi(uint256 _pid, address _user) external view returns (uint256 pending) {\n PoolInfo memory pool = poolInfo[_pid];\n UserInfo storage user = userInfo[_pid][_user];\n uint256 accPichiPerShare = pool.accPichiPerShare;\n uint256 lpSupply = lpToken[_pid].balanceOf(address(this));\n if (block.timestamp > pool.lastRewardTime && lpSupply != 0) {\n uint256 time = block.timestamp.sub(pool.lastRewardTime);\n uint256 pichiReward = time.mul(pichiPerSecond).mul(pool.allocPoint) / totalAllocPoint;\n accPichiPerShare = accPichiPerShare.add(pichiReward.mul(ACC_PICHI_PRECISION) / lpSupply);\n }\n pending = int256(user.amount.mul(accPichiPerShare) / ACC_PICHI_PRECISION).sub(user.rewardDebt).toUInt256();\n }\n\n /// @dev Update reward variables for all pools. Be careful of gas spending!\n /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.\n function massUpdatePools(uint256[] calldata pids) external {\n uint256 len = pids.length;\n for (uint256 i = 0; i < len; ++i) {\n updatePool(pids[i]);\n }\n }\n\n /// @dev Update reward variables of the given pool.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @return pool Returns the pool that was updated.\n function updatePool(uint256 pid) public returns (PoolInfo memory pool) {\n pool = poolInfo[pid];\n if (block.timestamp > pool.lastRewardTime) {\n uint256 lpSupply = lpToken[pid].balanceOf(address(this));\n if (lpSupply > 0) {\n uint256 time = block.timestamp.sub(pool.lastRewardTime);\n uint256 pichiReward = time.mul(pichiPerSecond).mul(pool.allocPoint) / totalAllocPoint;\n pool.accPichiPerShare = pool.accPichiPerShare.add((pichiReward.mul(ACC_PICHI_PRECISION) / lpSupply).to128());\n }\n pool.lastRewardTime = block.timestamp.to64();\n poolInfo[pid] = pool;\n emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accPichiPerShare);\n }\n }\n\n /// @dev Deposit LP tokens to MCV2 for PICHI allocation.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param amount LP token amount to deposit.\n /// @param to The receiver of `amount` deposit benefit.\n function deposit(uint256 pid, uint256 amount, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][to];\n\n // Effects\n user.amount = user.amount.add(amount);\n user.rewardDebt = user.rewardDebt.add(int256(amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION));\n\n // Interactions\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, to, to, 0, user.amount);\n }\n\n lpToken[pid].safeTransferFrom(msg.sender, address(this), amount);\n\n emit Deposit(msg.sender, pid, amount, to);\n }\n\n /// @dev Withdraw LP tokens from MCV2.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param amount LP token amount to withdraw.\n /// @param to Receiver of the LP tokens.\n function withdraw(uint256 pid, uint256 amount, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][msg.sender];\n\n // Effects\n user.rewardDebt = user.rewardDebt.sub(int256(amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION));\n user.amount = user.amount.sub(amount);\n\n // Interactions\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, msg.sender, to, 0, user.amount);\n }\n \n lpToken[pid].safeTransfer(to, amount);\n\n emit Withdraw(msg.sender, pid, amount, to);\n }\n\n /// @dev Harvest proceeds for transaction sender to `to`.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param to Receiver of PICHI rewards.\n function harvest(uint256 pid, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][msg.sender];\n int256 accumulatedPichi = int256(user.amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION);\n uint256 _pendingPichi = accumulatedPichi.sub(user.rewardDebt).toUInt256();\n\n // Effects\n user.rewardDebt = accumulatedPichi;\n\n // Interactions\n if (_pendingPichi != 0) {\n PICHI.safeTransfer(to, _pendingPichi);\n }\n \n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward( pid, msg.sender, to, _pendingPichi, user.amount);\n }\n\n emit Harvest(msg.sender, pid, _pendingPichi);\n }\n \n /// @dev Withdraw LP tokens from MCV2 and harvest proceeds for transaction sender to `to`.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param amount LP token amount to withdraw.\n /// @param to Receiver of the LP tokens and PICHI rewards.\n function withdrawAndHarvest(uint256 pid, uint256 amount, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][msg.sender];\n int256 accumulatedPichi = int256(user.amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION);\n uint256 _pendingPichi = accumulatedPichi.sub(user.rewardDebt).toUInt256();\n\n // Effects\n user.rewardDebt = accumulatedPichi.sub(int256(amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION));\n user.amount = user.amount.sub(amount);\n \n // Interactions\n PICHI.safeTransfer(to, _pendingPichi);\n\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, msg.sender, to, _pendingPichi, user.amount);\n }\n\n lpToken[pid].safeTransfer(to, amount);\n\n emit Withdraw(msg.sender, pid, amount, to);\n emit Harvest(msg.sender, pid, _pendingPichi);\n }\n\n /// @dev Withdraw without caring about rewards. EMERGENCY ONLY.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param to Receiver of the LP tokens.\n function emergencyWithdraw(uint256 pid, address to) public {\n UserInfo storage user = userInfo[pid][msg.sender];\n uint256 amount = user.amount;\n user.amount = 0;\n user.rewardDebt = 0;\n\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, msg.sender, to, 0, 0);\n }\n\n // Note: transfer can fail or succeed if `amount` is zero.\n lpToken[pid].safeTransfer(to, amount);\n emit EmergencyWithdraw(msg.sender, pid, amount, to);\n }\n}\n" }, "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n// a library for performing overflow-safe math, updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math)\nlibrary BoringMath {\n function add(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a + b) >= b, \"BoringMath: Add Overflow\");}\n function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a - b) <= a, \"BoringMath: Underflow\");}\n function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {require(b == 0 || (c = a * b)/b == a, \"BoringMath: Mul Overflow\");}\n function to128(uint256 a) internal pure returns (uint128 c) {\n require(a <= uint128(-1), \"BoringMath: uint128 Overflow\");\n c = uint128(a);\n }\n function to64(uint256 a) internal pure returns (uint64 c) {\n require(a <= uint64(-1), \"BoringMath: uint64 Overflow\");\n c = uint64(a);\n }\n function to32(uint256 a) internal pure returns (uint32 c) {\n require(a <= uint32(-1), \"BoringMath: uint32 Overflow\");\n c = uint32(a);\n }\n}\n\nlibrary BoringMath128 {\n function add(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a + b) >= b, \"BoringMath: Add Overflow\");}\n function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a - b) <= a, \"BoringMath: Underflow\");}\n}\n\nlibrary BoringMath64 {\n function add(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a + b) >= b, \"BoringMath: Add Overflow\");}\n function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a - b) <= a, \"BoringMath: Underflow\");}\n}\n\nlibrary BoringMath32 {\n function add(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a + b) >= b, \"BoringMath: Add Overflow\");}\n function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a - b) <= a, \"BoringMath: Underflow\");}\n}" }, "@boringcrypto/boring-solidity/contracts/BoringBatchable.sol": { "content": "// SPDX-License-Identifier: UNLICENSED\n// Audit on 5-Jan-2021 by Keno and BoringCrypto\n\n// P1 - P3: OK\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n// solhint-disable avoid-low-level-calls\n\nimport \"./libraries/BoringERC20.sol\";\n\n// T1 - T4: OK\ncontract BaseBoringBatchable {\n function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {\n // If the _res length is less than 68, then the transaction failed silently (without a revert message)\n if (_returnData.length < 68) return \"Transaction reverted silently\";\n\n assembly {\n // Slice the sighash.\n _returnData := add(_returnData, 0x04)\n }\n return abi.decode(_returnData, (string)); // All that remains is the revert string\n } \n \n // F3 - F9: OK\n // F1: External is ok here because this is the batch function, adding it to a batch makes no sense\n // 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\n // C1 - C21: OK\n // C3: The length of the loop is fully under user control, so can't be exploited\n // C7: Delegatecall is only used on the same contract, so it's safe\n function batch(bytes[] calldata calls, bool revertOnFail) external payable returns(bool[] memory successes, bytes[] memory results) {\n // Interactions\n successes = new bool[](calls.length);\n results = new bytes[](calls.length);\n for (uint256 i = 0; i < calls.length; i++) {\n (bool success, bytes memory result) = address(this).delegatecall(calls[i]);\n require(success || !revertOnFail, _getRevertMsg(result));\n successes[i] = success;\n results[i] = result;\n }\n }\n}\n\n// T1 - T4: OK\ncontract BoringBatchable is BaseBoringBatchable {\n // F1 - F9: OK\n // F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)\n // if part of a batch this could be used to grief once as the second call would not need the permit\n // C1 - C21: OK\n function permitToken(IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n // Interactions\n // X1 - X5\n token.permit(from, to, amount, deadline, v, r, s);\n }\n}" }, "@boringcrypto/boring-solidity/contracts/BoringOwnable.sol": { "content": "// SPDX-License-Identifier: MIT\n// Audit on 5-Jan-2021 by Keno and BoringCrypto\n\n// P1 - P3: OK\npragma solidity 0.6.12;\n\n// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol\n// Edited by BoringCrypto\n\n// T1 - T4: OK\ncontract BoringOwnableData {\n // V1 - V5: OK\n address public owner;\n // V1 - V5: OK\n address public pendingOwner;\n}\n\n// T1 - T4: OK\ncontract BoringOwnable is BoringOwnableData {\n // E1: OK\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n constructor () public {\n owner = msg.sender;\n emit OwnershipTransferred(address(0), msg.sender);\n }\n\n // F1 - F9: OK\n // C1 - C21: OK\n function transferOwnership(address newOwner, bool direct, bool renounce) public onlyOwner {\n if (direct) {\n // Checks\n require(newOwner != address(0) || renounce, \"Ownable: zero address\");\n\n // Effects\n emit OwnershipTransferred(owner, newOwner);\n owner = newOwner;\n pendingOwner = address(0);\n } else {\n // Effects\n pendingOwner = newOwner;\n }\n }\n\n // F1 - F9: OK\n // C1 - C21: OK\n function claimOwnership() public {\n address _pendingOwner = pendingOwner;\n \n // Checks\n require(msg.sender == _pendingOwner, \"Ownable: caller != pending owner\");\n\n // Effects\n emit OwnershipTransferred(owner, _pendingOwner);\n owner = _pendingOwner;\n pendingOwner = address(0);\n }\n\n // M1 - M5: OK\n // C1 - C21: OK\n modifier onlyOwner() {\n require(msg.sender == owner, \"Ownable: caller is not the owner\");\n _;\n }\n}" }, "contracts/libraries/SignedSafeMath.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\n\nlibrary SignedSafeMath {\n int256 private constant _INT256_MIN = -2**255;\n\n /**\n * @dev Returns the multiplication of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(int256 a, int256 b) internal pure returns (int256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) {\n return 0;\n }\n\n require(!(a == -1 && b == _INT256_MIN), \"SignedSafeMath: multiplication overflow\");\n\n int256 c = a * b;\n require(c / a == b, \"SignedSafeMath: multiplication overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the integer division of two signed integers. Reverts on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(int256 a, int256 b) internal pure returns (int256) {\n require(b != 0, \"SignedSafeMath: division by zero\");\n require(!(b == -1 && a == _INT256_MIN), \"SignedSafeMath: division overflow\");\n\n int256 c = a / b;\n\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(int256 a, int256 b) internal pure returns (int256) {\n int256 c = a - b;\n require((b >= 0 && c <= a) || (b < 0 && c > a), \"SignedSafeMath: subtraction overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the addition of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(int256 a, int256 b) internal pure returns (int256) {\n int256 c = a + b;\n require((b >= 0 && c >= a) || (b < 0 && c < a), \"SignedSafeMath: addition overflow\");\n\n return c;\n }\n\n function toUInt256(int256 a) internal pure returns (uint256) {\n require(a >= 0, \"Integer < 0\");\n return uint256(a);\n }\n}\n" }, "contracts/interfaces/IRewarder.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol\";\n\ninterface IRewarder {\n using BoringERC20 for IERC20;\n\n function onPichiReward(\n uint256 pid,\n address user,\n address recipient,\n uint256 pichiAmount,\n uint256 newLpAmount\n ) external;\n\n function pendingTokens(\n uint256 pid,\n address user,\n uint256 pichiAmount\n ) external view returns (IERC20[] memory, uint256[] memory);\n}\n" }, "contracts/mocks/RewarderMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\nimport \"../interfaces/IRewarder.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol\";\n\ncontract RewarderMock is IRewarder {\n using BoringMath for uint256;\n using BoringERC20 for IERC20;\n uint256 private immutable rewardMultiplier;\n IERC20 private immutable rewardToken;\n uint256 private constant REWARD_TOKEN_DIVISOR = 1e18;\n address private immutable MASTERCHEF_V2;\n\n constructor(\n uint256 _rewardMultiplier,\n IERC20 _rewardToken,\n address _MASTERCHEF_V2\n ) public {\n rewardMultiplier = _rewardMultiplier;\n rewardToken = _rewardToken;\n MASTERCHEF_V2 = _MASTERCHEF_V2;\n }\n\n function onPichiReward(\n uint256,\n address user,\n address to,\n uint256 pichiAmount,\n uint256\n ) external override onlyMCV2 {\n uint256 pendingReward = pichiAmount.mul(rewardMultiplier) / REWARD_TOKEN_DIVISOR;\n uint256 rewardBal = rewardToken.balanceOf(address(this));\n if (pendingReward > rewardBal) {\n rewardToken.safeTransfer(to, rewardBal);\n } else {\n rewardToken.safeTransfer(to, pendingReward);\n }\n }\n\n function pendingTokens(\n uint256 pid,\n address user,\n uint256 pichiAmount\n ) external view override returns (IERC20[] memory rewardTokens, uint256[] memory rewardAmounts) {\n IERC20[] memory _rewardTokens = new IERC20[](1);\n _rewardTokens[0] = (rewardToken);\n uint256[] memory _rewardAmounts = new uint256[](1);\n _rewardAmounts[0] = pichiAmount.mul(rewardMultiplier) / REWARD_TOKEN_DIVISOR;\n return (_rewardTokens, _rewardAmounts);\n }\n\n modifier onlyMCV2 {\n require(msg.sender == MASTERCHEF_V2, \"Only MCV2 can call this function.\");\n _;\n }\n}\n" }, "contracts/mocks/ComplexRewarderTime.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\nimport \"../interfaces/IRewarder.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/BoringOwnable.sol\";\nimport \"../MasterChefV2.sol\";\n\n/// @author @0xKeno\ncontract ComplexRewarderTime is IRewarder, BoringOwnable {\n using BoringMath for uint256;\n using BoringMath128 for uint128;\n using BoringERC20 for IERC20;\n\n IERC20 private immutable rewardToken;\n\n /// @dev Info of each MCV2 user.\n /// `amount` LP token amount the user has provided.\n /// `rewardDebt` The amount of PICHI entitled to the user.\n struct UserInfo {\n uint256 amount;\n uint256 rewardDebt;\n }\n\n /// @dev Info of each MCV2 pool.\n /// `allocPoint` The amount of allocation points assigned to the pool.\n /// Also known as the amount of PICHI to distribute per block.\n struct PoolInfo {\n uint128 accPichiPerShare;\n uint64 lastRewardTime;\n uint64 allocPoint;\n }\n\n /// @dev Info of each pool.\n mapping(uint256 => PoolInfo) public poolInfo;\n\n uint256[] public poolIds;\n\n /// @dev Info of each user that stakes LP tokens.\n mapping(uint256 => mapping(address => UserInfo)) public userInfo;\n /// @dev Total allocation points. Must be the sum of all allocation points in all pools.\n uint256 totalAllocPoint;\n\n uint256 public rewardPerSecond;\n uint256 private constant ACC_TOKEN_PRECISION = 1e12;\n\n address private immutable MASTERCHEF_V2;\n\n event LogOnReward(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event LogPoolAddition(uint256 indexed pid, uint256 allocPoint);\n event LogSetPool(uint256 indexed pid, uint256 allocPoint);\n event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accPichiPerShare);\n event LogRewardPerSecond(uint256 rewardPerSecond);\n event LogInit();\n\n constructor(\n IERC20 _rewardToken,\n uint256 _rewardPerSecond,\n address _MASTERCHEF_V2\n ) public {\n rewardToken = _rewardToken;\n rewardPerSecond = _rewardPerSecond;\n MASTERCHEF_V2 = _MASTERCHEF_V2;\n }\n\n function onPichiReward(\n uint256 pid,\n address _user,\n address to,\n uint256,\n uint256 lpToken\n ) external override onlyMCV2 {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][_user];\n uint256 pending;\n if (user.amount > 0) {\n pending = (user.amount.mul(pool.accPichiPerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt);\n rewardToken.safeTransfer(to, pending);\n }\n user.amount = lpToken;\n user.rewardDebt = lpToken.mul(pool.accPichiPerShare) / ACC_TOKEN_PRECISION;\n emit LogOnReward(_user, pid, pending, to);\n }\n\n function pendingTokens(\n uint256 pid,\n address user,\n uint256\n ) external view override returns (IERC20[] memory rewardTokens, uint256[] memory rewardAmounts) {\n IERC20[] memory _rewardTokens = new IERC20[](1);\n _rewardTokens[0] = (rewardToken);\n uint256[] memory _rewardAmounts = new uint256[](1);\n _rewardAmounts[0] = pendingToken(pid, user);\n return (_rewardTokens, _rewardAmounts);\n }\n\n /// @dev Sets the pichi per second to be distributed. Can only be called by the owner.\n /// @param _rewardPerSecond The amount of Pichi to be distributed per second.\n function setRewardPerSecond(uint256 _rewardPerSecond) public onlyOwner {\n rewardPerSecond = _rewardPerSecond;\n emit LogRewardPerSecond(_rewardPerSecond);\n }\n\n modifier onlyMCV2 {\n require(msg.sender == MASTERCHEF_V2, \"Only MCV2 can call this function.\");\n _;\n }\n\n /// @dev Returns the number of MCV2 pools.\n function poolLength() public view returns (uint256 pools) {\n pools = poolIds.length;\n }\n\n /// @dev Add a new LP to the pool. Can only be called by the owner.\n /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.\n /// @param allocPoint AP of the new pool.\n /// @param _pid Pid on MCV2\n function add(uint256 allocPoint, uint256 _pid) public onlyOwner {\n require(poolInfo[_pid].lastRewardTime == 0, \"Pool already exists\");\n uint256 lastRewardTime = block.timestamp;\n totalAllocPoint = totalAllocPoint.add(allocPoint);\n\n poolInfo[_pid] = PoolInfo({allocPoint: allocPoint.to64(), lastRewardTime: lastRewardTime.to64(), accPichiPerShare: 0});\n poolIds.push(_pid);\n emit LogPoolAddition(_pid, allocPoint);\n }\n\n /// @dev Update the given pool's PICHI allocation point and `IRewarder` contract. Can only be called by the owner.\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _allocPoint New AP of the pool.\n function set(uint256 _pid, uint256 _allocPoint) public onlyOwner {\n totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);\n poolInfo[_pid].allocPoint = _allocPoint.to64();\n emit LogSetPool(_pid, _allocPoint);\n }\n\n /// @dev View function to see pending Token\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _user Address of user.\n /// @return pending PICHI reward for a given user.\n function pendingToken(uint256 _pid, address _user) public view returns (uint256 pending) {\n PoolInfo memory pool = poolInfo[_pid];\n UserInfo storage user = userInfo[_pid][_user];\n uint256 accPichiPerShare = pool.accPichiPerShare;\n uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(_pid).balanceOf(MASTERCHEF_V2);\n if (block.timestamp > pool.lastRewardTime && lpSupply != 0) {\n uint256 time = block.timestamp.sub(pool.lastRewardTime);\n uint256 pichiReward = time.mul(rewardPerSecond).mul(pool.allocPoint) / totalAllocPoint;\n accPichiPerShare = accPichiPerShare.add(pichiReward.mul(ACC_TOKEN_PRECISION) / lpSupply);\n }\n pending = (user.amount.mul(accPichiPerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt);\n }\n\n /// @dev Update reward variables for all pools. Be careful of gas spending!\n /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.\n function massUpdatePools(uint256[] calldata pids) external {\n uint256 len = pids.length;\n for (uint256 i = 0; i < len; ++i) {\n updatePool(pids[i]);\n }\n }\n\n /// @dev Update reward variables of the given pool.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @return pool Returns the pool that was updated.\n function updatePool(uint256 pid) public returns (PoolInfo memory pool) {\n pool = poolInfo[pid];\n if (block.timestamp > pool.lastRewardTime) {\n uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(pid).balanceOf(MASTERCHEF_V2);\n\n if (lpSupply > 0) {\n uint256 time = block.timestamp.sub(pool.lastRewardTime);\n uint256 pichiReward = time.mul(rewardPerSecond).mul(pool.allocPoint) / totalAllocPoint;\n pool.accPichiPerShare = pool.accPichiPerShare.add((pichiReward.mul(ACC_TOKEN_PRECISION) / lpSupply).to128());\n }\n pool.lastRewardTime = block.timestamp.to64();\n poolInfo[pid] = pool;\n emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accPichiPerShare);\n }\n }\n}\n" }, "contracts/MasterChefV2.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/BoringBatchable.sol\";\nimport \"@boringcrypto/boring-solidity/contracts/BoringOwnable.sol\";\nimport \"./libraries/SignedSafeMath.sol\";\nimport \"./interfaces/IRewarder.sol\";\nimport \"./interfaces/IMasterChef.sol\";\n\ninterface IMigratorChef {\n // Take the current LP token address and return the new LP token address.\n // Migrator should have full access to the caller's LP token.\n function migrate(IERC20 token) external returns (IERC20);\n}\n\n/// @dev The (older) MasterChef contract gives out a constant number of PICHI tokens per block.\n/// It is the only address with minting rights for PICHI.\n/// The idea for this MasterChef V2 (MCV2) contract is therefore to be the owner of a dummy token\n/// that is deposited into the MasterChef V1 (MCV1) contract.\n/// The allocation point for this pool on MCV1 is the total allocation point for all pools that receive double incentives.\ncontract MasterChefV2 is BoringOwnable, BoringBatchable {\n using BoringMath for uint256;\n using BoringMath128 for uint128;\n using BoringERC20 for IERC20;\n using SignedSafeMath for int256;\n\n /// @dev Info of each MCV2 user.\n /// `amount` LP token amount the user has provided.\n /// `rewardDebt` The amount of PICHI entitled to the user.\n struct UserInfo {\n uint256 amount;\n int256 rewardDebt;\n }\n\n /// @dev Info of each MCV2 pool.\n /// `allocPoint` The amount of allocation points assigned to the pool.\n /// Also known as the amount of PICHI to distribute per block.\n struct PoolInfo {\n uint128 accPichiPerShare;\n uint64 lastRewardBlock;\n uint64 allocPoint;\n }\n\n /// @dev Address of MCV1 contract.\n IMasterChef public immutable MASTER_CHEF;\n /// @dev Address of PICHI contract.\n IERC20 public immutable PICHI;\n /// @dev The index of MCV2 master pool in MCV1.\n uint256 public immutable MASTER_PID;\n // @dev The migrator contract. It has a lot of power. Can only be set through governance (owner).\n IMigratorChef public migrator;\n\n /// @dev Info of each MCV2 pool.\n PoolInfo[] public poolInfo;\n /// @dev Address of the LP token for each MCV2 pool.\n IERC20[] public lpToken;\n /// @dev Address of each `IRewarder` contract in MCV2.\n IRewarder[] public rewarder;\n\n /// @dev Info of each user that stakes LP tokens.\n mapping (uint256 => mapping (address => UserInfo)) public userInfo;\n /// @dev Total allocation points. Must be the sum of all allocation points in all pools.\n uint256 public totalAllocPoint;\n\n uint256 private constant MASTERCHEF_PICHI_PER_BLOCK = 1e20;\n uint256 private constant ACC_PICHI_PRECISION = 1e12;\n\n event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);\n event Harvest(address indexed user, uint256 indexed pid, uint256 amount);\n event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken, IRewarder indexed rewarder);\n event LogSetPool(uint256 indexed pid, uint256 allocPoint, IRewarder indexed rewarder, bool overwrite);\n event LogUpdatePool(uint256 indexed pid, uint64 lastRewardBlock, uint256 lpSupply, uint256 accPichiPerShare);\n event LogInit();\n\n /// @param _MASTER_CHEF The PolyCityDex MCV1 contract address.\n /// @param _pichi The PICHI token contract address.\n /// @param _MASTER_PID The pool ID of the dummy token on the base MCV1 contract.\n constructor(IMasterChef _MASTER_CHEF, IERC20 _pichi, uint256 _MASTER_PID) public {\n MASTER_CHEF = _MASTER_CHEF;\n PICHI = _pichi;\n MASTER_PID = _MASTER_PID;\n }\n\n /// @dev Deposits a dummy token to `MASTER_CHEF` MCV1. This is required because MCV1 holds the minting rights for PICHI.\n /// Any balance of transaction sender in `dummyToken` is transferred.\n /// The allocation point for the pool on MCV1 is the total allocation point for all pools that receive double incentives.\n /// @param dummyToken The address of the ERC-20 token to deposit into MCV1.\n function init(IERC20 dummyToken) external {\n uint256 balance = dummyToken.balanceOf(msg.sender);\n require(balance != 0, \"MasterChefV2: Balance must exceed 0\");\n dummyToken.safeTransferFrom(msg.sender, address(this), balance);\n dummyToken.approve(address(MASTER_CHEF), balance);\n MASTER_CHEF.deposit(MASTER_PID, balance);\n emit LogInit();\n }\n\n /// @dev Returns the number of MCV2 pools.\n function poolLength() public view returns (uint256 pools) {\n pools = poolInfo.length;\n }\n\n /// @dev Add a new LP to the pool. Can only be called by the owner.\n /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.\n /// @param allocPoint AP of the new pool.\n /// @param _lpToken Address of the LP ERC-20 token.\n /// @param _rewarder Address of the rewarder delegate.\n function add(uint256 allocPoint, IERC20 _lpToken, IRewarder _rewarder) public onlyOwner {\n uint256 lastRewardBlock = block.number;\n totalAllocPoint = totalAllocPoint.add(allocPoint);\n lpToken.push(_lpToken);\n rewarder.push(_rewarder);\n\n poolInfo.push(PoolInfo({\n allocPoint: allocPoint.to64(),\n lastRewardBlock: lastRewardBlock.to64(),\n accPichiPerShare: 0\n }));\n emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken, _rewarder);\n }\n\n /// @dev Update the given pool's PICHI allocation point and `IRewarder` contract. Can only be called by the owner.\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _allocPoint New AP of the pool.\n /// @param _rewarder Address of the rewarder delegate.\n /// @param overwrite True if _rewarder should be `set`. Otherwise `_rewarder` is ignored.\n function set(uint256 _pid, uint256 _allocPoint, IRewarder _rewarder, bool overwrite) public onlyOwner {\n totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);\n poolInfo[_pid].allocPoint = _allocPoint.to64();\n if (overwrite) { rewarder[_pid] = _rewarder; }\n emit LogSetPool(_pid, _allocPoint, overwrite ? _rewarder : rewarder[_pid], overwrite);\n }\n\n /// @dev Set the `migrator` contract. Can only be called by the owner.\n /// @param _migrator The contract address to set.\n function setMigrator(IMigratorChef _migrator) public onlyOwner {\n migrator = _migrator;\n }\n\n /// @dev Migrate LP token to another LP contract through the `migrator` contract.\n /// @param _pid The index of the pool. See `poolInfo`.\n function migrate(uint256 _pid) public {\n require(address(migrator) != address(0), \"MasterChefV2: no migrator set\");\n IERC20 _lpToken = lpToken[_pid];\n uint256 bal = _lpToken.balanceOf(address(this));\n _lpToken.approve(address(migrator), bal);\n IERC20 newLpToken = migrator.migrate(_lpToken);\n require(bal == newLpToken.balanceOf(address(this)), \"MasterChefV2: migrated balance must match\");\n lpToken[_pid] = newLpToken;\n }\n\n /// @dev View function to see pending PICHI on frontend.\n /// @param _pid The index of the pool. See `poolInfo`.\n /// @param _user Address of user.\n /// @return pending PICHI reward for a given user.\n function pendingPichi(uint256 _pid, address _user) external view returns (uint256 pending) {\n PoolInfo memory pool = poolInfo[_pid];\n UserInfo storage user = userInfo[_pid][_user];\n uint256 accPichiPerShare = pool.accPichiPerShare;\n uint256 lpSupply = lpToken[_pid].balanceOf(address(this));\n if (block.number > pool.lastRewardBlock && lpSupply != 0) {\n uint256 blocks = block.number.sub(pool.lastRewardBlock);\n uint256 pichiReward = blocks.mul(pichiPerBlock()).mul(pool.allocPoint) / totalAllocPoint;\n accPichiPerShare = accPichiPerShare.add(pichiReward.mul(ACC_PICHI_PRECISION) / lpSupply);\n }\n pending = int256(user.amount.mul(accPichiPerShare) / ACC_PICHI_PRECISION).sub(user.rewardDebt).toUInt256();\n }\n\n /// @dev Update reward variables for all pools. Be careful of gas spending!\n /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.\n function massUpdatePools(uint256[] calldata pids) external {\n uint256 len = pids.length;\n for (uint256 i = 0; i < len; ++i) {\n updatePool(pids[i]);\n }\n }\n\n /// @dev Calculates and returns the `amount` of PICHI per block.\n function pichiPerBlock() public view returns (uint256 amount) {\n amount = uint256(MASTERCHEF_PICHI_PER_BLOCK)\n .mul(MASTER_CHEF.poolInfo(MASTER_PID).allocPoint) / MASTER_CHEF.totalAllocPoint();\n }\n\n /// @dev Update reward variables of the given pool.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @return pool Returns the pool that was updated.\n function updatePool(uint256 pid) public returns (PoolInfo memory pool) {\n pool = poolInfo[pid];\n if (block.number > pool.lastRewardBlock) {\n uint256 lpSupply = lpToken[pid].balanceOf(address(this));\n if (lpSupply > 0) {\n uint256 blocks = block.number.sub(pool.lastRewardBlock);\n uint256 pichiReward = blocks.mul(pichiPerBlock()).mul(pool.allocPoint) / totalAllocPoint;\n pool.accPichiPerShare = pool.accPichiPerShare.add((pichiReward.mul(ACC_PICHI_PRECISION) / lpSupply).to128());\n }\n pool.lastRewardBlock = block.number.to64();\n poolInfo[pid] = pool;\n emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accPichiPerShare);\n }\n }\n\n /// @dev Deposit LP tokens to MCV2 for PICHI allocation.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param amount LP token amount to deposit.\n /// @param to The receiver of `amount` deposit benefit.\n function deposit(uint256 pid, uint256 amount, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][to];\n\n // Effects\n user.amount = user.amount.add(amount);\n user.rewardDebt = user.rewardDebt.add(int256(amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION));\n\n // Interactions\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, to, to, 0, user.amount);\n }\n\n lpToken[pid].safeTransferFrom(msg.sender, address(this), amount);\n\n emit Deposit(msg.sender, pid, amount, to);\n }\n\n /// @dev Withdraw LP tokens from MCV2.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param amount LP token amount to withdraw.\n /// @param to Receiver of the LP tokens.\n function withdraw(uint256 pid, uint256 amount, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userInfo[pid][msg.sender];\n\n // Effects\n user.rewardDebt = user.rewardDebt.sub(int256(amount.mul(pool.accPichiPerShare) / ACC_PICHI_PRECISION));\n user.amount = user.amount.sub(amount);\n\n // Interactions\n IRewarder _rewarder = rewarder[pid];\n if (address(_rewarder) != address(0)) {\n _rewarder.onPichiReward(pid, msg.sender, to, 0, user.amount);\n }\n \n lpToken[pid].safeTransfer(to, amount);\n\n emit Withdraw(msg.sender, pid, amount, to);\n }\n\n /// @dev Harvest proceeds for transaction sender to `to`.\n /// @param pid The index of the pool. See `poolInfo`.\n /// @param to Receiver of PICHI rewards.\n function harvest(uint256 pid, address to) public {\n PoolInfo memory pool = updatePool(pid);\n UserInfo storage user = userIn