Skip to content

Commit

Permalink
fix: gori comments
Browse files Browse the repository at this point in the history
  • Loading branch information
dristpunk committed Dec 26, 2023
1 parent 06ba7e3 commit 5c3cb20
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 75 deletions.
6 changes: 3 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ number_underscore = 'thousands'
multiline_func_header = 'params_first'

[profile.default]
solc_version = '0.8.19'
solc_version = '0.8.20'
src = 'solidity'
test = 'solidity/test'
out = 'out'
libs = ['node_modules']
libs = ["node_modules", "lib"]
optimizer_runs = 10_000

[profile.optimized]
Expand All @@ -31,4 +31,4 @@ src = 'solidity/interfaces/'
runs = 1000

[rpc_endpoints]
mainnet = "${MAINNET_RPC}"
mainnet = "${MAINNET_RPC}"
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"scripts": {
"build": "forge build",
"build:optimized": "FOUNDRY_PROFILE=optimized forge build",
"coverage": "forge coverage --match-contract Unit",
"coverage": "forge coverage --match-contract Integration",
"deploy:goerli": "bash -c 'source .env && forge script DeployGoerli --rpc-url $GOERLI_RPC --broadcast --private-key $GOERLI_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'",
"deploy:mainnet": "bash -c 'source .env && forge script DeployMainnet --rpc-url $MAINNET_RPC --broadcast --private-key $MAINNET_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'",
"lint:check": "yarn lint:sol-tests && yarn lint:sol-logic && forge fmt --check",
Expand All @@ -32,14 +32,15 @@
"package.json": "sort-package-json"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.1",
"isolmate": "github:defi-wonderland/isolmate#59e1804",
"solmate": "^6.2.0"
},
"devDependencies": {
"@commitlint/cli": "17.0.3",
"@commitlint/config-conventional": "17.0.3",
"ds-test": "github:dapphub/ds-test#e282159",
"forge-std": "github:foundry-rs/forge-std#v1.7.3",
"forge-std": "github:foundry-rs/forge-std#v1.7.4",
"husky": ">=8",
"lint-staged": ">=10",
"solhint": "3.6.2",
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/Greeter.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

import {IERC20} from 'isolmate/interfaces/tokens/IERC20.sol';
import {IGreeter} from 'interfaces/IGreeter.sol';
Expand Down
60 changes: 35 additions & 25 deletions solidity/contracts/Unlock.sol
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity 0.8.20;

import {IERC20} from 'isolmate/interfaces/tokens/IERC20.sol';
import {IUnlock} from 'interfaces/IUnlock.sol';
import {Owned} from 'solmate/auth/Owned.sol';
import {Ownable2Step, Ownable} from '@openzeppelin/contracts/access/Ownable2Step.sol';

contract Unlock is Owned, IUnlock {
uint256 public constant TOTAL_SUPPLY = 24_960_000 ether;
contract Unlock is Ownable2Step, IUnlock {
uint256 public totalAmount;

uint256 public startTime;
mapping(address _token => uint256 _amount) public withdrawedSupply;
mapping(address _token => uint256 _amount) public withdrawnSupply;

constructor(uint256 _startTime, address _owner) Owned(_owner) {
constructor(uint256 _startTime, address _owner, uint256 _totalAmount) Ownable(_owner) {
startTime = _startTime;
totalAmount = _totalAmount;
}

function _unlockedSupply(address _token, uint256 _timestamp) internal view returns (uint256 _unlockedSupplyReturn) {
if (_timestamp < startTime + 365 days) {
_unlockedSupplyReturn = 0;
} else {
_unlockedSupplyReturn =
TOTAL_SUPPLY / 13 + (TOTAL_SUPPLY * 12 / 13) * (_timestamp - startTime - 365 days) / 365 days;
_unlockedSupplyReturn -= withdrawedSupply[_token];
}
function _unlockedSupply(uint256 _timestamp) internal view returns (uint256 _unlockedSupplyReturn) {
uint256 _firstMilestoneTime = startTime + 365 days; // 1st milestone is 1 year after start time

if (_timestamp < _firstMilestoneTime) return _unlockedSupplyReturn; // return 0 if not reached

uint256 _firstMilestoneUnlockedAmount = totalAmount / 13; // 1st milestone unlock amount
uint256 _restAmount = totalAmount - _firstMilestoneUnlockedAmount; // rest amount after 1st milestone
uint256 _timePassed = _timestamp - startTime - 365 days; // time passed after 1st milestone
uint256 _totalTime = 365 days; // total unlock time after 1st milestone

// f(x) = ax + b
// b = totalAmount / 13
// a = restAmount / totalTime
// x = timePassed
_unlockedSupplyReturn = _firstMilestoneUnlockedAmount + (_restAmount * _timePassed) / _totalTime;
}

function unlockedSupply(address _token) external view returns (uint256 _unlockedSupplyReturn) {
_unlockedSupplyReturn = _unlockedSupply(_token, block.timestamp);
function unlockedSupply() external view returns (uint256 _unlockedSupplyReturn) {
_unlockedSupplyReturn = _unlockedSupply(block.timestamp);
}

function unlockedAtTimestamp(
address _token,
uint256 _timestamp
) external view returns (uint256 _unlockedSupplyReturn) {
_unlockedSupplyReturn = _unlockedSupply(_token, _timestamp);
function unlockedAtTimestamp(uint256 _timestamp) external view returns (uint256 _unlockedSupplyReturn) {
_unlockedSupplyReturn = _unlockedSupply(_timestamp);
}

function withdraw(address _receiver, address _token, uint256 _amount) external {
if (_amount > _unlockedSupply(_token, block.timestamp)) revert InsufficientUnlockedSupply();
if (msg.sender != owner) revert Unauthorized();
withdrawedSupply[_token] += _amount;
function withdraw(address _receiver, address _token) external {
if (msg.sender != owner()) revert Unauthorized();

uint256 _amount = _unlockedSupply(block.timestamp) - withdrawnSupply[_token];
uint256 _balance = IERC20(_token).balanceOf(address(this));

if (_amount > _balance) _amount = _balance;

withdrawnSupply[_token] += _amount;
IERC20(_token).transfer(_receiver, _amount);
}
}
2 changes: 1 addition & 1 deletion solidity/interfaces/IGreeter.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

import {IERC20} from 'isolmate/interfaces/tokens/IERC20.sol';

Expand Down
12 changes: 6 additions & 6 deletions solidity/interfaces/IUnlock.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

interface IUnlock {
error InsufficientUnlockedSupply();
error Unauthorized();

function unlockedSupply(address _token) external view returns (uint256 _unlockedSupply);
function unlockedAtTimestamp(address _token, uint256 _timestamp) external view returns (uint256 _unlockedSupply);
function withdraw(address _receiver, address _token, uint256 _amount) external;
function unlockedSupply() external view returns (uint256 _unlockedSupply);
function unlockedAtTimestamp(uint256 _timestamp) external view returns (uint256 _unlockedSupply);
function withdraw(address _receiver, address _token) external;

function startTime() external view returns (uint256 _startTime);
function withdrawedSupply(address _token) external view returns (uint256 _withdrawedSupply);
function TOTAL_SUPPLY() external view returns (uint256 _totalSupply);
function withdrawnSupply(address _token) external view returns (uint256 _withdrawedSupply);
function totalAmount() external view returns (uint256 _totalAmount);
}
2 changes: 1 addition & 1 deletion solidity/scripts/Deploy.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

import {Script} from 'forge-std/Script.sol';
import {Greeter} from 'contracts/Greeter.sol';
Expand Down
4 changes: 2 additions & 2 deletions solidity/test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

import {Test} from 'forge-std/Test.sol';

Expand All @@ -20,6 +20,6 @@ contract IntegrationBase is Test {
_startTime = block.timestamp + 10 minutes;

vm.prank(_alice);
_unlock = new Unlock(_startTime, _owner);
_unlock = new Unlock(_startTime, _owner, 24_960_000 ether);
}
}
68 changes: 43 additions & 25 deletions solidity/test/integration/Unlock.t.sol
Original file line number Diff line number Diff line change
@@ -1,71 +1,89 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
pragma solidity =0.8.20;

import {IntegrationBase} from 'test/integration/IntegrationBase.sol';
import {IOwned} from 'test/utils/IOwned.sol';
import {IOwnable2Steps} from 'test/utils/IOwnable2Steps.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IUnlock} from 'interfaces/IUnlock.sol';
import {IERC20} from 'isolmate/interfaces/tokens/IERC20.sol';

contract IntegrationUnlock is IntegrationBase {
function test_Constructor() public {
assertEq(IOwned(address(_unlock)).owner(), _owner);
assertEq(IOwnable2Steps(address(_unlock)).owner(), _owner);
assertEq(_unlock.startTime(), block.timestamp + 10 minutes);
}

function test_UnlockedAtTimestamp() public {
assertEq(_unlock.unlockedAtTimestamp(_nextToken, _startTime), 0);
assertEq(_unlock.unlockedAtTimestamp(_nextToken, _startTime + 364 days), 0 ether);
assertEq(_unlock.unlockedAtTimestamp(_nextToken, _startTime + 365 days), 1_920_000 ether);
assertEq(_unlock.unlockedAtTimestamp(_startTime), 0);
assertEq(_unlock.unlockedAtTimestamp(_startTime + 364 days), 0 ether);
assertEq(_unlock.unlockedAtTimestamp(_startTime + 365 days), 1_920_000 ether);

assertEq(_unlock.unlockedAtTimestamp(_nextToken, _startTime + 365 days + 10 days) - 2_551_232 ether < 1 ether, true);
assertEq(
_unlock.unlockedAtTimestamp(_nextToken, _startTime + 365 days + 100 days) - 8_232_328 ether < 1 ether, true
);
assertEq(_unlock.unlockedAtTimestamp(_startTime + 365 days + 10 days) - 2_551_232 ether < 1 ether, true);
assertEq(_unlock.unlockedAtTimestamp(_startTime + 365 days + 100 days) - 8_232_328 ether < 1 ether, true);

assertEq(_unlock.unlockedAtTimestamp(_nextToken, _startTime + 365 days + 365 days), 24_960_000 ether);
assertEq(_unlock.unlockedAtTimestamp(_startTime + 365 days + 365 days), 24_960_000 ether);
}

function test_UnlockedAmount() public {
assertEq(_unlock.unlockedSupply(_nextToken), 0);
assertEq(_unlock.unlockedSupply(), 0);
vm.warp(_startTime + 364 days);
assertEq(_unlock.unlockedSupply(_nextToken), 0);
assertEq(_unlock.unlockedSupply(), 0);
vm.warp(_startTime + 365 days);
assertEq(_unlock.unlockedSupply(_nextToken), 1_920_000 ether);
assertEq(_unlock.unlockedSupply(), 1_920_000 ether);

vm.warp(_startTime + 365 days + 10 days);
assertEq(_unlock.unlockedSupply(_nextToken) - 2_551_232 ether < 1 ether, true);
assertEq(_unlock.unlockedSupply() - 2_551_232 ether < 1 ether, true);
vm.warp(_startTime + 365 days + 100 days);
assertEq(_unlock.unlockedSupply(_nextToken) - 8_232_328 ether < 1 ether, true);
assertEq(_unlock.unlockedSupply() - 8_232_328 ether < 1 ether, true);

vm.warp(_startTime + 365 days + 365 days);
assertEq(_unlock.unlockedSupply(_nextToken), 24_960_000 ether);
assertEq(_unlock.unlockedSupply(), 24_960_000 ether);
}

function test_WithdrawNoSupply() public {
vm.warp(_startTime + 364 days);
vm.prank(_owner);
vm.expectRevert(abi.encodeWithSelector(IUnlock.InsufficientUnlockedSupply.selector));
_unlock.withdraw(_alice, _nextToken, 1);
_unlock.withdraw(_alice, _nextToken);
assertEq(_unlock.withdrawnSupply(_nextToken), 0);
assertEq(IERC20(_nextToken).balanceOf(_alice), 0);
}

function test_WithdrawUnauthorized() public {
vm.warp(_startTime + 365 days);
vm.expectRevert(abi.encodeWithSelector(IUnlock.Unauthorized.selector));
_unlock.withdraw(_alice, _nextToken, 1);
_unlock.withdraw(_alice, _nextToken);
}

function test_WithdrawLegit() public {
deal(_nextToken, address(_unlock), 1_920_000 ether);
deal(_nextToken, address(_unlock), 2_000_000 ether); // deal more than withrawable
vm.warp(_startTime + 365 days);
vm.startPrank(_owner);

_unlock.withdraw(_alice, _nextToken, 1_920_000 ether);
assertEq(_unlock.withdrawedSupply(_nextToken), 1_920_000 ether);
_unlock.withdraw(_alice, _nextToken);
assertEq(_unlock.withdrawnSupply(_nextToken), 1_920_000 ether);
assertEq(IERC20(_nextToken).balanceOf(_alice), 1_920_000 ether);

vm.expectRevert(abi.encodeWithSelector(IUnlock.InsufficientUnlockedSupply.selector));
_unlock.withdraw(_alice, _nextToken, 1);
// try again and expect no changes
_unlock.withdraw(_alice, _nextToken);
assertEq(_unlock.withdrawnSupply(_nextToken), 1_920_000 ether);
assertEq(IERC20(_nextToken).balanceOf(_alice), 1_920_000 ether);

vm.stopPrank();
}

function test_transferOwnership() public {
vm.prank(_owner);
IOwnable2Steps(address(_unlock)).transferOwnership(_alice);
assertEq(IOwnable2Steps(address(_unlock)).pendingOwner(), _alice);
assertEq(IOwnable2Steps(address(_unlock)).owner(), _owner);

address _bob = makeAddr('bob');
vm.prank(_bob);
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, _bob));
IOwnable2Steps(address(_unlock)).acceptOwnership();

vm.prank(_alice);
IOwnable2Steps(address(_unlock)).acceptOwnership();
assertEq(IOwnable2Steps(address(_unlock)).owner(), _alice);
}
}
10 changes: 10 additions & 0 deletions solidity/test/utils/IOwnable2Steps.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;

interface IOwnable2Steps {
function transferOwnership(address _newOwner) external;
function acceptOwnership() external;

function pendingOwner() external view returns (address _pendingOwner);
function owner() external view returns (address _owner);
}
6 changes: 0 additions & 6 deletions solidity/test/utils/IOwned.sol

This file was deleted.

11 changes: 8 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@openzeppelin/contracts@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890"
integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w==

"@solidity-parser/parser@^0.14.1":
version "0.14.5"
resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804"
Expand Down Expand Up @@ -1067,9 +1072,9 @@ flatted@^2.0.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==

"forge-std@github:foundry-rs/forge-std#v1.7.3":
version "1.7.3"
resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/2f112697506eab12d433a65fdc31a639548fe365"
"forge-std@github:foundry-rs/forge-std#v1.7.4":
version "1.7.4"
resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/155d547c449afa8715f538d69454b83944117811"

fs-extra@^11.0.0:
version "11.1.1"
Expand Down

0 comments on commit 5c3cb20

Please sign in to comment.