diff --git a/solidity/contracts/ConnextVestingWallet.sol b/solidity/contracts/ConnextVestingWallet.sol index 588c149..d5a0999 100644 --- a/solidity/contracts/ConnextVestingWallet.sol +++ b/solidity/contracts/ConnextVestingWallet.sol @@ -29,7 +29,7 @@ contract ConnextVestingWallet is Ownable2Step, IConnextVestingWallet { uint64 public constant NEXT_TOKEN_LAUNCH = SEPT_05_2023; // Equals to Sept 5th 2023 /// @inheritdoc IConnextVestingWallet - address public constant NEXT_TOKEN = 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a; // Mainnet NEXT token address + IERC20 public constant NEXT_TOKEN = IERC20(0xFE67A4450907459c3e1FFf623aA927dD4e28c67a); // Mainnet NEXT token address /// @inheritdoc IConnextVestingWallet uint64 public constant UNLOCK_DURATION = ONE_YEAR + ONE_MONTH; // 13 months duration @@ -85,14 +85,14 @@ contract ConnextVestingWallet is Ownable2Step, IConnextVestingWallet { function release() public { uint256 _amount = releasable(); released += _amount; - IERC20(NEXT_TOKEN).transfer(owner(), _amount); - emit ERC20Released(NEXT_TOKEN, _amount); + NEXT_TOKEN.transfer(owner(), _amount); + emit ERC20Released(address(NEXT_TOKEN), _amount); } /// @inheritdoc IConnextVestingWallet function releasable() public view returns (uint256 _amount) { _amount = vestedAmount(uint64(block.timestamp)) - released; - uint256 _balance = IERC20(NEXT_TOKEN).balanceOf(address(this)); + uint256 _balance = NEXT_TOKEN.balanceOf(address(this)); _amount = _balance < _amount ? _balance : _amount; } @@ -103,7 +103,7 @@ contract ConnextVestingWallet is Ownable2Step, IConnextVestingWallet { function sendDust(IERC20 _token, uint256 _amount, address _to) external onlyOwner { if (_to == address(0)) revert ZeroAddress(); - if (_token == IERC20(NEXT_TOKEN) && released != TOTAL_AMOUNT) { + if (_token == NEXT_TOKEN && (released != TOTAL_AMOUNT || block.timestamp < UNLOCK_END)) { revert NotAllowed(); } @@ -118,7 +118,7 @@ contract ConnextVestingWallet is Ownable2Step, IConnextVestingWallet { * @inheritdoc IConnextVestingWallet * @dev This func is needed because only the recipients can claim */ - function claim(address _llamaVestAddress) external { - IVestingEscrowSimple(_llamaVestAddress).claim(address(this)); + function claim(IVestingEscrowSimple _llamaVest) external { + _llamaVest.claim(address(this)); } } diff --git a/solidity/interfaces/IConnextVestingWallet.sol b/solidity/interfaces/IConnextVestingWallet.sol index 4ffe06b..8277579 100644 --- a/solidity/interfaces/IConnextVestingWallet.sol +++ b/solidity/interfaces/IConnextVestingWallet.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.20; +import {IVestingEscrowSimple} from './IVestingEscrowSimple.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IConnextVestingWallet { @@ -37,7 +38,7 @@ interface IConnextVestingWallet { * @notice NEXT token address * @return _nextToken The address of the NEXT token */ - function NEXT_TOKEN() external view returns (address _nextToken); + function NEXT_TOKEN() external view returns (IERC20 _nextToken); /** * @notice Token launch date @@ -121,9 +122,9 @@ interface IConnextVestingWallet { /** * @notice Claim tokens from Llama Vesting contract - * @param _llamaVestAddress The address of the Llama Vesting contract + * @param _llamaVest The address of the Llama Vesting contract */ - function claim(address _llamaVestAddress) external; + function claim(IVestingEscrowSimple _llamaVest) external; /** * @notice Collect dust from the contract diff --git a/solidity/test/integration/ConnextVestingWallet.t.sol b/solidity/test/integration/ConnextVestingWallet.t.sol index b1373c0..1018f25 100644 --- a/solidity/test/integration/ConnextVestingWallet.t.sol +++ b/solidity/test/integration/ConnextVestingWallet.t.sol @@ -210,9 +210,17 @@ contract UnitConnextVestingWallet is Test, Constants { assertEq(_randomAddress.balance, _dustAmount); // Collect vesting token after the vesting period has ended - vm.warp(_firstMilestoneTimestamp + 365 days * 3 + 10 days); + vm.warp(_connextVestingWallet.UNLOCK_END()); assertEq(_nextToken.balanceOf(_randomAddress), 0); _connextVestingWallet.release(); + + // Can't collect the vesting token before the unlock period has ended + vm.warp(_connextVestingWallet.UNLOCK_END() - 1); + vm.expectRevert(abi.encodeWithSelector(IConnextVestingWallet.NotAllowed.selector)); + vm.prank(owner); + _connextVestingWallet.sendDust(_nextToken, _dustAmount, _randomAddress); + // After all tokens were released AND the unlock period has ended, the dust can be collected + vm.warp(_connextVestingWallet.UNLOCK_END()); vm.prank(owner); _connextVestingWallet.sendDust(_nextToken, _dustAmount, _randomAddress); assertEq(_nextToken.balanceOf(_randomAddress), _dustAmount); diff --git a/solidity/test/integration/LlamaVesting.t.sol b/solidity/test/integration/LlamaVesting.t.sol index 61d1356..87ec889 100644 --- a/solidity/test/integration/LlamaVesting.t.sol +++ b/solidity/test/integration/LlamaVesting.t.sol @@ -50,7 +50,7 @@ contract IntegrationLlamaVesting is IntegrationBase { */ function _warpAndWithdraw(uint256 _timestamp) internal { vm.warp(_timestamp); - _connextVestingWallet.claim(address(_llamaVest)); + _connextVestingWallet.claim(_llamaVest); _connextVestingWallet.release(); }