diff --git a/.env.example b/.env.example deleted file mode 100644 index b0effda..0000000 --- a/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -RPC_URL= -DEPLOYER_KEY= -DEPLOYER_ADDRESS=
-ETHERSCAN_API_KEY= -EXISTING_FACTORY= -EXISTING_TOKEN= diff --git a/contract-map-LinearVestingProjectUpgradeable.drawio b/contract-map-LinearVestingProjectUpgradeable.drawio new file mode 100644 index 0000000..4a5e946 --- /dev/null +++ b/contract-map-LinearVestingProjectUpgradeable.drawio @@ -0,0 +1,845 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contracts-layout.drawio b/contracts-layout.drawio new file mode 100644 index 0000000..32bfc8e --- /dev/null +++ b/contracts-layout.drawio @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contracts/linear-vesting/ILinearVestingProjectUpgradeable.sol b/contracts/linear-vesting/ILinearVestingProjectUpgradeable.sol index 4f49673..311d22b 100644 --- a/contracts/linear-vesting/ILinearVestingProjectUpgradeable.sol +++ b/contracts/linear-vesting/ILinearVestingProjectUpgradeable.sol @@ -7,14 +7,14 @@ pragma solidity ^0.8.4; * Taken from https://github.com/dandelionlabs-io/linear-vesting-contracts/blob/master/contracts/linear-vesting/ILinearVestingProjectUpgradeable.sol */ interface ILinearVestingProjectUpgradeable { - - /// @notice Pool definition - struct Pool { - string name; // Name of the pool + /// @notice Vesting Schedule definition + struct VestingSchedule { + address token; + string metadataUrl; // Name of the vesting schedule uint startTime; // Starting time of the vesting period in unix timestamp format uint endTime; // Ending time of the vesting period in unix timestamp format uint vestingDuration; // In seconds - uint amount; // Total size of pool + uint amount; // Total size of vesting uint totalClaimed; // Total amount claimed till moment uint grants; // Amount of stakeholders } @@ -26,38 +26,70 @@ interface ILinearVestingProjectUpgradeable { uint perSecond; // Reward per second } - /// @notice Event emitted when a new pool is created - event PoolAdded(uint indexed index, address createdBy, string name, uint startTime, uint endTime); + /// @notice Event emitted when a new vesting schedule is created + event VestingScheduleAdded( + address token, + string metadataUrl, + uint indexed index, + address createdBy, + uint startTime, + uint endTime + ); /// @notice Event emitted when a new grant is created - event GrantAdded(uint indexed poolIndex, address indexed addedBy, address indexed recipient, uint amount); + event GrantAdded( + uint indexed vestingIndex, + address indexed addedBy, + address indexed recipient, + uint amount + ); /// @notice Event emitted when tokens are claimed by a recipient from a grant - event GrantClaimed(uint indexed poolIndex, address indexed recipient, uint amountClaimed); + event GrantClaimed( + uint indexed vestingIndex, + address indexed recipient, + uint amountClaimed + ); /// @notice Event emitted when metadata url is changed event MetadataUrlChanged(address indexed changedBy, string metadataUrl); /** - * @notice Creates a new pool + * @notice Creates a new vesting schedule * @param _startTime starting time of the vesting period in timestamp format * @param _vestingDuration duration time of the vesting period in timestamp format */ - function createPool(string memory _name, uint256 _startTime, uint256 _vestingDuration) external returns (uint256); + function createVestingSchedule( + address token, + string memory _metadataUrl, + uint256 _startTime, + uint256 _vestingDuration + ) external returns (uint256); /** - * @notice Creates a new pool + * @notice Creates a new vesting schedule grants * @param _startTime starting time of the vesting period in timestamp format * @param _vestingDuration duration time of the vesting period in timestamp format */ - function createPoolWithGrants(string memory _name, uint256 _startTime, uint256 _vestingDuration, address[] memory _recipients, uint256[] memory _amounts) external returns (uint256); + function createVestingScheduleWithGrants( + address token, + string memory _metadataUrl, + uint256 _startTime, + uint256 _vestingDuration, + address[] memory _recipients, + uint256[] memory _amounts + ) external returns (uint256); /** * @notice Add list of grants in batch. - * @param _recipients list of addresses of the stakeholders - * @param _amounts list of amounts to be assigned to the stakeholders + * @param _recipients list of addresses of the beneficiaries + * @param _amounts list of amounts to be assigned to the beneficiaries */ - function addGrants(uint _poolIndex, address[] memory _recipients, uint256[] memory _amounts) external; + function addGrants( + uint _vestingIndex, + address[] memory _recipients, + uint256[] memory _amounts + ) external; /** * @notice Calculate the vested and unclaimed tokens available for `recipient` to claim @@ -65,21 +97,24 @@ interface ILinearVestingProjectUpgradeable { * @param _recipient The address that has a grant * @return The amount recipient can claim */ - function calculateGrantClaim(uint _poolIndex, address _recipient) external view returns (uint256); + function calculateGrantClaim( + uint _vestingIndex, + address _recipient + ) external view returns (uint256); /** * @notice Allows a grant recipient to claim their vested tokens * @dev Errors if no tokens have vested * @dev It is advised recipients check they are entitled to claim via `calculateGrantClaim` before calling this */ - function claimVestedTokens(uint _poolIndex) external; + function claimVestedTokens(uint _vestingIndex) external; /** * @notice Allows a grant recipient to claim multiple vested tokens * @dev Errors if no tokens have vested * @dev It is advised recipients check they are entitled to claim via `calculateGrantClaim` before calling this */ - function claimMultiplePools(uint[] memory _poolIndexes) external; + function claimMultipleVestings(uint[] memory _vestingIndexes) external; function setMetadataUrl(string memory _metadata) external; } diff --git a/contracts/linear-vesting/LinearVestingProjectFactoryUpgradeable.sol b/contracts/linear-vesting/LinearVestingProjectFactoryUpgradeable.sol index a2a9f73..afe0c4f 100644 --- a/contracts/linear-vesting/LinearVestingProjectFactoryUpgradeable.sol +++ b/contracts/linear-vesting/LinearVestingProjectFactoryUpgradeable.sol @@ -8,13 +8,12 @@ import "./LinearVestingProjectUpgradeable.sol"; import "./LinearVestingProjectBeacon.sol"; contract LinearVestingProjectFactoryUpgradeable is OwnableUpgradeable { - address[] private projects; uint public projectsCount; LinearVestingProjectBeacon beacon; /** - * @dev Emitted when a new project is created. + * @dev Emitted when a new project is created. */ event ProjectCreated( uint indexed index, @@ -24,12 +23,16 @@ contract LinearVestingProjectFactoryUpgradeable is OwnableUpgradeable { string metadataUrl ); - function __LinearVestingProjectFactory_initialize(address _initBlueprint) public initializer onlyInitializing { + function __LinearVestingProjectFactory_initialize( + address _initBlueprint + ) public initializer onlyInitializing { __Ownable_init(); __LinearVestingProjectFactory_initialize_unchained(_initBlueprint); } - function __LinearVestingProjectFactory_initialize_unchained(address _initBlueprint) public onlyInitializing { + function __LinearVestingProjectFactory_initialize_unchained( + address _initBlueprint + ) public onlyInitializing { beacon = new LinearVestingProjectBeacon(_initBlueprint); } @@ -41,7 +44,9 @@ contract LinearVestingProjectFactoryUpgradeable is OwnableUpgradeable { BeaconProxy project = new BeaconProxy( address(beacon), abi.encodeWithSelector( - LinearVestingProjectUpgradeable(address(0)).__LinearVestingProject_initialize.selector, + LinearVestingProjectUpgradeable(address(0)) + .__LinearVestingProject_initialize + .selector, _token, _metadataUrl ) @@ -69,7 +74,9 @@ contract LinearVestingProjectFactoryUpgradeable is OwnableUpgradeable { return address(beacon); } - function getProjectAddress(uint projectIndex) external view returns (address) { + function getProjectAddress( + uint projectIndex + ) external view returns (address) { return projects[projectIndex]; } diff --git a/contracts/linear-vesting/LinearVestingProjectTransferableUpgradeable.sol b/contracts/linear-vesting/LinearVestingProjectTransferableUpgradeable.sol index d6a19a1..ae638ea 100644 --- a/contracts/linear-vesting/LinearVestingProjectTransferableUpgradeable.sol +++ b/contracts/linear-vesting/LinearVestingProjectTransferableUpgradeable.sol @@ -1,18 +1,18 @@ -//// SPDX-License-Identifier: MIT -//pragma solidity ^0.8.4; -// -//import "./LinearVestingProjectUpgradeable.sol"; -// -//abstract contract LinearVestingProjectTransferableUpgradeable is LinearVestingProjectUpgradeable { +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.4; + +// import "./LinearVestingProjectUpgradeable.sol"; + +// abstract contract LinearVestingProjectTransferableUpgradeable is LinearVestingProjectUpgradeable { // using SafeMathUpgradeable for uint256; -// + // /// @notice Event emitted when the grant investor is changed // event GrantChanged(address indexed oldOwner, address indexed newOwner); -// + // /// @notice List of investors who got blacklist tokens. // /// @dev Structure of the map: investor => new address // mapping(address => address) public blacklist; -// + // /** // * @notice In case if the user doesn't want to change the grant. // * @param _oldAddress existing address from the investor which we want to change @@ -38,19 +38,19 @@ // tokenGrants[_oldAddress].amount > 0, // "VestingPeriod::changeInvestor: oldAddress has no remaining balance" // ); -// + // tokenGrants[_newAddress] = Grant( // tokenGrants[_oldAddress].amount, // tokenGrants[_oldAddress].totalClaimed, // tokenGrants[_oldAddress].perSecond // ); // delete tokenGrants[_oldAddress]; -// + // blacklist[_oldAddress] = _newAddress; -// + // emit GrantChanged(_oldAddress, _newAddress); // } -// + // function addTokenGrants( // address[] memory _recipients, // uint256[] memory _amounts @@ -67,7 +67,7 @@ // _recipients.length == _amounts.length, // "VestingPeriod::addTokenGrants: invalid parameters length (they should be same)" // ); -// + // uint256 amountSum = 0; // for (uint16 i = 0; i < _recipients.length; i++) { // require( @@ -82,20 +82,20 @@ // blacklist[_recipients[i]] == address(0), // "VestingPeriod:addTOkenGrants: Blacklisted address" // ); -// + // require( // _amounts[i] > 0, // "VestingPeriod::addTokenGrant: amount == 0" // ); // amountSum = amountSum.add(_amounts[i]); // } -// + // // Transfer the grant tokens under the control of the vesting contract // require( // token.transferFrom(msg.sender, address(this), amountSum), // "VestingPeriod::addTokenGrants: transfer failed" // ); -// + // for (uint16 i = 0; i < _recipients.length; i++) { // Grant memory grant = Grant({ // amount: _amounts[i], @@ -105,7 +105,7 @@ // tokenGrants[_recipients[i]] = grant; // emit GrantAdded(_recipients[i], _amounts[i]); // } -// + // pool.amount = pool.amount.add(amountSum); // } -//} +// } diff --git a/contracts/linear-vesting/LinearVestingProjectUpgradeable.sol b/contracts/linear-vesting/LinearVestingProjectUpgradeable.sol index 5e96234..856eb3c 100644 --- a/contracts/linear-vesting/LinearVestingProjectUpgradeable.sol +++ b/contracts/linear-vesting/LinearVestingProjectUpgradeable.sol @@ -11,7 +11,10 @@ import "./ILinearVestingProjectUpgradeable.sol"; * @dev Linear Vesting project for linear vesting grants distribution. * Taken from https://github.com/dandelionlabs-io/linear-vesting-contracts/blob/master/contracts/LinearVestingProjectUpgradeable.sol */ -contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestingProjectUpgradeable { +contract LinearVestingProjectUpgradeable is + ManageableUpgradeable, + ILinearVestingProjectUpgradeable +{ using SafeMathUpgradeable for uint; /// @dev Used to translate vesting periods specified in days to seconds @@ -20,8 +23,8 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin /// @notice ERC20 token IERC20Upgradeable public token; - /// @dev Each Linear Vesting project has many pools - Pool[] public pools; + /// @dev Each Organization has many vesting schedules + VestingSchedule[] public vestingSchedules; /// @notice Mapping of recipient address > token grant mapping(uint => mapping(address => Grant)) public grants; @@ -33,7 +36,10 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @notice Construct a new Vesting contract * @param _token Address of ERC20 token */ - function __LinearVestingProject_initialize(address _token, string calldata _metadataUrl) external initializer { + function __LinearVestingProject_initialize( + address _token, + string calldata _metadataUrl + ) external initializer { __ManageableUpgradeable_init(); require( @@ -50,54 +56,73 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @param _startTime starting time of the vesting period in timestamp format * @param _vestingDuration duration time of the vesting period in timestamp format */ - function createPool( - string memory _name, + function createVestingSchedule( + address _token, + string memory _metadataUrl, uint256 _startTime, uint256 _vestingDuration ) public override onlyManager returns (uint256) { require( _startTime > 0 && _vestingDuration > 0, - "LinearVestingProject::createPool: One of the time parameters is 0" + "LinearVestingProject::createVestingSchedule: One of the time parameters is 0" ); require( _startTime > block.timestamp, - "LinearVestingProject::createPool: Starting time shall be in a future time" + "LinearVestingProject::createVestingSchedule: Starting time shall be in a future time" ); require( _vestingDuration > 0, - "LinearVestingProject::createPool: Duration of the period must be > 0" + "LinearVestingProject::createVestingSchedule: Duration of the period must be > 0" ); if (_vestingDuration < SECONDS_PER_DAY) { require( _vestingDuration <= SECONDS_PER_DAY.mul(10).mul(365), - "LinearVestingProject::createPool: Duration should be less than 10 years" + "LinearVestingProject::createVestingSchedule: Duration should be less than 10 years" ); } - pools.push(Pool({ - name: _name, - startTime: _startTime, - vestingDuration: _vestingDuration, - endTime: _startTime.add(_vestingDuration), - amount: 0, - totalClaimed: 0, - grants: 0 - })); - - emit PoolAdded(pools.length.sub(1), msg.sender, _name, _startTime, _startTime.add(_vestingDuration)); - return pools.length.sub(1); + uint256 vestingIndex = vestingSchedules.length; + + vestingSchedules.push( + VestingSchedule({ + token: _token, + metadataUrl: _metadataUrl, + startTime: _startTime, + vestingDuration: _vestingDuration, + endTime: _startTime.add(_vestingDuration), + amount: 0, + totalClaimed: 0, + grants: 0 + }) + ); + + emit VestingScheduleAdded( + _token, + _metadataUrl, + vestingIndex, + msg.sender, + _startTime, + _startTime.add(_vestingDuration) + ); + return vestingIndex; } - function createPoolWithGrants( - string memory _name, + function createVestingScheduleWithGrants( + address _token, + string memory _metadataUrl, uint256 _startTime, uint256 _vestingDuration, address[] memory _recipients, uint256[] memory _amounts ) public override onlyManager returns (uint) { - uint poolIndex = createPool(_name, _startTime, _vestingDuration); - addGrants(poolIndex, _recipients, _amounts); - return poolIndex; + uint vestingIndex = createVestingSchedule( + _token, + _metadataUrl, + _startTime, + _vestingDuration + ); + addGrants(vestingIndex, _recipients, _amounts); + return vestingIndex; } /** @@ -106,7 +131,7 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @param _amounts list of amounts to be assigned to the stakeholders */ function addGrants( - uint _poolIndex, + uint _vestingIndex, address[] memory _recipients, uint256[] memory _amounts ) public virtual override onlyManager { @@ -123,11 +148,11 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin "LinearVestingProject::addTokenGrants: invalid parameters length (they should be same)" ); require( - _poolIndex < pools.length, + _vestingIndex < vestingSchedules.length, "LinearVestingProject::addTokenGrants: invalid pool index" ); - Pool memory pool = pools[_poolIndex]; + VestingSchedule memory vesting = vestingSchedules[_vestingIndex]; uint256 amountSum = 0; for (uint16 i = 0; i < _recipients.length; i++) { @@ -136,7 +161,7 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin "LinearVestingProject:addTokenGrants: there is an address with value 0" ); require( - grants[_poolIndex][_recipients[i]].amount == 0, + grants[_vestingIndex][_recipients[i]].totalClaimed == 0, "LinearVestingProject::addTokenGrants: a grant already exists for one of the accounts" ); require( @@ -156,14 +181,19 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin Grant memory grant = Grant({ amount: _amounts[i], totalClaimed: 0, - perSecond: _amounts[i].div(pool.vestingDuration) + perSecond: _amounts[i].div(vesting.vestingDuration) }); - grants[_poolIndex][_recipients[i]] = grant; - emit GrantAdded(_poolIndex, msg.sender, _recipients[i], _amounts[i]); + grants[_vestingIndex][_recipients[i]] = grant; + emit GrantAdded( + _vestingIndex, + msg.sender, + _recipients[i], + _amounts[i] + ); } - pool.amount = pool.amount.add(amountSum); - pool.grants = pool.grants.add(_recipients.length); + vesting.amount = vesting.amount.add(amountSum); + vesting.grants = vesting.grants.add(_recipients.length); } /** @@ -172,35 +202,38 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @param _recipient The address that has a grant * @return The amount recipient can claim */ - function calculateGrantClaim(uint _poolIndex, address _recipient) public view override returns (uint256) { + function calculateGrantClaim( + uint _vestingIndex, + address _recipient + ) public view override returns (uint256) { require( - _poolIndex < pools.length, + _vestingIndex < vestingSchedules.length, "LinearVestingProject::calculateGrantClaim: invalid pool index" ); - Pool memory pool = pools[_poolIndex]; + VestingSchedule memory vesting = vestingSchedules[_vestingIndex]; // For grants created with a future start date, that hasn't been reached, return 0, 0 - if (block.timestamp < pool.startTime) { + if (block.timestamp < vesting.startTime) { return 0; } uint256 cap = block.timestamp; - if (cap > pool.endTime) { - cap = pool.endTime; + if (cap > vesting.endTime) { + cap = vesting.endTime; } - uint256 elapsedTime = cap.sub(pool.startTime); + uint256 elapsedTime = cap.sub(vesting.startTime); // If over vesting duration, all tokens vested - if (elapsedTime >= pool.vestingDuration) { - uint256 remainingGrant = grants[_poolIndex][_recipient].amount.sub( - grants[_poolIndex][_recipient].totalClaimed - ); + if (elapsedTime >= vesting.vestingDuration) { + uint256 remainingGrant = grants[_vestingIndex][_recipient] + .totalClaimed + .sub(grants[_vestingIndex][_recipient].amount); return remainingGrant; } else { - uint256 amountVested = grants[_poolIndex][_recipient].perSecond.mul( - elapsedTime - ); + uint256 amountVested = grants[_vestingIndex][_recipient] + .perSecond + .mul(elapsedTime); uint256 claimableAmount = amountVested.sub( - grants[_poolIndex][_recipient].totalClaimed + grants[_vestingIndex][_recipient].totalClaimed ); return claimableAmount; } @@ -211,9 +244,11 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @dev Errors if no tokens have vested * @dev It is advised recipients check they are entitled to claim via `calculateGrantClaim` before calling this */ - function claimMultiplePools(uint[] memory _poolIndexes) external override { - for (uint i = 0; i < _poolIndexes.length; i++) { - claimVestedTokens(_poolIndexes[i]); + function claimMultipleVestings( + uint[] memory _vestingIndexes + ) external override { + for (uint i = 0; i < _vestingIndexes.length; i++) { + claimVestedTokens(_vestingIndexes[i]); } } @@ -222,14 +257,14 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin * @dev Errors if no tokens have vested * @dev It is advised recipients check they are entitled to claim via `calculateGrantClaim` before calling this */ - function claimVestedTokens(uint _poolIndex) public override { + function claimVestedTokens(uint _vestingIndex) public override { require( - _poolIndex < pools.length, - "LinearVestingProject::calculateGrantClaim: invalid pool index" + _vestingIndex < vestingSchedules.length, + "LinearVestingProject::calculateGrantClaim: invalid vesting index" ); - Pool memory pool = pools[_poolIndex]; + VestingSchedule memory vesting = vestingSchedules[_vestingIndex]; - uint256 amountVested = calculateGrantClaim(_poolIndex, msg.sender); + uint256 amountVested = calculateGrantClaim(_vestingIndex, msg.sender); require( amountVested > 0, "VestingPeriod::claimVestedTokens: amountVested is 0" @@ -239,17 +274,17 @@ contract LinearVestingProjectUpgradeable is ManageableUpgradeable, ILinearVestin "VestingPeriod::claimVestedTokens: transfer failed" ); - Grant storage grant = grants[_poolIndex][msg.sender]; + Grant storage grant = grants[_vestingIndex][msg.sender]; - grant.totalClaimed = uint256( - grant.totalClaimed.add(amountVested) - ); - pool.totalClaimed = pool.totalClaimed.add(amountVested); + grant.totalClaimed = uint256(grant.totalClaimed.add(amountVested)); + vesting.totalClaimed = vesting.totalClaimed.add(amountVested); - emit GrantClaimed(_poolIndex, msg.sender, amountVested); + emit GrantClaimed(_vestingIndex, msg.sender, amountVested); } - function setMetadataUrl(string calldata _metadataUrl) external override onlyManager { + function setMetadataUrl( + string calldata _metadataUrl + ) external override onlyManager { metadataUrl = _metadataUrl; emit MetadataUrlChanged(msg.sender, _metadataUrl); } diff --git a/contracts/linear-vesting/removable/ILinearVestingProjectRemovableUpgradeable.sol b/contracts/linear-vesting/removable/ILinearVestingProjectRemovableUpgradeable.sol index ce02ece..c8f9962 100644 --- a/contracts/linear-vesting/removable/ILinearVestingProjectRemovableUpgradeable.sol +++ b/contracts/linear-vesting/removable/ILinearVestingProjectRemovableUpgradeable.sol @@ -7,14 +7,19 @@ import "../ILinearVestingProjectUpgradeable.sol"; * @dev Linear Vesting project for linear vesting grants distribution. * Taken from https://github.com/dandelionlabs-io/linear-vesting-contracts/blob/master/contracts/linear-vesting/ILinearVestingProjectRemovableUpgradeable.sol */ -interface ILinearVestingProjectRemovableUpgradeable is ILinearVestingProjectUpgradeable { - +interface ILinearVestingProjectRemovableUpgradeable is + ILinearVestingProjectUpgradeable +{ /// @notice Event emitted when the grant stakeholder is deleted - event GrantRemoved(uint indexed poolIndex, address indexed removedBy, address indexed recipient); + event GrantRemoved( + uint indexed vestingIndex, + address indexed removedBy, + address indexed recipient + ); /** * @notice Deletes a grant from a pool by Id, refunds the remaining tokens from a grant. * @param _address existing address from the investor which we want to delete */ - function removeGrant(uint _poolIndex, address _address) external; + function removeGrant(uint _vestingIndex, address _address) external; } diff --git a/contracts/linear-vesting/removable/LinearVestingProjectRemovableUpgradeable.sol b/contracts/linear-vesting/removable/LinearVestingProjectRemovableUpgradeable.sol index 4c80e23..9dce6f0 100644 --- a/contracts/linear-vesting/removable/LinearVestingProjectRemovableUpgradeable.sol +++ b/contracts/linear-vesting/removable/LinearVestingProjectRemovableUpgradeable.sol @@ -4,27 +4,32 @@ pragma solidity ^0.8.4; import "../LinearVestingProjectUpgradeable.sol"; import "./ILinearVestingProjectRemovableUpgradeable.sol"; -contract LinearVestingProjectRemovableUpgradeable is LinearVestingProjectUpgradeable, ILinearVestingProjectRemovableUpgradeable { - +contract LinearVestingProjectRemovableUpgradeable is + LinearVestingProjectUpgradeable, + ILinearVestingProjectRemovableUpgradeable +{ /** * @notice Deletes a grant from a pool by Id, refunds the remaining tokens from a grant. * @param _address existing address from the investor which we want to delete */ - function removeGrant(uint _poolIndex, address _address) external override onlyManager { - Grant memory grant = grants[_poolIndex][_address]; + function removeGrant( + uint _vestingIndex, + address _address + ) external override onlyManager { + Grant memory grant = grants[_vestingIndex][_address]; require( grant.amount > 0, "LinearVestingProjectRemovable::removeGrant: grant not active" ); uint256 refund = grant.amount - grant.totalClaimed; - delete grants[_poolIndex][_address]; + delete grants[_vestingIndex][_address]; require( token.transfer(owner(), refund), "LinearVestingProjectRemovable::removeGrant: transfer failed" ); - emit GrantRemoved(_poolIndex, msg.sender, _address); + emit GrantRemoved(_vestingIndex, msg.sender, _address); } } diff --git a/hardhat.config.ts b/hardhat.config.ts index 4f50662..047235c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,33 +1,34 @@ import * as dotenv from "dotenv"; import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; -import '@openzeppelin/hardhat-upgrades'; +import "@openzeppelin/hardhat-upgrades"; dotenv.config(); const config: HardhatUserConfig = { defaultNetwork: "hardhat", networks: { - hardhat: { - }, + hardhat: {}, local: { - url: 'http://127.0.0.1:7545', - accounts: ['0x0e0e6ca08cc4b4cd0eb43c5b9934f51926b634d0387ecbf7c7c4dd456041db03'] + url: "http://127.0.0.1:7545", + accounts: [ + "0x0e0e6ca08cc4b4cd0eb43c5b9934f51926b634d0387ecbf7c7c4dd456041db03", + ], }, sepolia: { // network: 11155111, - url: 'https://sepolia.infura.io/v3/b280b8aa6cda4dba845afb03d46c2396', - accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [] + url: "https://sepolia.infura.io/v3/b280b8aa6cda4dba845afb03d46c2396", + accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [], }, mumbai: { - url: 'https://polygon-mumbai.infura.io/v3/b280b8aa6cda4dba845afb03d46c2396', - accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [] + url: "https://polygon-mumbai.infura.io/v3/b280b8aa6cda4dba845afb03d46c2396", + accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [], }, mainnet: { // network: 1, - url: process.env.RPC_URL || '', - accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [] - } + url: process.env.RPC_URL || "", + accounts: process.env.DEPLOYER_KEY ? [process.env.DEPLOYER_KEY] : [], + }, }, solidity: { version: "0.8.4", @@ -39,19 +40,19 @@ const config: HardhatUserConfig = { }, }, gasReporter: { - coinmarketcap: 'b5aa6c8e-e54a-4291-b372-6afac549d9fc', - currency: 'USD', + coinmarketcap: "b5aa6c8e-e54a-4291-b372-6afac549d9fc", + currency: "USD", enabled: true, - token: 'ETH' + token: "ETH", }, etherscan: { apiKey: { - mainnet: 'T3UIPRSIK9Q776Y87B48YWCIFC6EV71B96', - sepolia: 'T3UIPRSIK9Q776Y87B48YWCIFC6EV71B96', - mumbai: 'X1X9XZVFXI3S235YQGQ7A99CG648ADCUHF', - matic: 'X1X9XZVFXI3S235YQGQ7A99CG648ADCUHF', - } - } + mainnet: "T3UIPRSIK9Q776Y87B48YWCIFC6EV71B96", + sepolia: "T3UIPRSIK9Q776Y87B48YWCIFC6EV71B96", + mumbai: "X1X9XZVFXI3S235YQGQ7A99CG648ADCUHF", + matic: "X1X9XZVFXI3S235YQGQ7A99CG648ADCUHF", + }, + }, }; export default config; diff --git a/proxy-beacon-map-diagram.drawio b/proxy-beacon-map-diagram.drawio new file mode 100644 index 0000000..dfd2708 --- /dev/null +++ b/proxy-beacon-map-diagram.drawio @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/deploy-test.ts b/scripts/deploy-test.ts index 3d7bddf..80d03e7 100644 --- a/scripts/deploy-test.ts +++ b/scripts/deploy-test.ts @@ -1,35 +1,48 @@ -import {ethers, upgrades} from "hardhat"; +import { ethers, upgrades } from "hardhat"; -require('dotenv').config() +require("dotenv").config(); async function main() { - - const LinearVestingProjectUpgradeable = await ethers.getContractFactory("LinearVestingProjectUpgradeable"); - const LinearVestingProjectFactoryUpgradeable = await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); - const ERC20 = await ethers.getContractFactory("ERC20Mock"); - - const erc20 = await ERC20.deploy(); - await erc20.deployed(); - - console.log("ERC20 Mock token deployed to:", erc20.address); - - const projectBase = await LinearVestingProjectUpgradeable.deploy(); - - console.log("Vesting Project for beacon proxy deployed to:", projectBase.address); - - const projectFactory = await upgrades.deployProxy(LinearVestingProjectFactoryUpgradeable, [projectBase.address], {initializer: '__LinearVestingProjectFactory_initialize'}); - await projectFactory.deployed() - - console.log("Vesting Project Factory deployed to:", projectFactory.address); - - let tx = await projectFactory.create(erc20.address); - await tx.wait() - - const linearVestingProjectAddress = await projectFactory.getCollectionAddress(0) - console.log(`New linear vesting project deployed to ${linearVestingProjectAddress}`); + const LinearVestingProjectUpgradeable = await ethers.getContractFactory( + "TestLinearVestingProjectUpgradeable" + ); + const LinearVestingProjectFactoryUpgradeable = + await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); + const ERC20 = await ethers.getContractFactory("ERC20Mock"); + + const erc20 = await ERC20.deploy(); + await erc20.deployed(); + + console.log("ERC20 Mock token deployed to:", erc20.address); + + const projectBase = await LinearVestingProjectUpgradeable.deploy(); + + console.log( + "Vesting Project for beacon proxy deployed to:", + projectBase.address + ); + + const projectFactory = await upgrades.deployProxy( + LinearVestingProjectFactoryUpgradeable, + [projectBase.address], + { initializer: "__LinearVestingProjectFactory_initialize" } + ); + await projectFactory.deployed(); + + console.log("Vesting Project Factory deployed to:", projectFactory.address); + + let tx = await projectFactory.create(erc20.address); + await tx.wait(); + + const linearVestingProjectAddress = await projectFactory.getCollectionAddress( + 0 + ); + console.log( + `New linear vesting project deployed to ${linearVestingProjectAddress}` + ); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/test/linear-vesting/claims.ts b/test/linear-vesting/claims.ts index e41f489..938eb44 100644 --- a/test/linear-vesting/claims.ts +++ b/test/linear-vesting/claims.ts @@ -1,49 +1,68 @@ import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; -import {deployProjectFactoryWithProjectAndPool} from "./util/fixtures"; -import {setNextBlockTimestamp} from "./util/time"; +import { deployProjectFactoryWithProjectAndPool } from "./util/fixtures"; +import { setNextBlockTimestamp } from "./util/time"; describe("Claims", function () { - - let poolVestingTime, startingPeriodTime, amount, projectFactory, erc20, project, owner, manager, user1, user2; + let poolVestingTime, + startingPeriodTime, + amount, + projectFactory, + erc20, + project, + owner, + manager, + user1, + user2; beforeEach(async () => { - ({startingPeriodTime, poolVestingTime, amount, projectFactory, erc20, project, owner, manager, user1, user2 } = await loadFixture(deployProjectFactoryWithProjectAndPool)); + ({ + startingPeriodTime, + poolVestingTime, + amount, + projectFactory, + erc20, + project, + owner, + manager, + user1, + user2, + } = await loadFixture(deployProjectFactoryWithProjectAndPool)); }); it("ongoing grant claim should succeed", async function () { - const nextTs = startingPeriodTime + (poolVestingTime / 2); // elapsed half-time + const nextTs = startingPeriodTime + poolVestingTime / 2; // elapsed half-time await setNextBlockTimestamp(nextTs); - await expect( - project.connect(user1).claimVestedTokens(0) - ).to.emit(project, "GrantClaimed").withArgs(0, user1.address, amount.div(2)) + await expect(project.connect(user1).claimVestedTokens(0)) + .to.emit(project, "GrantClaimed") + .withArgs(0, user1.address, amount.div(2)); - const grant = await project.grants(0, user1.address) + const grant = await project.grants(0, user1.address); expect(grant.totalClaimed).to.equal(amount.div(2)); }); it("invalid pool index should fail", async function () { - const nextTs = startingPeriodTime + (poolVestingTime / 2); // elapsed half-time + const nextTs = startingPeriodTime + poolVestingTime / 2; // elapsed half-time await setNextBlockTimestamp(nextTs); - await expect( - project.connect(user1).claimVestedTokens(1) - ).to.rejectedWith("LinearVestingProject::calculateGrantClaim: invalid pool index"); + await expect(project.connect(user1).claimVestedTokens(1)).to.rejectedWith( + "LinearVestingProject::calculateGrantClaim: invalid vesting index" + ); }); it("invalid grantee should fail", async function () { - const nextTs = startingPeriodTime + (poolVestingTime / 2); // elapsed half-time + const nextTs = startingPeriodTime + poolVestingTime / 2; // elapsed half-time await setNextBlockTimestamp(nextTs); - await expect( - project.connect(user2).claimVestedTokens(0) - ).to.rejectedWith("VestingPeriod::claimVestedTokens: amountVested is 0'"); + await expect(project.connect(user2).claimVestedTokens(0)).to.rejectedWith( + "VestingPeriod::claimVestedTokens: amountVested is 0'" + ); }); it("claimable balance 0 should fail", async function () { - await expect( - project.connect(user1).claimVestedTokens(0) - ).to.rejectedWith("VestingPeriod::claimVestedTokens: amountVested is 0'"); + await expect(project.connect(user1).claimVestedTokens(0)).to.rejectedWith( + "VestingPeriod::claimVestedTokens: amountVested is 0'" + ); }); }); diff --git a/test/linear-vesting/factory.ts b/test/linear-vesting/factory.ts index 430c29d..32dcae0 100644 --- a/test/linear-vesting/factory.ts +++ b/test/linear-vesting/factory.ts @@ -1,38 +1,46 @@ import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; -import {deployProjectFactory} from "./util/fixtures"; -import {ethers, upgrades} from "hardhat"; -import {BigNumber} from "ethers"; +import { deployProjectFactory } from "./util/fixtures"; +import { ethers, upgrades } from "hardhat"; +import { BigNumber } from "ethers"; describe("Factory", function () { - let projectFactory, erc20, owner, manager, user1, user2; beforeEach(async () => { - ({projectFactory, erc20, owner, manager, user1, user2 } = await loadFixture(deployProjectFactory)); + ({ projectFactory, erc20, owner, manager, user1, user2 } = + await loadFixture(deployProjectFactory)); }); it("deploy factory should succeed", async function () { - const LinearVestingProjectRemovableUpgradeable = await ethers.getContractFactory("LinearVestingProjectRemovableUpgradeable"); - const LinearVestingProjectFactoryUpgradeable = await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); - - const projectBase = await LinearVestingProjectRemovableUpgradeable.connect(owner).deploy(); - - const projectFactory = await upgrades.deployProxy(LinearVestingProjectFactoryUpgradeable, [projectBase.address], { initializer: '__LinearVestingProjectFactory_initialize' }); - await projectFactory.deployed() + const LinearVestingProjectRemovableUpgradeable = + await ethers.getContractFactory( + "LinearVestingProjectRemovableUpgradeable" + ); + const LinearVestingProjectFactoryUpgradeable = + await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); + + const projectBase = await LinearVestingProjectRemovableUpgradeable.connect( + owner + ).deploy(); + + const projectFactory = await upgrades.deployProxy( + LinearVestingProjectFactoryUpgradeable, + [projectBase.address], + { initializer: "__LinearVestingProjectFactory_initialize" } + ); + await projectFactory.deployed(); }); it("create new project should succeed", async function () { expect(await projectFactory.projectsCount()).to.equal(BigNumber.from(0)); await expect( - projectFactory.createProject(erc20.address, 'Project Example', 'asd') - ).to.emit(projectFactory, "ProjectCreated") + projectFactory.createProject(erc20.address, "Project Example", "asd") + ).to.emit(projectFactory, "ProjectCreated"); expect(await projectFactory.projectsCount()).to.equal(BigNumber.from(1)); }); - it.skip("upgrade beacon should succeed", async function () { - - }); + it.skip("upgrade beacon should succeed", async function () {}); }); diff --git a/test/linear-vesting/grants.ts b/test/linear-vesting/grants.ts index 4f9df65..ab82034 100644 --- a/test/linear-vesting/grants.ts +++ b/test/linear-vesting/grants.ts @@ -1,104 +1,120 @@ -import {loadFixture, mine} from "@nomicfoundation/hardhat-network-helpers"; +import { loadFixture, mine } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; -import {deployProjectFactoryWithProjectAndPool} from "./util/fixtures"; +import { deployProjectFactoryWithProjectAndPool } from "./util/fixtures"; describe("Grants management", function () { - - let poolVestingTime, startingPeriodTime, amount, projectFactory, erc20, project, owner, manager, user1, user2; + let poolVestingTime, + startingPeriodTime, + amount, + projectFactory, + erc20, + project, + owner, + manager, + user1, + user2; beforeEach(async () => { - ({startingPeriodTime, poolVestingTime, amount, projectFactory, erc20, project, owner, manager, user1, user2 } = await loadFixture(deployProjectFactoryWithProjectAndPool)); + ({ + startingPeriodTime, + poolVestingTime, + amount, + projectFactory, + erc20, + project, + owner, + manager, + user1, + user2, + } = await loadFixture(deployProjectFactoryWithProjectAndPool)); }); describe("Adding new grants", function () { - it("add new grants by a manager should succeed", async function () { + const allowTx = await erc20.increaseAllowance(project.address, amount); + await allowTx.wait(); - const allowTx = await erc20.increaseAllowance(project.address, amount) - await allowTx.wait() + await expect(project.addGrants(0, [user2.address], [amount])) + .to.emit(project, "GrantAdded") + .withArgs(0, owner.address, user2.address, amount); - await expect( - project.addGrants(0, [user2.address], [amount]) - ).to.emit(project, "GrantAdded").withArgs(0, owner.address, user2.address, amount) - - const newGrant = await project.grants(0, user2.address) + const newGrant = await project.grants(0, user2.address); + console.log("Amount: " + amount); expect(newGrant.amount).to.equal(amount); }); it("user without manager role adding should fail", async function () { - - await expect( - project.connect(user1).addGrants(0, [], []) - ).to.rejectedWith("ManageableUpgradeable::onlyManager: the caller is not an manager."); + await expect(project.connect(user1).addGrants(0, [], [])).to.rejectedWith( + "ManageableUpgradeable::onlyManager: the caller is not an manager." + ); }); it("no grantees should fail", async function () { - - await expect( - project.addGrants(0, [], []) - ).to.rejectedWith("LinearVestingProject::addTokenGrants: no recipients"); + await expect(project.addGrants(0, [], [])).to.rejectedWith( + "LinearVestingProject::addTokenGrants: no recipients" + ); }); it("more than 100 grantees should fail", async function () { - - const grantAddresses = [] - const grantAmounts = [] - for (let i = 0; i < 101; i++){ - grantAddresses.push(user1.address) - grantAmounts.push(amount) + const grantAddresses = []; + const grantAmounts = []; + for (let i = 0; i < 101; i++) { + grantAddresses.push(user1.address); + grantAmounts.push(amount); } await expect( - project.addGrants(0, grantAddresses, grantAmounts) - ).to.rejectedWith("LinearVestingProject::addTokenGrants: too many grants, it will probably fail"); + project.addGrants(0, grantAddresses, grantAmounts) + ).to.rejectedWith( + "LinearVestingProject::addTokenGrants: too many grants, it will probably fail" + ); }); it("different quantity of grantees and amounts should fail", async function () { - - await expect( - project.addGrants(0, [user1.address], []) - ).to.rejectedWith("LinearVestingProject::addTokenGrants: invalid parameters length (they should be same)"); + await expect(project.addGrants(0, [user1.address], [])).to.rejectedWith( + "LinearVestingProject::addTokenGrants: invalid parameters length (they should be same)" + ); }); it("invalid pool index should fail", async function () { - await expect( - project.addGrants(1, [user1.address], [amount]) - ).to.rejectedWith("LinearVestingProject::addTokenGrants: invalid pool index"); + project.addGrants(1, [user1.address], [amount]) + ).to.rejectedWith( + "LinearVestingProject::addTokenGrants: invalid pool index" + ); }); }); describe("Remove existing grants", function () { it("remove existing grants by a manager should succeed", async function () { - const newGrant = await project.grants(0, user1.address) + const newGrant = await project.grants(0, user1.address); expect(newGrant.amount).to.equal(amount); - await expect( - project.removeGrant(0, user1.address) - ).to.emit(project, "GrantRemoved").withArgs(0, owner.address, user1.address) + await expect(project.removeGrant(0, user1.address)) + .to.emit(project, "GrantRemoved") + .withArgs(0, owner.address, user1.address); - const removedGrant = await project.grants(0, user1.address) + const removedGrant = await project.grants(0, user1.address); expect(removedGrant.amount).to.equal(0); }); it("user without manager role remove should fail", async function () { - await expect( - project.connect(user1).removeGrant(0, user1.address) - ).to.rejectedWith("ManageableUpgradeable::onlyManager: the caller is not an manager."); + project.connect(user1).removeGrant(0, user1.address) + ).to.rejectedWith( + "ManageableUpgradeable::onlyManager: the caller is not an manager." + ); }); it("invalid grant or without funds available should fail", async function () { - - await expect( - project.removeGrant(0, user2.address) - ).to.rejectedWith("LinearVestingProjectRemovable::removeGrant: grant not active"); + await expect(project.removeGrant(0, user2.address)).to.rejectedWith( + "LinearVestingProjectRemovable::removeGrant: grant not active" + ); }); it("invalid pool index should fail", async function () { - - await expect( - project.removeGrant(1, user1.address) - ).to.rejectedWith("LinearVestingProjectRemovable::removeGrant: grant not active"); + await expect(project.removeGrant(1, user1.address)).to.rejectedWith( + "LinearVestingProjectRemovable::removeGrant: grant not active" + ); }); }); }); diff --git a/test/linear-vesting/pools.ts b/test/linear-vesting/pools.ts index 5f8df74..2f69579 100644 --- a/test/linear-vesting/pools.ts +++ b/test/linear-vesting/pools.ts @@ -1,54 +1,94 @@ -import {loadFixture} from "@nomicfoundation/hardhat-network-helpers"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; -import {deployProjectFactoryWithProject} from "./util/fixtures"; -import {BigNumber} from "ethers"; -import {getLastBlockTimestamp} from "./util/time"; +import { BigNumber } from "ethers"; +import { deployProjectFactoryWithProject } from "./util/fixtures"; +import { getLastBlockTimestamp } from "./util/time"; describe("Pools creation", function () { - - const poolVestingTime = 100 - const poolName = 'Pool 1' + const poolVestingTime = 100; + const poolName = "Pool 1"; let amount, projectFactory, erc20, project, owner, manager, user1, user2; beforeEach(async () => { - ({projectFactory, erc20, project, owner, manager, user1, user2 } = await loadFixture(deployProjectFactoryWithProject)); + ({ projectFactory, erc20, project, owner, manager, user1, user2 } = + await loadFixture(deployProjectFactoryWithProject)); }); it("create pool with grants should work for manager", async function () { - const poolVestingTime = 100 - const amount = BigNumber.from("100000000000000000000") - const poolName = 'Pool 1' + const poolVestingTime = 100; + const amount = BigNumber.from("100000000000000000000"); + const poolName = "Pool 1"; + const token = erc20.address; - const allowTx = await erc20.increaseAllowance(project.address, amount) - await allowTx.wait() + const allowTx = await erc20.increaseAllowance(project.address, amount); + await allowTx.wait(); const currentTimeStamp = await getLastBlockTimestamp(); - let startingPeriodTime = currentTimeStamp + 100 + let startingPeriodTime = currentTimeStamp + 100; await expect( - project.createPoolWithGrants(poolName, startingPeriodTime, poolVestingTime, [user1.address], [amount]) - ).to.emit(project, "PoolAdded").withArgs(0, owner.address, poolName, startingPeriodTime, startingPeriodTime + poolVestingTime) + project.createVestingScheduleWithGrants( + token, + poolName, + startingPeriodTime, + poolVestingTime, + [user1.address], + [amount] + ) + ) + .to.emit(project, "VestingScheduleAdded") + .withArgs( + token, + poolName, + 0, + owner.address, + startingPeriodTime, + startingPeriodTime + poolVestingTime + ); }); it("create pool without grants should work for manager", async function () { - const currentTimeStamp = await getLastBlockTimestamp(); - let startingPeriodTime = currentTimeStamp + 100 + let startingPeriodTime = currentTimeStamp + 100; + let token = erc20.address; await expect( - project.createPool(poolName, startingPeriodTime, poolVestingTime) - ).to.emit(project, "PoolAdded").withArgs(0, owner.address, poolName, startingPeriodTime, startingPeriodTime + poolVestingTime) + project.createVestingSchedule( + token, + poolName, + startingPeriodTime, + poolVestingTime + ) + ) + .to.emit(project, "VestingScheduleAdded") + .withArgs( + token, + poolName, + 0, + owner.address, + startingPeriodTime, + startingPeriodTime + poolVestingTime + ); }); it("user without manager role create pool should fail", async function () { - const currentTimeStamp = await getLastBlockTimestamp(); - let startingPeriodTime = currentTimeStamp + 100 + let startingPeriodTime = currentTimeStamp + 100; + let token = erc20.address; await expect( - project.connect(user1).createPool(poolName, startingPeriodTime, poolVestingTime) - ).to.rejectedWith("ManageableUpgradeable::onlyManager: the caller is not an manager."); + project + .connect(user1) + .createVestingSchedule( + token, + poolName, + startingPeriodTime, + poolVestingTime + ) + ).to.rejectedWith( + "ManageableUpgradeable::onlyManager: the caller is not an manager." + ); }); }); diff --git a/test/linear-vesting/util/fixtures.ts b/test/linear-vesting/util/fixtures.ts index 6ad237e..1dfbdac 100644 --- a/test/linear-vesting/util/fixtures.ts +++ b/test/linear-vesting/util/fixtures.ts @@ -1,17 +1,25 @@ import { ethers, upgrades } from "hardhat"; -import {getLastBlockTimestamp} from "./time"; -import {BigNumber} from "ethers"; +import { getLastBlockTimestamp } from "./time"; +import { BigNumber } from "ethers"; async function deployProjectFactory() { const [owner, manager, user1, user2] = await ethers.getSigners(); - const LinearVestingProjectRemovableUpgradeable = await ethers.getContractFactory("LinearVestingProjectRemovableUpgradeable"); - const LinearVestingProjectFactoryUpgradeable = await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); + const LinearVestingProjectRemovableUpgradeable = + await ethers.getContractFactory("LinearVestingProjectRemovableUpgradeable"); + const LinearVestingProjectFactoryUpgradeable = + await ethers.getContractFactory("LinearVestingProjectFactoryUpgradeable"); - const projectBase = await LinearVestingProjectRemovableUpgradeable.connect(owner).deploy(); + const projectBase = await LinearVestingProjectRemovableUpgradeable.connect( + owner + ).deploy(); - const projectFactory = await upgrades.deployProxy(LinearVestingProjectFactoryUpgradeable, [projectBase.address], { initializer: '__LinearVestingProjectFactory_initialize' }); - await projectFactory.deployed() + const projectFactory = await upgrades.deployProxy( + LinearVestingProjectFactoryUpgradeable, + [projectBase.address], + { initializer: "__LinearVestingProjectFactory_initialize" } + ); + await projectFactory.deployed(); const ERC20 = await ethers.getContractFactory("ERC20Mock"); @@ -21,40 +29,87 @@ async function deployProjectFactory() { return { projectFactory, projectBase, erc20, owner, manager, user1, user2 }; } - async function deployProjectFactoryWithProject() { - const { projectFactory, projectBase, owner, erc20, manager, user1, user2 } = await deployProjectFactory() - - const LinearVestingProjectRemovableUpgradeable = await ethers.getContractFactory("LinearVestingProjectRemovableUpgradeable"); - - - const tx = await projectFactory.createProject(erc20.address, 'Project Example', 'asd') - await tx.wait(1) - - const address = await projectFactory.getProjectAddress(0) - - const project = LinearVestingProjectRemovableUpgradeable.attach(address) - - return { projectFactory, projectBase, project, erc20, owner, manager, user1, user2 }; + const { projectFactory, projectBase, owner, erc20, manager, user1, user2 } = + await deployProjectFactory(); + + const LinearVestingProjectRemovableUpgradeable = + await ethers.getContractFactory("LinearVestingProjectRemovableUpgradeable"); + + const tx = await projectFactory.createProject( + erc20.address, + "Project Example", + "asd" + ); + await tx.wait(1); + + const address = await projectFactory.getProjectAddress(0); + + const project = LinearVestingProjectRemovableUpgradeable.attach(address); + + return { + projectFactory, + projectBase, + project, + erc20, + owner, + manager, + user1, + user2, + }; } async function deployProjectFactoryWithProjectAndPool() { - const { projectFactory, projectBase, owner, project, erc20, manager, user1, user2 } = await deployProjectFactoryWithProject() - - const poolVestingTime = 100 - const amount = BigNumber.from("100000000000000000000") - - const allowTx = await erc20.increaseAllowance(project.address, amount) - await allowTx.wait() + const { + projectFactory, + projectBase, + project, + erc20, + owner, + manager, + user1, + user2, + } = await deployProjectFactoryWithProject(); + + const poolVestingTime = 100; + const amount = BigNumber.from("100000000000000000000"); + const token = erc20.address; + + const allowTx = await erc20.increaseAllowance(project.address, amount); + await allowTx.wait(); const currentTimeStamp = await getLastBlockTimestamp(); - let startingPeriodTime = currentTimeStamp + 100 - - let tx = await project.createPoolWithGrants('Pool 1', startingPeriodTime, poolVestingTime, [user1.address], [amount]); - await tx.wait() - - return { projectFactory, projectBase, project, erc20, owner, manager, user1, user2, poolVestingTime, amount, startingPeriodTime }; + let startingPeriodTime = currentTimeStamp + 100; + + let tx = await project.createVestingScheduleWithGrants( + token, + "Pool 1", + startingPeriodTime, + poolVestingTime, + [user1.address], + [amount] + ); + await tx.wait(); + + return { + projectFactory, + projectBase, + project, + erc20, + owner, + manager, + user1, + user2, + token, + startingPeriodTime, + poolVestingTime, + amount, + }; } -export { deployProjectFactory, deployProjectFactoryWithProject, deployProjectFactoryWithProjectAndPool }; +export { + deployProjectFactory, + deployProjectFactoryWithProject, + deployProjectFactoryWithProjectAndPool, +};