Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: simplify code #7

Merged
merged 15 commits into from
Feb 23, 2024
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ concurrency:

env:
MAINNET_RPC: ${{ secrets.MAINNET_RPC }}
DEPLOYER_PRIVATE_KEY: '0x115241e9f8d2246550d50641f38ee4170b937f25dfb983f7e34960f9670fc41d'
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}

jobs:
integration-tests:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
yarn-error.log
node_modules
.DS_STORE
.vscode

# Foundry files
cache
Expand Down
194 changes: 59 additions & 135 deletions solidity/contracts/ConnextVestingWallet.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

// solhint-disable-next-line no-unused-import
import {VestingWallet, VestingWalletWithCliff} from './VestingWalletWithCliff.sol';

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {Ownable2Step} from '@openzeppelin/contracts/access/Ownable2Step.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import {IConnextVestingWallet} from 'interfaces/IConnextVestingWallet.sol';
import {IVestingEscrowSimple} from 'interfaces/IVestingEscrowSimple.sol';

/**
Expand All @@ -16,183 +15,108 @@ import {IVestingEscrowSimple} from 'interfaces/IVestingEscrowSimple.sol';
* and 1/13 unlocks every month thereafter for 12 months. All tokens are unlocked after 24 months.
* https://forum.connext.network/t/rfc-partnership-token-agreements/938
*/
contract ConnextVestingWallet is VestingWalletWithCliff, Ownable2Step {
/*///////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/

/**
* @notice 1 year in seconds
*/
contract ConnextVestingWallet is Ownable2Step, IConnextVestingWallet {
/// @inheritdoc IConnextVestingWallet
uint64 public constant ONE_YEAR = 365 days;

/**
* @notice 1 month in seconds (on average)
*/
/// @inheritdoc IConnextVestingWallet
uint64 public constant ONE_MONTH = ONE_YEAR / 12;

/**
* @notice Sept 5th 2023 in seconds
*/
/// @inheritdoc IConnextVestingWallet
uint64 public constant SEPT_05_2023 = 1_693_872_000;

/**
* @notice Token launch date
* @dev Equals to Sept 5th 2023
*/
uint64 public constant NEXT_TOKEN_LAUNCH = SEPT_05_2023;
/// @inheritdoc IConnextVestingWallet
uint64 public constant NEXT_TOKEN_LAUNCH = SEPT_05_2023; // Equals to Sept 5th 2023

/**
* @notice NEXT token address
* @dev Mainnet address
*/
address public constant NEXT_TOKEN = 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a;
/// @inheritdoc IConnextVestingWallet
address public constant NEXT_TOKEN = 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a; // Mainnet NEXT token address

/**
* NOTE: The equivalent vesting schedule has a 13 months duration, with a 1 month cliff,
* offsetted to start from `Sept 5th 2024 - 1 month`: At Sept 5th 2024 the cliff
* is triggered unlocking 1/13 of the tokens, and then 1/13 of the tokens will
* be linearly unlocked every month after that.
*/
0xGorilla marked this conversation as resolved.
Show resolved Hide resolved
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_DURATION = ONE_YEAR + ONE_MONTH; // 13 months duration

/**
* @notice Vesting duration including one month of cliff
* @dev 13 months duration
*/
uint64 public constant VESTING_DURATION = ONE_YEAR + ONE_MONTH;
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_CLIFF_DURATION = ONE_MONTH; // 1 month cliff

/**
* @notice Vesting cliff duration
* @dev 1 month cliff
*/
uint64 public constant VESTING_CLIFF_DURATION = ONE_MONTH;
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_OFFSET = ONE_YEAR - ONE_MONTH; // 11 months offset

/**
* @notice Vesting warmup time
* @dev 11 months offset
*/
uint64 public constant VESTING_OFFSET = ONE_YEAR - ONE_MONTH;
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_START = NEXT_TOKEN_LAUNCH + UNLOCK_OFFSET; // Sept 5th 2024 - 1 month

/**
* @notice Vesting start date
* @dev Sept 5th 2024 - 1 month
*/
uint64 public constant VESTING_START_DATE = NEXT_TOKEN_LAUNCH + VESTING_OFFSET;
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_CLIFF = UNLOCK_START + UNLOCK_CLIFF_DURATION; // Sept 5th 2024

/*///////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IConnextVestingWallet
uint64 public constant UNLOCK_END = UNLOCK_START + UNLOCK_DURATION; // Sept 5th 2025

/**
* @notice Total amount of tokens to be vested
* @dev Set into constructor
*/
uint256 public immutable TOTAL_AMOUNT;
/// @inheritdoc IConnextVestingWallet
uint256 public immutable TOTAL_AMOUNT; // Set into constructor

/// @inheritdoc IConnextVestingWallet
uint256 public released;

/**
* @dev Init VestingWalletWithCliff and save total amout of tokens to be unlocked
* @param _beneficiary The address of the beneficiary
* @param _totalAmount The total amount of tokens to be unlocked
* @param _owner The address of the beneficiary
* @param _totalAmount The total amount of tokens to be unlocked
*/
constructor(
address _beneficiary,
uint256 _totalAmount
) VestingWalletWithCliff(_beneficiary, VESTING_START_DATE, VESTING_DURATION, VESTING_CLIFF_DURATION) {
constructor(address _owner, uint256 _totalAmount) Ownable(_owner) {
TOTAL_AMOUNT = _totalAmount;
}

/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

/**
* @notice Permission denied
*/
error NotAllowed();

/**
* @notice Zero address not allowed
* NOTE: The equivalent unlock schedule has a 13 months duration, with a 1 month cliff,
* offsetted to start from `Sept 5th 2024 - 1 month`: At Sept 5th 2024 the cliff
* is triggered unlocking 1/13 of the tokens, and then 1/13 of the tokens will
* be linearly unlocked every month after that.
*/
error ZeroAddress();

/*///////////////////////////////////////////////////////////////
OVERRIDES
//////////////////////////////////////////////////////////////*/

/**
* @inheritdoc VestingWallet
* @notice This contract is only meant to unlock NEXT tokens
*/
function vestedAmount(uint64) public view virtual override returns (uint256 _amount) {
return 0;
/// @inheritdoc IConnextVestingWallet
function vestedAmount(uint64 _timestamp) public view returns (uint256 _amount) {
if (_timestamp < UNLOCK_CLIFF) {
return 0;
} else if (_timestamp >= UNLOCK_END) {
return TOTAL_AMOUNT;
} else {
return (TOTAL_AMOUNT * (_timestamp - UNLOCK_START)) / UNLOCK_DURATION;
}
}

/**
* @inheritdoc VestingWallet
* @notice This contract is only meant to unlock NEXT tokens
*/
function vestedAmount(address _token, uint64 _timestamp) public view virtual override returns (uint256 _amount) {
if (_token != NEXT_TOKEN) return 0;

return _vestingSchedule(TOTAL_AMOUNT, _timestamp);
/// @inheritdoc IConnextVestingWallet
function release() public {
uint256 _amount = releasable();
released += _amount;
IERC20(NEXT_TOKEN).transfer(owner(), _amount);
emit ERC20Released(NEXT_TOKEN, _amount);
}

/**
* @inheritdoc VestingWallet
* @notice This contract is only meant to unlock NEXT tokens
*/
function releasable(address _token) public view virtual override returns (uint256 _amount) {
_amount = vestedAmount(_token, uint64(block.timestamp)) - released(_token);
uint256 _balance = IERC20(_token).balanceOf(address(this));
/// @inheritdoc IConnextVestingWallet
function releasable() public view returns (uint256 _amount) {
_amount = vestedAmount(uint64(block.timestamp)) - released;
uint256 _balance = IERC20(NEXT_TOKEN).balanceOf(address(this));
_amount = _balance < _amount ? _balance : _amount;
}

/**
* @inheritdoc Ownable2Step
* @dev Override needed by linearization
*/
function _transferOwnership(address _newOwner) internal virtual override(Ownable2Step, Ownable) {
super._transferOwnership(_newOwner);
}

/**
* @inheritdoc Ownable2Step
* @dev Override needed by linearization
*/
function transferOwnership(address _newOwner) public virtual override(Ownable2Step, Ownable) {
super.transferOwnership(_newOwner);
}

/*///////////////////////////////////////////////////////////////
CUSTOM LOGIC
//////////////////////////////////////////////////////////////*/

/**
* @notice Collect dust from the contract
* @inheritdoc IConnextVestingWallet
* @dev This contract allows to withdraw any token, with the exception of unlocked NEXT tokens
* @param _token The address of the token to withdraw
* @param _amount The amount of tokens to withdraw
* @param _to The address to send the tokens to
*/
function sendDust(IERC20 _token, uint256 _amount, address _to) external onlyOwner {
if (_to == address(0)) revert ZeroAddress();
if (_token == IERC20(NEXT_TOKEN) && released(NEXT_TOKEN) != TOTAL_AMOUNT) {

if (_token == IERC20(NEXT_TOKEN) && released != TOTAL_AMOUNT) {
revert NotAllowed();
}

if (_token == IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) {
// Sending ETH
payable(_to).transfer(_amount);
payable(_to).transfer(_amount); // Sending ETH
} else {
// Sending ERC20s
_token.transfer(_to, _amount);
_token.transfer(_to, _amount); // Sending ERC20s
}
}

/**
* @notice Claim tokens from Llama Vesting contract
* @inheritdoc IConnextVestingWallet
* @dev This func is needed because only the recipients can claim
* @param _llamaVestAddress The address of the Llama Vesting contract
*/
function claim(address _llamaVestAddress) external {
IVestingEscrowSimple(_llamaVestAddress).claim(address(this));
Expand Down
55 changes: 0 additions & 55 deletions solidity/contracts/VestingWalletWithCliff.sol

This file was deleted.

Loading
Loading