From b1fd1ff0e3c2df9a6ece2706de0215f9ef14dab6 Mon Sep 17 00:00:00 2001 From: elshan_eth Date: Mon, 29 Jan 2024 20:20:29 +0100 Subject: [PATCH] fix audit comments --- contracts/contracts/DevRewardDistributor.sol | 82 +++---- contracts/contracts/FluenceToken.sol | 27 +-- contracts/contracts/LPController.sol | 204 +++++++----------- contracts/contracts/Vesting.sol | 87 +++++--- contracts/contracts/VestingWithVoting.sol | 26 +-- .../contracts/interfaces/IVestingERC20.sol | 39 ---- contracts/deploy/0002_LPController.ts | 2 - contracts/test/Deploy.spec.ts | 18 +- contracts/test/e2e/E2E_LPController.spec.ts | 56 ++++- 9 files changed, 253 insertions(+), 288 deletions(-) delete mode 100644 contracts/contracts/interfaces/IVestingERC20.sol diff --git a/contracts/contracts/DevRewardDistributor.sol b/contracts/contracts/DevRewardDistributor.sol index a6d275b..c50e14c 100644 --- a/contracts/contracts/DevRewardDistributor.sol +++ b/contracts/contracts/DevRewardDistributor.sol @@ -17,47 +17,56 @@ contract DevRewardDistributor { /** * @notice Reward token - **/ + * + */ FluenceToken public immutable token; /** * @notice DAO timelock contract address - **/ + * + */ Executor public immutable executor; /** * @notice Canceler address (e.g. FluenceMultisig) - **/ + * + */ address public immutable canceler; /** * @notice Claiming end time - **/ + * + */ uint256 public immutable claimingEndTime; /** * @notice Time when this contract was deployed - **/ + * + */ uint256 public immutable deployTime; /** * @notice Merkle root from rewards tree - **/ + * + */ bytes32 public immutable merkleRoot; /** * @notice Period for dividing the reward - **/ + * + */ uint256 public immutable halvePeriod; /** * @notice Initial user's reward - **/ + * + */ uint256 public immutable initialReward; /** * @notice Bitmap with claimed users ids - **/ + * + */ mapping(uint256 => uint256) private claimedBitMap; /** @@ -66,18 +75,15 @@ contract DevRewardDistributor { * @param account - reward account * @param amount - reward amount * @param leaf - leaf with user's info in reward tree - **/ - event Claimed( - uint256 userId, - address account, - uint256 amount, - bytes32 leaf - ); + * + */ + event Claimed(uint256 indexed userId, address account, uint256 amount, bytes32 leaf); /** * @notice Emitted when claiming period is ended and tokens transfer to the executor * @param amount - remainder balance - **/ + * + */ event TransferUnclaimed(uint256 amount); /** @@ -88,7 +94,8 @@ contract DevRewardDistributor { * @param _initialReward - initial user reward * @param _claimingPeriod - claiming period * @param _canceler - can cancel distribution, and withdraw to _executor. - **/ + * + */ constructor( FluenceToken _token, Executor _executor, @@ -111,10 +118,7 @@ contract DevRewardDistributor { } modifier whenClaimingIs(bool isActive) { - require( - isClaimingActive() == isActive, - "Claiming status is not as expected" - ); + require(isClaimingActive() == isActive, "Claiming status is not as expected"); _; } @@ -124,7 +128,8 @@ contract DevRewardDistributor { * @param merkleProof - merkle proof for leaf * @param temporaryAddress - temporary Ethereum address that's used only for signing * @param signature - signature of temporary Ethereum address - **/ + * + */ function claimTokens( uint32 userId, bytes32[] calldata merkleProof, @@ -135,14 +140,9 @@ contract DevRewardDistributor { bytes32 leaf = keccak256(abi.encodePacked(userId, temporaryAddress)); - require( - MerkleProof.verify(merkleProof, merkleRoot, leaf), - "Valid proof required" - ); + require(MerkleProof.verify(merkleProof, merkleRoot, leaf), "Valid proof required"); - bytes32 msgHash = keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n20", msg.sender) - ); + bytes32 msgHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n20", msg.sender)); address signer = ECDSA.recover(msgHash, signature); require(signer == temporaryAddress, "Invalid signature"); @@ -158,14 +158,16 @@ contract DevRewardDistributor { /** * @notice used to move any remaining tokens out of the contract to Executor after expiration - **/ + * + */ function transferUnclaimed() external whenClaimingIs(false) { _withdraw(); } /** * @notice used to move any remaining tokens out of the contract to Executor (DAO) in emergency situation. - **/ + * + */ function withdraw() external { require(msg.sender == canceler, "Only canceler can withdraw"); _withdraw(); @@ -175,7 +177,8 @@ contract DevRewardDistributor { * @notice checks claimed bitMap for userId * @dev fork from uniswap merkle distributor, unmodified * @return - boolean - **/ + * + */ function isClaimed(uint256 index) public view returns (bool) { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; @@ -187,7 +190,8 @@ contract DevRewardDistributor { /** * @notice Checking if claiming is active * @return - boolean - **/ + * + */ function isClaimingActive() public view returns (bool) { return block.timestamp < claimingEndTime; } @@ -195,7 +199,8 @@ contract DevRewardDistributor { /** * @notice Get current user's reward * @return - boolean - **/ + * + */ function currentReward() public view returns (uint256) { if (!isClaimingActive()) { return 0; @@ -210,13 +215,12 @@ contract DevRewardDistributor { /** * @notice Sets a given user by index to claimed * @dev taken from uniswap merkle distributor, unmodified - **/ + * + */ function _setClaimed(uint256 index) private { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; - claimedBitMap[claimedWordIndex] = - claimedBitMap[claimedWordIndex] | - (1 << claimedBitIndex); + claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex); } function _withdraw() private { diff --git a/contracts/contracts/FluenceToken.sol b/contracts/contracts/FluenceToken.sol index d93cd80..b4db809 100644 --- a/contracts/contracts/FluenceToken.sol +++ b/contracts/contracts/FluenceToken.sol @@ -21,34 +21,25 @@ contract FluenceToken is _disableInitializers(); } - function initialize( - string memory name_, - string memory symbol_, - uint256 totalSupply_ - ) public initializer { + function initialize(string memory name_, string memory symbol_, uint256 totalSupply_) public initializer { __ERC20_init(name_, symbol_); + __ERC20Permit_init(name_); + __ERC20Votes_init(); + __Ownable_init(msg.sender); __UUPSUpgradeable_init(); _mint(msg.sender, totalSupply_); } - function _update( - address from, - address to, - uint256 value - ) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) { + function _update(address from, address to, uint256 value) + internal + override(ERC20Upgradeable, ERC20VotesUpgradeable) + { super._update(from, to, value); } - function nonces( - address owner - ) - public - view - override(ERC20PermitUpgradeable, NoncesUpgradeable) - returns (uint256) - { + function nonces(address owner) public view override(ERC20PermitUpgradeable, NoncesUpgradeable) returns (uint256) { return super.nonces(owner); } diff --git a/contracts/contracts/LPController.sol b/contracts/contracts/LPController.sol index 986a59c..59cea9f 100644 --- a/contracts/contracts/LPController.sol +++ b/contracts/contracts/LPController.sol @@ -18,13 +18,10 @@ import "./Executor.sol"; contract LPController is Ownable { using SafeERC20 for IERC20; - uint24 private constant _UNISWAP_FEE = 3000; - IBalancerLBPFactory public immutable balancerLBPFactory; IUniswapV3Factory public immutable uniswapFactory; IUniswapNFTManager public immutable uniswapPositionManager; IBalancerVault public immutable balancerVault; - IBalancerHelper public immutable balancerHelper; Executor public immutable daoExecutor; IERC20 public immutable token0; @@ -39,7 +36,6 @@ contract LPController is Ownable { IUniswapV3Factory _uniswapFactory, IUniswapNFTManager _uniswapPositionManager, IBalancerVault _balancerVault, - IBalancerHelper _balancerHelper, Executor _daoExecutor, IERC20 token0_, IERC20 token1_ @@ -47,7 +43,6 @@ contract LPController is Ownable { balancerLBPFactory = _balancerLBPFactory; uniswapPositionManager = _uniswapPositionManager; balancerVault = _balancerVault; - balancerHelper = _balancerHelper; daoExecutor = _daoExecutor; uniswapFactory = _uniswapFactory; @@ -64,7 +59,8 @@ contract LPController is Ownable { * @param initBalances - initial balances * @param lbpPoolDuration - duration pool working * @param swapFeePercentage - swapping fee in percentage - **/ + * + */ function createBalancerLBP( uint256[] calldata weights, uint256[] calldata endWeights, @@ -72,117 +68,75 @@ contract LPController is Ownable { uint256 lbpPoolDuration, uint256 swapFeePercentage ) external onlyOwner { - require( - address(liquidityBootstrappingPool) == address(0), - "LBP already exists" - ); + require(address(liquidityBootstrappingPool) == address(0), "LBP already exists"); require( - weights.length == 2 || - endWeights.length == 2 || - initBalances.length == 2, + weights.length == 2 && endWeights.length == 2 && initBalances.length == 2, "Length for weights, endWeights and initBalances must be 2" ); + IBalancerVault vault = balancerVault; + IERC20[] memory assets = new IERC20[](2); assets[0] = token0; assets[1] = token1; IBalancerLBP lbp = IBalancerLBP( - balancerLBPFactory.create( - "Fluence LBP", - "FLTLBP", - assets, - weights, - swapFeePercentage, - address(this), - true - ) + balancerLBPFactory.create("Fluence LBP", "FLTLBP", assets, weights, swapFeePercentage, address(this), false) ); + liquidityBootstrappingPool = lbp; + lbp.updateWeightsGradually(block.timestamp, block.timestamp + lbpPoolDuration, endWeights); - bytes32 _lbpPoolId = lbp.getPoolId(); + assets[0].safeTransferFrom(msg.sender, address(this), initBalances[0]); + assets[0].forceApprove(address(vault), initBalances[0]); - lbp.updateWeightsGradually( - block.timestamp, - block.timestamp + lbpPoolDuration, - endWeights - ); - - token0.transferFrom(msg.sender, address(this), initBalances[0]); - token0.forceApprove(address(balancerVault), initBalances[0]); - - token1.transferFrom(msg.sender, address(this), initBalances[1]); - token1.forceApprove(address(balancerVault), initBalances[1]); - - IBalancerVault.JoinPoolRequest memory request = IBalancerVault - .JoinPoolRequest({ - assets: assets, - maxAmountsIn: initBalances, - userData: abi.encode(0, initBalances), - fromInternalBalance: false - }); - - balancerVault.joinPool( - _lbpPoolId, - address(this), - address(this), - request - ); + assets[1].safeTransferFrom(msg.sender, address(this), initBalances[1]); + assets[1].forceApprove(address(vault), initBalances[1]); + bytes32 _lbpPoolId = lbp.getPoolId(); + IBalancerVault.JoinPoolRequest memory request = IBalancerVault.JoinPoolRequest({ + assets: assets, + maxAmountsIn: initBalances, + userData: abi.encode(0, initBalances), + fromInternalBalance: false + }); + vault.joinPool(_lbpPoolId, address(this), address(this), request); lbpPoolId = _lbpPoolId; - liquidityBootstrappingPool = lbp; } /** * @notice Set pool status - **/ + * + */ function setSwapEnabledInBalancerLBP(bool swapEnabled) external onlyOwner { liquidityBootstrappingPool.setSwapEnabled(swapEnabled); } /** * @notice Exit from the balancer LBP - **/ - function exitFromBalancerLBP() external onlyOwner { - uint256 bptBalance = IERC20(address(liquidityBootstrappingPool)) - .balanceOf(address(this)); + * @param amountsOut - minimum amounts of tokens from queryExit + */ + function exitFromBalancerLBP(uint256[] calldata amountsOut) external onlyOwner { + IBalancerLBP lbp = liquidityBootstrappingPool; + uint256 bptBalance = IERC20(address(lbp)).balanceOf(address(this)); require(bptBalance > 0, "No BPT balance"); - IERC20(address(liquidityBootstrappingPool)).approve( - address(balancerVault), - bptBalance - ); + liquidityBootstrappingPool.setSwapEnabled(false); + + IERC20(address(lbp)).approve(address(balancerVault), bptBalance); IERC20[] memory assets = new IERC20[](2); assets[0] = token0; assets[1] = token1; - IBalancerVault.ExitPoolRequest memory request = IBalancerVault - .ExitPoolRequest({ - assets: assets, - minAmountsOut: new uint256[](2), - userData: abi.encode(1, bptBalance), - toInternalBalance: false - }); - - (, uint256[] memory amountsOut) = balancerHelper.queryExit( - lbpPoolId, - address(this), - address(this), - request - ); - - request.minAmountsOut = amountsOut; - - balancerVault.exitPool( - lbpPoolId, - address(this), - payable(address(this)), - request - ); - - liquidityBootstrappingPool.setSwapEnabled(false); + IBalancerVault.ExitPoolRequest memory request = IBalancerVault.ExitPoolRequest({ + assets: assets, + minAmountsOut: amountsOut, + userData: abi.encode(1, bptBalance), + toInternalBalance: false + }); + balancerVault.exitPool(lbpPoolId, address(this), payable(address(this)), request); } /** @@ -190,59 +144,59 @@ contract LPController is Ownable { * @param tickLower - the minimum derivative price of position * @param tickLower - the maximum derivative price of position * @param sqrtPriceX96 - sqrt from initial price (Q64.96 format) - **/ + * @param swapFee - swap fee + * @param token0Amount - amount of token0 + * @param token1Amount - amount of token1 + * @param amount0Min - minimum amount of token0 + * @param amount1Min - minimum amount of token1 + */ function createUniswapLP( int24 tickLower, int24 tickUpper, - uint160 sqrtPriceX96 + uint160 sqrtPriceX96, + uint24 swapFee, + uint256 token0Amount, + uint256 token1Amount, + uint256 amount0Min, + uint256 amount1Min ) external onlyOwner { - require( - address(uniswapPool) == address(0), - "Uniswap pool already exists" - ); + require(address(uniswapPool) == address(0), "Uniswap pool already exists"); - uint256 balance0 = token0.balanceOf(address(this)); - uint256 balance1 = token1.balanceOf(address(this)); + require(token0Amount != 0, "Invalid amount of token0"); + require(token1Amount != 0, "Invalid amount of token1"); - require(balance0 != 0, "Invalid balance of token0"); - require(balance1 != 0, "Invalid balance of token1"); - - IUniswapV3Pool pool = IUniswapV3Pool( - uniswapFactory.createPool( - address(token0), - address(token1), - _UNISWAP_FEE - ) - ); - - pool.initialize(sqrtPriceX96); - - token0.forceApprove(address(uniswapPositionManager), balance0); - token1.forceApprove(address(uniswapPositionManager), balance1); - - IUniswapNFTManager.MintParams memory params = IUniswapNFTManager - .MintParams({ - token0: address(token0), - token1: address(token1), - fee: _UNISWAP_FEE, - tickLower: tickLower, - tickUpper: tickUpper, - amount0Desired: balance0, - amount1Desired: balance1, - amount0Min: 0, - amount1Min: 0, - recipient: address(daoExecutor), - deadline: block.timestamp - }); - - uniswapPositionManager.mint(params); + address _token0 = address(token0); + address _token1 = address(token1); + IUniswapNFTManager positionManager = uniswapPositionManager; + IUniswapV3Pool pool = + IUniswapV3Pool(positionManager.createAndInitializePoolIfNecessary(_token0, _token1, swapFee, sqrtPriceX96)); uniswapPool = pool; + + IERC20(_token0).forceApprove(address(positionManager), token0Amount); + IERC20(_token1).forceApprove(address(positionManager), token1Amount); + + IUniswapNFTManager.MintParams memory params = IUniswapNFTManager.MintParams({ + token0: _token0, + token1: _token1, + fee: swapFee, + tickLower: tickLower, + tickUpper: tickUpper, + amount0Desired: token0Amount, + amount1Desired: token1Amount, + amount0Min: amount0Min, + amount1Min: amount1Min, + recipient: address(daoExecutor), + deadline: block.timestamp + }); + + positionManager.mint(params); } /** * @notice Withdraw token from this contract to the DAO executor - **/ + * + */ function withdraw(IERC20 token, uint256 amount) external onlyOwner { require(token.balanceOf(address(this)) != 0, "Invalid balance"); diff --git a/contracts/contracts/Vesting.sol b/contracts/contracts/Vesting.sol index 483f062..052fcd3 100644 --- a/contracts/contracts/Vesting.sol +++ b/contracts/contracts/Vesting.sol @@ -3,35 +3,39 @@ pragma solidity >=0.8.15; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./FluenceToken.sol"; -import "./interfaces/IVestingERC20.sol"; /** * @title Vesting with Delayed Start * @notice Vesting Fluence token contract * @dev This contract implements the ERC20 standard. It is possible to add the contract to a wallet. Transferring to zero address is unlocking the released amount. */ -contract Vesting is IVestingERC20 { +contract Vesting is IERC20 { using SafeERC20 for IERC20; /** * @notice Returns the vesting token - **/ + * + */ FluenceToken public immutable token; /** * @notice Returns the start vesting time - **/ + * + */ uint256 public immutable startTimestamp; /** * @notice Returns the vesting duration since vesting start - **/ + * + */ uint256 public immutable vestingDuration; /** * @notice Returns the vesting contract decimals - **/ + * + */ uint8 public immutable decimals; bytes32 private immutable _name; @@ -42,12 +46,14 @@ contract Vesting is IVestingERC20 { /** * @notice Returns the locked vesting user's balance - **/ + * + */ mapping(address => uint256) public lockedBalances; /** * @notice Returns the current vesting user's balance - **/ + * + */ mapping(address => uint256) public balanceOf; uint256 private _totalSupply; @@ -61,7 +67,8 @@ contract Vesting is IVestingERC20 { * @param _vestingDuration - vesting duration * @param accounts - vesting accounts * @param amounts - vesting amounts of accounts - **/ + * + */ constructor( FluenceToken token_, string memory name_, @@ -71,10 +78,7 @@ contract Vesting is IVestingERC20 { address[] memory accounts, uint256[] memory amounts ) { - require( - accounts.length == amounts.length, - "accounts and amounts must have the same length" - ); + require(accounts.length == amounts.length, "accounts and amounts must have the same length"); require(bytes(name_).length <= 31, "invalid name length"); require(bytes(symbol_).length <= 31, "invalid symbol length"); @@ -97,13 +101,14 @@ contract Vesting is IVestingERC20 { uint256 amount = amounts[i]; lockedBalances[accounts[i]] = amount; balanceOf[accounts[i]] = amount; - _addTotalSupply(amount); + _totalSupply += amount; } } /** * @notice Returns vesting contract name - **/ + * + */ function name() external view returns (string memory n) { n = string(abi.encodePacked(_name)); uint256 length = _nameLength; @@ -114,7 +119,8 @@ contract Vesting is IVestingERC20 { /** * @notice Returns vesting contract symbol - **/ + * + */ function symbol() external view returns (string memory s) { s = string(abi.encodePacked(_symbol)); uint256 length = _symbolLength; @@ -126,7 +132,8 @@ contract Vesting is IVestingERC20 { /** * @notice Get a available amount by user * @return available amount - **/ + * + */ function getAvailableAmount(address account) public view returns (uint256) { if (block.timestamp <= startTimestamp) { return 0; @@ -149,16 +156,30 @@ contract Vesting is IVestingERC20 { return amount; } + /** + * @notice Unsupported operation + * + */ + function allowance(address owner, address spender) external view returns (uint256) { + return 0; + } + + /** + * @notice Returns total locked amount + * + */ + function totalSupply() external view virtual override returns (uint256) { + return _totalSupply; + } + /** * @notice Returns released amount * @param to - always address 0x00 * @param amount - the full released amount or part of it - **/ + * + */ function transfer(address to, uint256 amount) external returns (bool) { - require( - to == address(0x00), - "Transfer allowed only to the zero address" - ); + require(to == address(0x00), "Transfer allowed only to the zero address"); address sender = msg.sender; _burn(sender, amount); @@ -168,14 +189,19 @@ contract Vesting is IVestingERC20 { } /** - * @notice Returns total locked amount - **/ - function totalSupply() external view virtual override returns (uint256) { - return _totalSupply; + * @notice Unsupported operation + * + */ + function transferFrom(address from, address to, uint256 value) external returns (bool) { + revert("Unsupported operation"); } - function _addTotalSupply(uint256 amount) internal virtual { - _totalSupply += amount; + /** + * @notice Unsupported operation + * + */ + function approve(address spender, uint256 amount) external returns (bool) { + revert("Unsupported operation"); } function _burn(address from, uint256 amount) internal { @@ -191,9 +217,10 @@ contract Vesting is IVestingERC20 { _beforeBurn(from, amount); - IERC20(token).safeTransfer(from, amount); - balanceOf[from] -= amount; + _totalSupply -= amount; + + IERC20(token).safeTransfer(from, amount); } function _beforeBurn(address from, uint256 amount) internal virtual {} diff --git a/contracts/contracts/VestingWithVoting.sol b/contracts/contracts/VestingWithVoting.sol index 09329ed..f95d6f8 100644 --- a/contracts/contracts/VestingWithVoting.sol +++ b/contracts/contracts/VestingWithVoting.sol @@ -19,22 +19,11 @@ contract VestingWithVoting is Vesting, Votes { FluenceToken token_, string memory name_, string memory symbol_, - uint256 _cliffDuration, - uint256 _vestingDuration, + uint256 vestingDelay_, + uint256 vestingDuration_, address[] memory accounts, uint256[] memory amounts - ) - Vesting( - token_, - name_, - symbol_, - _cliffDuration, - _vestingDuration, - accounts, - amounts - ) - EIP712(name_, "1") - { + ) Vesting(token_, name_, symbol_, vestingDelay_, vestingDuration_, accounts, amounts) EIP712(name_, "1") { for (uint256 i = 0; i < accounts.length; i++) { address account = accounts[i]; _transferVotingUnits(address(0x00), account, amounts[i]); @@ -47,14 +36,7 @@ contract VestingWithVoting is Vesting, Votes { return _getTotalSupply(); } - function _addTotalSupply(uint256 amount) internal override {} - - function _getVotingUnits(address account) - internal - view - override - returns (uint256) - { + function _getVotingUnits(address account) internal view override returns (uint256) { return balanceOf[account]; } diff --git a/contracts/contracts/interfaces/IVestingERC20.sol b/contracts/contracts/interfaces/IVestingERC20.sol deleted file mode 100644 index 33413f9..0000000 --- a/contracts/contracts/interfaces/IVestingERC20.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - -pragma solidity >=0.8.15; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IVestingERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); - - function name() external view returns (string memory); - - function symbol() external view returns (string memory); - - function decimals() external view returns (uint8); - - function totalSupply() external view returns (uint256); - - function balanceOf(address account) external view returns (uint256); - - function transfer(address to, uint256 amount) external returns (bool); -} diff --git a/contracts/deploy/0002_LPController.ts b/contracts/deploy/0002_LPController.ts index 63ae64e..af9cccc 100644 --- a/contracts/deploy/0002_LPController.ts +++ b/contracts/deploy/0002_LPController.ts @@ -65,8 +65,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", config.deployment!.contracts!.balancerVault ?? "0xBA12222222228d8Ba445958a75a0704d566BF2C8", - config.deployment!.contracts!.balancerHelper ?? - "0x5aDDCCa35b7A0D07C74063c48700C8590E87864E", executorAddress, lbpConfigs[0].token, lbpConfigs[1].token, diff --git a/contracts/test/Deploy.spec.ts b/contracts/test/Deploy.spec.ts index 9e7d7aa..733b0c3 100644 --- a/contracts/test/Deploy.spec.ts +++ b/contracts/test/Deploy.spec.ts @@ -48,9 +48,11 @@ describe("Deploy script", () => { const setupTest = deployments.createFixture( async (hre: HardhatRuntimeEnvironment) => { const hardhatSigners = await hre.ethers.getSigners(); - usdToken = await new DevERC20__factory( - hardhatSigners[0] - ).deploy("USD", "USD", ethers.utils.parseEther(String(lbpUSDAmount))); + usdToken = await new DevERC20__factory(hardhatSigners[0]).deploy( + "USD", + "USD", + ethers.utils.parseEther(String(lbpUSDAmount)) + ); fluenceMultisig = hardhatSigners[hardhatSigners.length - 1]; @@ -293,8 +295,10 @@ describe("Deploy script", () => { "0x0000000000000000000000000000000000000000" ) ).to.eq(true); - - expect(await executor.hasRole(CANCELLER_ROLE, fluenceMultisig.address)).to.eq(true); + + expect( + await executor.hasRole(CANCELLER_ROLE, fluenceMultisig.address) + ).to.eq(true); }); it("DevRewardDistributor is correct", async () => { @@ -359,7 +363,9 @@ describe("Deploy script", () => { expect(await vesting.decimals()).to.eq(18); expect(await vesting.token()).to.eq(token.address); - expect(await vesting.vestingDuration()).to.eq(cfg.vestingDurationMonths * MONTH); + expect(await vesting.vestingDuration()).to.eq( + cfg.vestingDurationMonths * MONTH + ); const accounts = cfg.accounts; const amounts = cfg.amounts; for (let i = 0; i < accounts.length; i++) { diff --git a/contracts/test/e2e/E2E_LPController.spec.ts b/contracts/test/e2e/E2E_LPController.spec.ts index 6d029a9..6bf5918 100644 --- a/contracts/test/e2e/E2E_LPController.spec.ts +++ b/contracts/test/e2e/E2E_LPController.spec.ts @@ -14,6 +14,8 @@ import { IBalancerLBP__factory, DevERC20__factory, IERC20Metadata__factory, + IBalancerHelper, + IERC20__factory, } from "../../typechain"; import { BigNumber } from "ethers"; import { DAY } from "../../utils/time"; @@ -41,6 +43,7 @@ const setupTest = deployments.createFixture( lpController: LPController; fltToken: IERC20MetadataUpgradeable; usdToken: IERC20Metadata; + balancerHelper: IBalancerHelper; }> => { await deployments.fixture([]); const hardhatSigners = await hre.ethers.getSigners(); @@ -108,6 +111,10 @@ const setupTest = deployments.createFixture( await hre.deployments.get("FluenceToken") ).address )) as IERC20MetadataUpgradeable, + balancerHelper: (await ethers.getContractAt( + "IBalancerHelper", + "0x5aDDCCa35b7A0D07C74063c48700C8590E87864E" + )) as IBalancerHelper, }; } ); @@ -118,6 +125,7 @@ describe("LPController", () => { let vault: IBalancerVault; let lbpFactory: IBalancerLBPFactory; let lbp: IBalancerLBP; + let balancerHelper: IBalancerHelper; let poolId: string; let params: Array<{ token: IERC20MetadataUpgradeable; @@ -145,6 +153,7 @@ describe("LPController", () => { await settings.lpController.liquidityBootstrappingPool(), signer ); + balancerHelper = settings.balancerHelper; params = new Array( { @@ -204,7 +213,7 @@ describe("LPController", () => { const lbpPoolDurationDays = config.deployment!.pool!.lbpPoolDurationDays * DAY; - expect(await lbp.getSwapEnabled()).to.eq(true); + expect(await lbp.getSwapEnabled()).to.eq(false); expect(await lbp.getSwapFeePercentage()).to.eq( ethers.utils.parseUnits( String(config.deployment!.pool!.swapFeePercentage), @@ -246,15 +255,38 @@ describe("LPController", () => { }); it("setSwapEnabledInBalancerLBP", async () => { - await lpController.setSwapEnabledInBalancerLBP(false); - expect(await lbp.getSwapEnabled()).to.eq(false); - await lpController.setSwapEnabledInBalancerLBP(true); expect(await lbp.getSwapEnabled()).to.eq(true); + + await lpController.setSwapEnabledInBalancerLBP(false); + expect(await lbp.getSwapEnabled()).to.eq(false); }); it("exit from lbp", async () => { - const tx = await lpController.exitFromBalancerLBP(); + const userData = ethers.utils.defaultAbiCoder.encode( + ["uint256", "uint256"], + [ + 1, + await IERC20__factory.connect( + await lpController.liquidityBootstrappingPool(), + await ethers.provider.getSigner() + ).balanceOf(lpController.address), + ] + ); + + const queryExitRes = await balancerHelper.callStatic.queryExit( + await lpController.lbpPoolId(), + lpController.address, + lpController.address, + { + assets: [await lpController.token0(), await lpController.token1()], + minAmountsOut: new Array(2).fill(0), + userData: userData, + toInternalBalance: false, + } + ); + + const tx = await lpController.exitFromBalancerLBP(queryExitRes[1]); await tx.wait(); expect( await IERC20Metadata__factory.connect( @@ -305,7 +337,12 @@ describe("LPController", () => { await lpController.createUniswapLP( nearestUsableTick(-887272, spacings), nearestUsableTick(887272, spacings), - price.toString() + price.toString(), + 3000, + token0Balance, + token1Balance, + 0, + 0 ); const pool = await lpController.uniswapPool(); @@ -361,13 +398,18 @@ describe("LPController", () => { ); await expect( - lpControllerWithMainAccount.exitFromBalancerLBP() + lpControllerWithMainAccount.exitFromBalancerLBP([0, 0]) ).to.be.revertedWith( `OwnableUnauthorizedAccount("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65")` ); await expect( lpControllerWithMainAccount.createUniswapLP( + BigNumber.from(1), + BigNumber.from(1), + BigNumber.from(1), + BigNumber.from(1), + BigNumber.from(1), BigNumber.from(1), BigNumber.from(1), BigNumber.from(1)