├── .env.sample
├── .gitignore
├── .prettierrc
├── .solhint.json
├── README.md
├── contracts
├── Migrations.sol
├── dex-v2
│ ├── math
│ │ └── VaderMath.sol
│ ├── pool
│ │ ├── BasePoolV2.sol
│ │ └── VaderPoolV2.sol
│ ├── router
│ │ └── VaderRouterV2.sol
│ ├── utils
│ │ └── GasThrottle.sol
│ └── wrapper
│ │ ├── LPToken.sol
│ │ └── LPWrapper.sol
├── external
│ ├── UniswapV2ERC20.sol
│ ├── UniswapV2Pair.sol
│ ├── interfaces
│ │ └── AggregatorV3Interface.sol
│ └── libraries
│ │ ├── Babylonian.sol
│ │ ├── BitMath.sol
│ │ ├── FixedPoint.sol
│ │ ├── FullMath.sol
│ │ ├── TransferHelper.sol
│ │ ├── UQ112x112.sol
│ │ ├── UniswapMath.sol
│ │ ├── UniswapV2Library.sol
│ │ └── UniswapV2OracleLibrary.sol
├── governance
│ ├── GovernorAlpha.sol
│ └── Timelock.sol
├── interfaces
│ ├── dex-v2
│ │ ├── pool
│ │ │ ├── IBasePoolV2.sol
│ │ │ ├── IVaderPoolFactoryV2.sol
│ │ │ └── IVaderPoolV2.sol
│ │ ├── router
│ │ │ └── IVaderRouterV2.sol
│ │ └── wrapper
│ │ │ ├── ILPToken.sol
│ │ │ └── ILPWrapper.sol
│ ├── dex
│ │ ├── pool
│ │ │ ├── IBasePool.sol
│ │ │ ├── IVaderPool.sol
│ │ │ └── IVaderPoolFactory.sol
│ │ ├── queue
│ │ │ ├── IGasQueue.sol
│ │ │ └── ISwapQueue.sol
│ │ └── router
│ │ │ └── IVaderRouter.sol
│ ├── external
│ │ ├── chainlink
│ │ │ ├── IAggregator.sol
│ │ │ └── IAggregatorV3.sol
│ │ ├── uniswap
│ │ │ ├── IUniswapV2Callee.sol
│ │ │ ├── IUniswapV2ERC20.sol
│ │ │ ├── IUniswapV2Factory.sol
│ │ │ ├── IUniswapV2Pair.sol
│ │ │ ├── IUniswapV2Router01.sol
│ │ │ └── IUniswapV2Router02.sol
│ │ └── weth
│ │ │ └── IWETH.sol
│ ├── governance
│ │ └── ITimelock.sol
│ ├── lbt
│ │ ├── ILiquidityBasedTWAP.sol
│ │ ├── IUniswapTwap.sol
│ │ └── minter
│ │ │ └── IVaderMinterUpgradeable.sol
│ ├── reserve
│ │ └── IVaderReserve.sol
│ ├── shared
│ │ └── IERC20Extended.sol
│ ├── tokens
│ │ ├── IUSDV.sol
│ │ ├── IVader.sol
│ │ ├── converter
│ │ │ └── IConverter.sol
│ │ ├── validator
│ │ │ └── IUnlockValidator.sol
│ │ └── vesting
│ │ │ └── ILinearVesting.sol
│ └── x-vader
│ │ └── IXVader.sol
├── lbt
│ ├── LiquidityBasedTWAP.sol
│ ├── UniswapTwap.sol
│ └── minter
│ │ ├── VaderMinterStorage.sol
│ │ └── VaderMinterUpgradeable.sol
├── mocks
│ ├── Mock.sol
│ ├── MockAggregatorV3.sol
│ ├── MockConstants.sol
│ ├── MockGovernorAlpha.sol
│ ├── MockLBT.sol
│ ├── MockMTree.sol
│ ├── MockTarget.sol
│ ├── MockTimelock.sol
│ ├── MockToken.sol
│ ├── MockUSDV.sol
│ ├── MockUniswapV2Factory.sol
│ ├── MockUniswapV2Library.sol
│ ├── MockUniswapV2Router.sol
│ ├── MockVader.sol
│ └── MockXVader.sol
├── reserve
│ └── VaderReserve.sol
├── shared
│ └── ProtocolConstants.sol
├── staking-rewards
│ ├── IStakingRewards.sol
│ ├── Owned.sol
│ ├── Pausable.sol
│ ├── RewardsDistributionRecipient.sol
│ └── StakingRewards.sol
├── tokens
│ ├── USDV.sol
│ ├── Vader.sol
│ ├── converter
│ │ └── Converter.sol
│ ├── validator
│ │ └── UnlockValidator.sol
│ └── vesting
│ │ └── LinearVesting.sol
└── x-vader
│ └── XVader.sol
├── doc
├── vader.drawio
└── vader.drawio.png
├── migrations
├── 1_initial_migration.js
├── 2_deploy_vader.js
├── 3_deploy_converter.js
├── 4_deploy_vesting.js
├── 5_deploy_usdv.js
├── 6_deploy_validator.js
└── constants.js
├── package.json
├── test
├── .gitkeep
├── Converter.test.js
├── LinearVesting.test.js
├── Phase1.test.js
├── Vader.test.js
├── dex-v2
│ ├── BasePoolV2.test.js
│ ├── LPWrapper.test.js
│ ├── VaderPoolV2.test.js
│ └── VaderRouterV2.test.js
├── governance
│ ├── GovernorAlpha.cancel.test.js
│ ├── GovernorAlpha.castVote.test.js
│ ├── GovernorAlpha.execute.test.js
│ ├── GovernorAlpha.propose.test.js
│ ├── GovernorAlpha.queue.test.js
│ ├── GovernorAlpha.state.test.js
│ ├── GovernorAlpha.veto.test.js
│ ├── Timelock.test.js
│ └── helpers
│ │ └── index.js
├── integration
│ └── usdv-mint-burn.test.js
├── lbt
│ ├── LBTwap.test.js
│ └── minter
│ │ └── VaderMinterUpgradeable.test.js
├── reserve
│ └── VaderReserve.test.js
├── tokens
│ ├── USDV.test.js
│ └── validator
│ │ └── UnlockValidator.test.js
├── utils
│ └── index.js
└── x-vader
│ └── XVader.test.js
└── truffle-config.js
/.env.sample:
--------------------------------------------------------------------------------
1 | INFURA_API_KEY=
2 | ETHER_SCAN_API_KEY=
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # Custom
107 | .idea/
108 | package-lock.json
109 | build/
110 |
111 | .secret
112 | .DS_Store
113 | tmp
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 4,
4 | "semi": true
5 | }
6 |
--------------------------------------------------------------------------------
/.solhint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "solhint:recommended",
3 | "plugins": [],
4 | "rules": {
5 | "avoid-throw": "off",
6 | "avoid-suicide": "error",
7 | "avoid-sha3": "warn",
8 | "compiler-version": "off",
9 | "no-empty-blocks": "off"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vader-protocol-v2
2 |
3 | 
4 |
5 | ## Setup
6 |
7 | ```shell
8 | npm i
9 | # put your wallet seed here
10 | touch .secret
11 | # put your environment variables here
12 | cp .env.sample .env
13 | ```
14 |
15 | ## Test
16 |
17 | ```shell
18 | npx ganache-cli
19 | npx truffle test
20 |
21 | # prettier
22 | npm run lint
23 | # solhint
24 | npm run solhint
25 | ```
26 |
27 | ## Deploy
28 |
29 | ```shell
30 | # clean build
31 | npm run compile
32 | # deploy (run migration script from x to y)
33 | npx truffle migrate -f x --to y --network kovan
34 | # verify contract
35 | npx truffle run verify MyContract --network kovan
36 | ```
37 |
38 | ## Networks & Addresses
39 |
40 | ### Mainnet
41 |
42 | - Vether: 0x4Ba6dDd7b89ed838FEd25d208D4f644106E34279
43 | - Vader: 0x2602278EE1882889B946eb11DC0E810075650983
44 | - Converter: 0x6D4a43Ee4770a2Bab97460d3a3B783641D85d108
45 | - LinearVesting: 0xb3C600C04AaF603b0f422b73Db244216C2e491f6
46 | - USDV: 0xea3Fb6f331735252E7Bfb0b24b3B761301293DBe
47 | - UnlockValidator: 0xfcC4bCB2924ee4a0334A5B0b2C6ad893E8Ad3337
48 | - TWAP: 0x6a81be7f5c868f34f109d5b5f38ed67f3395f7b0
49 | - Minter: 0x00aadC47d91fD9CaC3369E6045042f9F99216B98
50 | - TimeLock: 0x0df9220aEaA28cE8bA06ADd7c5B3Cc6e7C1Cd511
51 |
52 | # Rinkeby
53 |
54 | - TimeLock: 0x4402A7C8829489705852e54Da50Ebec60C8C86a8
55 |
56 | ### Kovan
57 |
58 | - Vether: 0x87D96b9f386d70C72fD7DBcE5a3d2a7D3321446d
59 | - Vader: 0xcCb3AeF7Baa506e2D05193e38e88459F68AC1a8F
60 | - Converter: 0x8A313Fa0cb3ed92bE4Cae3a4deF7C32c78181E09
61 | - LinearVesting: 0xDaA4B82D5Bdd315a3191B080E26ff7A88eb8034E
62 | - USDV: 0xF5783253A21E5E740908CEdB800183b70A004479
63 | - UnlockValidator: 0x0c897109F807dB4dFbA87a776747F352f2Ef4A94
64 | - TWAP: 0x1bf8b6fde5c942867dd9d3dcea506de706fe98b4
65 | - Minter: 0x60933C457Bbca83FD2eA8018A4D2d6662B024B20
66 |
--------------------------------------------------------------------------------
/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.4.22 <0.9.0;
3 |
4 | contract Migrations {
5 | address public owner = msg.sender;
6 | uint256 public last_completed_migration;
7 |
8 | modifier restricted() {
9 | require(
10 | msg.sender == owner,
11 | "This function is restricted to the contract's owner"
12 | );
13 | _;
14 | }
15 |
16 | function setCompleted(uint256 completed) public restricted {
17 | last_completed_migration = completed;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/contracts/dex-v2/math/VaderMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | library VaderMath {
5 | /* ========== CONSTANTS ========== */
6 |
7 | uint256 public constant ONE = 1 ether;
8 |
9 | /* ========== LIBRARY FUNCTIONS ========== */
10 |
11 | /**
12 | * @dev Calculates the amount of liquidity units for the {vaderDeposited}
13 | * and {assetDeposited} amounts across {totalPoolUnits}.
14 | *
15 | * The {vaderBalance} and {assetBalance} are taken into account in order to
16 | * calculate any necessary slippage adjustment.
17 | */
18 | function calculateLiquidityUnits(
19 | uint256 vaderDeposited,
20 | uint256 vaderBalance,
21 | uint256 assetDeposited,
22 | uint256 assetBalance,
23 | uint256 totalPoolUnits
24 | ) internal pure returns (uint256) {
25 | // slipAdjustment
26 | uint256 slip = calculateSlipAdjustment(
27 | vaderDeposited,
28 | vaderBalance,
29 | assetDeposited,
30 | assetBalance
31 | );
32 |
33 | // (Va + vA)
34 | uint256 poolUnitFactor = (vaderBalance * assetDeposited) +
35 | (vaderDeposited * assetBalance);
36 |
37 | // 2VA
38 | uint256 denominator = ONE * 2 * vaderBalance * assetBalance;
39 |
40 | // P * [(Va + vA) / (2 * V * A)] * slipAdjustment
41 | return ((totalPoolUnits * poolUnitFactor) / denominator) * slip;
42 | }
43 |
44 | /**
45 | * @dev Calculates the necessary slippage adjustment for the {vaderDeposited} and {assetDeposited}
46 | * amounts across the total {vaderBalance} and {assetBalance} amounts.
47 | */
48 | function calculateSlipAdjustment(
49 | uint256 vaderDeposited,
50 | uint256 vaderBalance,
51 | uint256 assetDeposited,
52 | uint256 assetBalance
53 | ) internal pure returns (uint256) {
54 | // Va
55 | uint256 vaderAsset = vaderBalance * assetDeposited;
56 |
57 | // aV
58 | uint256 assetVader = assetBalance * vaderDeposited;
59 |
60 | // (v + V) * (a + A)
61 | uint256 denominator = (vaderDeposited + vaderBalance) *
62 | (assetDeposited + assetBalance);
63 |
64 | // 1 - [|Va - aV| / (v + V) * (a + A)]
65 | return ONE - (delta(vaderAsset, assetVader) / denominator);
66 | }
67 |
68 | /**
69 | * @dev Calculates the loss based on the supplied {releasedVader} and {releasedAsset}
70 | * compared to the supplied {originalVader} and {originalAsset}.
71 | */
72 | function calculateLoss(
73 | uint256 originalVader,
74 | uint256 originalAsset,
75 | uint256 releasedVader,
76 | uint256 releasedAsset
77 | ) internal pure returns (uint256 loss) {
78 | // [(A0 * P1) + V0]
79 | uint256 originalValue = ((originalAsset * releasedVader) /
80 | releasedAsset) + originalVader;
81 |
82 | // [(A1 * P1) + V1]
83 | uint256 releasedValue = ((releasedAsset * releasedVader) /
84 | releasedAsset) + releasedVader;
85 |
86 | // [(A0 * P1) + V0] - [(A1 * P1) + V1]
87 | if (originalValue > releasedValue) loss = originalValue - releasedValue;
88 | }
89 |
90 | /**
91 | * @dev Calculates the {amountOut} of the swap based on the supplied {amountIn}
92 | * across the supplied {reserveIn} and {reserveOut} amounts.
93 | */
94 | function calculateSwap(
95 | uint256 amountIn,
96 | uint256 reserveIn,
97 | uint256 reserveOut
98 | ) internal pure returns (uint256 amountOut) {
99 | // x * Y * X
100 | uint256 numerator = amountIn * reserveIn * reserveOut;
101 |
102 | // (x + X) ^ 2
103 | uint256 denominator = pow(amountIn + reserveIn);
104 |
105 | amountOut = numerator / denominator;
106 | }
107 |
108 | /**
109 | * @dev Calculates the {amountIn} of the swap based on the supplied {amountOut}
110 | * across the supplied {reserveIn} and {reserveOut} amounts.
111 | */
112 | function calculateSwapReverse(
113 | uint256 amountOut,
114 | uint256 reserveIn,
115 | uint256 reserveOut
116 | ) internal pure returns (uint256 amountIn) {
117 | // X * Y
118 | uint256 XY = reserveIn * reserveOut;
119 |
120 | // 2y
121 | uint256 y2 = amountOut * 2;
122 |
123 | // 4y
124 | uint256 y4 = y2 * 2;
125 |
126 | require(
127 | y4 < reserveOut,
128 | "VaderMath::calculateSwapReverse: Desired Output Exceeds Maximum Output Possible (1/4 of Liquidity Pool)"
129 | );
130 |
131 | // root(-X^2 * Y * (4y - Y)) => root(X^2 * Y * (Y - 4y)) as Y - 4y >= 0 => Y >= 4y holds true
132 | uint256 numeratorA = root(XY) * root(reserveIn * (reserveOut - y4));
133 |
134 | // X * (2y - Y) => 2yX - XY
135 | uint256 numeratorB = y2 * reserveIn;
136 | uint256 numeratorC = XY;
137 |
138 | // -1 * (root(-X^2 * Y * (4y - Y)) + (X * (2y - Y))) => -1 * (root(X^2 * Y * (Y - 4y)) + 2yX - XY) => XY - root(X^2 * Y * (Y - 4y) - 2yX
139 | uint256 numerator = numeratorC - numeratorA - numeratorB;
140 |
141 | // 2y
142 | uint256 denominator = y2;
143 |
144 | amountIn = numerator / denominator;
145 | }
146 |
147 | /**
148 | * @dev Calculates the difference between the supplied {a} and {b} values as a positive number.
149 | */
150 | function delta(uint256 a, uint256 b) internal pure returns (uint256) {
151 | return a > b ? a - b : b - a;
152 | }
153 |
154 | /**
155 | * @dev Calculates the power of 2 of the supplied {a} value.
156 | */
157 | function pow(uint256 a) internal pure returns (uint256) {
158 | return a * a;
159 | }
160 |
161 | /**
162 | * @dev Calculates the square root {c} of the supplied {a} value utilizing the Babylonian method:
163 | * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
164 | */
165 | function root(uint256 a) internal pure returns (uint256 c) {
166 | if (a > 3) {
167 | c = a;
168 | uint256 x = a / 2 + 1;
169 | while (x < c) {
170 | c = x;
171 | x = (a / x + x) / 2;
172 | }
173 | } else if (a != 0) {
174 | c = 1;
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/contracts/dex-v2/utils/GasThrottle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "../../shared/ProtocolConstants.sol";
5 |
6 | import "../../interfaces/external/chainlink/IAggregator.sol";
7 |
8 | abstract contract GasThrottle is ProtocolConstants {
9 | bool public gasThrottleEnabled = true;
10 |
11 | modifier validateGas() {
12 | // require(
13 | // gasThrottleEnabled &&
14 | // (block.basefee <= tx.gasprice &&
15 | // tx.gasprice <=
16 | // uint256(IAggregator(_FAST_GAS_ORACLE).latestAnswer())),
17 | // "GasThrottle::validateGas: Gas Exceeds Thresholds"
18 | // );
19 | _;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/contracts/dex-v2/wrapper/LPToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "@openzeppelin/contracts/access/Ownable.sol";
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | import "../../shared/ProtocolConstants.sol";
8 |
9 | import "../../interfaces/dex-v2/pool/IVaderPoolV2.sol";
10 | import "../../interfaces/dex-v2/wrapper/ILPToken.sol";
11 |
12 | contract LPToken is ILPToken, ProtocolConstants, ERC20, Ownable {
13 | IERC20Extended public immutable foreignAsset;
14 | IVaderPoolV2 public immutable pool;
15 |
16 | /* ========== CONSTRUCTOR ========== */
17 |
18 | constructor(IERC20Extended _foreignAsset, IVaderPoolV2 _pool)
19 | ERC20("VADER-V1", _calculateSymbol(_foreignAsset))
20 | {
21 | require(
22 | address(_foreignAsset) != _ZERO_ADDRESS &&
23 | address(_pool) != _ZERO_ADDRESS,
24 | "LPToken::constructor: Zero Address"
25 | );
26 | foreignAsset = _foreignAsset;
27 | pool = _pool;
28 | transferOwnership(address(_pool));
29 | }
30 |
31 | /* ========== VIEWS ========== */
32 |
33 | function totalSupply() public view override returns (uint256) {
34 | return pool.pairSupply(foreignAsset);
35 | }
36 |
37 | function balanceOf(address user) public view override returns (uint256) {
38 | if (user == address(pool)) return totalSupply() - ERC20.totalSupply();
39 | else return ERC20.balanceOf(user);
40 | }
41 |
42 | function _calculateSymbol(IERC20Extended token)
43 | internal
44 | view
45 | returns (string memory)
46 | {
47 | return _combine("V(", token.symbol(), "|USDV)");
48 | }
49 |
50 | function _combine(string memory a, string memory b)
51 | internal
52 | pure
53 | returns (string memory)
54 | {
55 | return _combine(a, b, "");
56 | }
57 |
58 | function _combine(
59 | string memory a,
60 | string memory b,
61 | string memory c
62 | ) internal pure returns (string memory) {
63 | return string(abi.encodePacked(a, b, c));
64 | }
65 |
66 | /* ========== RESTRICTED FUNCTIONS ========== */
67 |
68 | function mint(address to, uint256 amount) external onlyOwner {
69 | _mint(to, amount);
70 | }
71 |
72 | function burn(uint256 amount) external onlyOwner {
73 | _burn(msg.sender, amount);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/contracts/dex-v2/wrapper/LPWrapper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "./LPToken.sol";
5 |
6 | import "../../interfaces/dex-v2/wrapper/ILPWrapper.sol";
7 |
8 | contract LPWrapper is ILPWrapper, ProtocolConstants, Ownable {
9 | mapping(IERC20 => IERC20Extended) public override tokens;
10 |
11 | constructor(address pool) {
12 | transferOwnership(pool);
13 | }
14 |
15 | function createWrapper(IERC20 foreignAsset) external override onlyOwner {
16 | require(
17 | tokens[foreignAsset] == IERC20Extended(_ZERO_ADDRESS),
18 | "LPWrapper::createWrapper: Already Created"
19 | );
20 |
21 | // NOTE: Here, `owner` is the VaderPoolV2
22 | tokens[foreignAsset] = IERC20Extended(
23 | address(
24 | new LPToken(
25 | IERC20Extended(address(foreignAsset)),
26 | IVaderPoolV2(owner())
27 | )
28 | )
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/contracts/external/UniswapV2ERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../interfaces/external/uniswap/IUniswapV2ERC20.sol";
6 |
7 | contract UniswapV2ERC20 is IUniswapV2ERC20 {
8 | string public constant name = "Uniswap V2";
9 | string public constant symbol = "UNI-V2";
10 | uint8 public constant decimals = 18;
11 | uint256 public totalSupply;
12 | mapping(address => uint256) public balanceOf;
13 | mapping(address => mapping(address => uint256)) public allowance;
14 |
15 | bytes32 public DOMAIN_SEPARATOR;
16 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
17 | bytes32 public constant PERMIT_TYPEHASH =
18 | 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
19 | mapping(address => uint256) public nonces;
20 |
21 | constructor() {
22 | uint256 chainId;
23 | assembly {
24 | chainId := chainid()
25 | }
26 | DOMAIN_SEPARATOR = keccak256(
27 | abi.encode(
28 | keccak256(
29 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
30 | ),
31 | keccak256(bytes(name)),
32 | keccak256(bytes("1")),
33 | chainId,
34 | address(this)
35 | )
36 | );
37 | }
38 |
39 | function _mint(address to, uint256 value) internal {
40 | totalSupply = totalSupply + value;
41 | balanceOf[to] = balanceOf[to] + value;
42 | emit Transfer(address(0), to, value);
43 | }
44 |
45 | function _burn(address from, uint256 value) internal {
46 | balanceOf[from] = balanceOf[from] - value;
47 | totalSupply = totalSupply - value;
48 | emit Transfer(from, address(0), value);
49 | }
50 |
51 | function _approve(
52 | address owner,
53 | address spender,
54 | uint256 value
55 | ) private {
56 | allowance[owner][spender] = value;
57 | emit Approval(owner, spender, value);
58 | }
59 |
60 | function _transfer(
61 | address from,
62 | address to,
63 | uint256 value
64 | ) private {
65 | balanceOf[from] = balanceOf[from] - value;
66 | balanceOf[to] = balanceOf[to] + value;
67 | emit Transfer(from, to, value);
68 | }
69 |
70 | function approve(address spender, uint256 value) external returns (bool) {
71 | _approve(msg.sender, spender, value);
72 | return true;
73 | }
74 |
75 | function transfer(address to, uint256 value) external returns (bool) {
76 | _transfer(msg.sender, to, value);
77 | return true;
78 | }
79 |
80 | function transferFrom(
81 | address from,
82 | address to,
83 | uint256 value
84 | ) external returns (bool) {
85 | if (allowance[from][msg.sender] != type(uint256).max) {
86 | allowance[from][msg.sender] = allowance[from][msg.sender] - value;
87 | }
88 | _transfer(from, to, value);
89 | return true;
90 | }
91 |
92 | function permit(
93 | address owner,
94 | address spender,
95 | uint256 value,
96 | uint256 deadline,
97 | uint8 v,
98 | bytes32 r,
99 | bytes32 s
100 | ) external {
101 | require(deadline >= block.timestamp, "UniswapV2: EXPIRED");
102 | bytes32 digest = keccak256(
103 | abi.encodePacked(
104 | "\x19\x01",
105 | DOMAIN_SEPARATOR,
106 | keccak256(
107 | abi.encode(
108 | PERMIT_TYPEHASH,
109 | owner,
110 | spender,
111 | value,
112 | nonces[owner]++,
113 | deadline
114 | )
115 | )
116 | )
117 | );
118 | address recoveredAddress = ecrecover(digest, v, r, s);
119 | require(
120 | recoveredAddress != address(0) && recoveredAddress == owner,
121 | "UniswapV2: INVALID_SIGNATURE"
122 | );
123 | _approve(owner, spender, value);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/contracts/external/interfaces/AggregatorV3Interface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity ^0.8.0;
3 |
4 | interface AggregatorV3Interface {
5 | function decimals() external view returns (uint8);
6 |
7 | function description() external view returns (string memory);
8 |
9 | function version() external view returns (uint256);
10 |
11 | // getRoundData and latestRoundData should both raise "No data present"
12 | // if they do not have data to report, instead of returning unset values
13 | // which could be misinterpreted as actual reported values.
14 | function getRoundData(uint80 _roundId)
15 | external
16 | view
17 | returns (
18 | uint80 roundId,
19 | int256 answer,
20 | uint256 startedAt,
21 | uint256 updatedAt,
22 | uint80 answeredInRound
23 | );
24 |
25 | function latestRoundData()
26 | external
27 | view
28 | returns (
29 | uint80 roundId,
30 | int256 answer,
31 | uint256 startedAt,
32 | uint256 updatedAt,
33 | uint80 answeredInRound
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/external/libraries/Babylonian.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | // computes square roots using the babylonian method
6 | // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
7 | library Babylonian {
8 | // credit for this implementation goes to
9 | // https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
10 | function sqrt(uint256 x) internal pure returns (uint256) {
11 | if (x == 0) return 0;
12 | // this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
13 | // however that code costs significantly more gas
14 | uint256 xx = x;
15 | uint256 r = 1;
16 | if (xx >= 0x100000000000000000000000000000000) {
17 | xx >>= 128;
18 | r <<= 64;
19 | }
20 | if (xx >= 0x10000000000000000) {
21 | xx >>= 64;
22 | r <<= 32;
23 | }
24 | if (xx >= 0x100000000) {
25 | xx >>= 32;
26 | r <<= 16;
27 | }
28 | if (xx >= 0x10000) {
29 | xx >>= 16;
30 | r <<= 8;
31 | }
32 | if (xx >= 0x100) {
33 | xx >>= 8;
34 | r <<= 4;
35 | }
36 | if (xx >= 0x10) {
37 | xx >>= 4;
38 | r <<= 2;
39 | }
40 | if (xx >= 0x8) {
41 | r <<= 1;
42 | }
43 | r = (r + x / r) >> 1;
44 | r = (r + x / r) >> 1;
45 | r = (r + x / r) >> 1;
46 | r = (r + x / r) >> 1;
47 | r = (r + x / r) >> 1;
48 | r = (r + x / r) >> 1;
49 | r = (r + x / r) >> 1; // Seven iterations should be enough
50 | uint256 r1 = x / r;
51 | return (r < r1 ? r : r1);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/contracts/external/libraries/BitMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | library BitMath {
5 | // returns the 0 indexed position of the most significant bit of the input x
6 | // s.t. x >= 2**msb and x < 2**(msb+1)
7 | function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
8 | require(x > 0, "BitMath::mostSignificantBit: zero");
9 |
10 | if (x >= 0x100000000000000000000000000000000) {
11 | x >>= 128;
12 | r += 128;
13 | }
14 | if (x >= 0x10000000000000000) {
15 | x >>= 64;
16 | r += 64;
17 | }
18 | if (x >= 0x100000000) {
19 | x >>= 32;
20 | r += 32;
21 | }
22 | if (x >= 0x10000) {
23 | x >>= 16;
24 | r += 16;
25 | }
26 | if (x >= 0x100) {
27 | x >>= 8;
28 | r += 8;
29 | }
30 | if (x >= 0x10) {
31 | x >>= 4;
32 | r += 4;
33 | }
34 | if (x >= 0x4) {
35 | x >>= 2;
36 | r += 2;
37 | }
38 | if (x >= 0x2) r += 1;
39 | }
40 |
41 | // returns the 0 indexed position of the least significant bit of the input x
42 | // s.t. (x & 2**lsb) != 0 and (x & (2**(lsb) - 1)) == 0)
43 | // i.e. the bit at the index is set and the mask of all lower bits is 0
44 | function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
45 | require(x > 0, "BitMath::leastSignificantBit: zero");
46 |
47 | r = 255;
48 | if (x & type(uint128).max > 0) {
49 | r -= 128;
50 | } else {
51 | x >>= 128;
52 | }
53 | if (x & type(uint64).max > 0) {
54 | r -= 64;
55 | } else {
56 | x >>= 64;
57 | }
58 | if (x & type(uint32).max > 0) {
59 | r -= 32;
60 | } else {
61 | x >>= 32;
62 | }
63 | if (x & type(uint16).max > 0) {
64 | r -= 16;
65 | } else {
66 | x >>= 16;
67 | }
68 | if (x & type(uint8).max > 0) {
69 | r -= 8;
70 | } else {
71 | x >>= 8;
72 | }
73 | if (x & 0xf > 0) {
74 | r -= 4;
75 | } else {
76 | x >>= 4;
77 | }
78 | if (x & 0x3 > 0) {
79 | r -= 2;
80 | } else {
81 | x >>= 2;
82 | }
83 | if (x & 0x1 > 0) r -= 1;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/contracts/external/libraries/FixedPoint.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "./FullMath.sol";
5 | import "./Babylonian.sol";
6 | import "./BitMath.sol";
7 |
8 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
9 | library FixedPoint {
10 | // range: [0, 2**112 - 1]
11 | // resolution: 1 / 2**112
12 | struct uq112x112 {
13 | uint224 _x;
14 | }
15 |
16 | // range: [0, 2**144 - 1]
17 | // resolution: 1 / 2**112
18 | struct uq144x112 {
19 | uint256 _x;
20 | }
21 |
22 | uint8 public constant RESOLUTION = 112;
23 | uint256 public constant Q112 = 0x10000000000000000000000000000; // 2**112
24 | uint256 private constant Q224 =
25 | 0x100000000000000000000000000000000000000000000000000000000; // 2**224
26 | uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)
27 |
28 | // encode a uint112 as a UQ112x112
29 | function encode(uint112 x) internal pure returns (uq112x112 memory) {
30 | return uq112x112(uint224(x) << RESOLUTION);
31 | }
32 |
33 | // encodes a uint144 as a UQ144x112
34 | function encode144(uint144 x) internal pure returns (uq144x112 memory) {
35 | return uq144x112(uint256(x) << RESOLUTION);
36 | }
37 |
38 | // decode a UQ112x112 into a uint112 by truncating after the radix point
39 | function decode(uq112x112 memory self) internal pure returns (uint112) {
40 | return uint112(self._x >> RESOLUTION);
41 | }
42 |
43 | // decode a UQ144x112 into a uint144 by truncating after the radix point
44 | function decode144(uq144x112 memory self) internal pure returns (uint144) {
45 | return uint144(self._x >> RESOLUTION);
46 | }
47 |
48 | // multiply a UQ112x112 by a uint, returning a UQ144x112
49 | // reverts on overflow
50 | function mul(uq112x112 memory self, uint256 y)
51 | internal
52 | pure
53 | returns (uq144x112 memory)
54 | {
55 | uint256 z = 0;
56 | require(
57 | y == 0 || (z = self._x * y) / y == self._x,
58 | "FixedPoint::mul: overflow"
59 | );
60 | return uq144x112(z);
61 | }
62 |
63 | // multiply a UQ112x112 by an int and decode, returning an int
64 | // reverts on overflow
65 | function muli(uq112x112 memory self, int256 y)
66 | internal
67 | pure
68 | returns (int256)
69 | {
70 | uint256 z = FullMath.mulDiv(self._x, uint256(y < 0 ? -y : y), Q112);
71 | require(z < 2**255, "FixedPoint::muli: overflow");
72 | return y < 0 ? -int256(z) : int256(z);
73 | }
74 |
75 | // multiply a UQ112x112 by a UQ112x112, returning a UQ112x112
76 | // lossy
77 | function muluq(uq112x112 memory self, uq112x112 memory other)
78 | internal
79 | pure
80 | returns (uq112x112 memory)
81 | {
82 | if (self._x == 0 || other._x == 0) {
83 | return uq112x112(0);
84 | }
85 | uint112 upper_self = uint112(self._x >> RESOLUTION); // * 2^0
86 | uint112 lower_self = uint112(self._x & LOWER_MASK); // * 2^-112
87 | uint112 upper_other = uint112(other._x >> RESOLUTION); // * 2^0
88 | uint112 lower_other = uint112(other._x & LOWER_MASK); // * 2^-112
89 |
90 | // partial products
91 | uint224 upper = uint224(upper_self) * upper_other; // * 2^0
92 | uint224 lower = uint224(lower_self) * lower_other; // * 2^-224
93 | uint224 uppers_lowero = uint224(upper_self) * lower_other; // * 2^-112
94 | uint224 uppero_lowers = uint224(upper_other) * lower_self; // * 2^-112
95 |
96 | // so the bit shift does not overflow
97 | require(
98 | upper <= type(uint112).max,
99 | "FixedPoint::muluq: upper overflow"
100 | );
101 |
102 | // this cannot exceed 256 bits, all values are 224 bits
103 | uint256 sum = uint256(upper << RESOLUTION) +
104 | uppers_lowero +
105 | uppero_lowers +
106 | (lower >> RESOLUTION);
107 |
108 | // so the cast does not overflow
109 | require(sum <= type(uint224).max, "FixedPoint::muluq: sum overflow");
110 |
111 | return uq112x112(uint224(sum));
112 | }
113 |
114 | // divide a UQ112x112 by a UQ112x112, returning a UQ112x112
115 | function divuq(uq112x112 memory self, uq112x112 memory other)
116 | internal
117 | pure
118 | returns (uq112x112 memory)
119 | {
120 | require(other._x > 0, "FixedPoint::divuq: division by zero");
121 | if (self._x == other._x) {
122 | return uq112x112(uint224(Q112));
123 | }
124 | if (self._x <= type(uint144).max) {
125 | uint256 value = (uint256(self._x) << RESOLUTION) / other._x;
126 | require(value <= type(uint224).max, "FixedPoint::divuq: overflow");
127 | return uq112x112(uint224(value));
128 | }
129 |
130 | uint256 result = FullMath.mulDiv(Q112, self._x, other._x);
131 | require(result <= type(uint224).max, "FixedPoint::divuq: overflow");
132 | return uq112x112(uint224(result));
133 | }
134 |
135 | // returns a UQ112x112 which represents the ratio of the numerator to the denominator
136 | // can be lossy
137 | function fraction(uint256 numerator, uint256 denominator)
138 | internal
139 | pure
140 | returns (uq112x112 memory)
141 | {
142 | require(denominator > 0, "FixedPoint::fraction: division by zero");
143 | if (numerator == 0) return FixedPoint.uq112x112(0);
144 |
145 | if (numerator <= type(uint144).max) {
146 | uint256 result = (numerator << RESOLUTION) / denominator;
147 | require(
148 | result <= type(uint224).max,
149 | "FixedPoint::fraction: overflow"
150 | );
151 | return uq112x112(uint224(result));
152 | } else {
153 | uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
154 | require(
155 | result <= type(uint224).max,
156 | "FixedPoint::fraction: overflow"
157 | );
158 | return uq112x112(uint224(result));
159 | }
160 | }
161 |
162 | // take the reciprocal of a UQ112x112
163 | // reverts on overflow
164 | // lossy
165 | function reciprocal(uq112x112 memory self)
166 | internal
167 | pure
168 | returns (uq112x112 memory)
169 | {
170 | require(self._x != 0, "FixedPoint::reciprocal: reciprocal of zero");
171 | require(self._x != 1, "FixedPoint::reciprocal: overflow");
172 | return uq112x112(uint224(Q224 / self._x));
173 | }
174 |
175 | // square root of a UQ112x112
176 | // lossy between 0/1 and 40 bits
177 | function sqrt(uq112x112 memory self)
178 | internal
179 | pure
180 | returns (uq112x112 memory)
181 | {
182 | if (self._x <= type(uint144).max) {
183 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112)));
184 | }
185 |
186 | uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x);
187 | safeShiftBits -= safeShiftBits % 2;
188 | return
189 | uq112x112(
190 | uint224(
191 | Babylonian.sqrt(uint256(self._x) << safeShiftBits) <<
192 | ((112 - safeShiftBits) / 2)
193 | )
194 | );
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/contracts/external/libraries/FullMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: CC-BY-4.0
2 | pragma solidity =0.8.9;
3 |
4 | // taken from https://medium.com/coinmonks/math-in-solidity-part-3-percents-and-proportions-4db014e080b1
5 | // license is CC-BY-4.0
6 | library FullMath {
7 | function fullMul(uint256 x, uint256 y)
8 | internal
9 | pure
10 | returns (uint256 l, uint256 h)
11 | {
12 | uint256 mm = mulmod(x, y, type(uint256).max);
13 | l = x * y;
14 | h = mm - l;
15 | if (mm < l) h -= 1;
16 | }
17 |
18 | function fullDiv(
19 | uint256 l,
20 | uint256 h,
21 | uint256 d
22 | ) private pure returns (uint256) {
23 | uint256 pow2 = d & uint256(-int256(d));
24 | d /= pow2;
25 | l /= pow2;
26 | l += h * (uint256(-int256(pow2)) / pow2 + 1);
27 | uint256 r = 1;
28 | r *= 2 - d * r;
29 | r *= 2 - d * r;
30 | r *= 2 - d * r;
31 | r *= 2 - d * r;
32 | r *= 2 - d * r;
33 | r *= 2 - d * r;
34 | r *= 2 - d * r;
35 | r *= 2 - d * r;
36 | return l * r;
37 | }
38 |
39 | function mulDiv(
40 | uint256 x,
41 | uint256 y,
42 | uint256 d
43 | ) internal pure returns (uint256) {
44 | (uint256 l, uint256 h) = fullMul(x, y);
45 |
46 | uint256 mm = mulmod(x, y, d);
47 | if (mm > l) h -= 1;
48 | l -= mm;
49 |
50 | if (h == 0) return l / d;
51 |
52 | require(h < d, "FullMath: FULLDIV_OVERFLOW");
53 | return fullDiv(l, h, d);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/contracts/external/libraries/TransferHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
6 | library TransferHelper {
7 | function safeApprove(
8 | address token,
9 | address to,
10 | uint256 value
11 | ) internal {
12 | // bytes4(keccak256(bytes('approve(address,uint256)')));
13 | (bool success, bytes memory data) = token.call(
14 | abi.encodeWithSelector(0x095ea7b3, to, value)
15 | );
16 | require(
17 | success && (data.length == 0 || abi.decode(data, (bool))),
18 | "TransferHelper::safeApprove: approve failed"
19 | );
20 | }
21 |
22 | function safeTransfer(
23 | address token,
24 | address to,
25 | uint256 value
26 | ) internal {
27 | // bytes4(keccak256(bytes('transfer(address,uint256)')));
28 | (bool success, bytes memory data) = token.call(
29 | abi.encodeWithSelector(0xa9059cbb, to, value)
30 | );
31 | require(
32 | success && (data.length == 0 || abi.decode(data, (bool))),
33 | "TransferHelper::safeTransfer: transfer failed"
34 | );
35 | }
36 |
37 | function safeTransferFrom(
38 | address token,
39 | address from,
40 | address to,
41 | uint256 value
42 | ) internal {
43 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
44 | (bool success, bytes memory data) = token.call(
45 | abi.encodeWithSelector(0x23b872dd, from, to, value)
46 | );
47 | require(
48 | success && (data.length == 0 || abi.decode(data, (bool))),
49 | "TransferHelper::transferFrom: transferFrom failed"
50 | );
51 | }
52 |
53 | function safeTransferETH(address to, uint256 value) internal {
54 | (bool success, ) = to.call{value: value}(new bytes(0));
55 | require(
56 | success,
57 | "TransferHelper::safeTransferETH: ETH transfer failed"
58 | );
59 | }
60 | }
--------------------------------------------------------------------------------
/contracts/external/libraries/UQ112x112.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
6 |
7 | // range: [0, 2**112 - 1]
8 | // resolution: 1 / 2**112
9 |
10 | library UQ112x112 {
11 | uint224 constant Q112 = 2**112;
12 |
13 | // encode a uint112 as a UQ112x112
14 | function encode(uint112 y) internal pure returns (uint224 z) {
15 | z = uint224(y) * Q112; // never overflows
16 | }
17 |
18 | // divide a UQ112x112 by a uint112, returning a UQ112x112
19 | function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
20 | z = x / uint224(y);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/contracts/external/libraries/UniswapMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | // a library for performing various math operations
6 |
7 | library UniswapMath {
8 | function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
9 | z = x < y ? x : y;
10 | }
11 |
12 | // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
13 | function sqrt(uint256 y) internal pure returns (uint256 z) {
14 | if (y > 3) {
15 | z = y;
16 | uint256 x = y / 2 + 1;
17 | while (x < z) {
18 | z = x;
19 | x = (y / x + x) / 2;
20 | }
21 | } else if (y != 0) {
22 | z = 1;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/contracts/external/libraries/UniswapV2Library.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/utils/math/SafeMath.sol";
6 |
7 | import "../../interfaces/external/uniswap/IUniswapV2Pair.sol";
8 |
9 | library UniswapV2Library {
10 | using SafeMath for uint256;
11 |
12 | // returns sorted token addresses, used to handle return values from pairs sorted in this order
13 | function sortTokens(address tokenA, address tokenB)
14 | internal
15 | pure
16 | returns (address token0, address token1)
17 | {
18 | require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
19 | (token0, token1) = tokenA < tokenB
20 | ? (tokenA, tokenB)
21 | : (tokenB, tokenA);
22 | require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
23 | }
24 |
25 | // calculates the CREATE2 address for a pair without making any external calls
26 | function pairFor(
27 | address factory,
28 | address tokenA,
29 | address tokenB
30 | ) internal pure returns (address pair) {
31 | (address token0, address token1) = sortTokens(tokenA, tokenB);
32 | unchecked {
33 | pair = address(
34 | uint160(
35 | uint256(
36 | keccak256(
37 | abi.encodePacked(
38 | hex"ff",
39 | factory,
40 | keccak256(abi.encodePacked(token0, token1)),
41 | hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
42 | )
43 | )
44 | )
45 | )
46 | );
47 | }
48 | }
49 |
50 | // fetches and sorts the reserves for a pair
51 | function getReserves(
52 | address factory,
53 | address tokenA,
54 | address tokenB
55 | ) internal view returns (uint256 reserveA, uint256 reserveB) {
56 | (address token0, ) = sortTokens(tokenA, tokenB);
57 | (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
58 | pairFor(factory, tokenA, tokenB)
59 | ).getReserves();
60 | (reserveA, reserveB) = tokenA == token0
61 | ? (reserve0, reserve1)
62 | : (reserve1, reserve0);
63 | }
64 |
65 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
66 | function quote(
67 | uint256 amountA,
68 | uint256 reserveA,
69 | uint256 reserveB
70 | ) internal pure returns (uint256 amountB) {
71 | require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT");
72 | require(
73 | reserveA > 0 && reserveB > 0,
74 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
75 | );
76 | amountB = amountA.mul(reserveB) / reserveA;
77 | }
78 |
79 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
80 | function getAmountOut(
81 | uint256 amountIn,
82 | uint256 reserveIn,
83 | uint256 reserveOut
84 | ) internal pure returns (uint256 amountOut) {
85 | require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
86 | require(
87 | reserveIn > 0 && reserveOut > 0,
88 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
89 | );
90 | uint256 amountInWithFee = amountIn.mul(997);
91 | uint256 numerator = amountInWithFee.mul(reserveOut);
92 | uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
93 | amountOut = numerator / denominator;
94 | }
95 |
96 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
97 | function getAmountIn(
98 | uint256 amountOut,
99 | uint256 reserveIn,
100 | uint256 reserveOut
101 | ) internal pure returns (uint256 amountIn) {
102 | require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
103 | require(
104 | reserveIn > 0 && reserveOut > 0,
105 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
106 | );
107 | uint256 numerator = reserveIn.mul(amountOut).mul(1000);
108 | uint256 denominator = reserveOut.sub(amountOut).mul(997);
109 | amountIn = (numerator / denominator).add(1);
110 | }
111 |
112 | // performs chained getAmountOut calculations on any number of pairs
113 | function getAmountsOut(
114 | address factory,
115 | uint256 amountIn,
116 | address[] memory path
117 | ) internal view returns (uint256[] memory amounts) {
118 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
119 | amounts = new uint256[](path.length);
120 | amounts[0] = amountIn;
121 | for (uint256 i; i < path.length - 1; i++) {
122 | (uint256 reserveIn, uint256 reserveOut) = getReserves(
123 | factory,
124 | path[i],
125 | path[i + 1]
126 | );
127 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
128 | }
129 | }
130 |
131 | // performs chained getAmountIn calculations on any number of pairs
132 | function getAmountsIn(
133 | address factory,
134 | uint256 amountOut,
135 | address[] memory path
136 | ) internal view returns (uint256[] memory amounts) {
137 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
138 | amounts = new uint256[](path.length);
139 | amounts[amounts.length - 1] = amountOut;
140 | for (uint256 i = path.length - 1; i > 0; i--) {
141 | (uint256 reserveIn, uint256 reserveOut) = getReserves(
142 | factory,
143 | path[i - 1],
144 | path[i]
145 | );
146 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/contracts/external/libraries/UniswapV2OracleLibrary.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../../interfaces/external/uniswap/IUniswapV2Pair.sol";
6 | import "./FixedPoint.sol";
7 |
8 | // library with helper methods for oracles that are concerned with computing average prices
9 | library UniswapV2OracleLibrary {
10 | using FixedPoint for *;
11 |
12 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
13 | function currentBlockTimestamp() internal view returns (uint32) {
14 | return uint32(block.timestamp % 2**32);
15 | }
16 |
17 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
18 | function currentCumulativePrices(address pair)
19 | internal
20 | view
21 | returns (
22 | uint256 price0Cumulative,
23 | uint256 price1Cumulative,
24 | uint32 blockTimestamp
25 | )
26 | {
27 | blockTimestamp = currentBlockTimestamp();
28 | price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
29 | price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
30 |
31 | // if time has elapsed since the last update on the pair, mock the accumulated price values
32 | (
33 | uint112 reserve0,
34 | uint112 reserve1,
35 | uint32 blockTimestampLast
36 | ) = IUniswapV2Pair(pair).getReserves();
37 | if (blockTimestampLast != blockTimestamp) {
38 | // subtraction overflow is desired
39 | uint32 timeElapsed = blockTimestamp - blockTimestampLast;
40 | // addition overflow is desired
41 | // counterfactual
42 | price0Cumulative +=
43 | uint256(FixedPoint.fraction(reserve1, reserve0)._x) *
44 | timeElapsed;
45 | // counterfactual
46 | price1Cumulative +=
47 | uint256(FixedPoint.fraction(reserve0, reserve1)._x) *
48 | timeElapsed;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/pool/IBasePoolV2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6 |
7 | interface IBasePoolV2 {
8 | /* ========== STRUCTS ========== */
9 |
10 | struct Position {
11 | IERC20 foreignAsset;
12 | uint256 creation;
13 | uint256 liquidity;
14 | uint256 originalNative;
15 | uint256 originalForeign;
16 | }
17 |
18 | struct PairInfo {
19 | uint256 totalSupply;
20 | uint112 reserveNative;
21 | uint112 reserveForeign;
22 | uint32 blockTimestampLast;
23 | PriceCumulative priceCumulative;
24 | }
25 |
26 | struct PriceCumulative {
27 | uint256 nativeLast;
28 | uint256 foreignLast;
29 | }
30 |
31 | /* ========== FUNCTIONS ========== */
32 |
33 | function getReserves(
34 | IERC20 foreignAsset
35 | ) external view returns (
36 | uint112 reserve0,
37 | uint112 reserve1,
38 | uint32 blockTimestampLast
39 | );
40 |
41 | function nativeAsset() external view returns (IERC20);
42 |
43 | function supported(IERC20 token) external view returns (bool);
44 |
45 | function positionForeignAsset(uint256 id) external view returns (IERC20);
46 |
47 | function pairSupply(IERC20 foreignAsset) external view returns (uint256);
48 |
49 | function doubleSwap(
50 | IERC20 foreignAssetA,
51 | IERC20 foreignAssetB,
52 | uint256 foreignAmountIn,
53 | address to
54 | ) external returns (uint256);
55 |
56 | function swap(
57 | IERC20 foreignAsset,
58 | uint256 nativeAmountIn,
59 | uint256 foreignAmountIn,
60 | address to
61 | ) external returns (uint256);
62 |
63 | function mint(
64 | IERC20 foreignAsset,
65 | uint256 nativeDeposit,
66 | uint256 foreignDeposit,
67 | address from,
68 | address to
69 | ) external returns (uint256 liquidity);
70 |
71 | /* ========== EVENTS ========== */
72 |
73 | event Mint(
74 | address indexed sender,
75 | address indexed to,
76 | uint256 amount0,
77 | uint256 amount1
78 | ); // Adjustment -> new argument to which didnt exist
79 | event Burn(
80 | address indexed sender,
81 | uint256 amount0,
82 | uint256 amount1,
83 | address indexed to
84 | );
85 | event Swap(
86 | IERC20 foreignAsset,
87 | address indexed sender,
88 | uint256 amount0In,
89 | uint256 amount1In,
90 | uint256 amount0Out,
91 | uint256 amount1Out,
92 | address indexed to
93 | );
94 | event Sync(IERC20 foreignAsset, uint256 reserve0, uint256 reserve1); // Adjustment -> 112 to 256
95 | event PositionOpened(
96 | address indexed from,
97 | address indexed to,
98 | uint256 id,
99 | uint256 liquidity
100 | );
101 | event PositionClosed(
102 | address indexed sender,
103 | uint256 id,
104 | uint256 liquidity,
105 | uint256 loss
106 | );
107 | }
108 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/pool/IVaderPoolFactoryV2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "./IVaderPoolV2.sol";
6 |
7 | interface IVaderPoolFactoryV2 {
8 | /* ========== STRUCTS ========== */
9 |
10 | /* ========== FUNCTIONS ========== */
11 |
12 | function createPool(address tokenA, address tokenB)
13 | external
14 | returns (IVaderPoolV2);
15 |
16 | function getPool(address tokenA, address tokenB)
17 | external
18 | returns (IVaderPoolV2);
19 |
20 | function nativeAsset() external view returns (address);
21 |
22 | /* ========== EVENTS ========== */
23 |
24 | event PoolCreated(
25 | address token0,
26 | address token1,
27 | IVaderPoolV2 pool,
28 | uint256 totalPools
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/pool/IVaderPoolV2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7 |
8 | import "./IBasePoolV2.sol";
9 |
10 | interface IVaderPoolV2 is IBasePoolV2, IERC721 {
11 | /* ========== STRUCTS ========== */
12 | /* ========== FUNCTIONS ========== */
13 |
14 | function cumulativePrices(IERC20 foreignAsset)
15 | external
16 | view
17 | returns (
18 | uint256 price0CumulativeLast,
19 | uint256 price1CumulativeLast,
20 | uint32 blockTimestampLast
21 | );
22 |
23 | function mintFungible(
24 | IERC20 foreignAsset,
25 | uint256 nativeDeposit,
26 | uint256 foreignDeposit,
27 | address from,
28 | address to
29 | ) external returns (uint256 liquidity);
30 |
31 | function burnFungible(
32 | IERC20 foreignAsset,
33 | uint256 liquidity,
34 | address to
35 | ) external returns (uint256 amountNative, uint256 amountForeign);
36 |
37 | function burn(uint256 id, address to)
38 | external
39 | returns (
40 | uint256 amountNative,
41 | uint256 amountForeign,
42 | uint256 coveredLoss
43 | );
44 |
45 | function setQueue(bool _queueActive) external;
46 |
47 | function setTokenSupport(
48 | IERC20 foreignAsset,
49 | bool support,
50 | uint256 nativeDeposit,
51 | uint256 foreignDeposit,
52 | address from,
53 | address to
54 | ) external returns (uint256 liquidity);
55 |
56 | function setFungibleTokenSupport(IERC20 foreignAsset) external;
57 |
58 | function setGasThrottle(bool _gasThrottleEnabled) external;
59 |
60 | /* ========== EVENTS ========== */
61 |
62 | event QueueActive(bool activated);
63 | }
64 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/router/IVaderRouterV2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6 |
7 | interface IVaderRouterV2 {
8 | /* ========== STRUCTS ========== */
9 | /* ========== FUNCTIONS ========== */
10 |
11 | function addLiquidity(
12 | IERC20 tokenA,
13 | IERC20 tokenB,
14 | uint256 amountADesired,
15 | uint256 amountBDesired,
16 | uint256, // amountAMin = unused
17 | uint256, // amountBMin = unused
18 | address to,
19 | uint256 deadline
20 | ) external returns (uint256 liquidity);
21 |
22 | function addLiquidity(
23 | IERC20 tokenA,
24 | IERC20 tokenB,
25 | uint256 amountADesired,
26 | uint256 amountBDesired,
27 | address to,
28 | uint256 deadline
29 | ) external returns (uint256 liquidity);
30 |
31 | function removeLiquidity(
32 | address tokenA,
33 | address tokenB,
34 | uint256 id,
35 | uint256 amountAMin,
36 | uint256 amountBMin,
37 | address to,
38 | uint256 deadline
39 | ) external returns (uint256 amountA, uint256 amountB);
40 |
41 | function swapExactTokensForTokens(
42 | uint256 amountIn,
43 | uint256 amountOutMin,
44 | IERC20[] calldata path,
45 | address to,
46 | uint256 deadline
47 | ) external returns (uint256 amountOut);
48 |
49 | /* ========== EVENTS ========== */
50 | }
51 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/wrapper/ILPToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../../shared/IERC20Extended.sol";
6 |
7 | interface ILPToken {
8 | function mint(address to, uint256 amount) external;
9 |
10 | function burn(uint256 amount) external;
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex-v2/wrapper/ILPWrapper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../../shared/IERC20Extended.sol";
6 |
7 | interface ILPWrapper {
8 | function tokens(IERC20 foreignAsset) external view returns (IERC20Extended);
9 |
10 | function createWrapper(IERC20 foreignAsset) external;
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/pool/IBasePool.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IBasePool {
6 | /* ========== STRUCTS ========== */
7 |
8 | struct Position {
9 | uint256 creation;
10 | uint256 liquidity;
11 | uint256 originalNative;
12 | uint256 originalForeign;
13 | }
14 |
15 | /* ========== FUNCTIONS ========== */
16 |
17 | function swap(
18 | uint256 nativeAmountIn,
19 | uint256 foreignAmountIn,
20 | address to
21 | ) external returns (uint256);
22 |
23 | function swap(
24 | uint256 nativeAmountIn,
25 | uint256 foreignAmountIn,
26 | address to,
27 | bytes calldata
28 | ) external returns (uint256);
29 |
30 | function mint(address to) external returns (uint256 liquidity);
31 |
32 | function getReserves() external view returns (
33 | uint112 reserveNative,
34 | uint112 reserveForeign,
35 | uint32 blockTimestampLast
36 | );
37 |
38 | /* ========== EVENTS ========== */
39 |
40 | event Mint(
41 | address indexed sender,
42 | address indexed to,
43 | uint256 amount0,
44 | uint256 amount1
45 | ); // Adjustment -> new argument to which didnt exist
46 | event Burn(
47 | address indexed sender,
48 | uint256 amount0,
49 | uint256 amount1,
50 | address indexed to
51 | );
52 | event Swap(
53 | address indexed sender,
54 | uint256 amount0In,
55 | uint256 amount1In,
56 | uint256 amount0Out,
57 | uint256 amount1Out,
58 | address indexed to
59 | );
60 | event Sync(uint256 reserve0, uint256 reserve1); // Adjustment -> 112 to 256
61 | event PositionOpened(address indexed sender, uint256 id, uint256 liquidity);
62 | event PositionClosed(
63 | address indexed sender,
64 | uint256 id,
65 | uint256 liquidity,
66 | uint256 loss
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/pool/IVaderPool.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6 |
7 | import "./IBasePool.sol";
8 |
9 | interface IVaderPool is IBasePool, IERC721 {
10 | /* ========== STRUCTS ========== */
11 | /* ========== FUNCTIONS ========== */
12 |
13 | function burn(uint256 id, address to)
14 | external
15 | returns (
16 | uint256 amountNative,
17 | uint256 amountForeign,
18 | uint256 coveredLoss
19 | );
20 |
21 | function toggleQueue() external;
22 |
23 | /* ========== EVENTS ========== */
24 |
25 | event QueueActive(bool activated);
26 | }
27 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/pool/IVaderPoolFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "./IVaderPool.sol";
6 |
7 | interface IVaderPoolFactory {
8 | /* ========== STRUCTS ========== */
9 |
10 | /* ========== FUNCTIONS ========== */
11 |
12 | function createPool(address tokenA, address tokenB)
13 | external
14 | returns (IVaderPool);
15 |
16 | function getPool(address tokenA, address tokenB)
17 | external
18 | view
19 | returns (IVaderPool);
20 |
21 | function nativeAsset() external view returns (address);
22 |
23 | /* ========== EVENTS ========== */
24 |
25 | event PoolCreated(
26 | address token0,
27 | address token1,
28 | IVaderPool pool,
29 | uint256 index
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/queue/IGasQueue.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IGasQueue {
6 | /* ========== STRUCTS ========== */
7 | /* ========== FUNCTIONS ========== */
8 | /* ========== EVENTS ========== */
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/queue/ISwapQueue.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface ISwapQueue {
6 | /* ========== STRUCTS ========== */
7 |
8 | struct Node {
9 | uint256 value;
10 | uint256 previous;
11 | uint256 next;
12 | }
13 |
14 | struct Queue {
15 | mapping(uint256 => Node) linkedList;
16 | uint256 start;
17 | uint256 end;
18 | uint256 size;
19 | }
20 |
21 | /* ========== FUNCTIONS ========== */
22 | /* ========== EVENTS ========== */
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/interfaces/dex/router/IVaderRouter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6 |
7 | interface IVaderRouter {
8 | /* ========== STRUCTS ========== */
9 | /* ========== FUNCTIONS ========== */
10 |
11 | function addLiquidity(
12 | IERC20 tokenA,
13 | IERC20 tokenB,
14 | uint256 amountADesired,
15 | uint256 amountBDesired,
16 | uint256, // amountAMin = unused
17 | uint256, // amountBMin = unused
18 | address to,
19 | uint256 deadline
20 | )
21 | external
22 | returns (
23 | uint256 amountA,
24 | uint256 amountB,
25 | uint256 liquidity
26 | );
27 |
28 | function addLiquidity(
29 | IERC20 tokenA,
30 | IERC20 tokenB,
31 | uint256 amountADesired,
32 | uint256 amountBDesired,
33 | address to,
34 | uint256 deadline
35 | )
36 | external
37 | returns (
38 | uint256 amountA,
39 | uint256 amountB,
40 | uint256 liquidity
41 | );
42 |
43 | function removeLiquidity(
44 | address tokenA,
45 | address tokenB,
46 | uint256 id,
47 | uint256 amountAMin,
48 | uint256 amountBMin,
49 | address to,
50 | uint256 deadline
51 | ) external returns (uint256 amountA, uint256 amountB);
52 |
53 | function swapExactTokensForTokens(
54 | uint256 amountIn,
55 | uint256 amountOutMin,
56 | address[] calldata path,
57 | address to,
58 | uint256 deadline
59 | ) external returns (uint256 amountOut);
60 |
61 | /* ========== EVENTS ========== */
62 | }
63 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/chainlink/IAggregator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IAggregator {
6 | function latestAnswer() external view returns (int256);
7 | }
8 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/chainlink/IAggregatorV3.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity ^0.8.0;
3 |
4 | interface IAggregatorV3 {
5 | function decimals() external view returns (uint8);
6 |
7 | function description() external view returns (string memory);
8 |
9 | function version() external view returns (uint256);
10 |
11 | // getRoundData and latestRoundData should both raise "No data present"
12 | // if they do not have data to report, instead of returning unset values
13 | // which could be misinterpreted as actual reported values.
14 | function getRoundData(uint80 _roundId)
15 | external
16 | view
17 | returns (
18 | uint80 roundId,
19 | int256 answer,
20 | uint256 startedAt,
21 | uint256 updatedAt,
22 | uint80 answeredInRound
23 | );
24 |
25 | function latestRoundData()
26 | external
27 | view
28 | returns (
29 | uint80 roundId,
30 | int256 answer,
31 | uint256 startedAt,
32 | uint256 updatedAt,
33 | uint80 answeredInRound
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2Callee.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.5.0;
3 |
4 | interface IUniswapV2Callee {
5 | function uniswapV2Call(
6 | address sender,
7 | uint256 amount0,
8 | uint256 amount1,
9 | bytes calldata data
10 | ) external;
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2ERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | interface IUniswapV2ERC20 {
5 | event Approval(
6 | address indexed owner,
7 | address indexed spender,
8 | uint256 value
9 | );
10 | event Transfer(address indexed from, address indexed to, uint256 value);
11 |
12 | function name() external pure returns (string memory);
13 |
14 | function symbol() external pure returns (string memory);
15 |
16 | function decimals() external pure returns (uint8);
17 |
18 | function totalSupply() external view returns (uint256);
19 |
20 | function balanceOf(address owner) external view returns (uint256);
21 |
22 | function allowance(address owner, address spender)
23 | external
24 | view
25 | returns (uint256);
26 |
27 | function approve(address spender, uint256 value) external returns (bool);
28 |
29 | function transfer(address to, uint256 value) external returns (bool);
30 |
31 | function transferFrom(
32 | address from,
33 | address to,
34 | uint256 value
35 | ) external returns (bool);
36 |
37 | function DOMAIN_SEPARATOR() external view returns (bytes32);
38 |
39 | function PERMIT_TYPEHASH() external pure returns (bytes32);
40 |
41 | function nonces(address owner) external view returns (uint256);
42 |
43 | function permit(
44 | address owner,
45 | address spender,
46 | uint256 value,
47 | uint256 deadline,
48 | uint8 v,
49 | bytes32 r,
50 | bytes32 s
51 | ) external;
52 | }
53 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2Factory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.5.0;
3 |
4 | interface IUniswapV2Factory {
5 | event PairCreated(
6 | address indexed token0,
7 | address indexed token1,
8 | address pair,
9 | uint256
10 | );
11 |
12 | function feeTo() external view returns (address);
13 |
14 | function feeToSetter() external view returns (address);
15 |
16 | function getPair(address tokenA, address tokenB)
17 | external
18 | view
19 | returns (address pair);
20 |
21 | function allPairs(uint256) external view returns (address pair);
22 |
23 | function allPairsLength() external view returns (uint256);
24 |
25 | function createPair(address tokenA, address tokenB)
26 | external
27 | returns (address pair);
28 |
29 | function setFeeTo(address) external;
30 |
31 | function setFeeToSetter(address) external;
32 | }
33 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2Pair.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "./IUniswapV2ERC20.sol";
5 |
6 | interface IUniswapV2Pair is IUniswapV2ERC20 {
7 | event Mint(address indexed sender, uint256 amount0, uint256 amount1);
8 | event Burn(
9 | address indexed sender,
10 | uint256 amount0,
11 | uint256 amount1,
12 | address indexed to
13 | );
14 | event Swap(
15 | address indexed sender,
16 | uint256 amount0In,
17 | uint256 amount1In,
18 | uint256 amount0Out,
19 | uint256 amount1Out,
20 | address indexed to
21 | );
22 | event Sync(uint112 reserve0, uint112 reserve1);
23 |
24 | function MINIMUM_LIQUIDITY() external pure returns (uint256);
25 |
26 | function factory() external view returns (address);
27 |
28 | function token0() external view returns (address);
29 |
30 | function token1() external view returns (address);
31 |
32 | function getReserves()
33 | external
34 | view
35 | returns (
36 | uint112 reserve0,
37 | uint112 reserve1,
38 | uint32 blockTimestampLast
39 | );
40 |
41 | function price0CumulativeLast() external view returns (uint256);
42 |
43 | function price1CumulativeLast() external view returns (uint256);
44 |
45 | function kLast() external view returns (uint256);
46 |
47 | function mint(address to) external returns (uint256 liquidity);
48 |
49 | function burn(address to)
50 | external
51 | returns (uint256 amount0, uint256 amount1);
52 |
53 | function swap(
54 | uint256 amount0Out,
55 | uint256 amount1Out,
56 | address to,
57 | bytes calldata data
58 | ) external;
59 |
60 | function skim(address to) external;
61 |
62 | function sync() external;
63 |
64 | function initialize(address, address) external;
65 | }
66 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2Router01.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.6.2;
3 |
4 | interface IUniswapV2Router01 {
5 | function factory() external view returns (address);
6 |
7 | function WETH() external view returns (address);
8 |
9 | function addLiquidity(
10 | address tokenA,
11 | address tokenB,
12 | uint256 amountADesired,
13 | uint256 amountBDesired,
14 | uint256 amountAMin,
15 | uint256 amountBMin,
16 | address to,
17 | uint256 deadline
18 | )
19 | external
20 | returns (
21 | uint256 amountA,
22 | uint256 amountB,
23 | uint256 liquidity
24 | );
25 |
26 | function addLiquidityETH(
27 | address token,
28 | uint256 amountTokenDesired,
29 | uint256 amountTokenMin,
30 | uint256 amountETHMin,
31 | address to,
32 | uint256 deadline
33 | )
34 | external
35 | payable
36 | returns (
37 | uint256 amountToken,
38 | uint256 amountETH,
39 | uint256 liquidity
40 | );
41 |
42 | function removeLiquidity(
43 | address tokenA,
44 | address tokenB,
45 | uint256 liquidity,
46 | uint256 amountAMin,
47 | uint256 amountBMin,
48 | address to,
49 | uint256 deadline
50 | ) external returns (uint256 amountA, uint256 amountB);
51 |
52 | function removeLiquidityETH(
53 | address token,
54 | uint256 liquidity,
55 | uint256 amountTokenMin,
56 | uint256 amountETHMin,
57 | address to,
58 | uint256 deadline
59 | ) external returns (uint256 amountToken, uint256 amountETH);
60 |
61 | function removeLiquidityWithPermit(
62 | address tokenA,
63 | address tokenB,
64 | uint256 liquidity,
65 | uint256 amountAMin,
66 | uint256 amountBMin,
67 | address to,
68 | uint256 deadline,
69 | bool approveMax,
70 | uint8 v,
71 | bytes32 r,
72 | bytes32 s
73 | ) external returns (uint256 amountA, uint256 amountB);
74 |
75 | function removeLiquidityETHWithPermit(
76 | address token,
77 | uint256 liquidity,
78 | uint256 amountTokenMin,
79 | uint256 amountETHMin,
80 | address to,
81 | uint256 deadline,
82 | bool approveMax,
83 | uint8 v,
84 | bytes32 r,
85 | bytes32 s
86 | ) external returns (uint256 amountToken, uint256 amountETH);
87 |
88 | function swapExactTokensForTokens(
89 | uint256 amountIn,
90 | uint256 amountOutMin,
91 | address[] calldata path,
92 | address to,
93 | uint256 deadline
94 | ) external returns (uint256[] memory amounts);
95 |
96 | function swapTokensForExactTokens(
97 | uint256 amountOut,
98 | uint256 amountInMax,
99 | address[] calldata path,
100 | address to,
101 | uint256 deadline
102 | ) external returns (uint256[] memory amounts);
103 |
104 | function swapExactETHForTokens(
105 | uint256 amountOutMin,
106 | address[] calldata path,
107 | address to,
108 | uint256 deadline
109 | ) external payable returns (uint256[] memory amounts);
110 |
111 | function swapTokensForExactETH(
112 | uint256 amountOut,
113 | uint256 amountInMax,
114 | address[] calldata path,
115 | address to,
116 | uint256 deadline
117 | ) external returns (uint256[] memory amounts);
118 |
119 | function swapExactTokensForETH(
120 | uint256 amountIn,
121 | uint256 amountOutMin,
122 | address[] calldata path,
123 | address to,
124 | uint256 deadline
125 | ) external returns (uint256[] memory amounts);
126 |
127 | function swapETHForExactTokens(
128 | uint256 amountOut,
129 | address[] calldata path,
130 | address to,
131 | uint256 deadline
132 | ) external payable returns (uint256[] memory amounts);
133 |
134 | function quote(
135 | uint256 amountA,
136 | uint256 reserveA,
137 | uint256 reserveB
138 | ) external pure returns (uint256 amountB);
139 |
140 | function getAmountOut(
141 | uint256 amountIn,
142 | uint256 reserveIn,
143 | uint256 reserveOut
144 | ) external pure returns (uint256 amountOut);
145 |
146 | function getAmountIn(
147 | uint256 amountOut,
148 | uint256 reserveIn,
149 | uint256 reserveOut
150 | ) external pure returns (uint256 amountIn);
151 |
152 | function getAmountsOut(uint256 amountIn, address[] calldata path)
153 | external
154 | view
155 | returns (uint256[] memory amounts);
156 |
157 | function getAmountsIn(uint256 amountOut, address[] calldata path)
158 | external
159 | view
160 | returns (uint256[] memory amounts);
161 | }
162 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/uniswap/IUniswapV2Router02.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.6.2;
3 |
4 | import "./IUniswapV2Router01.sol";
5 |
6 | interface IUniswapV2Router02 is IUniswapV2Router01 {
7 | function removeLiquidityETHSupportingFeeOnTransferTokens(
8 | address token,
9 | uint256 liquidity,
10 | uint256 amountTokenMin,
11 | uint256 amountETHMin,
12 | address to,
13 | uint256 deadline
14 | ) external returns (uint256 amountETH);
15 |
16 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
17 | address token,
18 | uint256 liquidity,
19 | uint256 amountTokenMin,
20 | uint256 amountETHMin,
21 | address to,
22 | uint256 deadline,
23 | bool approveMax,
24 | uint8 v,
25 | bytes32 r,
26 | bytes32 s
27 | ) external returns (uint256 amountETH);
28 |
29 | function swapExactTokensForTokensSupportingFeeOnTransferTokens(
30 | uint256 amountIn,
31 | uint256 amountOutMin,
32 | address[] calldata path,
33 | address to,
34 | uint256 deadline
35 | ) external;
36 |
37 | function swapExactETHForTokensSupportingFeeOnTransferTokens(
38 | uint256 amountOutMin,
39 | address[] calldata path,
40 | address to,
41 | uint256 deadline
42 | ) external payable;
43 |
44 | function swapExactTokensForETHSupportingFeeOnTransferTokens(
45 | uint256 amountIn,
46 | uint256 amountOutMin,
47 | address[] calldata path,
48 | address to,
49 | uint256 deadline
50 | ) external;
51 | }
52 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/weth/IWETH.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity >=0.5.0;
3 |
4 | interface IWETH {
5 | function deposit() external payable;
6 |
7 | function transfer(address to, uint256 value) external returns (bool);
8 |
9 | function withdraw(uint256) external;
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/interfaces/governance/ITimelock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface ITimelock {
6 | function delay() external view returns (uint256);
7 |
8 | function GRACE_PERIOD() external pure returns (uint256);
9 |
10 | function acceptAdmin() external;
11 |
12 | function queuedTransactions(bytes32 hash) external view returns (bool);
13 |
14 | function queueTransaction(
15 | address target,
16 | uint256 value,
17 | string calldata signature,
18 | bytes calldata data,
19 | uint256 eta
20 | ) external returns (bytes32);
21 |
22 | function cancelTransaction(
23 | address target,
24 | uint256 value,
25 | string calldata signature,
26 | bytes calldata data,
27 | uint256 eta
28 | ) external;
29 |
30 | function executeTransaction(
31 | address target,
32 | uint256 value,
33 | string calldata signature,
34 | bytes calldata data,
35 | uint256 eta
36 | ) external payable returns (bytes memory);
37 | }
38 |
--------------------------------------------------------------------------------
/contracts/interfaces/lbt/ILiquidityBasedTWAP.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../../external/libraries/FixedPoint.sol";
6 |
7 | interface ILiquidityBasedTWAP {
8 | /* ========== STRUCTS ========== */
9 |
10 | struct ExchangePair {
11 | uint256 nativeTokenPriceCumulative;
12 | FixedPoint.uq112x112 nativeTokenPriceAverage;
13 | uint256 lastMeasurement;
14 | uint256 updatePeriod;
15 | uint256 pastLiquidityEvaluation;
16 | address foreignAsset;
17 | uint96 foreignUnit;
18 | }
19 |
20 | enum Paths {
21 | VADER,
22 | USDV
23 | }
24 |
25 | /* ========== FUNCTIONS ========== */
26 |
27 | function previousPrices(uint256 i) external returns (uint256);
28 |
29 | function maxUpdateWindow() external view returns (uint256);
30 |
31 | function getVaderPrice() external returns (uint256);
32 |
33 | function syncVaderPrice()
34 | external
35 | returns (
36 | uint256[] memory pastLiquidityWeights,
37 | uint256 pastTotalLiquidityWeight
38 | );
39 |
40 | function getUSDVPrice() external returns (uint256);
41 |
42 | function syncUSDVPrice()
43 | external
44 | returns (
45 | uint256[] memory pastLiquidityWeights,
46 | uint256 pastTotalLiquidityWeight
47 | );
48 |
49 | /* ========== EVENTS ========== */
50 | }
51 |
--------------------------------------------------------------------------------
/contracts/interfaces/lbt/IUniswapTwap.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 |
3 | pragma solidity 0.8.9;
4 |
5 | interface IUniswapTWAP {
6 | function maxUpdateWindow() external view returns (uint);
7 |
8 | function getVaderPrice() external returns (uint);
9 |
10 | function syncVaderPrice() external;
11 | }
--------------------------------------------------------------------------------
/contracts/interfaces/lbt/minter/IVaderMinterUpgradeable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import {Limits} from "../../../lbt/minter/VaderMinterStorage.sol";
6 |
7 | interface IVaderMinterUpgradeable {
8 | /* ========== FUNCTIONS ========== */
9 | function mint(uint256 vAmount, uint256 uAmountMinOut)
10 | external
11 | returns (uint256 uAmount);
12 |
13 | function burn(uint256 uAmount, uint256 vAmountMinOut)
14 | external
15 | returns (uint256 vAmount);
16 |
17 | function partnerMint(uint256 vAmount, uint256 uAmountMinOut) external returns (uint256 uAmount);
18 |
19 | function partnerBurn(uint256 uAmount, uint256 vAmountMinOut) external returns (uint256 vAmount);
20 |
21 | /* ========== EVENTS ========== */
22 |
23 | event DailyLimitsChanged(Limits previousLimits, Limits nextLimits);
24 | event WhitelistPartner(
25 | address partner,
26 | uint256 mintLimit,
27 | uint256 burnLimit,
28 | uint256 fee
29 | );
30 | event RemovePartner(address partner);
31 | event SetPartnerFee(address indexed partner, uint fee);
32 | event IncreasePartnerMintLimit(address indexed partner, uint mintLimit);
33 | event DecreasePartnerMintLimit(address indexed partner, uint mintLimit);
34 | event IncreasePartnerBurnLimit(address indexed partner, uint burnLimit);
35 | event DecreasePartnerBurnLimit(address indexed partner, uint burnLimit);
36 | event SetPartnerLockDuration(address indexed partner, uint lockDuration);
37 | }
38 |
--------------------------------------------------------------------------------
/contracts/interfaces/reserve/IVaderReserve.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IVaderReserve {
6 | /* ========== STRUCTS ========== */
7 | /* ========== FUNCTIONS ========== */
8 |
9 | function reimburseImpermanentLoss(address recipient, uint256 amount)
10 | external;
11 |
12 | function grant(address recipient, uint256 amount) external;
13 |
14 | function reserve() external view returns (uint256);
15 |
16 | /* ========== EVENTS ========== */
17 |
18 | event GrantDistributed(address recipient, uint256 amount);
19 | event LossCovered(address recipient, uint256 amount, uint256 actualAmount);
20 | }
21 |
--------------------------------------------------------------------------------
/contracts/interfaces/shared/IERC20Extended.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6 |
7 | interface IERC20Extended is IERC20 {
8 | function name() external view returns (string memory);
9 |
10 | function symbol() external view returns (string memory);
11 |
12 | function mint(address to, uint256 amount) external;
13 |
14 | function burn(uint256 amount) external;
15 | }
16 |
--------------------------------------------------------------------------------
/contracts/interfaces/tokens/IUSDV.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IUSDV {
6 | /* ========== ENUMS ========== */
7 |
8 | enum LockTypes {
9 | USDV,
10 | VADER
11 | }
12 |
13 | /* ========== STRUCTS ========== */
14 |
15 | struct Lock {
16 | LockTypes token;
17 | uint256 amount;
18 | uint256 release;
19 | }
20 |
21 | /* ========== FUNCTIONS ========== */
22 |
23 | function mint(
24 | address account,
25 | uint256 vAmount,
26 | uint256 uAmount,
27 | uint256 exchangeFee,
28 | uint256 window
29 | ) external returns (uint256);
30 |
31 | function burn(
32 | address account,
33 | uint256 uAmount,
34 | uint256 vAmount,
35 | uint256 exchangeFee,
36 | uint256 window
37 | ) external returns (uint256);
38 |
39 | /* ========== EVENTS ========== */
40 |
41 | event ExchangeFeeChanged(uint256 previousExchangeFee, uint256 exchangeFee);
42 | event DailyLimitChanged(uint256 previousDailyLimit, uint256 dailyLimit);
43 | event LockClaimed(
44 | address user,
45 | LockTypes lockType,
46 | uint256 lockAmount,
47 | uint256 lockRelease
48 | );
49 | event LockCreated(
50 | address user,
51 | LockTypes lockType,
52 | uint256 lockAmount,
53 | uint256 lockRelease
54 | );
55 | event ValidatorSet(address previous, address current);
56 | event GuardianSet(address previous, address current);
57 | event LockStatusSet(bool status);
58 | event MinterSet(address minter);
59 | }
60 |
--------------------------------------------------------------------------------
/contracts/interfaces/tokens/IVader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IVader {
6 | /* ========== FUNCTIONS ========== */
7 |
8 | function createEmission(address user, uint256 amount) external;
9 |
10 | /* ========== EVENTS ========== */
11 |
12 | event Emission(address to, uint256 amount);
13 |
14 | event EmissionChanged(uint256 previous, uint256 next);
15 |
16 | event MaxSupplyChanged(uint256 previous, uint256 next);
17 |
18 | event GrantClaimed(address indexed beneficiary, uint256 amount);
19 |
20 | event ProtocolInitialized(address converter, address vest);
21 |
22 | event USDVSet(address usdv);
23 |
24 | /* ========== DEPRECATED ========== */
25 |
26 | // function getCurrentEraEmission() external view returns (uint256);
27 |
28 | // function getEraEmission(uint256 currentSupply)
29 | // external
30 | // view
31 | // returns (uint256);
32 |
33 | // function calculateFee() external view returns (uint256 basisPoints);
34 | }
35 |
--------------------------------------------------------------------------------
/contracts/interfaces/tokens/converter/IConverter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface IConverter {
6 | /* ========== FUNCTIONS ========== */
7 |
8 | function convert(bytes32[] calldata proof, uint256 amount, uint256 minVader)
9 | external
10 | returns (uint256 vaderReceived);
11 |
12 | /* ========== EVENTS ========== */
13 |
14 | event Conversion(
15 | address indexed user,
16 | uint256 vetherAmount,
17 | uint256 vaderAmount
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/contracts/interfaces/tokens/validator/IUnlockValidator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../IUSDV.sol";
6 |
7 | interface IUnlockValidator {
8 | function isValid(
9 | address user,
10 | uint256 amount,
11 | IUSDV.LockTypes lockType
12 | ) external view returns (bool);
13 |
14 | event Invalidate(address account);
15 | event Validate(address account);
16 | }
17 |
--------------------------------------------------------------------------------
/contracts/interfaces/tokens/vesting/ILinearVesting.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | interface ILinearVesting {
6 | /* ========== STRUCTS ========== */
7 |
8 | // Struct of a vesting member, tight-packed to 256-bits
9 | struct Vester {
10 | uint192 amount;
11 | uint64 lastClaim;
12 | uint128 start;
13 | uint128 end;
14 | }
15 |
16 | /* ========== FUNCTIONS ========== */
17 |
18 | function getClaim(address _vester)
19 | external
20 | view
21 | returns (uint256 vestedAmount);
22 |
23 | function claim() external returns (uint256 vestedAmount);
24 |
25 | // function claimConverted() external returns (uint256 vestedAmount);
26 |
27 | function begin(address[] calldata vesters, uint192[] calldata amounts)
28 | external;
29 |
30 | function vestFor(address user, uint256 amount) external;
31 |
32 | /* ========== EVENTS ========== */
33 |
34 | event VestingInitialized(uint256 duration);
35 |
36 | event VestingCreated(address user, uint256 amount);
37 |
38 | event Vested(address indexed from, uint256 amount);
39 | }
40 |
--------------------------------------------------------------------------------
/contracts/interfaces/x-vader/IXVader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5 |
6 | interface IXVader is IERC20 {
7 | function getPastVotes(address account, uint256 blockNumber)
8 | external
9 | view
10 | returns (uint256);
11 |
12 | function getPastTotalSupply(uint256 blockNumber)
13 | external
14 | view
15 | returns (uint256);
16 | }
17 |
--------------------------------------------------------------------------------
/contracts/lbt/minter/VaderMinterStorage.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity =0.8.9;
3 |
4 | import "../../interfaces/lbt/ILiquidityBasedTWAP.sol";
5 |
6 | struct Limits {
7 | uint256 fee;
8 | uint256 mintLimit;
9 | uint256 burnLimit;
10 | uint256 lockDuration;
11 | }
12 |
13 | contract VaderMinterStorage {
14 | // The LBT pricing mechanism for the conversion
15 | ILiquidityBasedTWAP public lbt;
16 |
17 | // The 24 hour limits on USDV mints that are available for public minting and burning as well as the fee.
18 | Limits public dailyLimits;
19 |
20 | // The current cycle end timestamp
21 | uint256 public cycleTimestamp;
22 |
23 | // The current cycle cumulative mints
24 | uint256 public cycleMints;
25 |
26 | // The current cycle cumulative burns
27 | uint256 public cycleBurns;
28 |
29 | // The limits applied to each partner
30 | mapping(address => Limits) public partnerLimits;
31 |
32 | // Transmuter Contract
33 | address public transmuter;
34 | }
--------------------------------------------------------------------------------
/contracts/mocks/Mock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | contract Mock {
6 | // Accept all calls
7 | fallback() external payable {}
8 | }
9 |
--------------------------------------------------------------------------------
/contracts/mocks/MockAggregatorV3.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | contract MockAggregatorV3 {
8 | IERC20 public token;
9 | int256 public mockPrice;
10 | uint80 private _storedRoundId;
11 |
12 | constructor(IERC20 _token, int256 _mockPrice) {
13 | token = _token;
14 | mockPrice = _mockPrice;
15 | }
16 |
17 | function decimals() external pure returns (uint8) {
18 | return 8;
19 | }
20 |
21 | function version() external pure returns (uint256) {
22 | return 3;
23 | }
24 |
25 | function getRoundData(uint80 _roundId)
26 | external
27 | view
28 | returns (
29 | uint80 roundId,
30 | int256 answer,
31 | uint256 startedAt,
32 | uint256 updatedAt,
33 | uint80 answeredInRound
34 | )
35 | {
36 | // Mock Data
37 | roundId = _roundId;
38 | answer = mockPrice;
39 | startedAt = block.timestamp;
40 | updatedAt = block.timestamp;
41 | answeredInRound = roundId;
42 | }
43 |
44 | function latestRoundData()
45 | external
46 | view
47 | returns (
48 | uint80 roundId,
49 | int256 answer,
50 | uint256 startedAt,
51 | uint256 updatedAt,
52 | uint80 answeredInRound
53 | )
54 | {
55 | // Mock Data
56 | roundId = _storedRoundId + 1;
57 | answer = mockPrice;
58 | startedAt = block.timestamp;
59 | updatedAt = block.timestamp;
60 | answeredInRound = roundId;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/contracts/mocks/MockConstants.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../shared/ProtocolConstants.sol";
6 |
7 | contract MockConstants is ProtocolConstants {
8 | // Max VADER supply
9 | uint256 public constant INITIAL_VADER_SUPPLY = _INITIAL_VADER_SUPPLY;
10 |
11 | // Allocation for VETH holders
12 | uint256 public constant VETH_ALLOCATION = _VETH_ALLOCATION;
13 |
14 | // Vader -> Vether Conversion Rate (10000:1)
15 | uint256 public constant VADER_VETHER_CONVERSION_RATE =
16 | _VADER_VETHER_CONVERSION_RATE;
17 |
18 | // Team allocation vested over {VESTING_DURATION} years
19 | uint256 public constant TEAM_ALLOCATION = _TEAM_ALLOCATION;
20 |
21 | // Total grant tokens
22 | uint256 public constant GRANT_ALLOCATION = _GRANT_ALLOCATION;
23 |
24 | // Ecosystem growth fund unlocked for partnerships & USDV provision
25 | uint256 public constant ECOSYSTEM_GROWTH = _ECOSYSTEM_GROWTH;
26 |
27 | // Emission Era
28 | uint256 public constant EMISSION_ERA = _EMISSION_ERA;
29 |
30 | // One year, utility
31 | uint256 public constant ONE_YEAR = _ONE_YEAR;
32 |
33 | // Initial Emission Curve, 5
34 | uint256 public constant INITIAL_EMISSION_CURVE = _INITIAL_EMISSION_CURVE;
35 |
36 | // Vesting Duration
37 | uint256 public constant VESTING_DURATION = _VESTING_DURATION;
38 |
39 | // Basis Points
40 | uint256 public constant MAX_BASIS_POINTS = _MAX_BASIS_POINTS;
41 |
42 | // Fee Basis Points
43 | uint256 public constant MAX_FEE_BASIS_POINTS = _MAX_FEE_BASIS_POINTS;
44 |
45 | // Burn Address
46 | address public constant BURN = _BURN;
47 | }
48 |
--------------------------------------------------------------------------------
/contracts/mocks/MockGovernorAlpha.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../governance/GovernorAlpha.sol";
6 |
7 | contract MockGovernorAlpha is GovernorAlpha {
8 | constructor(
9 | address guardian_,
10 | address xVader_,
11 | address feeReceiver_,
12 | uint256 feeAmount_,
13 | address council_,
14 | uint256 votingPeriod_
15 | )
16 | GovernorAlpha(
17 | guardian_,
18 | xVader_,
19 | feeReceiver_,
20 | feeAmount_,
21 | council_,
22 | votingPeriod_
23 | )
24 | {}
25 |
26 | /// @notice mock function to get chain id from CHAINID opcode.
27 | /// Using ganache in truffle sets chainid but the a separate ganache or ganache in solidity-coverage
28 | /// does not set the CHAINID opcode and it default to 1, which results in web3.eth.getChainId and CHAINID opcode
29 | /// both returning different values.
30 | /// https://github.com/ethereum/web3.py/issues/1677
31 | function CHAINID() public view returns (uint256 chainId) {
32 | assembly {
33 | chainId := chainid()
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/contracts/mocks/MockLBT.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | contract MockLBT {
6 | // 1e18 = 1 USD
7 | uint256 private vaderUsdPrice = (8 * 1e18) / 100;
8 |
9 | uint256 private x;
10 |
11 | function getVaderPrice() external returns (uint256) {
12 | // Write something to suppress compiler warning
13 | x += 1;
14 | return vaderUsdPrice;
15 | }
16 |
17 | // test helpers
18 | function _setVaderUsdPrice_(uint256 _vaderUsdPrice) external {
19 | vaderUsdPrice = _vaderUsdPrice;
20 | }
21 |
22 | function _getVaderUsdPrice_() external view returns (uint256) {
23 | return vaderUsdPrice;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/contracts/mocks/MockMTree.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | contract MockMTree {
6 | function getRoot(
7 | address member,
8 | uint256 amount,
9 | uint256 salt,
10 | uint256 chainId
11 | ) external pure returns (bytes memory) {
12 | return abi.encodePacked(member, amount, salt, chainId);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/contracts/mocks/MockTarget.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../interfaces/governance/ITimelock.sol";
6 |
7 | contract MockTarget {
8 | bool public state;
9 | ITimelock public timelock;
10 |
11 | constructor(address _timelock) {
12 | timelock = ITimelock(_timelock);
13 | }
14 |
15 | function setStateToTrue() external onlyTimelock {
16 | state = true;
17 | }
18 |
19 | function changeState(bool _state) external onlyTimelock {
20 | state = _state;
21 | }
22 |
23 | modifier onlyTimelock() {
24 | require(msg.sender == address(timelock), "only timelock can call");
25 | _;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/contracts/mocks/MockTimelock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../governance/Timelock.sol";
6 |
7 | contract MockTimelock is Timelock {
8 | constructor(address admin_, uint256 delay_) Timelock(admin_, delay_) {}
9 |
10 | function GRACE_PERIOD() public pure override returns (uint256) {
11 | return 1 days;
12 | }
13 |
14 | function MINIMUM_DELAY() public pure override returns (uint256) {
15 | return 5 minutes;
16 | }
17 |
18 | function MAXIMUM_DELAY() public pure override returns (uint256) {
19 | return 15 minutes;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/contracts/mocks/MockToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | contract MockToken is ERC20 {
8 | uint8 internal immutable _decimals;
9 |
10 | constructor(
11 | string memory _symbol,
12 | string memory _name,
13 | uint8 __decimals
14 | ) ERC20(_symbol, _name) {
15 | _decimals = __decimals;
16 | }
17 |
18 | function mint(address account, uint256 amount) public {
19 | _mint(account, amount);
20 | }
21 |
22 | function burn(address account, uint256 amount) public {
23 | _burn(account, amount);
24 | }
25 |
26 | function decimals() public view override returns (uint8) {
27 | return _decimals;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/contracts/mocks/MockUSDV.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | contract MockUSDV is ERC20 {
8 | constructor() ERC20("Fake USDV", "USDV") {}
9 |
10 | function mint(
11 | address account,
12 | uint256,
13 | uint256 uAmount,
14 | uint256,
15 | uint256
16 | ) external returns (uint256) {
17 | _mint(account, uAmount);
18 | return uAmount;
19 | }
20 |
21 | function burn(
22 | address account,
23 | uint256 uAmount,
24 | uint256 vAmount,
25 | uint256,
26 | uint256
27 | ) external returns (uint256) {
28 | _burn(account, uAmount);
29 | return vAmount;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/contracts/mocks/MockUniswapV2Factory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../interfaces/external/uniswap/IUniswapV2Factory.sol";
6 | import "../external/UniswapV2Pair.sol";
7 |
8 | contract MockUniswapV2Factory is IUniswapV2Factory {
9 | address public feeTo;
10 | address public feeToSetter;
11 |
12 | mapping(address => mapping(address => address)) public getPair;
13 | address[] public allPairs;
14 |
15 | constructor(address _feeToSetter) {
16 | feeToSetter = _feeToSetter;
17 | }
18 |
19 | function allPairsLength() external view returns (uint256) {
20 | return allPairs.length;
21 | }
22 |
23 | function createPair(address tokenA, address tokenB)
24 | external
25 | returns (address pair)
26 | {
27 | require(tokenA != tokenB, "UniswapV2: IDENTICAL_ADDRESSES");
28 | (address token0, address token1) = tokenA < tokenB
29 | ? (tokenA, tokenB)
30 | : (tokenB, tokenA);
31 | require(token0 != address(0), "UniswapV2: ZERO_ADDRESS");
32 | require(
33 | getPair[token0][token1] == address(0),
34 | "UniswapV2: PAIR_EXISTS"
35 | ); // single check is sufficient
36 | bytes memory bytecode = type(UniswapV2Pair).creationCode;
37 | bytes32 salt = keccak256(abi.encodePacked(token0, token1));
38 | assembly {
39 | pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
40 | }
41 | IUniswapV2Pair(pair).initialize(token0, token1);
42 | getPair[token0][token1] = pair;
43 | getPair[token1][token0] = pair; // populate mapping in the reverse direction
44 | allPairs.push(pair);
45 | emit PairCreated(token0, token1, pair, allPairs.length);
46 | }
47 |
48 | function setFeeTo(address _feeTo) external {
49 | require(msg.sender == feeToSetter, "UniswapV2: FORBIDDEN");
50 | feeTo = _feeTo;
51 | }
52 |
53 | function setFeeToSetter(address _feeToSetter) external {
54 | require(msg.sender == feeToSetter, "UniswapV2: FORBIDDEN");
55 | feeToSetter = _feeToSetter;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/contracts/mocks/MockUniswapV2Library.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../external/libraries/UniswapV2Library.sol";
6 |
7 | contract MockUniswapV2Library {
8 | function pairFor(
9 | address _factory,
10 | address _token0,
11 | address _token1
12 | ) external pure returns (address) {
13 | return UniswapV2Library.pairFor(_factory, _token0, _token1);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/contracts/mocks/MockVader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6 |
7 | contract MockVader is ERC20 {
8 | constructor() ERC20("Fake VADER", "VADER") {}
9 |
10 | function mint(address account, uint256 amount) public {
11 | _mint(account, amount);
12 | }
13 |
14 | function burn(uint256 amount) public {
15 | _burn(msg.sender, amount);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/mocks/MockXVader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "../x-vader/XVader.sol";
6 |
7 | contract MockXVader is XVader {
8 | constructor(IERC20 _vader) XVader(_vader) {}
9 |
10 | function mint(address to, uint256 amount) external {
11 | ERC20Votes._mint(to, amount);
12 | }
13 |
14 | function burn(address from, uint256 amount) external {
15 | ERC20Votes._burn(from, amount);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/reserve/VaderReserve.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/access/Ownable.sol";
6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7 |
8 | import "../shared/ProtocolConstants.sol";
9 |
10 | import "../interfaces/reserve/IVaderReserve.sol";
11 | import "../interfaces/lbt/ILiquidityBasedTWAP.sol";
12 |
13 | contract VaderReserve is IVaderReserve, ProtocolConstants, Ownable {
14 | /* ========== LIBRARIES ========== */
15 |
16 | // Used for safe VADER transfers
17 | using SafeERC20 for IERC20;
18 |
19 | /* ========== STATE VARIABLES ========== */
20 |
21 | // The Vader token the reserve is handling
22 | IERC20 public immutable vader;
23 |
24 | // Router address for IL awards
25 | address public router;
26 |
27 | // Tracks last grant time for throttling
28 | uint256 public lastGrant;
29 |
30 | // LBT used for loss reimbursement
31 | ILiquidityBasedTWAP public lbt;
32 |
33 | /* ========== CONSTRUCTOR ========== */
34 |
35 | constructor(IERC20 _vader) {
36 | require(
37 | _vader != IERC20(_ZERO_ADDRESS),
38 | "VaderReserve::constructor: Incorrect Arguments"
39 | );
40 | vader = _vader;
41 | }
42 |
43 | /* ========== VIEWS ========== */
44 |
45 | function reserve() public view override returns (uint256) {
46 | return vader.balanceOf(address(this));
47 | }
48 |
49 | /* ========== MUTATIVE FUNCTIONS ========== */
50 |
51 | function grant(address recipient, uint256 amount)
52 | external
53 | override
54 | onlyOwner
55 | throttle
56 | {
57 | amount = _min(
58 | (reserve() * _MAX_GRANT_BASIS_POINTS) / _MAX_BASIS_POINTS,
59 | amount
60 | );
61 | vader.safeTransfer(recipient, amount);
62 |
63 | emit GrantDistributed(recipient, amount);
64 | }
65 |
66 | /* ========== RESTRICTED FUNCTIONS ========== */
67 |
68 | function initialize(
69 | ILiquidityBasedTWAP _lbt,
70 | address _router,
71 | address _dao
72 | ) external onlyOwner {
73 | require(
74 | _router != _ZERO_ADDRESS &&
75 | _lbt != ILiquidityBasedTWAP(_ZERO_ADDRESS),
76 | "VaderReserve::initialize: Incorrect Arguments"
77 | );
78 | router = _router;
79 | lbt = _lbt;
80 | transferOwnership(_dao);
81 | }
82 |
83 | function reimburseImpermanentLoss(address recipient, uint256 amount)
84 | external
85 | override
86 | {
87 | require(
88 | msg.sender == router,
89 | "VaderReserve::reimburseImpermanentLoss: Insufficient Priviledges"
90 | );
91 |
92 | // NOTE: Loss is in USDV, reimbursed in VADER
93 | // NOTE: If USDV LBT is working, prefer it otherwise use VADER price
94 | if (lbt.previousPrices(uint256(ILiquidityBasedTWAP.Paths.USDV)) != 0) {
95 | uint256 usdvPrice = lbt.getUSDVPrice();
96 |
97 | amount = amount / usdvPrice;
98 | } else {
99 | uint256 vaderPrice = lbt.getVaderPrice();
100 |
101 | amount = amount * vaderPrice;
102 | }
103 |
104 | uint256 actualAmount = _min(reserve(), amount);
105 |
106 | vader.safeTransfer(recipient, actualAmount);
107 |
108 | emit LossCovered(recipient, amount, actualAmount);
109 | }
110 |
111 | /* ========== INTERNAL FUNCTIONS ========== */
112 |
113 | /* ========== PRIVATE FUNCTIONS ========== */
114 |
115 | /**
116 | * @dev Calculates the minimum of the two values
117 | */
118 | function _min(uint256 a, uint256 b) private pure returns (uint256) {
119 | return a < b ? a : b;
120 | }
121 |
122 | /* ========== MODIFIERS ========== */
123 |
124 | modifier throttle() {
125 | require(
126 | lastGrant + _GRANT_DELAY <= block.timestamp,
127 | "VaderReserve::throttle: Grant Too Fast"
128 | );
129 | lastGrant = block.timestamp;
130 | _;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/contracts/shared/ProtocolConstants.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | abstract contract ProtocolConstants {
6 | /* ========== GENERAL ========== */
7 |
8 | // The zero address, utility
9 | address internal constant _ZERO_ADDRESS = address(0);
10 |
11 | // One year, utility
12 | uint256 internal constant _ONE_YEAR = 365 days;
13 |
14 | // Basis Points
15 | uint256 internal constant _MAX_BASIS_POINTS = 100_00;
16 |
17 | /* ========== VADER TOKEN ========== */
18 |
19 | // Max VADER supply
20 | uint256 internal constant _INITIAL_VADER_SUPPLY = 25_000_000_000 * 1 ether;
21 |
22 | // Allocation for VETH holders
23 | uint256 internal constant _VETH_ALLOCATION = 7_500_000_000 * 1 ether;
24 |
25 | // Team allocation vested over {VESTING_DURATION} years
26 | uint256 internal constant _TEAM_ALLOCATION = 2_500_000_000 * 1 ether;
27 |
28 | // Ecosystem growth fund unlocked for partnerships & USDV provision
29 | uint256 internal constant _ECOSYSTEM_GROWTH = 2_500_000_000 * 1 ether;
30 |
31 | // Total grant tokens
32 | uint256 internal constant _GRANT_ALLOCATION = 12_500_000_000 * 1 ether;
33 |
34 | // Emission Era
35 | uint256 internal constant _EMISSION_ERA = 24 hours;
36 |
37 | // Initial Emission Curve, 5
38 | uint256 internal constant _INITIAL_EMISSION_CURVE = 5;
39 |
40 | // Fee Basis Points
41 | uint256 internal constant _MAX_FEE_BASIS_POINTS = 1_00;
42 |
43 | /* ========== USDV TOKEN ========== */
44 |
45 | // Max locking duration
46 | uint256 internal constant _MAX_LOCK_DURATION = 30 days;
47 |
48 | /* ========== VESTING ========== */
49 |
50 | // Vesting Duration
51 | uint256 internal constant _VESTING_DURATION = 2 * _ONE_YEAR;
52 |
53 | /* ========== CONVERTER ========== */
54 |
55 | // Vader -> Vether Conversion Rate (10000:1)
56 | uint256 internal constant _VADER_VETHER_CONVERSION_RATE = 10_000;
57 |
58 | // Burn Address
59 | address internal constant _BURN =
60 | 0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD;
61 |
62 | /* ========== GAS QUEUE ========== */
63 |
64 | // Address of Chainlink Fast Gas Price Oracle
65 | address internal constant _FAST_GAS_ORACLE =
66 | 0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C;
67 |
68 | /* ========== VADER RESERVE ========== */
69 |
70 | // Minimum delay between grants
71 | uint256 internal constant _GRANT_DELAY = 30 days;
72 |
73 | // Maximum grant size divisor
74 | uint256 internal constant _MAX_GRANT_BASIS_POINTS = 10_00;
75 | }
76 |
--------------------------------------------------------------------------------
/contracts/staking-rewards/IStakingRewards.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | interface IStakingRewards {
5 | // Views
6 |
7 | function balanceOf(address account) external view returns (uint256);
8 |
9 | function earned(address account) external view returns (uint256);
10 |
11 | function getRewardForDuration() external view returns (uint256);
12 |
13 | function lastTimeRewardApplicable() external view returns (uint256);
14 |
15 | function rewardPerToken() external view returns (uint256);
16 |
17 | // function rewardsDistribution() external view returns (address);
18 |
19 | // function rewardsToken() external view returns (address);
20 |
21 | function totalSupply() external view returns (uint256);
22 |
23 | // Mutative
24 |
25 | function exit() external;
26 |
27 | function getReward() external;
28 |
29 | function stake(uint256 amount) external;
30 |
31 | function withdraw(uint256 amount) external;
32 | }
33 |
--------------------------------------------------------------------------------
/contracts/staking-rewards/Owned.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | contract Owned {
5 | address public owner;
6 | address public nominatedOwner;
7 |
8 | constructor(address _owner) {
9 | require(_owner != address(0), "Owner address cannot be 0");
10 | owner = _owner;
11 | emit OwnerChanged(address(0), _owner);
12 | }
13 |
14 | function nominateNewOwner(address _owner) external onlyOwner {
15 | nominatedOwner = _owner;
16 | emit OwnerNominated(_owner);
17 | }
18 |
19 | function acceptOwnership() external {
20 | require(
21 | msg.sender == nominatedOwner,
22 | "You must be nominated before you can accept ownership"
23 | );
24 | emit OwnerChanged(owner, nominatedOwner);
25 | owner = nominatedOwner;
26 | nominatedOwner = address(0);
27 | }
28 |
29 | modifier onlyOwner() {
30 | _onlyOwner();
31 | _;
32 | }
33 |
34 | function _onlyOwner() private view {
35 | require(
36 | msg.sender == owner,
37 | "Only the contract owner may perform this action"
38 | );
39 | }
40 |
41 | event OwnerNominated(address newOwner);
42 | event OwnerChanged(address oldOwner, address newOwner);
43 | }
44 |
--------------------------------------------------------------------------------
/contracts/staking-rewards/Pausable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | import "./Owned.sol";
5 |
6 | abstract contract Pausable is Owned {
7 | uint256 public lastPauseTime;
8 | bool public paused;
9 |
10 | constructor() {
11 | // This contract is abstract, and thus cannot be instantiated directly
12 | require(owner != address(0), "Owner must be set");
13 | // Paused will be false, and lastPauseTime will be 0 upon initialisation
14 | }
15 |
16 | /**
17 | * @notice Change the paused state of the contract
18 | * @dev Only the contract owner may call this.
19 | */
20 | function setPaused(bool _paused) external onlyOwner {
21 | // Ensure we're actually changing the state before we do anything
22 | if (_paused == paused) {
23 | return;
24 | }
25 |
26 | // Set our paused state.
27 | paused = _paused;
28 |
29 | // If applicable, set the last pause time.
30 | // C4-Audit Fix for Issue # 106
31 | if (_paused) {
32 | lastPauseTime = block.timestamp;
33 | }
34 |
35 | // Let everyone know that our pause state has changed.
36 | // C4-Audit Fix for Issue # 106
37 | emit PauseChanged(_paused);
38 | }
39 |
40 | event PauseChanged(bool isPaused);
41 |
42 | modifier notPaused() {
43 | require(!paused, "paused");
44 | _;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/contracts/staking-rewards/RewardsDistributionRecipient.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | import "./Owned.sol";
5 |
6 | abstract contract RewardsDistributionRecipient is Owned {
7 | address public rewardsDistribution;
8 |
9 | function notifyRewardAmount(uint256 reward) external virtual;
10 |
11 | modifier onlyRewardsDistribution() {
12 | require(msg.sender == rewardsDistribution, "not reward distribution");
13 | _;
14 | }
15 |
16 | function setRewardsDistribution(address _rewardsDistribution)
17 | external
18 | onlyOwner
19 | {
20 | rewardsDistribution = _rewardsDistribution;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/contracts/staking-rewards/StakingRewards.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
7 |
8 | import "./IStakingRewards.sol";
9 | import "./RewardsDistributionRecipient.sol";
10 | import "./Pausable.sol";
11 |
12 | contract StakingRewards is
13 | IStakingRewards,
14 | RewardsDistributionRecipient,
15 | ReentrancyGuard,
16 | Pausable
17 | {
18 | using SafeERC20 for IERC20;
19 |
20 | /* ========== STATE VARIABLES ========== */
21 |
22 | IERC20 public immutable rewardsToken;
23 | IERC20 public immutable stakingToken;
24 | uint256 public periodFinish = 0;
25 | uint256 public rewardRate = 0;
26 | uint256 public rewardsDuration = 7 days;
27 | uint256 public lastUpdateTime;
28 | uint256 public rewardPerTokenStored;
29 |
30 | mapping(address => uint256) public userRewardPerTokenPaid;
31 | mapping(address => uint256) public rewards;
32 |
33 | uint256 private _totalSupply;
34 | mapping(address => uint256) private _balances;
35 |
36 | /* ========== CONSTRUCTOR ========== */
37 |
38 | constructor(
39 | address _owner,
40 | address _rewardsDistribution,
41 | address _rewardsToken,
42 | address _stakingToken
43 | ) Owned(_owner) {
44 | rewardsToken = IERC20(_rewardsToken);
45 | stakingToken = IERC20(_stakingToken);
46 | rewardsDistribution = _rewardsDistribution;
47 | }
48 |
49 | /* ========== VIEWS ========== */
50 |
51 | function totalSupply() external view returns (uint256) {
52 | return _totalSupply;
53 | }
54 |
55 | function balanceOf(address account) external view returns (uint256) {
56 | return _balances[account];
57 | }
58 |
59 | function lastTimeRewardApplicable() public view returns (uint256) {
60 | return block.timestamp < periodFinish ? block.timestamp : periodFinish;
61 | }
62 |
63 | function rewardPerToken() public view returns (uint256) {
64 | if (_totalSupply == 0) {
65 | return rewardPerTokenStored;
66 | }
67 | return
68 | rewardPerTokenStored +
69 | (lastTimeRewardApplicable() -
70 | (lastUpdateTime * rewardRate * 1e18) /
71 | _totalSupply);
72 | }
73 |
74 | function earned(address account) public view returns (uint256) {
75 | return
76 | (_balances[account] *
77 | (rewardPerToken() - userRewardPerTokenPaid[account])) /
78 | 1e18 +
79 | rewards[account];
80 | }
81 |
82 | function getRewardForDuration() external view returns (uint256) {
83 | return rewardRate * rewardsDuration;
84 | }
85 |
86 | /* ========== MUTATIVE FUNCTIONS ========== */
87 |
88 | function stake(uint256 amount)
89 | external
90 | nonReentrant
91 | notPaused
92 | updateReward(msg.sender)
93 | {
94 | require(amount > 0, "Cannot stake 0");
95 | _totalSupply = _totalSupply + amount;
96 | _balances[msg.sender] = _balances[msg.sender] + amount;
97 | stakingToken.safeTransferFrom(msg.sender, address(this), amount);
98 | emit Staked(msg.sender, amount);
99 | }
100 |
101 | function withdraw(uint256 amount)
102 | public
103 | nonReentrant
104 | updateReward(msg.sender)
105 | {
106 | require(amount > 0, "Cannot withdraw 0");
107 | _totalSupply = _totalSupply - amount;
108 | _balances[msg.sender] = _balances[msg.sender] - amount;
109 | stakingToken.safeTransfer(msg.sender, amount);
110 | emit Withdrawn(msg.sender, amount);
111 | }
112 |
113 | function getReward() public nonReentrant updateReward(msg.sender) {
114 | uint256 reward = rewards[msg.sender];
115 | if (reward > 0) {
116 | rewards[msg.sender] = 0;
117 | rewardsToken.safeTransfer(msg.sender, reward);
118 | emit RewardPaid(msg.sender, reward);
119 | }
120 | }
121 |
122 | function exit() external {
123 | withdraw(_balances[msg.sender]);
124 | getReward();
125 | }
126 |
127 | /* ========== RESTRICTED FUNCTIONS ========== */
128 |
129 | function notifyRewardAmount(uint256 reward)
130 | external
131 | override
132 | onlyRewardsDistribution
133 | updateReward(address(0))
134 | {
135 | if (block.timestamp >= periodFinish) {
136 | rewardRate = reward / rewardsDuration;
137 | } else {
138 | uint256 remaining = periodFinish - block.timestamp;
139 | uint256 leftover = remaining * rewardRate;
140 | rewardRate = reward + leftover / rewardsDuration;
141 | }
142 |
143 | // Ensure the provided reward amount is not more than the balance in the contract.
144 | // This keeps the reward rate in the right range, preventing overflows due to
145 | // very high values of rewardRate in the earned and rewardsPerToken functions;
146 | // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
147 | uint256 balance = rewardsToken.balanceOf(address(this));
148 | require(
149 | rewardRate <= balance / rewardsDuration,
150 | "Provided reward too high"
151 | );
152 |
153 | lastUpdateTime = block.timestamp;
154 | periodFinish = block.timestamp + rewardsDuration;
155 | emit RewardAdded(reward);
156 | }
157 |
158 | // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
159 | function recoverERC20(address tokenAddress, uint256 tokenAmount)
160 | external
161 | onlyOwner
162 | {
163 | require(
164 | tokenAddress != address(stakingToken),
165 | "Cannot withdraw the staking token"
166 | );
167 | IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
168 | emit Recovered(tokenAddress, tokenAmount);
169 | }
170 |
171 | function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner {
172 | require(
173 | block.timestamp > periodFinish,
174 | "Previous rewards period must be complete before changing the duration for the new period"
175 | );
176 | rewardsDuration = _rewardsDuration;
177 | // C4-Audit Fix for Issue # 106
178 | emit RewardsDurationUpdated(_rewardsDuration);
179 | }
180 |
181 | /* ========== MODIFIERS ========== */
182 |
183 | modifier updateReward(address account) {
184 | rewardPerTokenStored = rewardPerToken();
185 | lastUpdateTime = lastTimeRewardApplicable();
186 | if (account != address(0)) {
187 | rewards[account] = earned(account);
188 | userRewardPerTokenPaid[account] = rewardPerTokenStored;
189 | }
190 | _;
191 | }
192 |
193 | /* ========== EVENTS ========== */
194 |
195 | event RewardAdded(uint256 reward);
196 | event Staked(address indexed user, uint256 amount);
197 | event Withdrawn(address indexed user, uint256 amount);
198 | event RewardPaid(address indexed user, uint256 reward);
199 | event RewardsDurationUpdated(uint256 newDuration);
200 | event Recovered(address token, uint256 amount);
201 | }
202 |
--------------------------------------------------------------------------------
/contracts/tokens/Vader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/access/Ownable.sol";
6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7 |
8 | import "../shared/ProtocolConstants.sol";
9 |
10 | import "../interfaces/tokens/IUSDV.sol";
11 | import "../interfaces/tokens/IVader.sol";
12 | import "../interfaces/tokens/vesting/ILinearVesting.sol";
13 | import "../interfaces/tokens/converter/IConverter.sol";
14 |
15 | /**
16 | * @dev Implementation of the {IVader} interface.
17 | *
18 | * The Vader token that acts as the backbone of the Vader protocol,
19 | * burned and minted to mint and burn USDV tokens respectively.
20 | *
21 | * The token has a fixed initial supply at 25 billion units that is meant to then
22 | * fluctuate depending on the amount of USDV minted into and burned from circulation.
23 | *
24 | * Emissions are initially controlled by the Vader team and then will be governed
25 | * by the DAO.
26 | */
27 | contract Vader is IVader, ProtocolConstants, ERC20, Ownable {
28 | /* ========== STATE VARIABLES ========== */
29 |
30 | // The Vader <-> Vether converter contract
31 | IConverter public converter;
32 |
33 | // The Vader Team vesting contract
34 | ILinearVesting public vest;
35 |
36 | // The USDV contract, used to apply proper access control
37 | IUSDV public usdv;
38 |
39 | // The initial maximum supply of the token, equivalent to 25 bn units
40 | uint256 public maxSupply = _INITIAL_VADER_SUPPLY;
41 |
42 | /* ========== CONSTRUCTOR ========== */
43 |
44 | /**
45 | * @dev Mints the ecosystem growth fund and grant allocation amount described in the whitepaper to the
46 | * token contract itself.
47 | *
48 | * As the token is meant to be minted and burned freely between USDV and itself,
49 | * there is no real initialization taking place apart from the initially minted
50 | * supply for the following components:
51 | *
52 | * - Grant Allocation: The amount of funds meant to be distributed by the DAO as grants to expand the protocol
53 | *
54 | * - Ecosystem Growth: An allocation that is released to strategic partners for the
55 | * protocol's expansion
56 | *
57 | * The latter two of the allocations are minted at a later date given that the addresses of
58 | * the converter and vesting contract are not known on deployment.
59 | */
60 | constructor() ERC20("Vader", "VADER") {
61 | _mint(address(this), _GRANT_ALLOCATION);
62 | _mint(address(this), _ECOSYSTEM_GROWTH);
63 | }
64 |
65 | /* ========== MUTATIVE FUNCTIONS ========== */
66 |
67 | /**
68 | * @dev Creates a manual emission event
69 | *
70 | * Emits an {Emission} event indicating the amount emitted as well as what the current
71 | * era's timestamp is.
72 | */
73 | function createEmission(address user, uint256 amount)
74 | external
75 | override
76 | onlyOwner
77 | {
78 | _transfer(address(this), user, amount);
79 | emit Emission(user, amount);
80 | }
81 |
82 | /* ========== RESTRICTED FUNCTIONS ========== */
83 |
84 | /**
85 | * @dev Sets the initial {converter} and {vest} contract addresses. Additionally, mints
86 | * the Vader amount available for conversion as well as the team allocation that is meant
87 | * to be vested to each respective contract.
88 | *
89 | * Emits a {ProtocolInitialized} event indicating all the supplied values of the function.
90 | *
91 | * Requirements:
92 | *
93 | * - the caller must be the deployer of the contract
94 | * - the contract must not have already been initialized
95 | */
96 | function setComponents(
97 | IConverter _converter,
98 | ILinearVesting _vest,
99 | address[] calldata vesters,
100 | uint192[] calldata amounts
101 | ) external onlyOwner {
102 | require(
103 | _converter != IConverter(_ZERO_ADDRESS) &&
104 | _vest != ILinearVesting(_ZERO_ADDRESS),
105 | "Vader::setComponents: Incorrect Arguments"
106 | );
107 | require(
108 | converter == IConverter(_ZERO_ADDRESS),
109 | "Vader::setComponents: Already Set"
110 | );
111 |
112 | converter = _converter;
113 | vest = _vest;
114 |
115 | _mint(address(_converter), _VETH_ALLOCATION);
116 | _mint(address(_vest), _TEAM_ALLOCATION);
117 |
118 | _vest.begin(vesters, amounts);
119 |
120 | emit ProtocolInitialized(address(_converter), address(_vest));
121 | }
122 |
123 | /**
124 | * @dev Set USDV
125 | * Emits a {USDVSet} event indicating that USDV is set
126 | *
127 | * Requirements:
128 | *
129 | * - the caller must be owner
130 | * - USDV must be of a non-zero address
131 | * - USDV must not be set
132 | */
133 | function setUSDV(IUSDV _usdv) external onlyOwner {
134 | require(
135 | _usdv != IUSDV(_ZERO_ADDRESS),
136 | "Vader::setUSDV: Invalid USDV address"
137 | );
138 | require(
139 | usdv == IUSDV(_ZERO_ADDRESS),
140 | "Vader::setUSDV: USDV already set"
141 | );
142 |
143 | usdv = _usdv;
144 | emit USDVSet(address(_usdv));
145 | }
146 |
147 | /**
148 | * @dev Allows a strategic partnership grant to be claimed.
149 | *
150 | * Emits a {GrantClaimed} event indicating the beneficiary of the grant as
151 | * well as the grant amount.
152 | *
153 | * Requirements:
154 | *
155 | * - the caller must be the DAO
156 | * - the token must hold sufficient Vader allocation for the grant
157 | * - the grant must be of a non-zero amount
158 | */
159 | function claimGrant(address beneficiary, uint256 amount)
160 | external
161 | onlyOwner
162 | {
163 | require(amount != 0, "Vader::claimGrant: Non-Zero Amount Required");
164 | emit GrantClaimed(beneficiary, amount);
165 | _transfer(address(this), beneficiary, amount);
166 | }
167 |
168 | /**
169 | * @dev Allows the maximum supply of the token to be adjusted.
170 | *
171 | * Emits an {MaxSupplyChanged} event indicating the previous and next maximum
172 | * total supplies.
173 | *
174 | * Requirements:
175 | *
176 | * - the caller must be the DAO
177 | * - the new maximum supply must be greater than the current supply
178 | */
179 | function adjustMaxSupply(uint256 _maxSupply) external onlyOwner {
180 | require(
181 | _maxSupply >= totalSupply(),
182 | "Vader::adjustMaxSupply: Max supply cannot subcede current supply"
183 | );
184 | emit MaxSupplyChanged(maxSupply, _maxSupply);
185 | maxSupply = _maxSupply;
186 | }
187 |
188 | /**
189 | * @dev Allows the USDV token to perform mints of VADER tokens
190 | *
191 | * Emits an ERC-20 {Transfer} event signaling the minting operation.
192 | *
193 | * Requirements:
194 | *
195 | * - the caller must be the USDV
196 | * - the new supply must be below the maximum supply
197 | */
198 | function mint(address _user, uint256 _amount) external onlyUSDV {
199 | require(
200 | maxSupply >= totalSupply() + _amount,
201 | "Vader::mint: Max supply reached"
202 | );
203 | _mint(_user, _amount);
204 | }
205 |
206 | /**
207 | * @dev Allows the USDV token to perform burns of VADER tokens
208 | *
209 | * Emits an ERC-20 {Transfer} event signaling the burning operation.
210 | *
211 | * Requirements:
212 | *
213 | * - the caller must be the USDV
214 | * - the USDV contract must have a sufficient VADER balance
215 | */
216 | function burn(uint256 _amount) external onlyUSDV {
217 | _burn(msg.sender, _amount);
218 | }
219 |
220 | /* ========== INTERNAL FUNCTIONS ========== */
221 |
222 | /* ========== PRIVATE FUNCTIONS ========== */
223 |
224 | /**
225 | * @dev Ensures only the USDV is able to invoke a particular function by validating that the
226 | * contract has been set up and that the msg.sender is the USDV address
227 | */
228 | function _onlyUSDV() private view {
229 | require(
230 | address(usdv) == msg.sender,
231 | "Vader::_onlyUSDV: Insufficient Privileges"
232 | );
233 | }
234 |
235 | /* ========== MODIFIERS ========== */
236 |
237 | /**
238 | * @dev Throws if invoked by anyone else other than the USDV
239 | */
240 | modifier onlyUSDV() {
241 | _onlyUSDV();
242 | _;
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/contracts/tokens/converter/Converter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6 | import "@openzeppelin/contracts/access/Ownable.sol";
7 | import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
8 |
9 | import "../../shared/ProtocolConstants.sol";
10 |
11 | import "../../interfaces/tokens/converter/IConverter.sol";
12 | import "../../interfaces/tokens/vesting/ILinearVesting.sol";
13 |
14 | /**
15 | * @dev Implementation of the {IConverter} interface.
16 | *
17 | * A simple converter contract that allows users to convert
18 | * their Vether tokens by "burning" them (See {convert}) to
19 | * acquire their equivalent Vader tokens based on the constant
20 | * {VADER_VETHER_CONVERSION_RATE}.
21 | *
22 | * The contract assumes that it has been sufficiently funded with
23 | * Vader tokens and will fail to execute trades if it has not been
24 | * done so yet.
25 | */
26 | contract Converter is IConverter, ProtocolConstants, Ownable {
27 | /* ========== LIBRARIES ========== */
28 |
29 | // Using MerkleProof for validating claims
30 | using MerkleProof for bytes32[];
31 |
32 | /* ========== STATE VARIABLES ========== */
33 |
34 | // The VETHER token
35 | IERC20 public immutable vether;
36 |
37 | // The VADER token
38 | IERC20 public immutable vader;
39 |
40 | // The VADER vesting contract
41 | ILinearVesting public vesting;
42 |
43 | // The merkle proof root for validating claims
44 | bytes32 public immutable root;
45 |
46 | // Unique deployment salt
47 | uint256 public immutable salt;
48 |
49 | // Signals whether a particular leaf has been claimed of the merkle proof
50 | mapping(bytes32 => bool) public claimed;
51 |
52 | /* ========== CONSTRUCTOR ========== */
53 |
54 | /**
55 | * @dev Initializes the contract's {vether} and {vader} addresses.
56 | *
57 | * Performs rudimentary checks to ensure that the variables haven't
58 | * been declared incorrectly.
59 | */
60 | constructor(
61 | IERC20 _vether,
62 | IERC20 _vader,
63 | bytes32 _root,
64 | uint256 _salt
65 | ) {
66 | require(
67 | _vether != IERC20(_ZERO_ADDRESS) && _vader != IERC20(_ZERO_ADDRESS),
68 | "Converter::constructor: Misconfiguration"
69 | );
70 |
71 | vether = _vether;
72 | vader = _vader;
73 |
74 | root = _root;
75 | salt = _salt;
76 | }
77 |
78 | /* ========== RESTRICTED FUNCTIONS ========== */
79 |
80 | /*
81 | * @dev Sets address of vesting contract.
82 | *
83 | * The LinearVesting and Converter contracts are dependent upon
84 | * each other, hence this setter is introduced.
85 | *
86 | * Also approves Vesting to spend Vader tokens on its behalf.
87 | *
88 | **/
89 | function setVesting(ILinearVesting _vesting) external onlyOwner {
90 | require(
91 | vesting == ILinearVesting(_ZERO_ADDRESS),
92 | "Converter::setVesting: Vesting is already set"
93 | );
94 | require(
95 | _vesting != ILinearVesting(_ZERO_ADDRESS),
96 | "Converter::setVesting: Cannot Set Zero Vesting Address"
97 | );
98 | vader.approve(address(_vesting), type(uint256).max);
99 | vesting = _vesting;
100 | }
101 |
102 | /* ========== MUTATIVE FUNCTIONS ========== */
103 |
104 | /**
105 | * @dev Allows a user to convert their Vether to Vader.
106 | *
107 | * Emits a {Conversion} event indicating the amount of Vether the user
108 | * "burned" and the amount of Vader that they acquired.
109 | *
110 | * Here, "burned" refers to the action of transferring them to an irrecoverable
111 | * address, the {BURN} address.
112 | *
113 | * Requirements:
114 | *
115 | * - the caller has approved the contract for the necessary amount via Vether
116 | * - the amount specified is non-zero
117 | * - the contract has been supplied with the necessary Vader amount to fulfill the trade
118 | */
119 | function convert(bytes32[] calldata proof, uint256 amount, uint256 minVader)
120 | external
121 | override
122 | returns (uint256 vaderReceived)
123 | {
124 | require(
125 | amount != 0,
126 | "Converter::convert: Non-Zero Conversion Amount Required"
127 | );
128 |
129 | ILinearVesting _vesting = vesting;
130 |
131 | require(
132 | _vesting != ILinearVesting(_ZERO_ADDRESS),
133 | "Converter::convert: Vesting is not set"
134 | );
135 |
136 | bytes32 leaf = keccak256(
137 | abi.encodePacked(msg.sender, amount, salt, getChainId())
138 | );
139 | require(
140 | !claimed[leaf] && proof.verify(root, leaf),
141 | "Converter::convert: Incorrect Proof Provided"
142 | );
143 | claimed[leaf] = true;
144 |
145 | uint256 allowance = vether.allowance(msg.sender, address(this));
146 |
147 | amount = amount > allowance ? allowance : amount;
148 |
149 | // NOTE: FoT is ignored as units are meant to be burned anyway
150 | vether.transferFrom(msg.sender, _BURN, amount);
151 |
152 | vaderReceived = amount * _VADER_VETHER_CONVERSION_RATE;
153 | require(vaderReceived >= minVader, "Converter::convert: Vader < min");
154 |
155 | emit Conversion(msg.sender, amount, vaderReceived);
156 |
157 | uint256 half = vaderReceived / 2;
158 | vader.transfer(msg.sender, half);
159 | _vesting.vestFor(msg.sender, vaderReceived - half);
160 | }
161 |
162 | /* ========== INTERNAL FUNCTIONS ========== */
163 | /*
164 | * @dev Returns the {chainId} of current network.
165 | **/
166 | function getChainId() internal view returns (uint256 chainId) {
167 | assembly {
168 | chainId := chainid()
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/contracts/tokens/validator/UnlockValidator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 |
3 | pragma solidity =0.8.9;
4 |
5 | import "@openzeppelin/contracts/access/Ownable.sol";
6 |
7 | import "../../interfaces/tokens/validator/IUnlockValidator.sol";
8 |
9 | contract UnlockValidator is IUnlockValidator, Ownable {
10 | /* ========== STATE VARIABLES ========== */
11 |
12 | // Mapping of blocked unlock accounts
13 | mapping(address => bool) private _isInvalidated;
14 |
15 | /* ========== VIEWS ========== */
16 |
17 | function isValid(
18 | address user,
19 | uint256,
20 | IUSDV.LockTypes
21 | ) external view override returns (bool) {
22 | return !_isInvalidated[user];
23 | }
24 |
25 | /* ========== RESTRICTED FUNCTIONS ========== */
26 |
27 | /*
28 | * @dev Sets an address as invalidated
29 | *
30 | * Requirements:
31 | * - Only existing owner can call this function.
32 | **/
33 | function invalidate(address _account) external onlyOwner {
34 | require(
35 | !_isInvalidated[_account],
36 | "UnlockValidator::invalidate: Already Invalid"
37 | );
38 | _isInvalidated[_account] = true;
39 | emit Invalidate(_account);
40 | }
41 |
42 | /*
43 | * @dev Removes invalidation from an address
44 | *
45 | * Requirements:
46 | * - Only existing owner can call this function.
47 | **/
48 | function validate(address _account) external onlyOwner {
49 | require(
50 | _isInvalidated[_account],
51 | "UnlockValidator::validate: Already Valid"
52 | );
53 | _isInvalidated[_account] = false;
54 | emit Validate(_account);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/contracts/x-vader/XVader.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
2 | pragma solidity 0.8.9;
3 |
4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
6 | import "../shared/ProtocolConstants.sol";
7 |
8 | contract XVader is ProtocolConstants, ERC20Votes {
9 | // Address of vader token
10 | IERC20 public immutable vader;
11 |
12 | /*
13 | * @dev Initializes contract's state by setting vader's tokens address and
14 | * setting current token's name and symbol.
15 | **/
16 | constructor(IERC20 _vader) ERC20Permit("XVader") ERC20("XVader", "xVADER") {
17 | require(
18 | _vader != IERC20(_ZERO_ADDRESS),
19 | "XVader::constructor: _vader cannot be a zero address"
20 | );
21 | vader = _vader;
22 | }
23 |
24 | // Locks vader and mints xVader
25 | function enter(uint256 _amount) external {
26 | // Gets the amount of vader locked in the contract
27 | uint256 totalVader = vader.balanceOf(address(this));
28 | // Gets the amount of xVader in existence
29 | uint256 totalShares = totalSupply();
30 |
31 | uint256 xVADERToMint = totalShares == 0 || totalVader == 0 // If no xVader exists, mint it 1:1 to the amount put in
32 | ? _amount // Calculate and mint the amount of xVader the vader is worth. // The ratio will change overtime, as xVader is burned/minted and // vader deposited + gained from fees / withdrawn.
33 | : (_amount * totalShares) / totalVader;
34 |
35 | _mint(msg.sender, xVADERToMint);
36 |
37 | // Lock the vader in the contract
38 | vader.transferFrom(msg.sender, address(this), _amount);
39 | }
40 |
41 | // Claim back your VADER
42 | // Unlocks the staked + gained vader and burns xVader
43 | function leave(uint256 _shares) external {
44 | // Gets the amount of xVader in existence
45 | uint256 totalShares = totalSupply();
46 | // Calculates the amount of vader the xVader is worth
47 | uint256 vaderAmount = (_shares * vader.balanceOf(address(this))) /
48 | totalShares;
49 |
50 | _burn(msg.sender, _shares);
51 | vader.transfer(msg.sender, vaderAmount);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/doc/vader.drawio:
--------------------------------------------------------------------------------
1 | 7Vxdc9o4FP01zOw+sIM/ZOAxgZBmJ93JLN0kfeootgJujEVlkUB//fpDNrYkiAdsy4R0+oCubUXcc3Xu0ZVMxxgt1tcELudfsYO8jt5z1h1j3NF1zdT1TvS/52wSiwW0xDAjrsNu2hqm7m/EjD1mXbkOCgo3Uow96i6LRhv7PrJpwQYJwW/F256xV/yrSzhDgmFqQ0+0PrgOnTOr1uttL3xB7mzO/vQAsAsLmN7MDMEcOvgtZzKuOsaIYEyTT4v1CHmR81K/JM9NdlzNBkaQT8s8MP16Ne4/joj5a7ju/ph3b7vrfpeh8wq9FfvC91ffvrAB003qBYJXvoOijnod4/Jt7lI0XUI7uvoW4h7a5nThhS0t/CgOjI31FRGK1jkTG+g1wgtEySa8hV3NnLbJ/J2033IYpPfMc+63mA0y2GdZ11vPhB+Yc+SOWg+/aS9ktr65/TG+uX8cO+6YdA1LcApywkhhTUzoHM+wD72rrfWy6LbtPbcYL5mzfiJKNyzs4YrioivR2qWP0eN/Adb6nrsyXrOe48Ymbfjh9809FDW/569tH4tb6XMBhYReRFMmNPjYRxmU0RfdGXTMFOAVsdE+B/bZ7IVkhuiekATyyCDIg9R9LQ6kcphNvT0w9w6CWVMLs2mUhFlXCjNQCbOWBzmD/D2YC3N5i/kOmA+DVIpUWUTNIxFlj95hNxxhlgpMrZgKsnbaRTIu9hQXF9kwSoXKvm+fy5Aj7MeZjLQuTepmg2lydwwU9QR0WuAp0yp6yhioFhTm4JOCSlMQOGsKAsKkunV9BMk9Cqjrz5RPLp6G1E+uLLA+rIyrcHKlyORnl9SrlkrJZpjtgXT/Aqxmha0NGiLDo4RAGlQ50vpvOr5XzlW8EGhUMknxBH0lgd0o78z/efj3evNrevPzbgoffo9ebv8eddNyXlvieN8gc2H82E5FCwzVgawZShn6EEWrdRpQtHJnlQz+XKlYQdLVPnydpO5kbZXUVoZSmAWS+xouZaohOYHRJI7cSXJWv23ZWiwHfXu4uDvOUc+u542wh0nYdtAzXHk0jkyCX5Bof8Y+Fa0VOHtotM3ZlqghfQ/bL/fQcx1IsfoA5bOwpdplaWnic5kkybC7yU8V8+pqNdPpbyvqZUuDaoXUUGSyQJJfw57cZYDepy4YLJPTDs/uOoqEQg7p6MZkMhpNJtVwXL8HChwnWWgACcWB2ihOlCuLuPJqxZnwKXSrNUtyYmJx3FfeFCyhn9qeVsRP7eGA8pdy5kInHGyhbymHj+fO/PCzjWIdZVxGCLg29C7YhYXrOMlkRoH7Gz7FXUXzZRlVkWOXgcsOGEd9hfM3SKayJugCNqPy8Bcm2XHIc4VvDYjQmxLojdqg1wXoT4c/nfpYcFg21x2b7OQbJH1OOQ44/JPxC/sj7/YjcEh1Gy1y94jbl0XCyMWZ9WuFY/aA9sssjqiunUzAi8ihs6c/dBDyZvgFwrH0uM9/bp9PSUVOYXu56UxYyNL4qABqWUiXsVBDYeIG8eLn3GPCHBY1idYvJ0r02mJC3KRtLCZsD7qLc48IniXKapX6IkKsXpyOVjlsrdcKjaMfe4C0Xo0Ddp3Zakjj6IN2hGU76/WGLkbZ7mOBqqoJYKiSST7AtoxZdvtN6WF0vS9M1dOpGvGi3RRFe7NVI5H39i25zkQ1gV4RJkNXvLYyZDr6VFRTNernuEN6SZxXLn942WL0KtI/NR6m3fseUi7AXlFAJ5LNxI866TOS3TPprSaXSkZLNOkJLpXKvrGzZy5UzhXZ2uhYruA7apwrxEKfnbz8cxpc4cEn5F1mVaaC0ov+VaT0ODqRHYJotBaXChbVdHLg2qfWKooueQ13T+xXzg388ev+odTAl1H4jmqmBlM8CEYJ9INnRCYELz75IQeVyUHVE/lh0Cg/yLb52iI36nldqBVywzy2aFZOJQgV1UPlhsZHYN2cIu4YpJzyySdbmIx+y5Yvpni6q5gL+BLTaaFaBWRc2jf1cimgtjITUHru8gNU0IEpkv7uF6iUbZSIC7jTqaDz6QgYikvoQNRNCSsJBOchGEJ7NuzGJSQg2etotIgO2lVEb4Lual00pzTyrsIF9fwAhcGdPQD86yNlFa7w0ijfUc0KF8gKvQ2dktqKLpkc47TauZ6iMvmKa1+kskaFWrpTny/BJsfdTgGRhgqw/N6c5A2NRo++pazymX8aLtqa9Zzvr6to2/R+jiYWWMI/F/+ewx1x7fNRrAP+dI5ky2bYKGOINP/JGNX+vMSeGVH9YdlhRYwx5AO1sm2esLn97eXk9u0vWBtX/wM=
--------------------------------------------------------------------------------
/doc/vader.drawio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vetherasset/vader-protocol-v2/1aa97ec03f17a6d24f8d56348cb3a3dfab403665/doc/vader.drawio.png
--------------------------------------------------------------------------------
/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | const Migrations = artifacts.require("Migrations");
2 |
3 | module.exports = function (deployer) {
4 | return;
5 | };
6 |
--------------------------------------------------------------------------------
/migrations/2_deploy_vader.js:
--------------------------------------------------------------------------------
1 | const Vader = artifacts.require("Vader");
2 |
3 | module.exports = function (deployer, network) {
4 | // skip development
5 | if (network == "development") {
6 | return
7 | }
8 |
9 | deployer.deploy(Vader);
10 | };
--------------------------------------------------------------------------------
/migrations/3_deploy_converter.js:
--------------------------------------------------------------------------------
1 | const { VETHER, VADER } = require("./constants");
2 |
3 | const Converter = artifacts.require("Converter");
4 |
5 | const MERKLE_ROOTS = {
6 | mainnet:
7 | "0x93bc4275f0e850574c848d04f1a8edbb63a1d961524541e618d28f31b2c6684d",
8 | kovan: "0x076beee425cd687f1c68f81585d9cd19398b7e80cdcc48465c175e959946fdcd",
9 | };
10 |
11 | const CONVERTER_SALTS = {
12 | mainnet: 13662469,
13 | kovan: 28516565,
14 | };
15 |
16 | module.exports = async function (deployer, network) {
17 | // skip development
18 | if (network == "development") {
19 | return;
20 | }
21 |
22 | const vether = VETHER[network];
23 | const vader = VADER[network];
24 | const root = MERKLE_ROOTS[network];
25 | const salt = CONVERTER_SALTS[network];
26 |
27 | await deployer.deploy(Converter, vether, vader, root, salt);
28 | };
29 |
--------------------------------------------------------------------------------
/migrations/4_deploy_vesting.js:
--------------------------------------------------------------------------------
1 | const { VADER, CONVERTER } = require("./constants");
2 | const LinearVesting = artifacts.require("LinearVesting");
3 |
4 | module.exports = async function (deployer, network) {
5 | // skip development
6 | if (network == "development") {
7 | return;
8 | }
9 |
10 | const vader = VADER[network];
11 | const converter = CONVERTER[network];
12 |
13 | await deployer.deploy(LinearVesting, vader, converter);
14 | };
15 |
--------------------------------------------------------------------------------
/migrations/5_deploy_usdv.js:
--------------------------------------------------------------------------------
1 | const USDV = artifacts.require("USDV");
2 | const { VADER } = require("./constants");
3 |
4 | module.exports = async function (deployer, network) {
5 | // skip development
6 | if (network == "development") {
7 | return;
8 | }
9 |
10 | const vader = VADER[network];
11 | console.log(`Vader: ${vader}`);
12 |
13 | await deployer.deploy(USDV, vader);
14 | };
15 |
--------------------------------------------------------------------------------
/migrations/6_deploy_validator.js:
--------------------------------------------------------------------------------
1 | const UnlockValidator = artifacts.require("UnlockValidator");
2 |
3 | module.exports = async function (deployer, network) {
4 | // skip development
5 | if (network == "development") {
6 | return;
7 | }
8 |
9 | await deployer.deploy(UnlockValidator);
10 | };
11 |
--------------------------------------------------------------------------------
/migrations/constants.js:
--------------------------------------------------------------------------------
1 | const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
2 |
3 | const VETHER = {
4 | mainnet: "0x4Ba6dDd7b89ed838FEd25d208D4f644106E34279",
5 | kovan: "0x87D96b9f386d70C72fD7DBcE5a3d2a7D3321446d",
6 | };
7 |
8 | const VADER = {
9 | mainnet: "0x2602278EE1882889B946eb11DC0E810075650983",
10 | kovan: "0xcCb3AeF7Baa506e2D05193e38e88459F68AC1a8F",
11 | };
12 |
13 | const CONVERTER = {
14 | mainnet: "0x6D4a43Ee4770a2Bab97460d3a3B783641D85d108",
15 | kovan: "0x8A313Fa0cb3ed92bE4Cae3a4deF7C32c78181E09",
16 | };
17 |
18 | const VESTING = {
19 | mainnet: "0xb3C600C04AaF603b0f422b73Db244216C2e491f6",
20 | kovan: "0xDaA4B82D5Bdd315a3191B080E26ff7A88eb8034E",
21 | };
22 |
23 | module.exports = {
24 | ZERO_ADDRESS,
25 | VETHER,
26 | VADER,
27 | CONVERTER,
28 | VESTING,
29 | };
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vader-protocol-v2",
3 | "version": "1.0.0",
4 | "description": "Vader Protocol",
5 | "main": "truffle-config.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "truffle test",
11 | "coverage": "truffle run coverage",
12 | "compile": "rm -rf ./build && truffle compile",
13 | "solhint": "solhint 'contracts/**/*.sol'",
14 | "lint": "prettier --write contracts/*.sol contracts/**/*.sol"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/vether/vader-protocol-v2.git"
19 | },
20 | "author": "",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/vether/vader-protocol-v2.git"
24 | },
25 | "homepage": "https://github.com/vether/vader-protocol-v2#readme",
26 | "dependencies": {
27 | "@openzeppelin/contracts": "4.3.2",
28 | "@openzeppelin/contracts-upgradeable": "^4.4.1",
29 | "@openzeppelin/test-helpers": "^0.5.6",
30 | "@truffle/hdwallet-provider": "^2.0.0",
31 | "solidity-coverage": "^0.7.17"
32 | },
33 | "devDependencies": {
34 | "dotenv": "^11.0.0",
35 | "ethers": "^5.5.1",
36 | "ganache-cli": "^6.12.2",
37 | "keccak256": "^1.0.3",
38 | "merkletreejs": "^0.2.24",
39 | "prettier": "^2.5.1",
40 | "prettier-plugin-solidity": "^1.0.0-beta.19",
41 | "solhint": "^3.3.6",
42 | "truffle-plugin-verify": "^0.5.20"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vetherasset/vader-protocol-v2/1aa97ec03f17a6d24f8d56348cb3a3dfab403665/test/.gitkeep
--------------------------------------------------------------------------------
/test/Converter.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | TEN_UNITS,
10 | UNSET_ADDRESS,
11 | parseUnits,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 |
16 | // Project Specific Constants
17 | PROJECT_CONSTANTS,
18 | } = require("./utils")(artifacts);
19 |
20 | const keccak256 = require("keccak256");
21 | const { MerkleTree } = require("merkletreejs");
22 |
23 | const salt = 123;
24 | const chainId = 1337;
25 |
26 | contract("Converter", (accounts) => {
27 | describe("construction", () => {
28 | it("should prevent deployment of the converter with a zero address Vether / Vader contract", async () => {
29 | if (Array.isArray(accounts))
30 | accounts = await verboseAccounts(accounts);
31 |
32 | const { mockMTree } = await deployMock(accounts);
33 | const data = await mockMTree.getRoot(
34 | accounts.account0,
35 | TEN_UNITS,
36 | salt,
37 | chainId
38 | );
39 |
40 | const tree = new MerkleTree([data], keccak256, {
41 | hashLeaves: true,
42 | sortPairs: true,
43 | });
44 |
45 | const merkelRoot = tree.getHexRoot();
46 |
47 | await assertErrors(
48 | deployMock(accounts, {
49 | Converter: () => [
50 | UNSET_ADDRESS,
51 | accounts.account0,
52 | merkelRoot,
53 | salt,
54 | ],
55 | }),
56 | "Converter::constructor: Misconfiguration"
57 | );
58 |
59 | await assertErrors(
60 | deployMock(accounts, {
61 | Converter: () => [
62 | accounts.account0,
63 | UNSET_ADDRESS,
64 | merkelRoot,
65 | salt,
66 | ],
67 | }),
68 | "Converter::constructor: Misconfiguration"
69 | );
70 | });
71 |
72 | it("should deploy the Converter contract with a correct state", async () => {
73 | if (Array.isArray(accounts))
74 | accounts = await verboseAccounts(accounts);
75 | const { converter, vether, vader, vesting, ADMINISTRATOR } =
76 | await deployMock(accounts);
77 | await converter.setVesting(vesting.address, ADMINISTRATOR);
78 | assert.ok(converter.address);
79 |
80 | assert.equal(await converter.vether(), vether.address);
81 | assert.equal(await converter.vader(), vader.address);
82 | });
83 | });
84 |
85 | describe("initialization", () => {
86 | it("should properly initialize the converter by having Vader mint the corresponding amount of tokens to it", async () => {
87 | const { vader, vesting, converter, ADMINISTRATOR } =
88 | await deployMock();
89 |
90 | const { VETH_ALLOCATION, TEAM_ALLOCATION } = PROJECT_CONSTANTS;
91 |
92 | assertBn(await vader.balanceOf(converter.address), 0);
93 |
94 | await vader.setComponents(
95 | converter.address,
96 | vesting.address,
97 | [accounts.account0],
98 | [TEAM_ALLOCATION],
99 | ADMINISTRATOR
100 | );
101 |
102 | assertBn(await vader.balanceOf(converter.address), VETH_ALLOCATION);
103 | });
104 | });
105 |
106 | describe("conversion", () => {
107 | it("should disallow zero-value conversions", async () => {
108 | const { converter } = await deployMock();
109 |
110 | await assertErrors(
111 | converter.convert([], 0, 0),
112 | "Converter::convert: Non-Zero Conversion Amount Required"
113 | );
114 | });
115 |
116 | it("should fail to convert with incorect proof", async () => {
117 | const { converter, vether } = await deployMock();
118 |
119 | await vether.mint(accounts.account0, TEN_UNITS);
120 |
121 | await assertErrors(
122 | converter.convert([], TEN_UNITS, 0),
123 | "Converter::convert: Incorrect Proof Provided"
124 | );
125 | });
126 |
127 | it("should properly support one-way conversion from Vader to Vether", async () => {
128 | if (Array.isArray(accounts))
129 | accounts = await verboseAccounts(accounts);
130 | const { mockMTree } = await deployMock(accounts);
131 | const data = await mockMTree.getRoot(
132 | accounts.account0,
133 | TEN_UNITS,
134 | salt,
135 | chainId
136 | );
137 | const tree = new MerkleTree([data], keccak256, {
138 | hashLeaves: true,
139 | sortPairs: true,
140 | });
141 | const leaf = keccak256(data);
142 | const merkelRoot = tree.getHexRoot();
143 | const proof = tree.getHexProof(leaf);
144 | const { converter, vether, vader, vesting, ADMINISTRATOR } =
145 | await deployMock(accounts, {
146 | Converter: (_, { vader, vether, ADMINISTRATOR }) => [
147 | vether.address,
148 | vader.address,
149 | merkelRoot,
150 | salt,
151 | ADMINISTRATOR,
152 | ],
153 | });
154 |
155 | const {
156 | VADER_VETHER_CONVERSION_RATE,
157 | VETH_ALLOCATION,
158 | BURN,
159 | TEAM_ALLOCATION,
160 | } = PROJECT_CONSTANTS;
161 | await vader.setComponents(
162 | converter.address,
163 | vesting.address,
164 | [accounts.account1],
165 | [TEAM_ALLOCATION],
166 | ADMINISTRATOR
167 | );
168 | await vether.mint(accounts.account0, TEN_UNITS);
169 | await vether.approve(converter.address, TEN_UNITS, {
170 | from: accounts.account0,
171 | });
172 | assertBn(await vether.balanceOf(accounts.account0), TEN_UNITS);
173 | assertBn(await vether.balanceOf(BURN), 0);
174 | assertBn(await vader.balanceOf(accounts.account0), 0);
175 | assertBn(await vader.balanceOf(converter.address), VETH_ALLOCATION);
176 | const expectedConversion = TEN_UNITS.mul(
177 | VADER_VETHER_CONVERSION_RATE
178 | );
179 | await converter.setVesting(vesting.address, ADMINISTRATOR);
180 | assertEvents(
181 | await converter.convert(proof, TEN_UNITS, expectedConversion),
182 | {
183 | Conversion: {
184 | user: accounts.account0,
185 | vetherAmount: TEN_UNITS,
186 | vaderAmount: expectedConversion,
187 | },
188 | }
189 | );
190 | assertBn(await vether.balanceOf(accounts.account0), 0);
191 | assertBn(await vether.balanceOf(BURN), TEN_UNITS);
192 | assertBn(
193 | await vader.balanceOf(converter.address),
194 | VETH_ALLOCATION.sub(expectedConversion)
195 | );
196 | });
197 | });
198 | });
199 |
--------------------------------------------------------------------------------
/test/dex-v2/BasePoolV2.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | UNSET_ADDRESS,
10 | DEFAULT_CONFIGS,
11 | TEN_UNITS,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 | time,
16 | big,
17 | parseUnits,
18 |
19 | // Project Specific Constants
20 | PROJECT_CONSTANTS,
21 |
22 | mintAndApprove,
23 | } = require("../utils")(artifacts);
24 |
25 | contract("Base Pool V2", (accounts) => {
26 | describe("construction", () => {
27 | it("should validate the pool state", async () => {
28 | if (Array.isArray(accounts))
29 | accounts = await verboseAccounts(accounts);
30 | const { poolV2 } = await deployMock(accounts);
31 |
32 | assert.notEqual(await poolV2.nativeAsset, UNSET_ADDRESS);
33 | });
34 | });
35 |
36 | describe("should fail when called by non router address", () => {
37 | it("BasePoolV2::mint", async () => {
38 | const { poolV2 } = await deployMock();
39 | const mockAddress = accounts.account5;
40 |
41 | await assertErrors(
42 | poolV2.mint(
43 | mockAddress,
44 | 0,
45 | 0,
46 | mockAddress,
47 | mockAddress
48 | ),
49 | "BasePoolV2::_onlyRouter: Only Router is allowed to call"
50 | )
51 | });
52 |
53 | it("BasePoolV2::doubleSwap", async () => {
54 | const { poolV2 } = await deployMock();
55 | const mockAddress = accounts.account5;
56 |
57 | await assertErrors(
58 | poolV2.doubleSwap(
59 | mockAddress,
60 | mockAddress,
61 | 0,
62 | mockAddress
63 | ),
64 | "BasePoolV2::_onlyRouter: Only Router is allowed to call"
65 | )
66 | });
67 |
68 | it("BasePoolV2::swap", async () => {
69 | const { poolV2 } = await deployMock();
70 | const mockAddress = accounts.account5;
71 |
72 | await assertErrors(
73 | poolV2.swap(
74 | mockAddress,
75 | 0,
76 | 0,
77 | mockAddress
78 | ),
79 | "BasePoolV2::_onlyRouter: Only Router is allowed to call"
80 | )
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/test/dex-v2/LPWrapper.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | UNSET_ADDRESS,
10 | DEFAULT_CONFIGS,
11 | TEN_UNITS,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 | time,
16 | big,
17 | parseUnits,
18 |
19 | // Project Specific Constants
20 | PROJECT_CONSTANTS,
21 | mintAndApprove,
22 | } = require("../utils")(artifacts);
23 |
24 | contract("LPWrapper", (accounts) => {
25 | describe("construction", () => {
26 | it("should now allow construction with bad arguments", async () => {
27 | if (Array.isArray(accounts))
28 | accounts = await verboseAccounts(accounts);
29 |
30 | await assertErrors(
31 | deployMock(accounts, {
32 | LPWrapper: () => [UNSET_ADDRESS],
33 | }),
34 | "LPWrapper::constructor: Misconfiguration"
35 | );
36 | });
37 | });
38 |
39 | describe("create wrapper", () => {
40 | it("should now allow to create wrappers from a non owner account", async () => {
41 | if (Array.isArray(accounts))
42 | accounts = await verboseAccounts(accounts);
43 |
44 | const { lpWrapper, dai, poolV2, synthFactory, routerV2 } = await deployMock(
45 | accounts
46 | );
47 |
48 | await poolV2.initialize(lpWrapper.address, synthFactory.address, routerV2.address);
49 |
50 | await assertErrors(
51 | lpWrapper.createWrapper(await dai.address),
52 | "Ownable: caller is not the owner"
53 | );
54 | });
55 |
56 | it("should now allow to create wrappers that are already created", async () => {
57 | const { poolV2, dai, mockUsdv, routerV2 } = await deployMock();
58 |
59 | const amount = parseUnits(100, 18);
60 | const balance = parseUnits(10000000, 18);
61 |
62 | // Set support for the tokens
63 | await poolV2.setTokenSupport(dai.address, true);
64 | await poolV2.setTokenSupport(mockUsdv.address, true);
65 |
66 | // Construct the deadline
67 | const latestBlock = await web3.eth.getBlock("latest");
68 | const deadline = latestBlock.timestamp + 1000;
69 |
70 | mintAndApprove(accounts.account0, routerV2.address, dai, balance);
71 | mintAndApprove(
72 | accounts.account0,
73 | routerV2.address,
74 | mockUsdv,
75 | balance
76 | );
77 |
78 | await mockUsdv.approve(poolV2.address, balance);
79 | await dai.approve(poolV2.address, balance);
80 |
81 | const liquidity = parseUnits(10000, 18);
82 |
83 | // Add liquidity
84 | await routerV2.addLiquidity(
85 | mockUsdv.address,
86 | dai.address,
87 | liquidity,
88 | liquidity,
89 | accounts.account0,
90 | deadline
91 | );
92 |
93 | // Mint dai synth
94 | await poolV2.mintSynth(
95 | dai.address,
96 | amount,
97 | accounts.account0,
98 | accounts.account0
99 | );
100 |
101 | await poolV2.setFungibleTokenSupport(dai.address);
102 |
103 | await assertErrors(
104 | poolV2.setFungibleTokenSupport(dai.address),
105 | "LPWrapper::createWrapper: Already Created"
106 | );
107 | });
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/test/governance/GovernorAlpha.cancel.test.js:
--------------------------------------------------------------------------------
1 | const { time } = require("@openzeppelin/test-helpers");
2 | const {
3 | // Deployment Function
4 | deployMock,
5 |
6 | // Testing Utilities
7 | assertErrors,
8 | assertEvents,
9 |
10 | // Library Functions
11 | verboseAccounts,
12 | big,
13 | parseUnits,
14 | } = require("../utils")(artifacts);
15 |
16 | const {
17 | prepareTargetsAndData,
18 | advanceBlockToVotingPeriodEnd,
19 | proposalFee,
20 | description,
21 | } = require("./helpers")({
22 | artifacts,
23 | parseUnits,
24 | big,
25 | });
26 |
27 | const proposalId = big(1);
28 |
29 | contract("GovernorAlpha.cancel", (accounts) => {
30 | before(async function () {
31 | if (Array.isArray(accounts)) accounts = await verboseAccounts(accounts);
32 |
33 | const { governorAlpha, timelock, mockXVader } = await deployMock(
34 | accounts
35 | );
36 | await governorAlpha.setTimelock(timelock.address);
37 |
38 | const { targetsData } = await prepareTargetsAndData({
39 | timelock,
40 | deploy: true,
41 | });
42 |
43 | await mockXVader.mint(accounts.account0, proposalFee.mul(big(4)));
44 |
45 | await mockXVader.approve(governorAlpha.address, proposalFee.mul(big(4)));
46 |
47 | this.governorAlpha = governorAlpha;
48 | this.targetsData = targetsData;
49 | this.mockXVader = mockXVader;
50 | });
51 |
52 | it("should not cancel proposal by non guardian", async function () {
53 | await this.mockXVader.mint(accounts.account1, proposalFee);
54 | await this.mockXVader.approve(this.governorAlpha.address, proposalFee, {
55 | from: accounts.account1,
56 | });
57 |
58 | const { signatures, targetAddresses, values, calldatas } =
59 | this.targetsData;
60 |
61 | await this.governorAlpha.propose(
62 | targetAddresses,
63 | values,
64 | signatures,
65 | calldatas,
66 | description,
67 | {
68 | from: accounts.account1,
69 | }
70 | );
71 |
72 | await assertErrors(
73 | this.governorAlpha.cancel(proposalId, {
74 | from: accounts.account1,
75 | }),
76 | "only guardian can call"
77 | );
78 | });
79 |
80 | it("should successfully cancel proposal when it is pending", async function () {
81 | const proposalTwoId = big(2);
82 | const { signatures, targetAddresses, values, calldatas } =
83 | this.targetsData;
84 |
85 | await this.governorAlpha.propose(
86 | targetAddresses,
87 | values,
88 | signatures,
89 | calldatas,
90 | description
91 | );
92 |
93 | assertEvents(await this.governorAlpha.cancel(proposalTwoId), {
94 | ProposalCanceled: {
95 | id: proposalTwoId,
96 | },
97 | });
98 | });
99 |
100 | it("should successfully cancel proposal when it is active", async function () {
101 | const proposalThreeId = big(3);
102 | const { signatures, targetAddresses, values, calldatas } =
103 | this.targetsData;
104 |
105 | await this.governorAlpha.propose(
106 | targetAddresses,
107 | values,
108 | signatures,
109 | calldatas,
110 | description
111 | );
112 |
113 | await time.advanceBlockTo(
114 | (await web3.eth.getBlock("latest")).number + 1
115 | );
116 |
117 | assertEvents(await this.governorAlpha.cancel(proposalThreeId), {
118 | ProposalCanceled: {
119 | id: proposalThreeId,
120 | },
121 | });
122 | });
123 |
124 | it("should successfully cancel proposal when it is queued", async function () {
125 | const proposalFourId = big(4);
126 | const { signatures, targetAddresses, values, calldatas } =
127 | this.targetsData;
128 |
129 | await this.mockXVader.mint(accounts.voter, parseUnits(1000, 18));
130 | await this.mockXVader.delegate(accounts.voter, {
131 | from: accounts.voter
132 | });
133 |
134 | await this.governorAlpha.propose(
135 | targetAddresses,
136 | values,
137 | signatures,
138 | calldatas,
139 | description
140 | );
141 |
142 | await time.advanceBlockTo(
143 | (await web3.eth.getBlock("latest")).number + 1
144 | );
145 |
146 | await this.governorAlpha.castVote(proposalFourId, true, {
147 | from: accounts.voter
148 | });
149 |
150 | await advanceBlockToVotingPeriodEnd({
151 | governorAlpha: this.governorAlpha,
152 | });
153 |
154 | await this.governorAlpha.queue(proposalFourId);
155 |
156 | assertEvents(await this.governorAlpha.cancel(proposalFourId), {
157 | ProposalCanceled: {
158 | id: big(proposalFourId),
159 | },
160 | });
161 | });
162 |
163 | it("should not cancel proposal when it is already executed", async function () {
164 | const proposalFiveId = big(5);
165 | const { signatures, targetAddresses, values, calldatas } =
166 | this.targetsData;
167 |
168 | await this.governorAlpha.propose(
169 | targetAddresses,
170 | values,
171 | signatures,
172 | calldatas,
173 | description
174 | );
175 |
176 | await time.advanceBlockTo(
177 | (await web3.eth.getBlock("latest")).number + 1
178 | );
179 |
180 | await this.governorAlpha.castVote(proposalFiveId, true, {
181 | from: accounts.voter
182 | });
183 |
184 | await advanceBlockToVotingPeriodEnd({
185 | governorAlpha: this.governorAlpha,
186 | });
187 |
188 | await this.governorAlpha.queue(proposalFiveId);
189 |
190 | await time.increaseTo(
191 | big((await web3.eth.getBlock("latest")).timestamp).add(big(15 * 60))
192 | );
193 |
194 | await this.governorAlpha.execute(proposalFiveId);
195 |
196 | await assertErrors(
197 | this.governorAlpha.cancel(proposalFiveId),
198 | "GovernorAlpha::cancel: cannot cancel executed proposal"
199 | );
200 | });
201 | });
202 |
--------------------------------------------------------------------------------
/test/governance/GovernorAlpha.castVote.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertErrors,
7 | assertEvents,
8 |
9 | // Library Functions
10 | verboseAccounts,
11 | big,
12 | parseUnits,
13 | rpc,
14 | } = require("../utils")(artifacts);
15 |
16 | const {
17 | prepareTargetsAndData,
18 | getTypedDataForVoteBySignature,
19 | decodeSignature,
20 | proposalFee,
21 | description,
22 | } = require("./helpers")({
23 | artifacts,
24 | parseUnits,
25 | big,
26 | });
27 |
28 | const proposalId = big(1);
29 |
30 | contract("GovernorAlpha.castVote", (accounts) => {
31 | before(async function () {
32 | if (Array.isArray(accounts)) accounts = await verboseAccounts(accounts);
33 |
34 | const { governorAlpha, timelock, mockXVader } =
35 | await deployMock(accounts);
36 |
37 | const { targetsData } = await prepareTargetsAndData({
38 | timelock,
39 | deploy: true,
40 | });
41 |
42 | await mockXVader.mint(accounts.account0, proposalFee.mul(big(3)));
43 | await mockXVader.approve(governorAlpha.address, proposalFee.mul(big(3)));
44 |
45 | await mockXVader.mint(accounts.voter, parseUnits(1000, 18));
46 | await mockXVader.delegate(accounts.voter, {
47 | from: accounts.voter
48 | });
49 |
50 | await mockXVader.mint(accounts.account3, parseUnits(1000, 18));
51 | await mockXVader.delegate(accounts.account3, {
52 | from: accounts.account3
53 | });
54 |
55 | await mockXVader.mint(accounts.account4, parseUnits(1000, 18));
56 | await mockXVader.delegate(accounts.account4, {
57 | from: accounts.account4
58 | });
59 |
60 | const { signatures, targetAddresses, values, calldatas } = targetsData;
61 |
62 | await governorAlpha.propose(
63 | targetAddresses,
64 | values,
65 | signatures,
66 | calldatas,
67 | description
68 | );
69 |
70 | this.governorAlpha = governorAlpha;
71 | this.mockXVader = mockXVader;
72 | });
73 |
74 | it("fails when proposal is pending", async function () {
75 | await assertErrors(
76 | this.governorAlpha.castVote(proposalId, true),
77 | "GovernorAlpha::_castVote: voting is closed"
78 | );
79 | });
80 |
81 | it("fails when voter has already casted vote", async function () {
82 | await this.governorAlpha.castVote(proposalId, true, {
83 | from: accounts.voter
84 | });
85 |
86 | await assertErrors(
87 | this.governorAlpha.castVote(proposalId, true, {
88 | from: accounts.voter
89 | }),
90 | "GovernorAlpha::_castVote: voter already voted"
91 | );
92 | });
93 |
94 | it("should cast vote and assert the VoteCast event's data", async function () {
95 | const startBlock = (await this.governorAlpha.proposals(proposalId)).startBlock;
96 | const votes = await this.mockXVader.getPastVotes(accounts.account3, startBlock);
97 | const support = true;
98 |
99 | assertEvents(
100 | await this.governorAlpha.castVote(proposalId, support, {
101 | from: accounts.account3,
102 | }),
103 | {
104 | VoteCast: {
105 | voter: accounts.account3,
106 | proposalId,
107 | support,
108 | votes,
109 | },
110 | }
111 | );
112 |
113 | assert.equal(
114 | (
115 | await this.governorAlpha.getReceipt(proposalId, accounts.account3)
116 | )[0],
117 | true
118 | );
119 | });
120 |
121 | describe("vote by signature", () => {
122 | before(async function () {
123 | this.voter = accounts.account4;
124 | const typedData = getTypedDataForVoteBySignature({
125 | verifyingContract: this.governorAlpha.address,
126 | chainId: (await this.governorAlpha.CHAINID()).toNumber(),
127 | });
128 |
129 | const result = await rpc({
130 | method: "eth_signTypedData",
131 | params: [this.voter, typedData],
132 | from: this.voter,
133 | });
134 |
135 | [this.v, this.r, this.s] = Object.values(
136 | decodeSignature(result.result)
137 | );
138 |
139 | [this.id, this.support] = Object.values(typedData.message);
140 |
141 | const startBlock = (await this.governorAlpha.proposals(proposalId)).startBlock;
142 | this.votes = await this.mockXVader.getPastVotes(this.voter, startBlock)
143 | });
144 |
145 | it("should successfully cast vote", async function () {
146 | const { id, support, v, r, s, voter, votes } = this;
147 |
148 | assertEvents(
149 | await this.governorAlpha.castVoteBySig(id, support, v, r, s, {
150 | from: voter,
151 | }),
152 | {
153 | VoteCast: {
154 | voter,
155 | proposalId: big(id),
156 | support,
157 | votes,
158 | },
159 | }
160 | );
161 | });
162 |
163 | it("fails to replay signature of vote casting", async function () {
164 | const { id, support, v, r, s, voter } = this;
165 |
166 | await assertErrors(
167 | this.governorAlpha.castVoteBySig(id, support, v, r, s, {
168 | from: voter,
169 | }),
170 | "GovernorAlpha::_castVote: voter already voted"
171 | );
172 | });
173 | });
174 | });
175 |
--------------------------------------------------------------------------------
/test/governance/GovernorAlpha.execute.test.js:
--------------------------------------------------------------------------------
1 | const { time } = require("@openzeppelin/test-helpers");
2 | const {
3 | // Deployment Function
4 | deployMock,
5 |
6 | // Testing Utilities
7 | assertErrors,
8 | assertEvents,
9 |
10 | // Library Functions
11 | verboseAccounts,
12 | big,
13 | parseUnits,
14 | } = require("../utils")(artifacts);
15 |
16 | const {
17 | prepareTargetsAndData,
18 | advanceBlockToVotingPeriodEnd,
19 | proposalFee,
20 | description,
21 | } = require("./helpers")({
22 | artifacts,
23 | parseUnits,
24 | big,
25 | });
26 |
27 | const proposalId = big(1);
28 |
29 | contract("GovernorAlpha.execute", (accounts) => {
30 | before(async function () {
31 | if (Array.isArray(accounts)) accounts = await verboseAccounts(accounts);
32 |
33 | const { governorAlpha, timelock, mockXVader } = await deployMock(
34 | accounts
35 | );
36 | await governorAlpha.setTimelock(timelock.address);
37 |
38 | const { targetsData } = await prepareTargetsAndData({
39 | timelock,
40 | deploy: true,
41 | });
42 |
43 | await mockXVader.mint(accounts.account0, proposalFee);
44 | await mockXVader.approve(governorAlpha.address, proposalFee);
45 |
46 | await mockXVader.mint(accounts.voter, parseUnits(1000, 18));
47 | await mockXVader.delegate(accounts.voter, {
48 | from: accounts.voter
49 | });
50 |
51 | const { signatures, targetAddresses, values, calldatas } = targetsData;
52 |
53 | await governorAlpha.propose(
54 | targetAddresses,
55 | values,
56 | signatures,
57 | calldatas,
58 | description
59 | );
60 |
61 | await time.advanceBlockTo(
62 | (await web3.eth.getBlock("latest")).number + 1
63 | );
64 |
65 | await governorAlpha.castVote(proposalId, true, {
66 | from: accounts.voter,
67 | });
68 |
69 | await advanceBlockToVotingPeriodEnd({
70 | governorAlpha,
71 | });
72 |
73 | this.governorAlpha = governorAlpha;
74 | });
75 |
76 | it("should not execute proposal when not in queue", async function () {
77 | await assertErrors(
78 | this.governorAlpha.execute(proposalId),
79 | "GovernorAlpha::execute: proposal can only be executed if it is queued"
80 | );
81 | });
82 |
83 | it("should successfully execute proposal and assert ProposalExecuted event's data", async function () {
84 | await this.governorAlpha.queue(proposalId);
85 | await time.increaseTo(
86 | big((await web3.eth.getBlock("latest")).timestamp).add(big(15 * 60)) // increase time to eta
87 | );
88 | assertEvents(await this.governorAlpha.execute(proposalId), {
89 | ProposalExecuted: {
90 | id: proposalId,
91 | },
92 | });
93 | });
94 | });
95 |
--------------------------------------------------------------------------------
/test/governance/GovernorAlpha.state.test.js:
--------------------------------------------------------------------------------
1 | const { time } = require("@openzeppelin/test-helpers");
2 | const {
3 | // Deployment Function
4 | deployMock,
5 |
6 | // Testing Utilities
7 | assertErrors,
8 | assertBn,
9 |
10 | // Project Specific Constants
11 | UNSET_ADDRESS,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 | big,
16 | parseUnits,
17 | } = require("../utils")(artifacts);
18 |
19 | const { advanceBlockToVotingPeriodEnd, getTxHash } = require("./helpers")({
20 | artifacts,
21 | parseUnits,
22 | big,
23 | });
24 |
25 | contract("GovernorAlpha state change tests", (accounts) => {
26 | before(async function () {
27 | if (Array.isArray(accounts)) accounts = await verboseAccounts(accounts);
28 |
29 | const { governorAlpha, timelock, mockXVader } = await deployMock(
30 | accounts
31 | );
32 | await governorAlpha.setTimelock(timelock.address);
33 |
34 | const { governorAlpha: governorAlphaTwo } = await deployMock(accounts);
35 | await governorAlphaTwo.setTimelock(timelock.address);
36 |
37 | this.governorAlpha = governorAlpha;
38 | this.timelock = timelock;
39 | this.eta = big((await web3.eth.getBlock("latest")).timestamp).add(
40 | big(12).mul(big(60))
41 | ); // eta is 12 minutes
42 | this.governorAlphaTwo = governorAlphaTwo;
43 | this.mockXVader = mockXVader;
44 | });
45 |
46 | describe("changeFeeReceiver", () => {
47 | it("non-guardian should fail to call", async function () {
48 | await assertErrors(
49 | this.governorAlpha.changeFeeReceiver(accounts.account1, {
50 | from: accounts.account1,
51 | }),
52 | "only guardian can call"
53 | );
54 | });
55 |
56 | it("guardian should successfully call", async function () {
57 | await this.governorAlpha.changeFeeReceiver(accounts.account1);
58 |
59 | assert.equal(
60 | await this.governorAlpha.feeReceiver(),
61 | accounts.account1
62 | );
63 | });
64 | });
65 |
66 | describe("changeFeeAmount", () => {
67 | it("non-guardian should fail to call", async function () {
68 | await assertErrors(
69 | this.governorAlpha.changeFeeAmount(parseUnits(10000, 18), {
70 | from: accounts.account1,
71 | }),
72 | "only guardian can call"
73 | );
74 | });
75 |
76 | it("guardian should successfully call", async function () {
77 | await this.governorAlpha.changeFeeAmount(parseUnits(10000, 18));
78 |
79 | assertBn(
80 | await this.governorAlpha.feeAmount(),
81 | parseUnits(10000, 18)
82 | );
83 | });
84 | });
85 |
86 | describe("__queueSetTimelockPendingAdmin", () => {
87 | it("non-guardian should fail to call", async function () {
88 | await assertErrors(
89 | this.governorAlpha.__queueSetTimelockPendingAdmin(
90 | this.governorAlphaTwo.address,
91 | this.eta,
92 | {
93 | from: accounts.account1,
94 | }
95 | ),
96 | "only guardian can call"
97 | );
98 | });
99 |
100 | it("guardian should successfully call", async function () {
101 | await this.governorAlpha.__queueSetTimelockPendingAdmin(
102 | this.governorAlphaTwo.address,
103 | this.eta
104 | );
105 |
106 | const txHash = getTxHash([
107 | this.timelock.address,
108 | 0,
109 | "setPendingAdmin(address)",
110 | web3.eth.abi.encodeParameters(
111 | ["address"],
112 | [this.governorAlphaTwo.address]
113 | ),
114 | this.eta,
115 | ]);
116 |
117 | assert.equal(await this.timelock.queuedTransactions(txHash), true);
118 | });
119 | });
120 |
121 | describe("__executeSetTimelockPendingAdmin", () => {
122 | it("non-guardian should fail to call", async function () {
123 | await assertErrors(
124 | this.governorAlpha.__executeSetTimelockPendingAdmin(
125 | this.governorAlphaTwo.address,
126 | this.eta,
127 | {
128 | from: accounts.account1,
129 | }
130 | ),
131 | "only guardian can call"
132 | );
133 | });
134 |
135 | it("guardian should successfully call", async function () {
136 | await time.increaseTo(this.eta);
137 |
138 | await this.governorAlpha.__executeSetTimelockPendingAdmin(
139 | this.governorAlphaTwo.address,
140 | this.eta
141 | );
142 | });
143 | });
144 |
145 | describe("__acceptAdmin", () => {
146 | it("non-guardian should fail to call", async function () {
147 | await assertErrors(
148 | this.governorAlphaTwo.__acceptAdmin({
149 | from: accounts.account1,
150 | }),
151 | "only guardian can call"
152 | );
153 | });
154 |
155 | it("guardian should successfully call", async function () {
156 | await this.governorAlphaTwo.__acceptAdmin();
157 |
158 | assert.equal(
159 | await this.timelock.admin(),
160 | this.governorAlphaTwo.address
161 | );
162 | });
163 | });
164 |
165 | describe("__abdicate", () => {
166 | it("non-guardian should fail to call", async function () {
167 | await assertErrors(
168 | this.governorAlpha.__abdicate({
169 | from: accounts.account1,
170 | }),
171 | "only guardian can call"
172 | );
173 | });
174 |
175 | it("guardian should successfully call", async function () {
176 | await this.governorAlpha.__abdicate();
177 |
178 | assert.equal(await this.governorAlpha.guardian(), UNSET_ADDRESS);
179 | });
180 | });
181 |
182 | describe("changeCouncil", () => {
183 | it("non-timelock should fail to call", async function () {
184 | await assertErrors(
185 | this.governorAlpha.changeCouncil(accounts.account1),
186 | "only timelock can call"
187 | );
188 | });
189 |
190 | it("time lock successfully changes council", async () => {
191 | const proposalId = big(1);
192 | const { governorAlpha, timelock, mockXVader } = await deployMock(
193 | accounts
194 | );
195 | await governorAlpha.setTimelock(timelock.address);
196 |
197 | await mockXVader.mint(accounts.account0, parseUnits(1000, 18));
198 | await mockXVader.approve(governorAlpha.address, parseUnits(1000, 18));
199 |
200 | await mockXVader.mint(accounts.voter, parseUnits(1000, 18));
201 | await mockXVader.delegate(accounts.voter, {
202 | from: accounts.voter,
203 | })
204 |
205 | const calldata = governorAlpha.contract.methods[
206 | "changeCouncil(address)"
207 | ](accounts.account1).encodeABI();
208 |
209 | await governorAlpha.propose(
210 | [governorAlpha.address],
211 | [0],
212 | [""],
213 | [calldata],
214 | "change council address"
215 | );
216 |
217 | await time.advanceBlockTo(
218 | (await web3.eth.getBlock("latest")).number + 1
219 | );
220 |
221 | await governorAlpha.castVote(proposalId, true, {
222 | from: accounts.voter,
223 | });
224 |
225 | await advanceBlockToVotingPeriodEnd({
226 | governorAlpha,
227 | });
228 |
229 | await governorAlpha.queue(proposalId);
230 |
231 | // increase time pass eta
232 | await time.increaseTo(
233 | big((await web3.eth.getBlock("latest")).timestamp).add(
234 | big(16).mul(big(60))
235 | )
236 | );
237 |
238 | await governorAlpha.execute(proposalId);
239 |
240 | assert.equal(await governorAlpha.council(), accounts.account1);
241 | });
242 | });
243 | });
244 |
--------------------------------------------------------------------------------
/test/governance/GovernorAlpha.veto.test.js:
--------------------------------------------------------------------------------
1 | const { time } = require("@openzeppelin/test-helpers");
2 | const {
3 | // Deployment Function
4 | deployMock,
5 |
6 | // Testing Utilities
7 | assertErrors,
8 | assertEvents,
9 |
10 | // Library Functions
11 | verboseAccounts,
12 | big,
13 | parseUnits,
14 | } = require("../utils")(artifacts);
15 |
16 | const {
17 | prepareTargetsAndData,
18 | advanceBlockToVotingPeriodEnd,
19 | proposalFee,
20 | description,
21 | } = require("./helpers")({
22 | artifacts,
23 | parseUnits,
24 | big,
25 | });
26 |
27 | contract("GovernorAlpha.veto", (accounts) => {
28 | before(async function () {
29 | if (Array.isArray(accounts)) accounts = await verboseAccounts(accounts);
30 |
31 | const { governorAlpha, timelock, mockXVader } = await deployMock(
32 | accounts
33 | );
34 | await governorAlpha.setTimelock(timelock.address);
35 |
36 | const { targetsData } = await prepareTargetsAndData({
37 | timelock,
38 | deploy: true,
39 | });
40 |
41 | await mockXVader.mint(accounts.account0, proposalFee.mul(big(4)));
42 |
43 | await mockXVader.approve(governorAlpha.address, proposalFee.mul(big(4)));
44 |
45 | this.governorAlpha = governorAlpha;
46 | this.targetsData = targetsData;
47 | this.count = 1;
48 | });
49 |
50 | afterEach(async function () {
51 | await this.governorAlpha.cancel(this.count);
52 | this.count += 1;
53 | });
54 |
55 | it("non-council should not be able to veto proposal", async function () {
56 | const { signatures, targetAddresses, values, calldatas } =
57 | this.targetsData;
58 |
59 | await this.governorAlpha.propose(
60 | targetAddresses,
61 | values,
62 | signatures,
63 | calldatas,
64 | description
65 | );
66 |
67 | await assertErrors(
68 | this.governorAlpha.veto(1, true, {
69 | from: accounts.account1,
70 | }),
71 | "only council can call"
72 | );
73 | });
74 |
75 | it("fails to veto proposal when it is succeeded", async function () {
76 | const { signatures, targetAddresses, values, calldatas } =
77 | this.targetsData;
78 |
79 | await this.governorAlpha.propose(
80 | targetAddresses,
81 | values,
82 | signatures,
83 | calldatas,
84 | description
85 | );
86 |
87 | await time.advanceBlockTo(
88 | (await web3.eth.getBlock("latest")).number + 1
89 | );
90 |
91 | await this.governorAlpha.castVote(2, true);
92 |
93 | await advanceBlockToVotingPeriodEnd({
94 | governorAlpha: this.governorAlpha,
95 | });
96 |
97 | await assertErrors(
98 | this.governorAlpha.veto(2, true),
99 | "Proposal can only be vetoed when active"
100 | );
101 | });
102 |
103 | it("fails to veto a proposal for changing council", async function () {
104 | const { signatures, targetAddresses, values, calldatas } =
105 | this.targetsData;
106 |
107 | const _targetAddresses = [
108 | ...targetAddresses,
109 | this.governorAlpha.address,
110 | ];
111 | _targetAddresses.shift();
112 |
113 | const _calldatas = [
114 | ...calldatas,
115 | web3.eth.abi.encodeParameters(
116 | [
117 | 'bytes4',
118 | 'address'
119 | ],
120 | [
121 | web3.utils.keccak256('changeCouncil(address)').slice(0, 10),
122 | accounts.account5
123 | ])
124 | ];
125 | _calldatas.shift();
126 |
127 | await this.governorAlpha.propose(
128 | _targetAddresses,
129 | values,
130 | signatures,
131 | _calldatas,
132 | description
133 | );
134 |
135 | await advanceBlockToVotingPeriodEnd({
136 | governorAlpha: this.governorAlpha,
137 | });
138 |
139 | await assertErrors(
140 | this.governorAlpha.veto(3, true),
141 | "GovernorAlpha::veto: council cannot veto a council changing proposal"
142 | );
143 | });
144 |
145 | it("successfully vetoes a proposal and asserts ProposalVetoed's data", async function () {
146 | const { signatures, targetAddresses, values, calldatas } =
147 | this.targetsData;
148 |
149 | await this.governorAlpha.propose(
150 | targetAddresses,
151 | values,
152 | signatures,
153 | calldatas,
154 | description
155 | );
156 |
157 | const proposalId = 4;
158 | const support = true;
159 |
160 | assertEvents(await this.governorAlpha.veto(proposalId, support), {
161 | ProposalVetoed: {
162 | proposalId,
163 | support,
164 | },
165 | });
166 | });
167 | });
168 |
--------------------------------------------------------------------------------
/test/governance/helpers/index.js:
--------------------------------------------------------------------------------
1 | const { time } = require('@openzeppelin/test-helpers');
2 |
3 | module.exports = ({
4 | artifacts,
5 | parseUnits,
6 | big,
7 | }) => {
8 | // Mocks
9 | const MockTarget = artifacts.require('MockTarget');
10 |
11 | // governance specific constants
12 | const proposalFee = parseUnits(1000, 18);
13 | const defaultSignature = 'setStateToTrue()';
14 | const description = 'A proposal';
15 |
16 | const cached = {};
17 |
18 | const prepareTargetsAndData = async ({
19 | timelock,
20 | numberOfMocks = 3,
21 | signature = defaultSignature,
22 | value = 0,
23 | deploy = false,
24 | arg,
25 | }) => {
26 | if (!deploy) {
27 | if (cached) return cached;
28 |
29 | log('prepareTargetsAndData: Incorrect Deployment Invocation');
30 | process.exit(1);
31 | }
32 |
33 | const basicArray = new Array(numberOfMocks).fill(0);
34 |
35 | cached.targets = await basicArray
36 | .reduce(
37 | async (acc) => [
38 | ...(await acc),
39 | (await MockTarget.new(timelock.address)),
40 | ],
41 | [],
42 | );
43 |
44 | const targetAddresses = basicArray.map((_, idx) => cached.targets[idx].address);
45 |
46 | const values = basicArray.map(() => big(value));
47 |
48 | const signatures = basicArray.map(() => signature);
49 |
50 | const calldata = arg
51 | ? cached.targets[0].contract.methods[signature](arg).encodeABI()
52 | : cached.targets[0].contract.methods[signature]().encodeABI();
53 |
54 | const calldatas = basicArray.map(() => calldata);
55 |
56 | cached.targetsData = {
57 | signatures,
58 | targetAddresses,
59 | values,
60 | calldatas,
61 | };
62 |
63 | return cached;
64 | };
65 |
66 | const decodeSignature = (sig) => {
67 | const signature = sig.substring(2);
68 | const r = `0x${signature.substring(0, 64)}`;
69 | const s = `0x${signature.substring(64, 128)}`;
70 | const v = parseInt(signature.substring(128, 130), 16);
71 |
72 | return {
73 | v,
74 | r,
75 | s,
76 | };
77 | };
78 |
79 | const getTypedDataForVoteBySignature = ({
80 | verifyingContract,
81 | chainId,
82 | proposalId = 1,
83 | support = true,
84 | name = 'Vader Governor Alpha',
85 | }) => ({
86 | types: {
87 | EIP712Domain: [
88 | { name: 'name', type: 'string' },
89 | { name: 'chainId', type: 'uint256' },
90 | { name: 'verifyingContract', type: 'address' },
91 | ],
92 | Ballot: [
93 | { name: 'proposalId', type: 'uint256' },
94 | { name: 'support', type: 'bool' },
95 | ],
96 | },
97 | primaryType: 'Ballot',
98 | domain: {
99 | // eslint-disable-next-line no-restricted-globals
100 | name,
101 | chainId,
102 | verifyingContract,
103 | },
104 | message: {
105 | proposalId,
106 | support,
107 | },
108 | });
109 |
110 | const advanceBlockToVotingPeriodEnd = async ({
111 | governorAlpha,
112 | }) => {
113 | const votingPeriod = await governorAlpha.VOTING_PERIOD();
114 | await time.advanceBlockTo(
115 | (await web3.eth.getBlock('latest'))
116 | .number + votingPeriod.toNumber(),
117 | );
118 | };
119 |
120 | const getTxHash = (args) => web3.utils.keccak256(
121 | web3.eth.abi.encodeParameters(
122 | [
123 | 'address',
124 | 'uint256',
125 | 'string',
126 | 'bytes',
127 | 'uint256',
128 | ],
129 | [
130 | ...args,
131 | ],
132 | ),
133 | );
134 |
135 | return {
136 | prepareTargetsAndData,
137 | decodeSignature,
138 | getTypedDataForVoteBySignature,
139 | advanceBlockToVotingPeriodEnd,
140 | proposalFee,
141 | description,
142 | getTxHash,
143 | };
144 | };
145 |
--------------------------------------------------------------------------------
/test/integration/usdv-mint-burn.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 |
8 | // Library Functions
9 | verboseAccounts,
10 | time,
11 | big,
12 | } = require("../utils")(artifacts);
13 |
14 | const MAX_FEE = big(10000);
15 | const ONE_VADER = big(10).pow(big(18));
16 |
17 | // Vader amount minted to users
18 | const V_AMOUNT = big(100).mul(ONE_VADER);
19 |
20 | // Daily limits
21 | const FEE = big(200);
22 | const MINT_LIMIT = 200n * 10n ** 18n;
23 | const BURN_LIMIT = 100n * 10n ** 18n;
24 | const LOCK_DURATION = 10;
25 | // Partner limits
26 | const PARTNER_FEE = big(100);
27 | const PARTNER_MINT_LIMIT = 300n * 10n ** 18n;
28 | const PARTNER_BURN_LIMIT = 200n * 10n ** 18n;
29 | const PARTNER_LOCK_DURATION = 0;
30 |
31 | contract("VADER + USDV + VaderMinter", (accounts) => {
32 | let vader;
33 | let usdv;
34 | let minter;
35 | let validator;
36 | let lbt;
37 | let admin;
38 | let user;
39 | let partner;
40 | before(async () => {
41 | accounts = await verboseAccounts(accounts);
42 | const cache = await deployMock(accounts);
43 |
44 | vader = cache.vader;
45 | usdv = cache.usdv;
46 | minter = cache.vaderMinter;
47 | validator = cache.validator;
48 | lbt = cache.mockLbt;
49 | admin = accounts.administrator;
50 | user = accounts.account0;
51 | partner = accounts.account1;
52 |
53 | // Vader setup //
54 | const converter = cache.mock;
55 | const vesting = cache.mock;
56 | await vader.setComponents(converter.address, vesting.address, [], [], {
57 | from: admin,
58 | });
59 |
60 | // transfer Vader to users
61 | await vader.createEmission(user, V_AMOUNT, { from: admin });
62 | await vader.createEmission(partner, V_AMOUNT, { from: admin });
63 |
64 | // Minter setup
65 | await minter.initialize({ from: admin });
66 | await minter.setDailyLimits(
67 | FEE,
68 | MINT_LIMIT,
69 | BURN_LIMIT,
70 | LOCK_DURATION,
71 | { from: admin }
72 | );
73 | await minter.whitelistPartner(
74 | partner,
75 | PARTNER_FEE,
76 | PARTNER_MINT_LIMIT,
77 | PARTNER_BURN_LIMIT,
78 | PARTNER_LOCK_DURATION,
79 | { from: admin }
80 | );
81 | await minter.setLBT(lbt.address, { from: admin });
82 |
83 | // USDV setup
84 | await usdv.setValidator(validator.address, { from: admin });
85 | await usdv.setMinter(minter.address, { from: admin });
86 |
87 | // Vader final setup
88 | await vader.setUSDV(usdv.address, { from: admin });
89 | });
90 |
91 | const snapshot = async () => {
92 | return {
93 | vader: {
94 | user: await vader.balanceOf(user),
95 | partner: await vader.balanceOf(partner),
96 | usdv: await vader.balanceOf(usdv.address),
97 | },
98 | usdv: {
99 | user: await usdv.balanceOf(user),
100 | partner: await usdv.balanceOf(partner),
101 | usdv: await usdv.balanceOf(usdv.address),
102 | },
103 | };
104 | };
105 |
106 | function calcFee(amount, fee) {
107 | return amount.mul(fee).div(MAX_FEE);
108 | }
109 |
110 | describe("user", () => {
111 | it("should mint USDV", async () => {
112 | await vader.approve(usdv.address, V_AMOUNT, { from: user });
113 |
114 | const _before = await snapshot();
115 | await minter.mint(V_AMOUNT, 1, { from: user });
116 | const _after = await snapshot();
117 |
118 | const vPrice = await lbt._getVaderUsdPrice_();
119 | let uAmount = vPrice.mul(V_AMOUNT).div(ONE_VADER);
120 | const fee = calcFee(uAmount, await minter.getPublicFee());
121 | uAmount = uAmount.sub(fee);
122 |
123 | assertBn(_after.vader.user, _before.vader.user.sub(V_AMOUNT));
124 |
125 | // USDV is locked
126 | if (LOCK_DURATION > 0) {
127 | assertBn(_after.usdv.user, _before.usdv.user);
128 | assertBn(_after.usdv.usdv, _before.usdv.usdv.add(uAmount));
129 | } else {
130 | assertBn(_after.usdv.user, _before.usdv.user.add(uAmount));
131 | assertBn(_after.usdv.usdv, _before.usdv.usdv);
132 | }
133 | });
134 |
135 | it("should burn USDV", async () => {
136 | if (LOCK_DURATION > 0) {
137 | await time.increase(LOCK_DURATION);
138 | await usdv.claim(0, { from: user });
139 | }
140 |
141 | const uAmount = await usdv.balanceOf(user);
142 |
143 | const _before = await snapshot();
144 | await minter.burn(uAmount, 1, { from: user });
145 | const _after = await snapshot();
146 |
147 | const vPrice = await lbt._getVaderUsdPrice_();
148 | let vAmount = uAmount.mul(ONE_VADER).div(vPrice);
149 | const fee = calcFee(vAmount, await minter.getPublicFee());
150 | vAmount = vAmount.sub(fee);
151 |
152 | assertBn(_after.usdv.user, _before.usdv.user.sub(uAmount));
153 |
154 | // Vader is locked
155 | if (LOCK_DURATION > 0) {
156 | assertBn(_after.vader.user, _before.vader.user);
157 | assertBn(_after.vader.usdv, _before.vader.usdv.add(vAmount));
158 | } else {
159 | assertBn(_after.vader.user, _before.vader.user.add(vAmount));
160 | assertBn(_after.vader.usdv, _before.vader.usdv);
161 | }
162 | });
163 | });
164 |
165 | describe("partner", () => {
166 | it("should mint USDV", async () => {
167 | await vader.approve(usdv.address, V_AMOUNT, { from: partner });
168 |
169 | const _before = await snapshot();
170 | await minter.partnerMint(V_AMOUNT, 1, { from: partner });
171 | const _after = await snapshot();
172 |
173 | const vPrice = await lbt._getVaderUsdPrice_();
174 | let uAmount = vPrice.mul(V_AMOUNT).div(ONE_VADER);
175 | const fee = calcFee(uAmount, PARTNER_FEE);
176 | uAmount = uAmount.sub(fee);
177 |
178 | assertBn(_after.vader.partner, _before.vader.partner.sub(V_AMOUNT));
179 |
180 | // USDV is locked
181 | if (PARTNER_LOCK_DURATION > 0) {
182 | assertBn(_after.usdv.partner, _before.usdv.partner);
183 | assertBn(_after.usdv.usdv, _before.usdv.usdv.add(uAmount));
184 | } else {
185 | assertBn(
186 | _after.usdv.partner,
187 | _before.usdv.partner.add(uAmount)
188 | );
189 | assertBn(_after.usdv.usdv, _before.usdv.usdv);
190 | }
191 | });
192 |
193 | it("should burn USDV", async () => {
194 | if (PARTNER_LOCK_DURATION > 0) {
195 | await time.increase(PARTNER_LOCK_DURATION);
196 | await usdv.claim(0, { from: partner });
197 | }
198 |
199 | const uAmount = await usdv.balanceOf(partner);
200 |
201 | const _before = await snapshot();
202 | await minter.partnerBurn(uAmount, 1, { from: partner });
203 | const _after = await snapshot();
204 |
205 | const vPrice = await lbt._getVaderUsdPrice_();
206 | let vAmount = uAmount.mul(ONE_VADER).div(vPrice);
207 | const fee = calcFee(vAmount, PARTNER_FEE);
208 | vAmount = vAmount.sub(fee);
209 |
210 | assertBn(_after.usdv.partner, _before.usdv.partner.sub(uAmount));
211 |
212 | // Vader is locked
213 | if (PARTNER_LOCK_DURATION > 0) {
214 | assertBn(_after.vader.partner, _before.vader.partner);
215 | assertBn(_after.vader.usdv, _before.vader.usdv.add(vAmount));
216 | } else {
217 | assertBn(
218 | _after.vader.partner,
219 | _before.vader.partner.add(vAmount)
220 | );
221 | assertBn(_after.vader.usdv, _before.vader.usdv);
222 | }
223 | });
224 | });
225 | });
226 |
--------------------------------------------------------------------------------
/test/reserve/VaderReserve.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | UNSET_ADDRESS,
10 | DEFAULT_CONFIGS,
11 | TEN_UNITS,
12 | parseUnits,
13 |
14 | // Library Functions
15 | verboseAccounts,
16 | time,
17 | big,
18 |
19 | // Project Specific Constants
20 | PROJECT_CONSTANTS,
21 | } = require("../utils")(artifacts);
22 |
23 | contract("VaderReserve", (accounts) => {
24 | describe("construction", () => {
25 | it("should not allow construction with incorrect arguments", async () => {
26 | if (Array.isArray(accounts))
27 | accounts = await verboseAccounts(accounts);
28 |
29 | await assertErrors(
30 | deployMock(accounts, {
31 | VaderReserve: () => [UNSET_ADDRESS],
32 | }),
33 | "VaderReserve::constructor: Incorrect Arguments"
34 | );
35 | });
36 |
37 | it("should construct the reserve", async () => {
38 | if (Array.isArray(accounts))
39 | accounts = await verboseAccounts(accounts);
40 |
41 | const { reserve, vader } = await deployMock(accounts);
42 |
43 | // Check the state
44 | assert.notEqual(reserve.address, UNSET_ADDRESS);
45 | assert.equal(await reserve.vader(), vader.address);
46 | assert.equal(await reserve.router(), UNSET_ADDRESS);
47 | });
48 | });
49 |
50 | describe("initialize", () => {
51 | it("should not allow to initialize with incorrect arguments", async () => {
52 | if (Array.isArray(accounts))
53 | accounts = await verboseAccounts(accounts);
54 |
55 | const { reserve, router } = await deployMock(accounts);
56 |
57 | await assertErrors(
58 | reserve.initialize(UNSET_ADDRESS, accounts.dao),
59 | "VaderReserve::initialize: Incorrect Arguments"
60 | );
61 |
62 | await assertErrors(
63 | reserve.initialize(router.address, UNSET_ADDRESS),
64 | "VaderReserve::initialize: Incorrect Arguments"
65 | );
66 |
67 | await assertErrors(
68 | reserve.initialize(router.address, accounts.dao, {
69 | from: accounts.account1,
70 | }),
71 | "Ownable: caller is not the owner"
72 | );
73 | });
74 | });
75 |
76 | describe("reserve", () => {
77 | it("should return the vader balance of the reserve contract", async () => {
78 | if (Array.isArray(accounts))
79 | accounts = await verboseAccounts(accounts);
80 |
81 | const { reserve } = await deployMock(accounts);
82 | const amount = await reserve.reserve();
83 | // We dont have a reserve balance yet
84 | assert.equal(amount, 0);
85 | });
86 | });
87 |
88 | describe("grant", () => {
89 | it("should grant from the owners account and check the balance and state", async () => {
90 | if (Array.isArray(accounts))
91 | accounts = await verboseAccounts(accounts);
92 |
93 | const { reserve, routerV2, vader } = await deployMock(accounts);
94 |
95 | // Init
96 | await reserve.initialize(routerV2.address, accounts.dao);
97 |
98 | // Check if the ownership is now the dao
99 | assert.equal(await reserve.owner(), accounts.dao);
100 |
101 | const amount = parseUnits(1000, 18);
102 |
103 | // Grant once
104 | await reserve.grant(accounts.account1, amount, {
105 | from: accounts.dao,
106 | });
107 |
108 | // Check the vader balance
109 | // For the moment it should be 0 as we dont have vader yet
110 | assertBn(await vader.balanceOf(accounts.account1), 0);
111 | });
112 | });
113 |
114 | describe("reimburseImpermanentLoss", () => {
115 | it("should not allow to reimburseImpermanentLoss if caller is not the router", async () => {
116 | if (Array.isArray(accounts))
117 | accounts = await verboseAccounts(accounts);
118 |
119 | const { reserve } = await deployMock(accounts);
120 | await assertErrors(
121 | reserve.reimburseImpermanentLoss(accounts.account1, 1000),
122 | "VaderReserve::reimburseImpermanentLoss: Insufficient Priviledges"
123 | );
124 | });
125 | });
126 |
127 | describe("throttle", () => {
128 | it("should not allow to call grant to fast", async () => {
129 | if (Array.isArray(accounts))
130 | accounts = await verboseAccounts(accounts);
131 |
132 | const { reserve, routerV2 } = await deployMock(accounts);
133 |
134 | // Init
135 | await reserve.initialize(routerV2.address, accounts.dao);
136 |
137 | const amount = parseUnits(1000, 18);
138 |
139 | // Grant once
140 | await reserve.grant(accounts.account1, amount, {
141 | from: accounts.dao,
142 | });
143 | // Grant again
144 | await assertErrors(
145 | reserve.grant(accounts.account1, amount, {
146 | from: accounts.dao,
147 | }),
148 | "VaderReserve::throttle: Grant Too Fast"
149 | );
150 | });
151 | });
152 | });
153 |
--------------------------------------------------------------------------------
/test/tokens/validator/UnlockValidator.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | UNSET_ADDRESS,
10 | TEN_UNITS,
11 | parseUnits,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 | time,
16 | big,
17 |
18 | // Project Specific Utilities
19 | advanceEpochs,
20 |
21 | // Project Specific Constants
22 | PROJECT_CONSTANTS,
23 | } = require("../../utils")(artifacts);
24 |
25 | contract("UnlockValidator", (accounts) => {
26 | before(async () => {
27 | accounts = await verboseAccounts(accounts);
28 | });
29 |
30 | describe("constructor", () => {
31 | it("should deploy", async () => {
32 | const { validator } = await deployMock(accounts);
33 | assert.equal(await validator.owner(), accounts.administrator);
34 | });
35 | });
36 |
37 | describe("invalidate", () => {
38 | it("should disallow if not owner", async () => {
39 | const { validator } = await deployMock();
40 |
41 | await assertErrors(
42 | validator.invalidate(accounts.account0, {
43 | from: accounts.account0,
44 | }),
45 | "Ownable: caller is not the owner"
46 | );
47 | });
48 |
49 | it("should allow owner to call", async () => {
50 | const { validator, ADMINISTRATOR } = await deployMock();
51 |
52 | assertEvents(
53 | await validator.invalidate(accounts.account0, ADMINISTRATOR),
54 | {
55 | Invalidate: {
56 | account: accounts.account0,
57 | },
58 | }
59 | );
60 |
61 | assert.equal(
62 | await validator.isValid(accounts.account0, 0, 0),
63 | false
64 | );
65 | });
66 |
67 | it("should disallow if already invalidated", async () => {
68 | const { validator, ADMINISTRATOR } = await deployMock();
69 |
70 | await assertErrors(
71 | validator.invalidate(accounts.account0, ADMINISTRATOR),
72 | "UnlockValidator::invalidate: Already Invalid"
73 | );
74 | });
75 | });
76 |
77 | describe("validate", () => {
78 | it("should disallow if not owner", async () => {
79 | const { validator } = await deployMock();
80 |
81 | await assertErrors(
82 | validator.validate(accounts.account0, {
83 | from: accounts.account0,
84 | }),
85 | "Ownable: caller is not the owner"
86 | );
87 | });
88 |
89 | it("should allow owner to call", async () => {
90 | const { validator, ADMINISTRATOR } = await deployMock();
91 |
92 | assertEvents(
93 | await validator.validate(accounts.account0, ADMINISTRATOR),
94 | {
95 | Validate: {
96 | account: accounts.account0,
97 | },
98 | }
99 | );
100 |
101 | assert.equal(
102 | await validator.isValid(accounts.account0, 0, 0),
103 | true
104 | );
105 | });
106 |
107 | it("should disallow if already validated", async () => {
108 | const { validator, ADMINISTRATOR } = await deployMock();
109 |
110 | await assertErrors(
111 | validator.validate(accounts.account0, ADMINISTRATOR),
112 | "UnlockValidator::validate: Already Valid"
113 | );
114 | });
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/test/x-vader/XVader.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | // Deployment Function
3 | deployMock,
4 |
5 | // Testing Utilities
6 | assertBn,
7 | assertErrors,
8 | assertEvents,
9 | UNSET_ADDRESS,
10 | DEFAULT_CONFIGS,
11 | TEN_UNITS,
12 |
13 | // Library Functions
14 | verboseAccounts,
15 | time,
16 | big,
17 | parseUnits,
18 |
19 | // Project Specific Constants
20 | PROJECT_CONSTANTS,
21 |
22 | mintAndApprove,
23 | } = require("../utils")(artifacts);
24 |
25 | contract("XVader", (accounts) => {
26 | describe("construction", () => {
27 | it("should fail to deploy contract with zero vader address", async () => {
28 | if (Array.isArray(accounts))
29 | accounts = await verboseAccounts(accounts);
30 |
31 | await assertErrors(
32 | deployMock(accounts, {
33 | MockXVader: () => [UNSET_ADDRESS],
34 | }),
35 | "XVader::constructor: _vader cannot be a zero address",
36 | );
37 | });
38 |
39 | it("should deploy XVader and assert its state", async () => {
40 | if (Array.isArray(accounts))
41 | accounts = await verboseAccounts(accounts);
42 |
43 | const name = "XVader";
44 | const symbol = "xVADER";
45 | const { mockXVader, vader } = await deployMock(accounts);
46 |
47 | assert.equal(await mockXVader.name(), name);
48 | assert.equal(await mockXVader.symbol(), symbol);
49 | assert.equal(await mockXVader.vader(), vader.address);
50 | });
51 | });
52 |
53 | describe("test 'enter' and 'leave' functions", () => {
54 | it("should mint 1:1 of Vader to XVader when XVader supply is zero", async () => {
55 | if (Array.isArray(accounts))
56 | accounts = await verboseAccounts(accounts);
57 |
58 | const { mockXVader, token } = await deployMock(accounts, {
59 | MockXVader: (_, { token }) => [token.address],
60 | });
61 |
62 | await token.mint(accounts.account0, parseUnits(1000, 18));
63 | await token.approve(mockXVader.address, parseUnits(1000, 18));
64 | await mockXVader.enter(parseUnits(1000, 18));
65 |
66 | assertBn(await token.balanceOf(mockXVader.address), parseUnits(1000, 18));
67 | assertBn(await token.balanceOf(accounts.account0), big(0));
68 | assertBn(await mockXVader.balanceOf(accounts.account0), parseUnits(1000, 18));
69 | assertBn(await mockXVader.totalSupply(), parseUnits(1000, 18));
70 | });
71 |
72 | it("should successfully redeem vader from XVader", async () => {
73 | const { mockXVader, token } = await deployMock();
74 | await mockXVader.leave(parseUnits(1000, 18));
75 |
76 | assertBn(await token.balanceOf(mockXVader.address), big(0));
77 | assertBn(await token.balanceOf(accounts.account0), parseUnits(1000, 18));
78 | assertBn(await mockXVader.balanceOf(accounts.account0), big(0));
79 | assertBn(await mockXVader.totalSupply(), big(0));
80 | });
81 |
82 | it("should mint XVader on pro-rata basis to its vader supply", async () => {
83 | const { mockXVader, token } = await deployMock();
84 | await token.approve(mockXVader.address, parseUnits(1000, 18));
85 | await mockXVader.enter(parseUnits(500, 18));
86 |
87 | // mint XVader when its supply is non zero.
88 | const oldXVaderBalance = await mockXVader.balanceOf(accounts.account0);
89 | const expectedIncrease = await parseUnits(500, 18)
90 | .mul(await token.balanceOf(mockXVader.address))
91 | .div(await mockXVader.totalSupply());
92 |
93 | await mockXVader.enter(parseUnits(500, 18));
94 | assertBn(await mockXVader.balanceOf(accounts.account0), oldXVaderBalance.add(expectedIncrease));
95 | });
96 | });
97 | });
--------------------------------------------------------------------------------
/truffle-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * trufflesuite.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura accounts
13 | * are available for free at: infura.io/register.
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | require("dotenv").config();
22 |
23 | const HDWalletProvider = require("@truffle/hdwallet-provider");
24 | const fs = require("fs");
25 | const mnemonic = fs.readFileSync(".secret").toString().trim();
26 |
27 | module.exports = {
28 | /**
29 | * Networks define how you connect to your ethereum client and let you set the
30 | * defaults web3 uses to send transactions. If you don't specify one truffle
31 | * will spin up a development blockchain for you on port 9545 when you
32 | * run `develop` or `test`. You can ask a truffle command to use a specific
33 | * network from the command line, e.g
34 | *
35 | * $ truffle test --network
36 | */
37 |
38 | networks: {
39 | // Useful for testing. The `development` name is special - truffle uses it by default
40 | // if it's defined here and no other network is specified at the command line.
41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal
42 | // tab if you use this network and you must also set the `host`, `port` and `network_id`
43 | // options below to some value.
44 | //
45 | development: {
46 | host: "127.0.0.1", // Localhost (default: none)
47 | port: 8545, // Standard Ethereum port (default: none)
48 | network_id: "*", // Any network (default: none)
49 | },
50 | // Another network with more advanced options...
51 | // advanced: {
52 | // port: 8777, // Custom port
53 | // network_id: 1342, // Custom network
54 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000)
55 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
56 | // from: , // Account to send txs from (default: accounts[0])
57 | // websocket: true // Enable EventEmitter interface for web3 (default: false)
58 | // },
59 | // Useful for deploying to a public network.
60 | // NB: It's important to wrap the provider as a function.
61 | // ropsten: {
62 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
63 | // network_id: 3, // Ropsten's id
64 | // gas: 5500000, // Ropsten has a lower block limit than mainnet
65 | // confirmations: 2, // # of confs to wait between deployments. (default: 0)
66 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
67 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
68 | // },
69 | kovan: {
70 | provider: () =>
71 | new HDWalletProvider(
72 | mnemonic,
73 | `wss://kovan.infura.io/ws/v3/${process.env.INFURA_API_KEY}`
74 | ),
75 | network_id: 42,
76 | gas: 3000000,
77 | confirmations: 2,
78 | timeoutBlocks: 200,
79 | skipDryRun: true,
80 | },
81 | mainnet: {
82 | provider: () =>
83 | new HDWalletProvider(
84 | mnemonic,
85 | `wss://mainnet.infura.io/ws/v3/${process.env.INFURA_API_KEY}`
86 | ),
87 | network_id: 1,
88 | gas: 3000000,
89 | confirmations: 2,
90 | timeoutBlocks: 200,
91 | skipDryRun: true,
92 | },
93 | // Useful for private networks
94 | // private: {
95 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
96 | // network_id: 2111, // This network is yours, in the cloud.
97 | // production: true // Treats this network as if it was a public net. (default: false)
98 | // }
99 | },
100 |
101 | // Set default mocha options here, use special reporters etc.
102 | mocha: {
103 | // timeout: 100000
104 | reporter: "eth-gas-reporter",
105 | },
106 |
107 | // Configure your compilers
108 | compilers: {
109 | solc: {
110 | version: "0.8.9", // Fetch exact version from solc-bin (default: truffle's version)
111 | settings: {
112 | optimizer: {
113 | enabled: true,
114 | runs: 200,
115 | },
116 | },
117 | },
118 | },
119 | plugins: ["solidity-coverage", "truffle-plugin-verify"],
120 | api_keys: {
121 | etherscan: process.env.ETHER_SCAN_API_KEY,
122 | },
123 | };
124 |
--------------------------------------------------------------------------------