Skip to content

Commit

Permalink
Merge pull request #2 from 1inch/feature/vested-voting-power
Browse files Browse the repository at this point in the history
[SC-773] add VestedVotingPower contract
  • Loading branch information
ZumZoom authored Mar 14, 2023
2 parents 36cd28b + e2048f6 commit c2b532c
Show file tree
Hide file tree
Showing 17 changed files with 2,896 additions and 1,196 deletions.
25 changes: 25 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Setup

runs:
using: composite
steps:
- uses: actions/setup-node@v3
with:
node-version: 16

- run: npm install -g yarn
shell: bash

- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
shell: bash

- uses: actions/cache@v3
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn
shell: bash
73 changes: 9 additions & 64 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,93 +4,38 @@ on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v2
with:
node-version: '14.x'

- run: npm install -g yarn

- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn lint

test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v2
with:
node-version: '14.x'

- run: npm install -g yarn

- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- name: Create env file
run: |
touch .env
echo MAINNET_RPC_URL=${{ secrets.MAINNET_RPC_URL }} >> .env
- run: yarn
- run: yarn test

coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v2
with:
node-version: 14.x

- run: npm install -g yarn

- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- name: Create env file
run: |
touch .env
echo MAINNET_RPC_URL=${{ secrets.MAINNET_RPC_URL }} >> .env
- run: yarn
- run: yarn coverage

- name: Coveralls
uses: coverallsapp/[email protected]
- uses: codecov/codecov-action@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.CODECOV_TOKEN }}
27 changes: 24 additions & 3 deletions .solcover.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
module.exports = {
skipFiles: [
'mocks'
]
configureYulOptimizer: true,
solcOptimizerDetails: {
yul: true,
yulDetails: {
optimizerSteps:
"dhfoDgvlfnTUtnIf" + // None of these can make stack problems worse
"[" +
"xa[r]EscLM" + // Turn into SSA and simplify
"cCTUtTOntnfDIl" + // Perform structural simplification
"Lcl" + // Simplify again
"Vcl [j]" + // Reverse SSA

// should have good "compilability" property here.

"Tpel" + // Run functional expression inliner
"xa[rl]" + // Prune a bit more in SSA
"xa[r]cL" + // Turn into SSA again and simplify
"gvf" + // Run full inliner
"CTUca[r]LSsTFOtfDnca[r]Ilc" + // SSA plus simplify
"]" +
"jml[jl] VcTOcl jml",
},
},
skipFiles: ['interfaces'],
}
2 changes: 2 additions & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"rules": {
"compiler-version": ["error", "^0.8.0"],
"private-vars-leading-underscore": "error",
"no-global-import": "off",
"not-rely-on-time": "off",
"func-visibility": ["error", { "ignoreConstructors": true }]
}
}
15 changes: 11 additions & 4 deletions contracts/VestedToken.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.9;

pragma abicoder v1;
pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IStepVesting.sol";
import "./interfaces/IVestedToken.sol";

contract VestedToken is Ownable {
contract VestedToken is IVestedToken, Ownable {
using EnumerableSet for EnumerableSet.AddressSet;

event Transfer(address indexed from, address indexed to, uint256 value);
Expand All @@ -25,6 +24,14 @@ contract VestedToken is Ownable {
inchToken = _inchToken;
}

function owner() public view override(IVestedToken, Ownable) returns (address) {
return Ownable.owner();
}

function transferOwnership(address newOwner) public override(IVestedToken, Ownable) onlyOwner {
Ownable.transferOwnership(newOwner);
}

function name() external pure returns(string memory) {
return "1INCH Token (Vested)";
}
Expand Down
84 changes: 84 additions & 0 deletions contracts/VestedVotingPower.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./interfaces/IStepVesting.sol";
import "./interfaces/ISt1inch.sol";
import "./interfaces/IVestedToken.sol";
import "./VotingPowerCalculator.sol";

contract VestedVotingPower is Ownable, VotingPowerCalculator {
using EnumerableSet for EnumerableSet.AddressSet;

IVestedToken public immutable vestedToken;
mapping (address => EnumerableSet.AddressSet) private _vestingsByReceiver;

uint256 private constant _VOTING_POWER_DIVIDER = 20;

constructor(IVestedToken _vestedToken, ISt1inch st1inch) VotingPowerCalculator(st1inch.expBase(), st1inch.origin()) {
vestedToken = _vestedToken;
}

function vestedTokenTransferOwnership(address newOwner) external onlyOwner {
vestedToken.transferOwnership(newOwner);
}

function vestingsByReceiver(address receiver) external view returns (address[] memory) {
return _vestingsByReceiver[receiver].values();
}

function votingPowerOf(address account) external view returns (uint256 votingPower) {
EnumerableSet.AddressSet storage vestings = _vestingsByReceiver[account];
uint256 len = vestings.length();
unchecked {
for (uint256 i = 0; i < len; i++) {
IStepVesting vesting = IStepVesting(vestings.at(i));
uint256 started = vesting.started();
uint256 cliffDuration = vesting.cliffDuration();
uint256 stepDuration = vesting.stepDuration();
if (block.timestamp < started + cliffDuration) {
votingPower += _votingPowerAt(_balanceAt(vesting.cliffAmount() / _VOTING_POWER_DIVIDER, started + cliffDuration), block.timestamp);
}
uint256 numOfSteps = vesting.numOfSteps();
for (uint256 j = 0; j < numOfSteps; j++) {
uint256 stepUnlockTimestamp = started + cliffDuration + stepDuration * (j + 1);
if (block.timestamp < stepUnlockTimestamp) {
votingPower += _votingPowerAt(_balanceAt(vesting.stepAmount() / _VOTING_POWER_DIVIDER, stepUnlockTimestamp), block.timestamp);
}
}
}
}
return votingPower;
}

function registerVestings(address[] calldata vestings) external onlyOwner {
if (vestedToken.owner() == address(this)) {
vestedToken.registerVestings(vestings);
}
uint256 len = vestings.length;
unchecked {
for (uint256 i = 0; i < len; i++) {
address vesting = vestings[i];
address receiver = IStepVesting(vesting).receiver();
require(_vestingsByReceiver[receiver].add(vesting), "Vesting is already registered");
}
}
}

function deregisterVestings(address[] calldata vestings) external onlyOwner {
if (vestedToken.owner() == address(this)) {
vestedToken.deregisterVestings(vestings);
}
uint256 len = vestings.length;
unchecked {
for (uint256 i = 0; i < len; i++) {
address vesting = vestings[i];
address receiver = IStepVesting(vesting).receiver();
EnumerableSet.AddressSet storage receiverVestings = _vestingsByReceiver[receiver];
require(receiverVestings.remove(vesting), "Vesting is not registered");
}
}
}
}
Loading

0 comments on commit c2b532c

Please sign in to comment.