From 675b195bf2f5e7c76af77e403d9b1bb02e3c8d30 Mon Sep 17 00:00:00 2001 From: Vitalii Koval Date: Tue, 30 May 2023 12:55:45 +0400 Subject: [PATCH 1/4] add lp farm --- contracts/farm/Farm.sol | 190 +++++++++++++++++++++++++ contracts/farm/IFarm.sol | 26 ++++ contracts/farm/libraries/SafeToken.sol | 36 +++++ package.json | 3 +- test/farm.test.js | 136 ++++++++++++++++++ test/helpers/time.js | 34 +++++ 6 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 contracts/farm/Farm.sol create mode 100644 contracts/farm/IFarm.sol create mode 100644 contracts/farm/libraries/SafeToken.sol create mode 100644 test/farm.test.js create mode 100644 test/helpers/time.js diff --git a/contracts/farm/Farm.sol b/contracts/farm/Farm.sol new file mode 100644 index 0000000..6547656 --- /dev/null +++ b/contracts/farm/Farm.sol @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +import "./libraries/SafeToken.sol"; +import "./IFarm.sol"; +import "../core/interfaces/IERC20.sol"; + +contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { + using SafeToken for address; + + struct StakeInfo { + uint256 amount; + uint256 rewardDebt; + } + + struct PoolInfo { + uint256 allocPoint; + uint256 lastRewardBlock; + uint256 accRewardPerShare; + } + + address public rewardToken; + + uint256 public rewardPerBlock; + uint256 public totalAllocPoint = 0; + + mapping(address => PoolInfo) public poolInfos; + mapping(address => mapping(address => StakeInfo)) public stakes; + mapping(address => uint256) private lpTokenBalances; + + event LogPoolAdded(address indexed lpToken, uint256 allocPoint); + event LogPoolAllocationUpdate(address indexed lpToken, uint256 allocPoint); + event LogPoolRewardUpdate( + address indexed lpToken, + uint256 accRewardPerShare, + uint256 lastRewardBlock + ); + event LogDeposit(address indexed user, uint256 amount); + event LogWithdraw(address indexed user, uint256 amount); + event LogRewardPaid(address indexed user, uint256 reward); + + function initialize( + address _rewardToken, + uint256 _rewardPerBlock + ) external initializer { + OwnableUpgradeable.__Ownable_init(); + PausableUpgradeable.__Pausable_init(); + + rewardToken = _rewardToken; + rewardPerBlock = _rewardPerBlock; + } + + function addPool( + address _lpToken, + uint _allocPoint + ) external override onlyOwner whenNotPaused { + poolInfos[_lpToken] = PoolInfo({ + allocPoint: _allocPoint, + lastRewardBlock: block.number, + accRewardPerShare: 0 + }); + + totalAllocPoint += _allocPoint; + + emit LogPoolAdded(_lpToken, _allocPoint); + } + + function updateAllocation( + address _lpToken, + uint _allocPoint + ) external override onlyOwner whenNotPaused { + PoolInfo storage pool = poolInfos[_lpToken]; + + totalAllocPoint = totalAllocPoint + _allocPoint - pool.allocPoint; + pool.allocPoint = _allocPoint; + + emit LogPoolAllocationUpdate(_lpToken, _allocPoint); + } + + function deposit(address _lpToken, uint256 _amount) external override whenNotPaused { + require(_lpToken != address(0), "Zero address"); + require(_amount > 0, "Zero amount"); + + updatePoolReward(_lpToken); + + PoolInfo storage pool = poolInfos[_lpToken]; + StakeInfo storage stake = stakes[_lpToken][msg.sender]; + + if (stake.amount > 0) { + uint256 pending = (stake.amount * pool.accRewardPerShare) / + 1e12 - + stake.rewardDebt; + if (pending > 0) { + address(rewardToken).safeTransfer(msg.sender, pending); + emit LogRewardPaid(msg.sender, pending); + } + } + + stake.amount += _amount; + stake.rewardDebt = (stake.amount * pool.accRewardPerShare) / 1e12; + + _lpToken.safeTransferFrom(msg.sender, address(this), _amount); + lpTokenBalances[_lpToken] += _amount; + + emit LogDeposit(msg.sender, _amount); + } + + function withdraw( + address _lpToken, + uint256 _amount + ) external override whenNotPaused { + require(_lpToken != address(0), "Zero address"); + PoolInfo storage pool = poolInfos[_lpToken]; + StakeInfo storage stake = stakes[_lpToken][msg.sender]; + + require(stake.amount >= _amount, "Not sufficient amount"); + + updatePoolReward(_lpToken); + + uint256 pending = (stake.amount * pool.accRewardPerShare) / + 1e12 - + stake.rewardDebt; + if (pending > 0) { + address(rewardToken).safeTransfer(msg.sender, pending); + emit LogRewardPaid(msg.sender, pending); + } + + if (_amount > 0) { + stake.amount -= _amount; + _lpToken.safeTransfer(msg.sender, _amount); + lpTokenBalances[_lpToken] += _amount; + } + + stake.rewardDebt = (stake.amount * pool.accRewardPerShare) / 1e12; + + emit LogWithdraw(msg.sender, _amount); + } + + function emergencyWithdraw(address _lpToken) override external whenPaused { + StakeInfo storage stake = stakes[_lpToken][msg.sender]; + require(stake.amount > 0, "Zero balance"); + + uint amount = stake.amount; + + stake.amount = 0; + stake.rewardDebt = 0; + + _lpToken.safeTransfer(msg.sender, amount); + + emit LogWithdraw(msg.sender, amount); + } + + function setRewardPerBlock(uint256 _rewardPerBlock) override external onlyOwner { + rewardPerBlock = _rewardPerBlock; + } + + function pause() external override onlyOwner { + _pause(); + } + + function updatePoolReward(address _lpToken) public override { + PoolInfo storage pool = poolInfos[_lpToken]; + + if (block.number <= pool.lastRewardBlock) { + return; + } + + uint256 lpSupply = lpTokenBalances[_lpToken]; + + if (lpSupply == 0) { + pool.lastRewardBlock = block.number; + return; + } + + uint256 multiplier = block.number - pool.lastRewardBlock; + uint256 reward = (multiplier * rewardPerBlock * pool.allocPoint) / + totalAllocPoint; + pool.accRewardPerShare += (reward * 1e12) / lpSupply; + pool.lastRewardBlock = block.number; + + emit LogPoolRewardUpdate( + _lpToken, + pool.accRewardPerShare, + pool.lastRewardBlock + ); + } +} diff --git a/contracts/farm/IFarm.sol b/contracts/farm/IFarm.sol new file mode 100644 index 0000000..a2fd3ba --- /dev/null +++ b/contracts/farm/IFarm.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +interface IFarm { + function addPool(address _lpToken, uint _allocPoint) external; + + function updateAllocation(address _lpToken, uint _allocPoint) external; + + function deposit(address _lpToken, uint256 _amount) external; + + function withdraw(address _lpToken, uint256 _amount) external; + + function emergencyWithdraw(address _lpToken) external; + + function setRewardPerBlock(uint256 _rewardPerBlock) external; + + function pause() external; + + function updatePoolReward(address _lpToken) external; + + function rewardPerBlock() external view returns (uint); + + function totalAllocPoint() external view returns (uint); + + function rewardToken() external view returns (address); +} diff --git a/contracts/farm/libraries/SafeToken.sol b/contracts/farm/libraries/SafeToken.sol new file mode 100644 index 0000000..de3596c --- /dev/null +++ b/contracts/farm/libraries/SafeToken.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +interface ERC20Interface { + function balanceOf(address user) external view returns (uint256); +} + +library SafeToken { + function myBalance(address token) internal view returns (uint256) { + return ERC20Interface(token).balanceOf(address(this)); + } + + function balanceOf(address token, address user) internal view returns (uint256) { + return ERC20Interface(token).balanceOf(user); + } + + function safeApprove(address token, address to, uint256 value) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); // bytes4(keccak256(bytes('approve(address,uint256)'))); + require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove"); + } + + function safeTransfer(address token, address to, uint256 value) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); // bytes4(keccak256(bytes('transfer(address,uint256)'))); + require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer"); + } + + function safeTransferFrom(address token, address from, address to, uint256 value) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); + require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom"); + } + + function safeTransferETH(address to, uint256 value) internal { + (bool success, ) = to.call{ value: value }(new bytes(0)); + require(success, "!safeTransferETH"); + } +} diff --git a/package.json b/package.json index 13d8778..5b94878 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "license": "GPL-3.0", "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^1.0.2", - "@openzeppelin/contracts": "^4.0.0", + "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/contracts-upgradeable": "^4.7.3", "@uniswap/v2-core": "1.0.0", "@uniswap/v2-periphery": "^1.1.0-beta.0", "dotenv": "^16.0.1", diff --git a/test/farm.test.js b/test/farm.test.js new file mode 100644 index 0000000..f01a68b --- /dev/null +++ b/test/farm.test.js @@ -0,0 +1,136 @@ +const { advanceBlock } = require('./helpers/time'); + +const { ethers } = require("hardhat"); +const { expect } = require("chai"); +const { parseEther } = require("ethers/lib/utils"); + +describe("Farm", () => { + beforeEach(async () => { + const signers = await ethers.getSigners(); + + this.dev = signers[0]; + this.alice = signers[1]; + this.bob = signers[2]; + + const BEP20 = await ethers.getContractFactory("BEP20"); + + this.reward = await BEP20.deploy('RewardToken', 'RWD'); + this.lp1 = await BEP20.deploy('LPToken', 'LP1'); + this.lp2 = await BEP20.deploy('LPToken', 'LP2'); + this.lp3 = await BEP20.deploy('LPToken', 'LP2'); + + const Farm = await ethers.getContractFactory("Farm"); + this.farm = await Farm.deploy() + await this.farm.initialize(this.reward.address, '100'); + + this.farmAsAlice = this.farm.connect(this.alice); + this.lp1AsAlice = this.lp1.connect(this.alice); + this.lp2AsAlice = this.lp2.connect(this.alice); + this.lp3AsAlice = this.lp3.connect(this.alice); + + await this.reward.mint(this.farm.address, parseEther('2000')) + + await this.lp1.mint(this.bob.address, '2000') + await this.lp2.mint(this.bob.address, '2000') + await this.lp3.mint(this.bob.address, '2000') + + await this.lp1.mint(this.alice.address, '2000') + await this.lp2.mint(this.alice.address, '2000') + await this.lp3.mint(this.alice.address, '2000') + }); + it('comlex scenario', async () => { + await this.farm.addPool(this.lp1.address, '2000'); + await this.farm.addPool(this.lp2.address, '200'); + await this.farm.addPool(this.lp3.address, '1000'); + + await this.lp1AsAlice.approve(this.farm.address, '1000'); + await this.lp2AsAlice.approve(this.farm.address, '1000'); + await this.lp3AsAlice.approve(this.farm.address, '1000'); + + await this.farmAsAlice.deposit(this.lp1.address, '1000'); + await this.farmAsAlice.deposit(this.lp2.address, '1000'); + await this.farmAsAlice.deposit(this.lp3.address, '1000'); + + for (let i = 0; i < 10; i++) { + await advanceBlock(); + } + + // const m = 1685365744 - 26 + await this.farm.updatePoolReward(this.lp1.address); + await this.farm.updatePoolReward(this.lp2.address); + await this.farm.updatePoolReward(this.lp3.address); + + const pool1 = await this.farm.poolInfos(this.lp1.address); + const pool2 = await this.farm.poolInfos(this.lp2.address); + const pool3 = await this.farm.poolInfos(this.lp3.address); + + expect(pool1.accRewardPerShare).to.be.eq('812000000000'); + expect(pool2.accRewardPerShare).to.be.eq('81000000000'); + expect(pool3.accRewardPerShare).to.be.eq('406000000000'); + + await this.farmAsAlice.withdraw(this.lp1.address, '0'); // 999 + expect(await this.reward.balanceOf(this.alice.address)).to.be.eq('999'); + await this.farmAsAlice.withdraw(this.lp2.address, '0'); // 99 + expect(await this.reward.balanceOf(this.alice.address)).to.be.eq('1098'); + await this.farmAsAlice.withdraw(this.lp3.address, '0'); // 599 + expect(await this.reward.balanceOf(this.alice.address)).to.be.eq('1597'); + }) + + it('deposit/withdraw', async () => { + await this.farm.addPool(this.lp1.address, '1000'); + + await this.lp1AsAlice.approve(this.farm.address, '2000'); + await this.farmAsAlice.deposit(this.lp1.address, '2000'); + + expect(await this.lp1.balanceOf(this.alice.address)).to.be.eq('0'); + expect(await this.lp1.balanceOf(this.farm.address)).to.be.eq('2000'); + + await this.farmAsAlice.withdraw(this.lp1.address, '1000'); + + expect(await this.lp1.balanceOf(this.alice.address)).to.be.eq('1000'); + expect(await this.lp1.balanceOf(this.farm.address)).to.be.eq('1000'); + }) + + it('emergency withdraw', async () => { + await this.farm.addPool(this.lp1.address, '1000'); + + await this.lp1AsAlice.approve(this.farm.address, '2000'); + await this.farmAsAlice.deposit(this.lp1.address, '2000'); + + expect(await this.lp1.balanceOf(this.alice.address)).to.be.eq('0'); + expect(await this.lp1.balanceOf(this.farm.address)).to.be.eq('2000'); + + await this.farm.pause(); + await this.farmAsAlice.emergencyWithdraw(this.lp1.address); + + expect(await this.lp1.balanceOf(this.alice.address)).to.be.eq('2000'); + expect(await this.lp1.balanceOf(this.farm.address)).to.be.eq('0'); + }) + + it('update allocation', async () => { + await this.farm.addPool(this.lp1.address, '1000'); + + let pool = await this.farm.poolInfos(this.lp1.address); + expect(pool.allocPoint).to.be.eq('1000'); + + await this.farm.updateAllocation(this.lp1.address, '2000'); + + pool = await this.farm.poolInfos(this.lp1.address); + expect(pool.allocPoint).to.be.eq('2000'); + }) + + it('update rewardPerBlock', async () => { + expect(await this.farm.rewardPerBlock()).to.be.eq('100'); + + await this.farm.setRewardPerBlock('200'); + + expect(await this.farm.rewardPerBlock()).to.be.eq('200'); + }) + + it('access control', async () => { + await expect(this.farmAsAlice.addPool(this.lp1.address, '1000')).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(this.farmAsAlice.setRewardPerBlock('0')).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(this.farmAsAlice.pause()).to.be.revertedWith('Ownable: caller is not the owner'); + await expect(this.farmAsAlice.updateAllocation(this.lp1.address, '1')).to.be.revertedWith('Ownable: caller is not the owner'); + }) +}); \ No newline at end of file diff --git a/test/helpers/time.js b/test/helpers/time.js new file mode 100644 index 0000000..5f3e7e8 --- /dev/null +++ b/test/helpers/time.js @@ -0,0 +1,34 @@ +const { ethers } = require("hardhat"); + +async function latest() { + const block = await ethers.provider.getBlock("latest") + return ethers.BigNumber.from(block.timestamp) +} + +async function latestNumber() { + const block = await ethers.provider.getBlock("latest") + return ethers.BigNumber.from(block.number) +} + +async function advanceBlock() { + await ethers.provider.send("evm_mine", []) +} + +async function increase(duration) { + await ethers.provider.send("evm_increaseTime", [Number(duration)]) + await advanceBlock() +} + +// async function increase2(integer) { +// // First we increase the time +// await web3.currentProvider.send({ +// jsonrpc: '2.0', +// method: 'evm_increaseTime', +// params: [Number(integer)], +// id: 0, +// }, () => {}); +// // Then we mine a block to actually get the time change to occurs +// await advanceBlock(); +// } + +module.exports = { latest, advanceBlock, increase, latestNumber} \ No newline at end of file From 30318b3032ca2b77f4c852c6fe187d5f05261bc1 Mon Sep 17 00:00:00 2001 From: Vitalii Koval Date: Wed, 7 Jun 2023 14:27:18 +0400 Subject: [PATCH 2/4] update events and pausing/unpausing flow --- contracts/farm/Farm.sol | 27 ++++++++++++++++----------- contracts/farm/IFarm.sol | 2 ++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/contracts/farm/Farm.sol b/contracts/farm/Farm.sol index 6547656..69b00f8 100644 --- a/contracts/farm/Farm.sol +++ b/contracts/farm/Farm.sol @@ -38,9 +38,9 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { uint256 accRewardPerShare, uint256 lastRewardBlock ); - event LogDeposit(address indexed user, uint256 amount); - event LogWithdraw(address indexed user, uint256 amount); - event LogRewardPaid(address indexed user, uint256 reward); + event LogDeposit(address indexed user, address indexed lpToken, uint256 amount); + event LogWithdraw(address indexed user, address indexed lpToken, uint256 amount); + event LogRewardPaid(address indexed user, address indexed lpToken, uint256 reward); function initialize( address _rewardToken, @@ -95,7 +95,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { stake.rewardDebt; if (pending > 0) { address(rewardToken).safeTransfer(msg.sender, pending); - emit LogRewardPaid(msg.sender, pending); + emit LogRewardPaid(msg.sender, _lpToken, pending); } } @@ -105,7 +105,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { _lpToken.safeTransferFrom(msg.sender, address(this), _amount); lpTokenBalances[_lpToken] += _amount; - emit LogDeposit(msg.sender, _amount); + emit LogDeposit(msg.sender, _lpToken, _amount); } function withdraw( @@ -125,7 +125,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { stake.rewardDebt; if (pending > 0) { address(rewardToken).safeTransfer(msg.sender, pending); - emit LogRewardPaid(msg.sender, pending); + emit LogRewardPaid(msg.sender, _lpToken, pending); } if (_amount > 0) { @@ -136,7 +136,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { stake.rewardDebt = (stake.amount * pool.accRewardPerShare) / 1e12; - emit LogWithdraw(msg.sender, _amount); + emit LogWithdraw(msg.sender, _lpToken, _amount); } function emergencyWithdraw(address _lpToken) override external whenPaused { @@ -150,10 +150,10 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { _lpToken.safeTransfer(msg.sender, amount); - emit LogWithdraw(msg.sender, amount); + emit LogWithdraw(msg.sender, _lpToken, amount); } - function setRewardPerBlock(uint256 _rewardPerBlock) override external onlyOwner { + function setRewardPerBlock(uint256 _rewardPerBlock) override external onlyOwner whenNotPaused { rewardPerBlock = _rewardPerBlock; } @@ -161,6 +161,12 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { _pause(); } + + function unpause() external override onlyOwner { + _unpause(); + } + + function updatePoolReward(address _lpToken) public override { PoolInfo storage pool = poolInfos[_lpToken]; @@ -176,8 +182,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { } uint256 multiplier = block.number - pool.lastRewardBlock; - uint256 reward = (multiplier * rewardPerBlock * pool.allocPoint) / - totalAllocPoint; + uint256 reward = (multiplier * rewardPerBlock * pool.allocPoint) / totalAllocPoint; pool.accRewardPerShare += (reward * 1e12) / lpSupply; pool.lastRewardBlock = block.number; diff --git a/contracts/farm/IFarm.sol b/contracts/farm/IFarm.sol index a2fd3ba..c886a84 100644 --- a/contracts/farm/IFarm.sol +++ b/contracts/farm/IFarm.sol @@ -16,6 +16,8 @@ interface IFarm { function pause() external; + function unpause() external; + function updatePoolReward(address _lpToken) external; function rewardPerBlock() external view returns (uint); From 3e9b3181f9531ecfdd87768f4780bf7e84bc20d1 Mon Sep 17 00:00:00 2001 From: Vitalii Koval Date: Thu, 8 Jun 2023 16:24:28 +0400 Subject: [PATCH 3/4] fix lp balances calculation --- contracts/farm/Farm.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/farm/Farm.sol b/contracts/farm/Farm.sol index 69b00f8..b07dcd9 100644 --- a/contracts/farm/Farm.sol +++ b/contracts/farm/Farm.sol @@ -55,7 +55,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { function addPool( address _lpToken, - uint _allocPoint + uint256 _allocPoint ) external override onlyOwner whenNotPaused { poolInfos[_lpToken] = PoolInfo({ allocPoint: _allocPoint, @@ -70,7 +70,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { function updateAllocation( address _lpToken, - uint _allocPoint + uint256 _allocPoint ) external override onlyOwner whenNotPaused { PoolInfo storage pool = poolInfos[_lpToken]; @@ -131,7 +131,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { if (_amount > 0) { stake.amount -= _amount; _lpToken.safeTransfer(msg.sender, _amount); - lpTokenBalances[_lpToken] += _amount; + lpTokenBalances[_lpToken] -= _amount; } stake.rewardDebt = (stake.amount * pool.accRewardPerShare) / 1e12; @@ -143,12 +143,13 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { StakeInfo storage stake = stakes[_lpToken][msg.sender]; require(stake.amount > 0, "Zero balance"); - uint amount = stake.amount; + uint256 amount = stake.amount; stake.amount = 0; stake.rewardDebt = 0; _lpToken.safeTransfer(msg.sender, amount); + lpTokenBalances[_lpToken] -= amount; emit LogWithdraw(msg.sender, _lpToken, amount); } From e5e711bbe70da68e1087a04e599e751520d75783 Mon Sep 17 00:00:00 2001 From: Vitalii Koval Date: Fri, 9 Jun 2023 17:04:12 +0400 Subject: [PATCH 4/4] add deployment script --- contracts/farm/Farm.sol | 3 ++- scripts/deploy-farm.js | 26 ++++++++++++++++++++++++++ test/farm.test.js | 8 ++++---- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 scripts/deploy-farm.js diff --git a/contracts/farm/Farm.sol b/contracts/farm/Farm.sol index b07dcd9..ba7aa24 100644 --- a/contracts/farm/Farm.sol +++ b/contracts/farm/Farm.sol @@ -25,7 +25,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { address public rewardToken; uint256 public rewardPerBlock; - uint256 public totalAllocPoint = 0; + uint256 public totalAllocPoint; mapping(address => PoolInfo) public poolInfos; mapping(address => mapping(address => StakeInfo)) public stakes; @@ -51,6 +51,7 @@ contract Farm is IFarm, OwnableUpgradeable, PausableUpgradeable { rewardToken = _rewardToken; rewardPerBlock = _rewardPerBlock; + totalAllocPoint = 0; } function addPool( diff --git a/scripts/deploy-farm.js b/scripts/deploy-farm.js new file mode 100644 index 0000000..507869c --- /dev/null +++ b/scripts/deploy-farm.js @@ -0,0 +1,26 @@ +const { ethers , upgrades} = require("hardhat"); + +const rewardAddress = '0x0000000000000000000000000000000000000000'; +const rewardPerBLock = '0'; + +async function main() { + let admin; + + [admin] = await ethers.getSigners(); + + console.log("Deploying contracts with the account:", admin.address); + console.log("Account balance:", (await admin.getBalance()).toString()); + + const Farm = await ethers.getContractFactory("Farm", admin); + const farm = await upgrades.deployProxy(Farm, [rewardAddress, rewardPerBLock]); + await farm.deployed(); + + console.log("Farm address: " + farm.address); +} + +main() +.then(() => process.exit(0)) +.catch((error) => { + console.error(error); + process.exit(1); +}); \ No newline at end of file diff --git a/test/farm.test.js b/test/farm.test.js index f01a68b..7b2ce99 100644 --- a/test/farm.test.js +++ b/test/farm.test.js @@ -1,4 +1,4 @@ -const { advanceBlock } = require('./helpers/time'); +const { advanceBlock, latestNumber } = require('./helpers/time'); const { ethers } = require("hardhat"); const { expect } = require("chai"); @@ -60,9 +60,9 @@ describe("Farm", () => { await this.farm.updatePoolReward(this.lp2.address); await this.farm.updatePoolReward(this.lp3.address); - const pool1 = await this.farm.poolInfos(this.lp1.address); - const pool2 = await this.farm.poolInfos(this.lp2.address); - const pool3 = await this.farm.poolInfos(this.lp3.address); + let pool1 = await this.farm.poolInfos(this.lp1.address); + let pool2 = await this.farm.poolInfos(this.lp2.address); + let pool3 = await this.farm.poolInfos(this.lp3.address); expect(pool1.accRewardPerShare).to.be.eq('812000000000'); expect(pool2.accRewardPerShare).to.be.eq('81000000000');