├── .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 | ![vader](./doc/vader.drawio.png) 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 | --------------------------------------------------------------------------------