diff --git a/contracts/OleLpStakeAutomator.sol b/contracts/OleLpStakeAutomator.sol new file mode 100644 index 0000000..db7f490 --- /dev/null +++ b/contracts/OleLpStakeAutomator.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.7.6; + +import "./DelegateInterface.sol"; +import "./Adminable.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./OleLpStakeAutomatorInterface.sol"; +import "./lib/TransferHelper.sol"; + +contract OleLpStakeAutomator is DelegateInterface, Adminable, ReentrancyGuard, OleLpStakeAutomatorInterface, OleLpStakeAutomatorStorage { + using TransferHelper for IERC20; + using SafeMath for uint; + + function initialize( + XOLEInterface _xole, + IERC20 _ole, + IERC20 _otherToken, + IERC20 _lpToken, + IWETH _nativeToken, + IUniswapV2Router01 _router + ) public { + require(msg.sender == admin, "NAD"); + xole = _xole; + ole = _ole; + otherToken = _otherToken; + lpToken = _lpToken; + nativeToken = _nativeToken; + router = _router; + } + + function createLockBoth(uint oleAmount, uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) external payable override nonReentrant { + transferInBothAndLock(oleAmount, otherAmount, unlockTime, oleMin, otherMin); + } + + function createLockOLE(uint oleAmount, uint unlockTime, uint oleMin, uint otherMin) external override nonReentrant { + transferInOleAndLock(oleAmount, unlockTime, oleMin, otherMin); + } + + function createLockOther(uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) external payable override nonReentrant { + transferInOtherAndLock(otherAmount, unlockTime, oleMin, otherMin); + } + + + function increaseAmountBoth(uint oleAmount, uint otherAmount, uint oleMin, uint otherMin) external payable override nonReentrant { + transferInBothAndLock(oleAmount, otherAmount, 0, oleMin, otherMin); + } + + function increaseAmountOLE(uint oleAmount, uint oleMin, uint otherMin) external override nonReentrant { + transferInOleAndLock(oleAmount, 0, oleMin, otherMin); + } + + function increaseAmountOther(uint otherAmount, uint oleMin, uint otherMin) external payable override nonReentrant { + transferInOtherAndLock(otherAmount, 0, oleMin, otherMin); + } + + + function withdrawBoth(uint oleMin, uint otherMin) external override nonReentrant { + (uint oleOut, uint otherOut) = removeLiquidity(oleMin, otherMin); + doTransferOut(msg.sender, ole, oleOut); + doTransferOut(msg.sender, otherToken, otherOut); + } + + function withdrawOle(uint oleMin, uint otherMin) external override nonReentrant { + (uint oleOut, uint otherOut) = removeLiquidity(oleMin, otherMin); + //swap + otherToken.safeApprove(address(router), otherOut); + uint[] memory amounts = router.swapExactTokensForTokens(otherOut, 0, getPath(ole), address(this), timestamp()); + uint oleSwapIn = amounts[1]; + doTransferOut(msg.sender, ole, oleOut.add(oleSwapIn)); + } + + function withdrawOther(uint oleMin, uint otherMin) external override nonReentrant { + (uint oleOut, uint otherOut) = removeLiquidity(oleMin, otherMin); + //swap + ole.safeApprove(address(router), oleOut); + uint[] memory amounts = router.swapExactTokensForTokens(oleOut, 0, getPath(otherToken), address(this), timestamp()); + uint otherSwapIn = amounts[1]; + doTransferOut(msg.sender, otherToken, otherOut.add(otherSwapIn)); + } + + function transferInBothAndLock(uint oleAmount, uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) internal { + // transferIn + uint oleIn = transferIn(msg.sender, ole, oleAmount); + uint otherIn = transferIn(msg.sender, otherToken, otherAmount); + // add liquidity and increase amount + addLiquidityAndLock(oleIn, otherIn, unlockTime, oleMin, otherMin); + } + + function transferInOleAndLock(uint oleAmount, uint unlockTime, uint oleMin, uint otherMin) internal { + // transferIn + uint oleIn = transferIn(msg.sender, ole, oleAmount); + // swap + uint oleSwapOut = oleIn.div(2); + ole.safeApprove(address(router), oleSwapOut); + uint[] memory amounts = router.swapExactTokensForTokens(oleSwapOut, 0, getPath(otherToken), address(this), timestamp()); + uint otherIn = amounts[1]; + // add liquidity and create lock + addLiquidityAndLock(oleIn.sub(oleSwapOut), otherIn, unlockTime, oleMin, otherMin); + } + + function transferInOtherAndLock(uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) internal { + // transferIn + uint otherIn = transferIn(msg.sender, otherToken, otherAmount); + // swap + uint otherSwapOut = otherIn.div(2); + otherToken.safeApprove(address(router), otherSwapOut); + uint[] memory amounts = router.swapExactTokensForTokens(otherSwapOut, 0, getPath(ole), address(this), timestamp()); + uint oleIn = amounts[1]; + // add liquidity and create lock + addLiquidityAndLock(oleIn, otherIn.sub(otherSwapOut), unlockTime, oleMin, otherMin); + } + + function addLiquidityAndLock(uint oleIn, uint otherIn, uint unlockTime, uint oleMin, uint otherMin) internal { + // add liquidity + ole.safeApprove(address(router), oleIn); + otherToken.safeApprove(address(router), otherIn); + (uint oleOut, uint otherOut, uint liquidity) = router.addLiquidity(address(ole), address(otherToken), oleIn, otherIn, oleMin, otherMin, address(this), timestamp()); + // create lock + lpToken.safeApprove(address(xole), liquidity); + if (unlockTime > 0) { + xole.create_lock_for(msg.sender, liquidity, unlockTime); + } else { + xole.increase_amount_for(msg.sender, liquidity); + } + // back remainder + if (oleIn > oleOut) { + doTransferOut(msg.sender, ole, oleIn - oleOut); + } + if (otherIn > otherOut) { + doTransferOut(msg.sender, otherToken, otherIn - otherOut); + } + } + + function removeLiquidity(uint oleMin, uint otherMin) internal returns (uint oleOut, uint otherOut){ + //withdraw + xole.withdraw_automator(msg.sender); + uint liquidity = lpToken.balanceOf(address(this)); + lpToken.safeApprove(address(router), liquidity); + //remove liquidity + (oleOut, otherOut) = router.removeLiquidity(address(ole), address(otherToken), liquidity, oleMin, otherMin, address(this), timestamp()); + } + + function transferIn(address from, IERC20 token, uint amount) internal returns (uint) { + if (isNativeToken(token)) { + nativeToken.deposit{value : msg.value}(); + return msg.value; + } else { + return token.safeTransferFrom(from, address(this), amount); + } + } + + function doTransferOut(address to, IERC20 token, uint amount) internal { + if (isNativeToken(token)) { + nativeToken.withdraw(amount); + (bool success,) = to.call{value : amount}(""); + require(success); + } else { + token.safeTransfer(to, amount); + } + } + + function isNativeToken(IERC20 token) internal view returns (bool) { + return address(token) == address(nativeToken); + } + + function getPath(IERC20 destToken) internal view returns (address[] memory path) { + path = new address[](2); + path[0] = address(destToken) == address(ole) ? address(otherToken) : address(ole); + path[1] = address(destToken) == address(ole) ? address(ole) : address(otherToken); + } + + function timestamp() internal view returns (uint){ + return block.timestamp; + } +} + diff --git a/contracts/OleLpStakeAutomatorDelegator.sol b/contracts/OleLpStakeAutomatorDelegator.sol new file mode 100644 index 0000000..33e8551 --- /dev/null +++ b/contracts/OleLpStakeAutomatorDelegator.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.7.6; + +import "./Adminable.sol"; +import "./DelegatorInterface.sol"; + + +contract OleLpStakeAutomatorDelegator is DelegatorInterface, Adminable { + + constructor(address _xole, + address _ole, + address _otherToken, + address _lpToken, + address _nativeToken, + address _router, + address payable admin_, + address implementation_) { + admin = msg.sender; + // Creator of the contract is admin during initialization + // First delegate gets to initialize the delegator (i.e. storage contract) + delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,address,address,address,address)", + _xole, + _ole, + _otherToken, + _lpToken, + _nativeToken, + _router)); + implementation = implementation_; + // Set the proper admin now that initialization is done + admin = admin_; + } + + /** + * Called by the admin to update the implementation of the delegator + * @param implementation_ The address of the new implementation for delegation + */ + function setImplementation(address implementation_) public override onlyAdmin { + address oldImplementation = implementation; + implementation = implementation_; + emit NewImplementation(oldImplementation, implementation); + } +} \ No newline at end of file diff --git a/contracts/OleLpStakeAutomatorInterface.sol b/contracts/OleLpStakeAutomatorInterface.sol new file mode 100644 index 0000000..91c150a --- /dev/null +++ b/contracts/OleLpStakeAutomatorInterface.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.7.6; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol'; +import "./XOLEInterface.sol"; +import "./IWETH.sol"; + +contract OleLpStakeAutomatorStorage { + XOLEInterface public xole; + IERC20 public ole; + IERC20 public otherToken; + IERC20 public lpToken; + IWETH public nativeToken; + IUniswapV2Router01 router; +} + +interface OleLpStakeAutomatorInterface { + + function createLockBoth(uint oleAmount, uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) external payable; + + function createLockOLE(uint oleAmount, uint unlockTime, uint oleMin, uint otherMin) external ; + + function createLockOther(uint otherAmount, uint unlockTime, uint oleMin, uint otherMin) external payable; + + + function increaseAmountBoth(uint oleAmount, uint otherAmount, uint oleMin, uint otherMin) external payable; + + function increaseAmountOLE(uint oleAmount, uint oleMin, uint otherMin) external ; + + function increaseAmountOther(uint otherAmount, uint oleMin, uint otherMin) external payable; + + + function withdrawBoth(uint oleMin, uint otherMin) external; + + function withdrawOle(uint oleMin, uint otherMin) external; + + function withdrawOther(uint oleMin, uint otherMin) external; + + +} \ No newline at end of file diff --git a/contracts/XOLE.sol b/contracts/XOLE.sol index 1d85e01..ffe4509 100644 --- a/contracts/XOLE.sol +++ b/contracts/XOLE.sol @@ -26,6 +26,10 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent IERC20 public shareToken; + IERC20 public oleLpStakeToken; + + address public oleLpStakeAutomator; + /* We cannot really do block numbers per se b/c slope is per time, not per block and per block could be fairly bad b/c Ethereum changes blocktimes. What we can do is to extrapolate ***At functions @@ -75,6 +79,17 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent shareToken = IERC20(_shareToken); } + function setOleLpStakeToken(address _oleLpStakeToken) external override onlyAdmin { + require(_oleLpStakeToken != address(0), "0x"); + require(address(oleLpStakeToken) == address(0), "Initialized"); + oleLpStakeToken = IERC20(_oleLpStakeToken); + } + + function setOleLpStakeAutomator(address _oleLpStakeAutomator) external override onlyAdmin { + require(_oleLpStakeAutomator != address(0), "0x"); + oleLpStakeAutomator = _oleLpStakeAutomator; + } + // Fees sharing functions ===== function withdrawDevFund() external override { require(msg.sender == dev, "Dev only"); @@ -99,7 +114,7 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent function shareableTokenAmountInternal() internal view returns (uint256 shareableAmount){ uint claimable = claimableTokenAmountInternal(); - if (address(shareToken) == address(oleToken)) { + if (address(shareToken) == address(oleLpStakeToken)) { shareableAmount = shareToken.balanceOf(address(this)).sub(totalLocked).sub(claimable).sub(devFund); } else { shareableAmount = shareToken.balanceOf(address(this)).sub(claimable).sub(devFund); @@ -135,8 +150,8 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent } } // If fromToken is ole, check amount - if (fromToken == address(oleToken)) { - require(oleToken.balanceOf(address(this)).sub(totalLocked) >= amount, 'Exceed OLE balance'); + if (fromToken == address(oleLpStakeToken)) { + require(oleLpStakeToken.balanceOf(address(this)).sub(totalLocked) >= amount, 'Exceed OLE balance'); } uint newReward; if (fromToken == address(shareToken)) { @@ -204,16 +219,23 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent /// @param _value Amount to deposit /// @param _unlock_time Epoch time when tokens unlock, rounded down to whole weeks function create_lock(uint256 _value, uint256 _unlock_time) external override nonReentrant() { - // Locktime is rounded down to weeks - uint256 unlock_time = _unlock_time.div(WEEK).mul(WEEK); - LockedBalance memory _locked = locked[msg.sender]; + uint256 unlock_time = create_lock_check(msg.sender, _value, _unlock_time); + _deposit_for(msg.sender, _value, unlock_time, locked[msg.sender], CREATE_LOCK_TYPE); + } + function create_lock_for(address to, uint256 _value, uint256 _unlock_time) external override nonReentrant() { + uint256 unlock_time = create_lock_check(to, _value, _unlock_time); + _deposit_for(to, _value, unlock_time, locked[to], CREATE_LOCK_TYPE); + } + + function create_lock_check(address to, uint256 _value, uint256 _unlock_time) internal view returns (uint unlock_time) { + // Locktime is rounded down to weeks + unlock_time = _unlock_time.div(WEEK).mul(WEEK); + LockedBalance memory _locked = locked[to]; require(_value > 0, "Non zero value"); require(_locked.amount == 0, "Withdraw old tokens first"); - require(unlock_time > block.timestamp, "Can only lock until time in the future"); + require(unlock_time >= block.timestamp + (2 * WEEK), "Can only lock until time in the future"); require(unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"); - - _deposit_for(msg.sender, _value, unlock_time, _locked, CREATE_LOCK_TYPE); } @@ -221,11 +243,20 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent /// without modifying the unlock time /// @param _value Amount of tokens to deposit and add to the lock function increase_amount(uint256 _value) external override nonReentrant() { - LockedBalance memory _locked = locked[msg.sender]; + LockedBalance memory _locked = increase_amount_check(msg.sender, _value); + _deposit_for(msg.sender, _value, 0, _locked, INCREASE_LOCK_AMOUNT); + } + + function increase_amount_for(address to, uint256 _value) external override nonReentrant() { + LockedBalance memory _locked = increase_amount_check(to, _value); + _deposit_for(to, _value, 0, _locked, INCREASE_LOCK_AMOUNT); + } + + function increase_amount_check(address to, uint256 _value) internal view returns (LockedBalance memory _locked) { + _locked = locked[to]; require(_value > 0, "need non - zero value"); require(_locked.amount > 0, "No existing lock found"); require(_locked.end > block.timestamp, "Cannot add to expired lock. Withdraw"); - _deposit_for(msg.sender, _value, 0, _locked, INCREASE_LOCK_AMOUNT); } /// @notice Extend the unlock time for `msg.sender` to `_unlock_time` @@ -234,9 +265,9 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent LockedBalance memory _locked = locked[msg.sender]; // Locktime is rounded down to weeks uint256 unlock_time = _unlock_time.div(WEEK).mul(WEEK); - require(_locked.end > block.timestamp, "Lock expired"); require(_locked.amount > 0, "Nothing is locked"); require(unlock_time > _locked.end, "Can only increase lock duration"); + require(unlock_time >= block.timestamp + (2 * WEEK), "Can only lock until time in the future"); require(unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"); _deposit_for(msg.sender, 0, unlock_time, _locked, INCREASE_UNLOCK_TIME); @@ -249,8 +280,8 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent /// @param _locked Previous locked amount / timestamp /// @param _type For event only. function _deposit_for(address _addr, uint256 _value, uint256 unlock_time, LockedBalance memory _locked, int128 _type) internal { - uint256 locked_before = totalLocked; - totalLocked = locked_before.add(_value); + totalLocked = totalLocked.add(_value); + uint256 prevBalance = balances[_addr]; // Adding to existing lock, or if a lock is expired - creating a new one _locked.amount = _locked.amount.add(_value); @@ -260,7 +291,7 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent locked[_addr] = _locked; if (_value != 0) { - require(IERC20(oleToken).transferFrom(msg.sender, address(this), _value)); + oleLpStakeToken.safeTransferFrom(msg.sender, address(this), _value); } uint calExtraValue = _value; @@ -276,7 +307,7 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent } else { _mint(_addr, calExtraValue); } - emit Deposit(_addr, _value, _locked.end, _type, block.timestamp); + emit Deposit(_addr, _value, _locked.end, _type, prevBalance, balances[_addr]); } function _mint(address account, uint amount) internal { @@ -314,20 +345,29 @@ contract XOLE is DelegateInterface, Adminable, XOLEInterface, XOLEStorage, Reent /// @notice Withdraw all tokens for `msg.sender` /// @dev Only possible if the lock has expired function withdraw() external override nonReentrant() { - LockedBalance memory _locked = locked[msg.sender]; + _withdraw_for(msg.sender, msg.sender); + } + + function withdraw_automator(address owner) external override nonReentrant() { + require(oleLpStakeAutomator == msg.sender, "Not automator"); + _withdraw_for(owner, oleLpStakeAutomator); + } + + function _withdraw_for(address owner, address to) internal { + LockedBalance memory _locked = locked[owner]; require(_locked.amount > 0, "Nothing to withdraw"); require(block.timestamp >= _locked.end, "The lock didn't expire"); + uint256 prevBalance = balances[owner]; uint256 value = _locked.amount; totalLocked = totalLocked.sub(value); _locked.end = 0; _locked.amount = 0; - locked[msg.sender] = _locked; - oleToken.safeTransfer(msg.sender, value); - _burn(msg.sender); - emit Withdraw(msg.sender, value, block.timestamp); + locked[to] = _locked; + oleLpStakeToken.safeTransfer(to, value); + _burn(owner); + emit Withdraw(owner, value, prevBalance, balances[owner]); } - /// Delegate votes from `msg.sender` to `delegatee` /// @param delegatee The address to delegate votes to function delegate(address delegatee) public { diff --git a/contracts/XOLEInterface.sol b/contracts/XOLEInterface.sol index 57b60b3..4543341 100644 --- a/contracts/XOLEInterface.sol +++ b/contracts/XOLEInterface.sol @@ -126,15 +126,17 @@ contract XOLEStorage { event Deposit ( address indexed provider, uint256 value, - uint256 indexed locktime, + uint256 unlocktime, int128 type_, - uint256 ts + uint256 prevBalance, + uint256 balance ); event Withdraw ( address indexed provider, uint256 value, - uint256 ts + uint256 prevBalance, + uint256 balance ); event Supply ( @@ -175,17 +177,26 @@ interface XOLEInterface { function setShareToken(address _shareToken) external; + function setOleLpStakeToken(address _oleLpStakeToken) external; + + function setOleLpStakeAutomator(address _oleLpStakeAutomator) external; // xOLE functions function create_lock(uint256 _value, uint256 _unlock_time) external; + function create_lock_for(address to, uint256 _value, uint256 _unlock_time) external; + function increase_amount(uint256 _value) external; + function increase_amount_for(address to, uint256 _value) external; + function increase_unlock_time(uint256 _unlock_time) external; function withdraw() external; + function withdraw_automator(address owner) external; + function balanceOf(address addr) external view returns (uint256); } diff --git a/test/FeesShareTest.js b/test/FeesShareTest.js index e80f3ed..69b854a 100644 --- a/test/FeesShareTest.js +++ b/test/FeesShareTest.js @@ -87,10 +87,15 @@ contract("XOLE", async accounts => { assert.equal(await pair.token1(), await gotPair.token1()); xole = await utils.createXOLE(ole.address, admin, dev, dexAgg.address); await xole.setShareToken(ole.address); + await xole.setOleLpStakeToken(ole.address, {from: admin}); m.log("Created xOLE", last8(xole.address)); await utils.mint(usdt, xole.address, 10000); resetStep(); + let lastbk = await web3.eth.getBlock('latest'); + let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); + m.log("Move time to start of the week", new Date(timeToMove)); + await advanceBlockAndSetTime(timeToMove); let snapshot = await timeMachine.takeSnapshot(); snapshotId = snapshot['result']; }); @@ -104,7 +109,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK + 10); await xole.convertToSharingToken(toWei(1), 0, usdtOLEDexData); m.log("devFund:", (await xole.devFund()).toString()); @@ -129,7 +135,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK + 10); await xole.convertToSharingToken(toWei(10000), 0, '0x'); @@ -150,7 +157,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK + 10); await xole.convertToSharingToken(toWei(1000), 0, daiOLEDexData); m.log("xOLE OLE balance:", await ole.balanceOf(xole.address)); @@ -188,7 +196,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK); assert.equal('10000000000000000000000', (await usdt.balanceOf(xole.address)).toString()); await xole.convertToSharingToken(toWei(1000), 0, daiUsdtDexData); m.log("xOLE USDT balance:", await usdt.balanceOf(xole.address)); @@ -212,7 +221,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK + 10); assert.equal('10000000000000000000000', (await usdt.balanceOf(xole.address)).toString()); await xole.convertToSharingToken(toWei(1000), 0, "0x01" + "000000" + "03" + addressToBytes(dai.address) + addressToBytes(usdt.address) + addressToBytes(ole.address)); m.log("xOLE USDT balance:", await usdt.balanceOf(xole.address)); @@ -243,8 +253,8 @@ contract("XOLE", async accounts => { let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); m.log("Move time to start of the week", new Date(timeToMove)); - step("John stake 500 1 weeks"); - await xole.create_lock(toWei(500), timeToMove + WEEK + 60, {from: john}); + step("John stake 500 2 weeks"); + await xole.create_lock(toWei(500), timeToMove + 2 * WEEK + 60, {from: john}); step("Tom stake 500 2 weeks"); await xole.create_lock(toWei(500), timeToMove + (2 * WEEK) + 60 * 60, {from: tom}); assertPrint("Total staked:", toWei(1000), await xole.totalLocked()); @@ -266,8 +276,8 @@ contract("XOLE", async accounts => { let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); m.log("Move time to start of the week", new Date(timeToMove)); - step("John stake 500 1 weeks"); - await xole.create_lock(toWei(500), timeToMove + WEEK + 10, {from: john}); + step("John stake 500 2 weeks"); + await xole.create_lock(toWei(500), timeToMove + 2 * WEEK + 10, {from: john}); step("Tom stake 500 2 weeks"); await xole.create_lock(toWei(500), timeToMove + (2 * WEEK) + 60 * 60, {from: tom}); await xole.increase_amount(toWei(500), {from: tom}); @@ -290,8 +300,8 @@ contract("XOLE", async accounts => { let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); m.log("Move time to start of the week", new Date(timeToMove)); - step("John stake 500 1 weeks"); - await xole.create_lock(toWei(500), timeToMove + WEEK + 60, {from: john}); + step("John stake 500 2 weeks"); + await xole.create_lock(toWei(500), timeToMove + 2 * WEEK + 60, {from: john}); step("Tom stake 500 2 weeks"); lastbk = await web3.eth.getBlock('latest'); await xole.create_lock(toWei(500), timeToMove + (2 * WEEK) + 60 * 60, {from: tom}); @@ -312,10 +322,11 @@ contract("XOLE", async accounts => { await ole.approve(xole.address, toWei(500), {from: john}); await ole.approve(xole.address, toWei(1000), {from: tom}); let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); m.log("Move time to start of the week", new Date(timeToMove)); - step("John stake 500 1 weeks"); - await xole.create_lock(toWei(500), timeToMove + WEEK + 10, {from: john}); + step("John stake 500 2 weeks"); + await xole.create_lock(toWei(500), timeToMove + 2 * WEEK + 10, {from: john}); step("Tom stake 500 2 weeks"); await xole.create_lock(toWei(500), timeToMove + (2 * WEEK) + 60 * 60, {from: tom}); step("New reward 1"); @@ -333,7 +344,7 @@ contract("XOLE", async accounts => { m.log("lastbk.timestamp after=", lastbk.timestamp); await xole.withdraw({from: tom}); - assertPrint("Total Extra Token:", toWei(500), await xole.totalSupply()); + assertPrint("Total Extra Token:", "520800000000000000000", await xole.totalSupply()); assertPrint("Tom Extra Token:", 0, await xole.balanceOf(tom)); await xole.convertToSharingToken(toWei(1), 0, daiOLEDexData); @@ -353,11 +364,12 @@ contract("XOLE", async accounts => { await ole.approve(xole.address, toWei(300), {from: tom}); let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); step("John stake 500"); - await xole.create_lock(toWei(500), lastbk.timestamp + WEEK, {from: john}); + await xole.create_lock(toWei(500), lastbk.timestamp + 2 * WEEK + 10, {from: john}); assertPrint("John staked:", toWei(500), (await xole.locked(john)).amount); step("Tom stake 300"); - await xole.create_lock(toWei(300), lastbk.timestamp + WEEK, {from: tom}); + await xole.create_lock(toWei(300), lastbk.timestamp + 2 * WEEK + 10, {from: tom}); assertPrint("Tom staked:", toWei(300), (await xole.locked(tom)).amount); assertPrint("Total staked:", toWei(800), await xole.totalLocked()); step("New reward 1"); @@ -407,15 +419,14 @@ contract("XOLE", async accounts => { step("John stack more, but earning should not change because no new reward"); await ole.approve(xole.address, toWei(1000), {from: john}); lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(1000), lastbk.timestamp + WEEK, {from: john}); + await xole.create_lock(toWei(1000), lastbk.timestamp + 3 * WEEK, {from: john}); step("New reward 100"); await xole.convertToSharingToken(toWei(100), 0, daiOLEDexData); assertPrint("Dev Fund:", '199596275059873518079', await xole.devFund()); - await advanceMultipleBlocksAndTime(10); lastbk = await web3.eth.getBlock('latest'); - await xole.increase_unlock_time(lastbk.timestamp + 2 * WEEK, {from: john}); + await xole.increase_unlock_time(lastbk.timestamp + 5 * WEEK, {from: john}); assertPrint("Dev Fund:", '199596275059873518079', await xole.devFund()); step("New reward 100"); @@ -479,7 +490,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(10000), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(10000), lastbk.timestamp + 2 * WEEK + 10); assert.equal('10000000000000000000000', (await usdt.balanceOf(xole.address)).toString()); await assertThrows(xole.convertToSharingToken(toWei(1000), '10906610893880149131582', daiUsdtDexData), 'buy amount less than min'); @@ -492,7 +504,8 @@ contract("XOLE", async accounts => { await ole.mint(admin, toWei(10000)); await ole.approve(xole.address, toWei(10000)); let lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(1), lastbk.timestamp + WEEK); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + await xole.create_lock(toWei(1), lastbk.timestamp + 2 * WEEK + 10); assert.equal('0', (await xole.shareableTokenAmount()).toString()); assert.equal('0', (await xole.claimableTokenAmount()).toString()); @@ -577,6 +590,27 @@ contract("XOLE", async accounts => { web3.eth.abi.encodeParameters(['address'], [shareToken]), 0), 'Transaction execution reverted'); }) + + it("Admin setOleLpStakeToken test", async () => { + let oleLpStakeToken = ole.address; + let timeLock = await utils.createTimelock(admin); + let xole0 = await utils.createXOLE(ole.address, timeLock.address, dev, dexAgg.address, accounts[0]); + await timeLock.executeTransaction(xole0.address, 0, 'setOleLpStakeToken(address)', + web3.eth.abi.encodeParameters(['address'], [oleLpStakeToken]), 0) + assert.equal(oleLpStakeToken, await xole0.oleLpStakeToken()); + await assertThrows(xole0.setOleLpStakeToken(oleLpStakeToken), 'caller must be admin'); + }) + + it("Admin setOleLpStakeAutomator test", async () => { + let oleLpStakeAutomator = ole.address; + let timeLock = await utils.createTimelock(admin); + let xole0 = await utils.createXOLE(ole.address, timeLock.address, dev, dexAgg.address, accounts[0]); + await timeLock.executeTransaction(xole0.address, 0, 'setOleLpStakeAutomator(address)', + web3.eth.abi.encodeParameters(['address'], [oleLpStakeAutomator]), 0) + assert.equal(oleLpStakeAutomator, await xole0.oleLpStakeAutomator()); + await assertThrows(xole0.setOleLpStakeAutomator(oleLpStakeAutomator), 'caller must be admin'); + }) + it("Admin withdrawCommunityFund test", async () => { let to = accounts[7]; let timeLock = await utils.createTimelock(admin); diff --git a/test/OleLpStakeAutomatorTest.js b/test/OleLpStakeAutomatorTest.js new file mode 100644 index 0000000..6b7408d --- /dev/null +++ b/test/OleLpStakeAutomatorTest.js @@ -0,0 +1,651 @@ +const OLEToken = artifacts.require("OLEToken"); +const { + assertPrint, + approxAssertPrint, + createEthDexAgg, + createUniswapV2Factory, + createXOLE, assertThrows, toWei +} = require("./utils/OpenLevUtil"); +const m = require('mocha-logger'); +const {advanceMultipleBlocksAndTime, advanceBlockAndSetTime, toBN} = require("./utils/EtheUtil"); +const utils = require("./utils/OpenLevUtil"); +const timeMachine = require("ganache-time-traveler"); +const UniswapV2Factory = artifacts.require("UniswapV2Factory"); +const UniswapV2Router = artifacts.require("UniswapV2Router02"); + +const OleLpStakeAutomatorDelegator = artifacts.require("OleLpStakeAutomatorDelegator"); + +const OleLpStakeAutomator = artifacts.require("OleLpStakeAutomator"); +const TestToken = artifacts.require("MockERC20"); + +contract("xOLE", async accounts => { + + let DAY = 86400; + let WEEK = 7 * DAY; + + let _1000 = "1000000000000000000000"; + let _500 = "500000000000000000000"; + + let bob = accounts[0]; + let alice = accounts[1]; + let admin = accounts[2]; + let dev = accounts[3]; + + let ole; + let oleWethLpToken; + let oleUsdtLpToken; + let xole; + let weth; + let usdt; + let factory; + let router; + let nativeAutomator; + let erc20Automator; + let snapshotId; + beforeEach(async () => { + let snapshot = await timeMachine.takeSnapshot(); + snapshotId = snapshot['result']; + ole = await OLEToken.new(admin, accounts[0], "Open Leverage Token", "OLE"); + await ole.mint(bob, _1000); + await ole.mint(alice, _1000); + usdt = await TestToken.new('USDT', 'USDT'); + await usdt.mint(admin, _1000); + await usdt.mint(bob, _1000); + await usdt.mint(alice, _1000); + // uniswap + weth = await utils.createWETH(); + factory = await UniswapV2Factory.new("0x0000000000000000000000000000000000000000"); + router = await UniswapV2Router.new(factory.address, weth.address); + let block = await web3.eth.getBlock("latest"); + + await ole.approve(router.address, utils.toWei(1), {from: admin}); + await web3.eth.sendTransaction({from: accounts[9], to: admin, value: utils.toWei(1)}); + await router.addLiquidityETH(ole.address, utils.toWei(1), utils.toWei(1), utils.toWei(1), admin, block.timestamp + 60, { + from: admin, + value: utils.toWei(1) + }); + + await ole.approve(router.address, utils.toWei(1), {from: admin}); + await usdt.approve(router.address, utils.toWei(1), {from: admin}); + await router.addLiquidity(ole.address, usdt.address, utils.toWei(1), utils.toWei(1), utils.toWei(1), utils.toWei(1), admin, block.timestamp + 60, { + from: admin + }); + + //xole + xole = await createXOLE(ole.address, admin, dev, "0x0000000000000000000000000000000000000000", admin); + // native automator + oleWethLpToken = await factory.getPair(ole.address, weth.address); + nativeAutomator = await OleLpStakeAutomator.new(); + nativeAutomator = await OleLpStakeAutomator.at((await OleLpStakeAutomatorDelegator.new(xole.address, ole.address, weth.address, oleWethLpToken, weth.address, router.address, admin, nativeAutomator.address)).address); + // erc20 automator + oleUsdtLpToken = await factory.getPair(ole.address, usdt.address); + erc20Automator = await OleLpStakeAutomator.new(); + erc20Automator = await OleLpStakeAutomator.at((await OleLpStakeAutomatorDelegator.new(xole.address, ole.address, usdt.address, oleUsdtLpToken, weth.address, router.address, admin, erc20Automator.address)).address); + // init + let lastbk = await web3.eth.getBlock('latest'); + m.log("lastbk", lastbk.timestamp); + let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK) + 10; + m.log("timeToMove", timeToMove); + m.log("Move time to start of the week", new Date(timeToMove * 1000)); + await advanceBlockAndSetTime(timeToMove); + + }); + + afterEach(async () => { + await timeMachine.revertToSnapshot(snapshotId); + }); + it("Native automator createLockBoth test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + m.log("unlockTime", unlockTime); + await ole.approve(nativeAutomator.address, oleAmount, {from: alice}); + //minAmount check + await assertThrows(nativeAutomator.createLockBoth(oleAmount, 0, unlockTime, toWei(2), toWei(2), { + from: alice, + value: otherAmount + }), 'INSUFFICIENT'); + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + let ethBalanceBefore = await web3.eth.getBalance(bob); + await ole.approve(nativeAutomator.address, oleAmount, {from: bob}); + let r = await nativeAutomator.createLockBoth(oleAmount, 0, unlockTime, oleAmount, otherAmount, { + from: bob, + value: toWei(2) + }); + m.log("createLockBoth Gas Used:", r.receipt.gasUsed); + + let ethBalanceAfter = await web3.eth.getBalance(bob); + assertPrint("Bob back 1 eth", toBN(ethBalanceBefore).sub(toBN(ethBalanceAfter)).lt(toWei(2)), true); + // xole check + assertPrint("Alice's balance of ole", "999000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "1020800000000000000", await xole.balanceOf(alice)); + + assertPrint("Bob's balance of ole", "999000000000000000000", await ole.balanceOf(bob)); + assertPrint("Bob's balance of xole", "1020800000000000000", await xole.balanceOf(bob)); + }) + + it("Erc20 automator createLockBoth test", async () => { + + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + await ole.approve(erc20Automator.address, oleAmount, {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + //minAmount check + await assertThrows(erc20Automator.createLockBoth(oleAmount, otherAmount, unlockTime, toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + await ole.approve(erc20Automator.address, oleAmount, {from: bob}); + await usdt.approve(erc20Automator.address, toWei(2), {from: bob}); + await erc20Automator.createLockBoth(oleAmount, toWei(2), unlockTime, oleAmount, otherAmount, { + from: bob + }); + // xole check + assertPrint("Alice's balance of ole", "999000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "1020800000000000000", await xole.balanceOf(alice)); + + assertPrint("Bob's balance of ole", "999000000000000000000", await ole.balanceOf(bob)); + assertPrint("Bob's balance of xole", "1020800000000000000", await xole.balanceOf(bob)); + }) + it("Native automator createLockOLE test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + await ole.approve(nativeAutomator.address, oleAmount, {from: alice}); + + //minAmount check + await assertThrows(nativeAutomator.createLockOLE(oleAmount, unlockTime, toWei(1), toWei(1), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + + let ethBalanceBefore = await web3.eth.getBalance(alice); + let r = await nativeAutomator.createLockOLE(toWei(2), unlockTime, toWei(0), toWei(0), { + from: alice + }); + m.log("createLockOLE Gas Used:", r.receipt.gasUsed); + + let ethBalanceAfter = await web3.eth.getBalance(alice); + assertPrint("Alice back 0.2 eth", toBN(ethBalanceAfter).sub(toBN(ethBalanceBefore)).gt(toWei(0)), true); + + //xole check + assertPrint("Alice's balance of ole", "998000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "510399999999999998", await xole.balanceOf(alice)); + + }) + + it("Erc20 automator createLockOLE test", async () => { + + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + await ole.approve(erc20Automator.address, oleAmount, {from: alice}); + + //minAmount check + await assertThrows(erc20Automator.createLockOLE(oleAmount, unlockTime, toWei(1), toWei(1), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + + await erc20Automator.createLockOLE(toWei(2), unlockTime, toWei(0), toWei(0), { + from: alice + }); + + //xole check + assertPrint("Alice's balance of ole", "998000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of usdt", "1000248873309964947421", await usdt.balanceOf(alice)); + assertPrint("Alice's balance of xole", "510399999999999998", await xole.balanceOf(alice)); + + }) + + it("Native automator createLockOther test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + + //minAmount check + await assertThrows(nativeAutomator.createLockOther(0, unlockTime, toWei(1), toWei(1), { + from: alice, + value: otherAmount + }), 'INSUFFICIENT'); + + //back remainder check + let r = await nativeAutomator.createLockOther(0, unlockTime, toWei(0), toWei(0), { + from: alice, + value: toWei(2) + }); + m.log("createLockOther Gas Used:", r.receipt.gasUsed); + + //xole check + assertPrint("Alice's balance of ole", "1000248873309964947421", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "510399999999999998", await xole.balanceOf(alice)); + + }) + + it("Erc20 automator createLockOther test", async () => { + + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + await usdt.approve(erc20Automator.address, otherAmount, {from: alice}); + + //minAmount check + await assertThrows(erc20Automator.createLockOther(otherAmount, unlockTime, toWei(1), toWei(1), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + + await erc20Automator.createLockOther(toWei(2), unlockTime, toWei(0), toWei(0), { + from: alice + }); + + //xole check + assertPrint("Alice's balance of ole", "1000248873309964947421", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "510399999999999998", await xole.balanceOf(alice)); + + }) + + it("Native automator increaseAmountBoth test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + m.log("unlockTime", unlockTime); + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + + //minAmount check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await assertThrows(nativeAutomator.increaseAmountBoth(oleAmount, 0, toWei(2), toWei(2), { + from: alice, + value: otherAmount + }), 'INSUFFICIENT'); + + let r = await nativeAutomator.increaseAmountBoth(toWei(2), 0, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + m.log("increaseAmountBoth Gas Used:", r.receipt.gasUsed); + + // xole check + assertPrint("Alice's balance of ole", "998000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "2041600000000000000", await xole.balanceOf(alice)); + + }) + + it("Erc20 automator increaseAmountBoth test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + m.log("unlockTime", unlockTime); + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + + //minAmount check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await assertThrows(erc20Automator.increaseAmountBoth(oleAmount, otherAmount, toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + await erc20Automator.increaseAmountBoth(toWei(2), otherAmount, oleAmount, otherAmount, { + from: alice + }); + + // xole check + assertPrint("Alice's balance of ole", "998000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of usdt", "998000000000000000000", await usdt.balanceOf(alice)); + assertPrint("Alice's balance of xole", "2041600000000000000", await xole.balanceOf(alice)); + + }) + + it("Native automator increaseAmountOLE test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + + //minAmount check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await assertThrows(nativeAutomator.increaseAmountOLE(oleAmount, toWei(1), toWei(1), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + let ethBalanceBefore = await web3.eth.getBalance(alice); + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + let r = await nativeAutomator.increaseAmountOLE(toWei(2), toWei(0), toWei(0), { + from: alice + }); + m.log("increaseAmountOLE Gas Used:", r.receipt.gasUsed); + + let ethBalanceAfter = await web3.eth.getBalance(alice); + assertPrint("Alice back 0.2 eth", toBN(ethBalanceAfter).sub(toBN(ethBalanceBefore)).gt(toWei(0)), true); + + //xole check + assertPrint("Alice's balance of ole", "997000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of xole", "1701333333333333332", await xole.balanceOf(alice)); + + }) + + it("Erc20 automator increaseAmountOLE test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + + //minAmount check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await assertThrows(erc20Automator.increaseAmountOLE(oleAmount, toWei(1), toWei(1), { + from: alice + }), 'INSUFFICIENT'); + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.increaseAmountOLE(toWei(2), toWei(0), toWei(0), { + from: alice + }); + //xole check + assertPrint("Alice's balance of ole", "997000000000000000000", await ole.balanceOf(alice)); + assertPrint("Alice's balance of usdt", "999220442664887109331", await usdt.balanceOf(alice)); + assertPrint("Alice's balance of xole", "1701333333333333332", await xole.balanceOf(alice)); + + }) + + it("Native automator increaseAmountOther test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + + //minAmount check + await assertThrows(nativeAutomator.increaseAmountOther(0, toWei(1), toWei(1), { + from: alice, + value: otherAmount + }), 'INSUFFICIENT'); + //back remainder check + + let r = await nativeAutomator.increaseAmountOther(0, toWei(0), toWei(0), { + from: alice, + value: toWei(2) + }); + m.log("increaseAmountOther Gas Used:", r.receipt.gasUsed); + + //xole check + assertPrint("Alice's balance of xole", "1701333333333333332", await xole.balanceOf(alice)); + assertPrint("Alice's balance of ole", "999220442664887109331", await ole.balanceOf(alice)); + }) + + it("Erc20 automator increaseAmountOther test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + + //minAmount check + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await assertThrows(erc20Automator.increaseAmountOther(otherAmount, toWei(1), toWei(1), { + from: alice, + }), 'INSUFFICIENT'); + //back remainder check + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.increaseAmountOther(toWei(2), toWei(0), toWei(0), { + from: alice + }); + //xole check + assertPrint("Alice's balance of xole", "1701333333333333332", await xole.balanceOf(alice)); + assertPrint("Alice's balance of ole", "999220442664887109331", await ole.balanceOf(alice)); + }) + + it("Native automator withdrawBoth test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(nativeAutomator.withdrawBoth(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + let ethBalanceBefore = await web3.eth.getBalance(alice); + + let r = await nativeAutomator.withdrawBoth(0, 0, { + from: alice + }); + m.log("withdrawBoth Gas Used:", r.receipt.gasUsed); + + let ethBalanceAfter = await web3.eth.getBalance(alice); + assertPrint("Alice back 1 eth", toBN(ethBalanceAfter).sub(toBN(ethBalanceBefore)).gt(toWei(0)), true); + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + assertPrint("Alice's balance of ole", "1000000000000000000000", await ole.balanceOf(alice)); + + }) + + it("Erc20 automator withdrawBoth test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(erc20Automator.withdrawBoth(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + await erc20Automator.withdrawBoth(0, 0, { + from: alice + }); + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + assertPrint("Alice's balance of usdt", "1000000000000000000000", await usdt.balanceOf(alice)); + assertPrint("Alice's balance of ole", "1000000000000000000000", await ole.balanceOf(alice)); + + }) + + it("Native automator withdrawOle test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(nativeAutomator.withdrawOle(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + let r = await nativeAutomator.withdrawOle(0, 0, { + from: alice + }); + m.log("withdrawOle Gas Used:", r.receipt.gasUsed); + + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + assertPrint("Alice's balance of ole", "1000499248873309964947", await ole.balanceOf(alice)); + + }) + + it("Erc20 automator withdrawOle test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(erc20Automator.withdrawOle(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + await erc20Automator.withdrawOle(0, 0, { + from: alice + }); + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + assertPrint("Alice's balance of ole", "1000499248873309964947", await ole.balanceOf(alice)); + + }) + + it("Native automator withdrawOther test", async () => { + await xole.setOleLpStakeToken(oleWethLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(nativeAutomator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(nativeAutomator.address, toWei(2), {from: alice}); + await nativeAutomator.createLockBoth(toWei(2), 0, unlockTime, oleAmount, otherAmount, { + from: alice, + value: otherAmount + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(nativeAutomator.withdrawOther(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + + let ethBalanceBefore = await web3.eth.getBalance(alice); + let r = await nativeAutomator.withdrawOther(0, 0, { + from: alice + }); + m.log("withdrawOther Gas Used:", r.receipt.gasUsed); + + let ethBalanceAfter = await web3.eth.getBalance(alice); + assertPrint("Alice back 1.4 eth", toBN(ethBalanceAfter).sub(toBN(ethBalanceBefore)).gt(toWei(1)), true); + + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + + }) + + it("Erc20 automator withdrawOther test", async () => { + await xole.setOleLpStakeToken(oleUsdtLpToken, {from: admin}); + await xole.setOleLpStakeAutomator(erc20Automator.address, {from: admin}); + let oleAmount = toWei(1); + let otherAmount = toWei(1); + let unlockTime = (await web3.eth.getBlock('latest')).timestamp + 3 * WEEK + DAY; + //back remainder check + await ole.approve(erc20Automator.address, toWei(2), {from: alice}); + await usdt.approve(erc20Automator.address, toWei(2), {from: alice}); + await erc20Automator.createLockBoth(toWei(2), otherAmount, unlockTime, oleAmount, otherAmount, { + from: alice + }); + await advanceBlockAndSetTime(unlockTime + WEEK); + //minAmount check + await assertThrows(erc20Automator.withdrawOther(toWei(2), toWei(2), { + from: alice + }), 'INSUFFICIENT'); + + await erc20Automator.withdrawOther(0, 0, { + from: alice + }); + + //xole check + assertPrint("Alice's balance of xole", "0", await xole.balanceOf(alice)); + assertPrint("Alice's balance of usdt", "1000499248873309964947", await usdt.balanceOf(alice)); + + }) + + /*** Admin Test ***/ + + it("Admin initialize test", async () => { + await assertThrows(nativeAutomator.initialize(admin, admin, admin, admin, admin, admin, {from: alice}), 'NAD'); + await nativeAutomator.initialize(admin, admin, admin, admin, admin, admin, {from: admin}); + }) + + it("Admin setImplementation test", async () => { + let automator = await OleLpStakeAutomatorDelegator.at(nativeAutomator.address); + await assertThrows(automator.setImplementation(admin, {from: alice}), 'caller must be admin'); + await automator.setImplementation(admin, {from: admin}); + }); + +}) diff --git a/test/ZGovernorTest.js b/test/ZGovernorTest.js index c6e8096..f51d3b2 100644 --- a/test/ZGovernorTest.js +++ b/test/ZGovernorTest.js @@ -34,7 +34,8 @@ contract("GovernorAlphaTest", async accounts => { tlAdmin = await MockTLAdmin.new(timelock.address); xole = await createXOLE(ole.address, admin, admin, "0x0000000000000000000000000000000000000000"); - + await xole.setShareToken(ole.address); + await xole.setOleLpStakeToken(ole.address, {from: admin}); gov = await GovernorAlpha.new(timelock.address, xole.address, admin); await timelock.setPendingAdmin(gov.address, {from: admin}); await gov.__acceptAdmin({from: admin}); @@ -403,7 +404,7 @@ contract("GovernorAlphaTest", async accounts => { await ole.mint(proposeAccount, toWei(240000)); await ole.approve(xole.address, toWei(240000), {from: proposeAccount}); lastbk = await web3.eth.getBlock('latest'); - await xole.create_lock(toWei(240000), lastbk.timestamp + (DAY * 7), {from: proposeAccount}); + await xole.create_lock(toWei(240000), lastbk.timestamp + (DAY * 28), {from: proposeAccount}); let lastBlockNum = await web3.eth.getBlockNumber(); await advanceMultipleBlocksAndTime(1); let vote = await xole.getPriorVotes(proposeAccount, lastBlockNum); diff --git a/test/xOLELockTest.js b/test/xOLELockTest.js index 1442ce4..6c0ef9f 100644 --- a/test/xOLELockTest.js +++ b/test/xOLELockTest.js @@ -4,10 +4,11 @@ const { approxAssertPrint, createEthDexAgg, createUniswapV2Factory, - createXOLE + createXOLE, assertThrows, toWei } = require("./utils/OpenLevUtil"); const m = require('mocha-logger'); const {advanceMultipleBlocksAndTime, advanceBlockAndSetTime, toBN} = require("./utils/EtheUtil"); +const timeMachine = require("ganache-time-traveler"); contract("xOLE", async accounts => { @@ -28,11 +29,12 @@ contract("xOLE", async accounts => { let uniswapFactory; - let ole + let ole; + let stakeLpToken; let xole; let stages = {}; - + let snapshotId; beforeEach(async () => { ole = await OLEToken.new(admin, accounts[0], "Open Leverage Token", "OLE"); await ole.mint(bob, _1000); @@ -41,47 +43,126 @@ contract("xOLE", async accounts => { uniswapFactory = await createUniswapV2Factory(admin); let dexAgg = await createEthDexAgg(uniswapFactory.address, "0x0000000000000000000000000000000000000000", admin); xole = await createXOLE(ole.address, admin, dev, dexAgg.address, admin); - + stakeLpToken = ole.address; + await xole.setOleLpStakeToken(stakeLpToken, {from: admin}); let lastbk = await web3.eth.getBlock('latest'); let timeToMove = lastbk.timestamp + (WEEK - lastbk.timestamp % WEEK); m.log("Move time to start of the week", new Date(timeToMove)); await advanceBlockAndSetTime(timeToMove); + let snapshot = await timeMachine.takeSnapshot(); + snapshotId = snapshot['result']; }) + afterEach(async () => { + await timeMachine.revertToSnapshot(snapshotId); + }); + it("Create lock, increase amount, increase lock time", async () => { await ole.approve(xole.address, _1000 + "0", {"from": alice}); await ole.approve(xole.address, _1000 + "0", {"from": bob}); - let lastbk = await web3.eth.getBlock('latest'); - let end = lastbk.timestamp + WEEK; + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; + m.log("Alice locked start ", lastbk.timestamp); + m.log("Alice locked end ", end); m.log("Alice creates lock with 500 till time ", end, new Date(end)); await xole.create_lock(_500, end, {"from": alice}); assertPrint("Alice locked amount", _500, (await xole.locked(alice)).amount); approxAssertPrint("Alice locked end", end, (await xole.locked(alice)).end); - approxAssertPrint("xOLE Total supply", "500000000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "500000000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("xOLE Total supply", "510400000000000000000", await xole.totalSupply()); + approxAssertPrint("Alice's balance of xOLE", "510400000000000000000", await xole.balanceOf(alice)); assertPrint("Bob's balance of xOLE", "0", await xole.balanceOf(bob)); - await advanceMultipleBlocksAndTime(10); + await advanceBlockAndSetTime(lastbk.timestamp - 10); m.log("Alice increase amount with 500"); await xole.increase_amount(_500, {"from": alice}); assertPrint("Alice locked amount", _1000, (await xole.locked(alice)).amount); approxAssertPrint("Alice locked end", end, (await xole.locked(alice)).end); // end isn't changed - approxAssertPrint("xOLE Total supply", "1000000000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("xOLE Total supply", "1020800000000000000000", await xole.totalSupply()); + approxAssertPrint("Alice's balance of xOLE", "1020800000000000000000", await xole.balanceOf(alice)); assertPrint("Bob's balance of xOLE", "0", await xole.balanceOf(bob)); - await advanceMultipleBlocksAndTime(10); + await advanceBlockAndSetTime(lastbk.timestamp - 10); m.log("Alice increase lock time by 1 week"); await xole.increase_unlock_time(end + WEEK, {"from": alice}); assertPrint("Alice locked amount", _1000, (await xole.locked(alice)).amount); approxAssertPrint("Alice locked end", end + WEEK, (await xole.locked(alice)).end); // end isn't changed - approxAssertPrint("xOLE Total supply", "1000000000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("xOLE Total supply", "1041600000000000000000", await xole.totalSupply()); + approxAssertPrint("Alice's balance of xOLE", "1041600000000000000000", await xole.balanceOf(alice)); assertPrint("Bob's balance of xOLE", "0", await xole.balanceOf(bob)); }) + it("Create lock for other, and withdraw", async () => { + await ole.approve(xole.address, _1000 + "0", {"from": alice}); + + let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; + m.log("Alice creates lock to bob with 500 till time ", end, new Date(end)); + await xole.create_lock_for(bob, _500, end, {"from": alice}); + let bobBalance = await xole.balanceOf(bob); + let aliceBalance = await xole.balanceOf(alice); + assertPrint("Bob's balance of xOLE", "510400000000000000000", bobBalance); + assertPrint("Alice's balance of xOLE", "0", aliceBalance); + await advanceBlockAndSetTime(end + 60 * 60 * 24); + await xole.withdraw({from: bob}); + let withdrawAmount = await (await OLEToken.at(stakeLpToken)).balanceOf(bob); + assertPrint("Bob's stakeToken amount", "1500000000000000000000", withdrawAmount); + }) + + it("Create lock for other, and withdraw", async () => { + await ole.approve(xole.address, _1000 + "0", {"from": alice}); + + let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; + m.log("Alice creates lock to bob with 500 till time ", end, new Date(end)); + await xole.create_lock_for(bob, _500, end, {"from": alice}); + let bobBalance = await xole.balanceOf(bob); + let aliceBalance = await xole.balanceOf(alice); + assertPrint("Bob's balance of xOLE", "510400000000000000000", bobBalance); + assertPrint("Alice's balance of xOLE", "0", aliceBalance); + await advanceBlockAndSetTime(end + 60 * 60 * 24); + await xole.withdraw({from: bob}); + let withdrawAmount = await (await OLEToken.at(stakeLpToken)).balanceOf(bob); + assertPrint("Bob's stakeToken amount", "1500000000000000000000", withdrawAmount); + }) + it("Increase amount lock for other, and withdraw", async () => { + await ole.approve(xole.address, _1000 + "0", {"from": alice}); + + let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; + m.log("Alice creates lock to bob with 500 till time ", end, new Date(end)); + await xole.create_lock(_500, end, {"from": alice}); + let aliceBalance = await xole.balanceOf(alice); + assertPrint("Alice's balance of xOLE", "510400000000000000000", aliceBalance); + await ole.approve(xole.address, _1000 + "0", {"from": bob}); + await xole.increase_amount_for(alice, _1000, {"from": bob}); + aliceBalance = await xole.balanceOf(alice); + assertPrint("Alice's balance of xOLE", "1531200000000000000000", aliceBalance); + await advanceBlockAndSetTime(end + 60 * 60 * 24); + await xole.withdraw({from: alice}); + let withdrawAmount = await (await OLEToken.at(stakeLpToken)).balanceOf(alice); + assertPrint("Alice's stakeToken amount", "2000000000000000000000", withdrawAmount); + }) + it("Increase amount lock for other, and withdraw", async () => { + await ole.approve(xole.address, _1000 + "0", {"from": alice}); + let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; + m.log("Alice creates lock to bob with 500 till time ", end, new Date(end)); + await xole.create_lock(_500, end, {"from": alice}); + let aliceBalance = await xole.balanceOf(alice); + assertPrint("Alice's balance of xOLE", "510400000000000000000", aliceBalance); + await advanceBlockAndSetTime(end + 60 * 60 * 24); + await assertThrows(xole.withdraw_automator(alice), 'Not automator'); + await xole.setOleLpStakeAutomator(bob, {from: admin}); + await xole.withdraw_automator(alice, {from: bob}); + let withdrawAmount = await (await OLEToken.at(stakeLpToken)).balanceOf(bob); + assertPrint("Bob's stakeToken amount", "1500000000000000000000", withdrawAmount); + }) it("Lock to get voting powers, and withdraw", async () => { if (process.env.FASTMODE === 'true') { @@ -92,57 +173,81 @@ contract("xOLE", async accounts => { await ole.approve(xole.address, _1000 + "0", {"from": alice}); await ole.approve(xole.address, _1000 + "0", {"from": bob}); - assertPrint("Totol Supply", "0", await xole.totalSupply()); + assertPrint("Total Supply", "0", await xole.totalSupply()); assertPrint("Alice's Balance", "0", await xole.balanceOf(alice)); assertPrint("Bob's Balance", "0", await xole.balanceOf(bob)); let lastbk = await web3.eth.getBlock('latest'); stages["before_deposits"] = {bknum: lastbk.number, bltime: lastbk.timestamp}; - let end = lastbk.timestamp + WEEK; + await advanceBlockAndSetTime(lastbk.timestamp - 10); + let end = lastbk.timestamp + 2 * WEEK; m.log("Alice creates lock with 1000 till time ", end, new Date(end)); await xole.create_lock(_1000, end, {"from": alice}); lastbk = await web3.eth.getBlock('latest'); stages["alice_deposit"] = {bknum: lastbk.number, bltime: lastbk.timestamp}; - approxAssertPrint("xOLE Total supply", "1000000000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); - assertPrint("Bob's balance of xOLE", "0", await xole.balanceOf(bob)); - - - await advanceMultipleBlocksAndTime(1000); - - approxAssertPrint("xOLE Total supply", "1000000000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("xOLE Total supply", "1020800000000000000000", await xole.totalSupply()); + approxAssertPrint("Alice's balance of xOLE", "1020800000000000000000", await xole.balanceOf(alice)); assertPrint("Bob's balance of xOLE", "0", await xole.balanceOf(bob)); lastbk = await web3.eth.getBlock('latest'); - end = lastbk.timestamp + WEEK * 3; + await advanceBlockAndSetTime(lastbk.timestamp - 10); + end = lastbk.timestamp + WEEK * 3 + 100; + m.log("Bob creates lock till time ", end, new Date(end)); await xole.create_lock(_1000, end, {"from": bob}); lastbk = await web3.eth.getBlock('latest'); stages["bob_deposit"] = {bknum: lastbk.number, bltime: lastbk.timestamp}; m.log("xole.totalSupply()", (await xole.totalSupply()).toString()); - approxAssertPrint("xOLE Total supply", "2020800000000000000000", await xole.totalSupply()); - approxAssertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); - approxAssertPrint("Bob's balance of xOLE", "1020800000000000000000", await xole.balanceOf(bob)); + approxAssertPrint("xOLE Total supply", "2062400000000000000000", await xole.totalSupply()); + approxAssertPrint("Alice's balance of xOLE", "1020800000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("Bob's balance of xOLE", "1041600000000000000000", await xole.balanceOf(bob)); await advanceBlockAndSetTime(end + 60 * 60 * 24); lastbk = await web3.eth.getBlock('latest'); stages["check_point"] = {bknum: lastbk.number, bltime: lastbk.timestamp}; - approxAssertPrint("xOLE Total supply", "2020800000000000000000", await xole.totalSupply()); - assertPrint("Alice's balance of xOLE", "1000000000000000000000", await xole.balanceOf(alice)); - approxAssertPrint("Bob's balance of xOLE", "1020800000000000000000", await xole.balanceOf(bob)); + approxAssertPrint("xOLE Total supply", "2062400000000000000000", await xole.totalSupply()); + assertPrint("Alice's balance of xOLE", "1020800000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("Bob's balance of xOLE", "1041600000000000000000", await xole.balanceOf(bob)); // m.log("Alice withdraw"); await xole.withdraw({from: alice}); approxAssertPrint("Alice's balance of ole", _1000, await ole.balanceOf(alice)); - approxAssertPrint("xOLE Total supply", "1020800000000000000000", await xole.totalSupply()); + approxAssertPrint("xOLE Total supply", "1041600000000000000000", await xole.totalSupply()); assertPrint("Alice's balance of xOLE", "0", await xole.balanceOf(alice)); - approxAssertPrint("Bob's balance of xOLE", "1020800000000000000000", await xole.balanceOf(bob)); + approxAssertPrint("Bob's balance of xOLE", "1041600000000000000000", await xole.balanceOf(bob)); + + }) + it("Create lock 2 weeks", async () => { + await ole.approve(xole.address, _1000 + "0", {"from": alice}); + let lastbk = await web3.eth.getBlock('latest'); + await advanceBlockAndSetTime(lastbk.timestamp + 1); + let end = lastbk.timestamp + 1 * WEEK; + m.log("Alice creates lock to bob with 500 till time ", end, new Date(end)); + await assertThrows(xole.create_lock(_500, end, {"from": alice}), 'Can only lock until time in the future'); + end = lastbk.timestamp + 2 * WEEK; + await assertThrows(xole.create_lock(_500, end, {"from": alice}), 'Can only lock until time in the future'); + await advanceBlockAndSetTime(lastbk.timestamp - 1); + await xole.create_lock(_500, end, {"from": alice}); + assertPrint("Alice's balance of xOLE", "510400000000000000000", await xole.balanceOf(alice)); + // increase time before unlock time + await advanceBlockAndSetTime(end - 1 * WEEK + 1); + await assertThrows(xole.increase_unlock_time(end + 1 * WEEK, {"from": alice}), 'Can only lock until time in the future'); + await advanceBlockAndSetTime(end - 1 * WEEK - 10); + await xole.increase_unlock_time(end + 3 * WEEK, {"from": alice}); + assertPrint("Alice's balance of xOLE", "531200000000000000000", await xole.balanceOf(alice)); + approxAssertPrint("Alice locked end", end + 3 * WEEK, (await xole.locked(alice)).end); + // increase time after unlock time + await advanceBlockAndSetTime(end + 3 * WEEK + 1); + lastbk = await web3.eth.getBlock('latest'); + await assertThrows(xole.increase_unlock_time(lastbk.timestamp + 1 * WEEK, {"from": alice}), 'Can only lock until time in the future'); + await xole.increase_unlock_time(lastbk.timestamp + 4 * WEEK, {"from": alice}); + approxAssertPrint("Alice locked end", lastbk.timestamp + 4 * WEEK, (await xole.locked(alice)).end); + assertPrint("Alice's balance of xOLE", "520800000000000000000", await xole.balanceOf(alice)); }) })