├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierignore ├── .solhint.json ├── .solhintignore ├── README.md ├── contracts ├── BTRFLY.sol ├── BondDepository.sol ├── BondingCalculator27.sol ├── BtrflyOhmBondingCalculator.sol ├── InvestorClaim.sol ├── LPBondDepository.sol ├── LPBondDepositoryRewardBased.sol ├── Mock │ ├── CRV.sol │ ├── CVX.sol │ └── CurveHelper.sol ├── OHMStake.sol ├── PBTRFLY.sol ├── RedeemHelper.sol ├── ReserveIOUERC20.sol ├── Staking.sol ├── StakingDistributor.sol ├── StakingHelper.sol ├── StakingWarmup.sol ├── StandardBondingCalculator.sol ├── Treasury.sol ├── WXBTRFLY.sol ├── XBTRFLY.sol └── thecosomata │ └── ThecosomataETH.sol ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts └── deploy.ts ├── test ├── Bonds.ts ├── LiveLPBondsLELB.ts ├── LiveLPBondsLEOB.ts ├── LiveLPBondsOEOB.ts ├── ProtocolDeployment.ts ├── ThecosomataETH.ts ├── constants.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 2 | ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/ 3 | PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es2021: true, 5 | mocha: true, 6 | node: true, 7 | }, 8 | plugins: ["@typescript-eslint"], 9 | extends: [ 10 | "standard", 11 | "plugin:prettier/recommended", 12 | "plugin:node/recommended", 13 | ], 14 | parser: "@typescript-eslint/parser", 15 | parserOptions: { 16 | ecmaVersion: 12, 17 | }, 18 | rules: { 19 | "node/no-unsupported-features/es-syntax": [ 20 | "error", 21 | { ignores: ["modules"] }, 22 | ], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hardhat.config.ts 2 | scripts 3 | test 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["error", "^0.8.0"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Contracts That Power the Redacted Cartel Protocol🦋: 2 | 3 | Here are the contracts that power REDACTED V1. Our V1 is a friendly fork of OHM, however, it contains a handful of key differences: 4 | - Reserve assets have variable values (that can be set by calling setFloor() within our treasury) 5 | - Bond contracts have been designed to return fees to Olympus 6 | - Different bonding calculators due to our main LP pair being between 2 9dp coins, as opposed to a 9dp/18dp pair 7 | 8 | ## Tech 9 | 10 | Redacted Cartel uses a number of open source projects to work properly: 11 | 12 | - Hardhat - Ethereum development environment. 13 | - Typescript - Strongly Typed JS 14 | - Typechain - TS Types for Solidity Smart Contracts 15 | - Node - JS runtime 16 | 17 | 18 | And of course Redacted Cartel itself is open source with a public repository on GitHub. 19 | 20 | ## Installation 21 | 22 | Redacted Smart Contracts requires [Node.js](https://nodejs.org/) v10+ to run. 23 | 24 | Install the dependencies and devDependencies and start the server. 25 | 26 | ```sh 27 | cd Redacted-Smart-Contracts 28 | yarn install 29 | ``` 30 | 31 | 32 | ## Deployed Contracts 33 | 34 | 35 | 36 | | Contract Name | Address | 37 | | ------ | ------ | 38 | | PBTRFLY | 0x57503824e256e878db8136fde66f155c49e362df | 39 | | BTRFLY | 0xc0d4ceb216b3ba9c3701b291766fdcba977cec3a | 40 | | xBTRFLY | 0xCC94Faf235cC5D3Bf4bEd3a30db5984306c86aBC | 41 | | Treasury | 0x086C98855dF3C78C6b481b6e1D47BeF42E9aC36B | 42 | | REDACTEDBondingCalculator | 0xCA1d53E40eab232DEff03Dc824410100BcCCF2bC | 43 | |Staking Distributor|0xB2120AE79d838d6703Cf6d2ac5cC68b5DB10683F | 44 | | Staking | 0xBdE4Dfb0dbb0Dd8833eFb6C5BD0Ce048C852C487 | 45 | | Warmup | 0x7521C8c7ba7e1F650c1109c40876C5Dd52f5614c | 46 | | Staking Helper | 0xC0840Ec5527d3e70d66AE6575642916F3Fd18aDf | 47 | | BtrflyOhmBondingCalculator | 0xa77b57445FA262CaE325DeD434Df89302c93f59A| 48 | | LP Bond (OHM / BTRFLY) | 0x1fDf1233f85A3BAe9594B0558e4EC8Febe8c6720| 49 | | CVX Bond | 0xe2eF3B60B0B3087cf1d1179D899a7cD7a11a9fCa| 50 | | CRV Bond | 0x765c7cfed02f2d9583eac8229930f3650af42c77| 51 | 52 | 53 | ## Development 54 | Running All tests 55 | ```sh 56 | npx hardhat test 57 | ``` 58 | 59 | Running Specific tests with network 60 | ```sh 61 | npx hardhat test ./test/contract --network 62 | ``` 63 | #### Comiling Contracts 64 | ```sh 65 | npx hardhat compile 66 | ``` 67 | 68 | ### Extra Hardhat features 69 | ```shell 70 | npx hardhat accounts 71 | npx hardhat compile 72 | npx hardhat clean 73 | npx hardhat test 74 | npx hardhat node 75 | npx hardhat help 76 | REPORT_GAS=true npx hardhat test 77 | npx hardhat coverage 78 | npx hardhat run scripts/deploy.ts 79 | TS_NODE_FILES=true npx ts-node scripts/deploy.ts 80 | npx eslint '**/*.{js,ts}' 81 | npx eslint '**/*.{js,ts}' --fix 82 | npx prettier '**/*.{json,sol,md}' --check 83 | npx prettier '**/*.{json,sol,md}' --write 84 | npx solhint 'contracts/**/*.sol' 85 | npx solhint 'contracts/**/*.sol' --fix 86 | ``` 87 | [//]: # 88 | [node.js]: 89 | 90 | -------------------------------------------------------------------------------- /contracts/BondingCalculator27.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library FullMath { 5 | function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { 6 | uint256 mm = mulmod(x, y, uint256(-1)); 7 | l = x * y; 8 | h = mm - l; 9 | if (mm < l) h -= 1; 10 | } 11 | 12 | function fullDiv( 13 | uint256 l, 14 | uint256 h, 15 | uint256 d 16 | ) private pure returns (uint256) { 17 | uint256 pow2 = d & -d; 18 | d /= pow2; 19 | l /= pow2; 20 | l += h * ((-pow2) / pow2 + 1); 21 | uint256 r = 1; 22 | r *= 2 - d * r; 23 | r *= 2 - d * r; 24 | r *= 2 - d * r; 25 | r *= 2 - d * r; 26 | r *= 2 - d * r; 27 | r *= 2 - d * r; 28 | r *= 2 - d * r; 29 | r *= 2 - d * r; 30 | return l * r; 31 | } 32 | 33 | function mulDiv( 34 | uint256 x, 35 | uint256 y, 36 | uint256 d 37 | ) internal pure returns (uint256) { 38 | (uint256 l, uint256 h) = fullMul(x, y); 39 | uint256 mm = mulmod(x, y, d); 40 | if (mm > l) h -= 1; 41 | l -= mm; 42 | require(h < d, 'FullMath::mulDiv: overflow'); 43 | return fullDiv(l, h, d); 44 | } 45 | } 46 | 47 | library Babylonian { 48 | 49 | function sqrt(uint256 x) internal pure returns (uint256) { 50 | if (x == 0) return 0; 51 | 52 | uint256 xx = x; 53 | uint256 r = 1; 54 | if (xx >= 0x100000000000000000000000000000000) { 55 | xx >>= 128; 56 | r <<= 64; 57 | } 58 | if (xx >= 0x10000000000000000) { 59 | xx >>= 64; 60 | r <<= 32; 61 | } 62 | if (xx >= 0x100000000) { 63 | xx >>= 32; 64 | r <<= 16; 65 | } 66 | if (xx >= 0x10000) { 67 | xx >>= 16; 68 | r <<= 8; 69 | } 70 | if (xx >= 0x100) { 71 | xx >>= 8; 72 | r <<= 4; 73 | } 74 | if (xx >= 0x10) { 75 | xx >>= 4; 76 | r <<= 2; 77 | } 78 | if (xx >= 0x8) { 79 | r <<= 1; 80 | } 81 | r = (r + x / r) >> 1; 82 | r = (r + x / r) >> 1; 83 | r = (r + x / r) >> 1; 84 | r = (r + x / r) >> 1; 85 | r = (r + x / r) >> 1; 86 | r = (r + x / r) >> 1; 87 | r = (r + x / r) >> 1; // Seven iterations should be enough 88 | uint256 r1 = x / r; 89 | return (r < r1 ? r : r1); 90 | } 91 | } 92 | 93 | library BitMath { 94 | 95 | function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { 96 | require(x > 0, 'BitMath::mostSignificantBit: zero'); 97 | 98 | if (x >= 0x100000000000000000000000000000000) { 99 | x >>= 128; 100 | r += 128; 101 | } 102 | if (x >= 0x10000000000000000) { 103 | x >>= 64; 104 | r += 64; 105 | } 106 | if (x >= 0x100000000) { 107 | x >>= 32; 108 | r += 32; 109 | } 110 | if (x >= 0x10000) { 111 | x >>= 16; 112 | r += 16; 113 | } 114 | if (x >= 0x100) { 115 | x >>= 8; 116 | r += 8; 117 | } 118 | if (x >= 0x10) { 119 | x >>= 4; 120 | r += 4; 121 | } 122 | if (x >= 0x4) { 123 | x >>= 2; 124 | r += 2; 125 | } 126 | if (x >= 0x2) r += 1; 127 | } 128 | } 129 | 130 | library FixedPoint { 131 | // range: [0, 2**112 - 1] 132 | // resolution: 1 / 2**112 133 | struct uq112x112 { 134 | uint224 _x; 135 | } 136 | 137 | // range: [0, 2**144 - 1] 138 | // resolution: 1 / 2**112 139 | struct uq144x112 { 140 | uint256 _x; 141 | } 142 | 143 | uint8 private constant RESOLUTION = 112; 144 | uint256 private constant Q112 = 0x10000000000000000000000000000; 145 | uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; 146 | uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) 147 | 148 | // decode a UQ112x112 into a uint112 by truncating after the radix point 149 | function decode(uq112x112 memory self) internal pure returns (uint112) { 150 | return uint112(self._x >> RESOLUTION); 151 | } 152 | 153 | // decode a uq112x112 into a uint with 18 decimals of precision 154 | function decode112with18(uq112x112 memory self) internal pure returns (uint) { 155 | return uint(self._x) / 5192296858534827; 156 | } 157 | 158 | function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { 159 | require(denominator > 0, 'FixedPoint::fraction: division by zero'); 160 | if (numerator == 0) return FixedPoint.uq112x112(0); 161 | 162 | if (numerator <= uint144(-1)) { 163 | uint256 result = (numerator << RESOLUTION) / denominator; 164 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 165 | return uq112x112(uint224(result)); 166 | } else { 167 | uint256 result = FullMath.mulDiv(numerator, Q112, denominator); 168 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 169 | return uq112x112(uint224(result)); 170 | } 171 | } 172 | 173 | // square root of a UQ112x112 174 | // lossy between 0/1 and 40 bits 175 | function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) { 176 | if (self._x <= uint144(-1)) { 177 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112))); 178 | } 179 | 180 | uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x); 181 | safeShiftBits -= safeShiftBits % 2; 182 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2))); 183 | } 184 | } 185 | 186 | library SafeMath { 187 | 188 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 189 | uint256 c = a + b; 190 | require(c >= a, "SafeMath: addition overflow"); 191 | 192 | return c; 193 | } 194 | 195 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 196 | return sub(a, b, "SafeMath: subtraction overflow"); 197 | } 198 | 199 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 200 | require(b <= a, errorMessage); 201 | uint256 c = a - b; 202 | 203 | return c; 204 | } 205 | 206 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 207 | 208 | if (a == 0) { 209 | return 0; 210 | } 211 | 212 | uint256 c = a * b; 213 | require(c / a == b, "SafeMath: multiplication overflow"); 214 | 215 | return c; 216 | } 217 | 218 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 219 | return div(a, b, "SafeMath: division by zero"); 220 | } 221 | 222 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 223 | require(b > 0, errorMessage); 224 | uint256 c = a / b; 225 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 226 | 227 | return c; 228 | } 229 | 230 | function sqrrt(uint256 a) internal pure returns (uint c) { 231 | if (a > 3) { 232 | c = a; 233 | uint b = add( div( a, 2), 1 ); 234 | while (b < c) { 235 | c = b; 236 | b = div( add( div( a, b ), b), 2 ); 237 | } 238 | } else if (a != 0) { 239 | c = 1; 240 | } 241 | } 242 | } 243 | 244 | interface IERC20 { 245 | function decimals() external view returns (uint8); 246 | } 247 | 248 | interface IUniswapV2ERC20 { 249 | function totalSupply() external view returns (uint); 250 | } 251 | 252 | interface IUniswapV2Pair is IUniswapV2ERC20 { 253 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 254 | function token0() external view returns ( address ); 255 | function token1() external view returns ( address ); 256 | } 257 | 258 | interface IBondingCalculator { 259 | function valuation( address pair_, uint amount_ ) external view returns ( uint _value ); 260 | } 261 | 262 | contract BondingCalculator27 is IBondingCalculator { 263 | 264 | using FixedPoint for *; 265 | using SafeMath for uint; 266 | using SafeMath for uint112; 267 | 268 | address public immutable BTRFLY; 269 | 270 | constructor( address _BTRFLY ) { 271 | require( _BTRFLY != address(0) ); 272 | BTRFLY = _BTRFLY; 273 | } 274 | 275 | function getKValue( address _pair ) public view returns( uint k_ ) { 276 | uint token0 = IERC20( IUniswapV2Pair( _pair ).token0() ).decimals(); 277 | uint token1 = IERC20( IUniswapV2Pair( _pair ).token1() ).decimals(); 278 | uint decimals = token0.add( token1 ).sub( IERC20( _pair ).decimals() ); 279 | 280 | (uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 281 | k_ = reserve0.mul(reserve1).div( 10 ** decimals ); 282 | } 283 | 284 | function getTotalValue( address _pair ) public view returns ( uint _value ) { 285 | _value = getKValue( _pair ).sqrrt().mul(2); 286 | } 287 | 288 | function valuation( address _pair, uint amount_ ) external view override returns ( uint _value ) { 289 | uint totalValue = getTotalValue( _pair ); 290 | uint totalSupply = IUniswapV2Pair( _pair ).totalSupply(); 291 | 292 | _value = totalValue.mul( FixedPoint.fraction( amount_, totalSupply ).decode112with18() ).div( 1e27 ); 293 | } 294 | 295 | function markdown( address _pair ) external view returns ( uint ) { 296 | ( uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 297 | 298 | uint reserve; 299 | if ( IUniswapV2Pair( _pair ).token0() == BTRFLY ) { 300 | reserve = reserve1; 301 | } else { 302 | reserve = reserve0; 303 | } 304 | return reserve.mul( 2 * ( 10 ** IERC20( BTRFLY ).decimals() ) ).div( getTotalValue( _pair ) ); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /contracts/BtrflyOhmBondingCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library SafeMath { 5 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 6 | uint256 c = a + b; 7 | require(c >= a, "SafeMath: addition overflow"); 8 | 9 | return c; 10 | } 11 | 12 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 13 | return sub(a, b, "SafeMath: subtraction overflow"); 14 | } 15 | 16 | function sub( 17 | uint256 a, 18 | uint256 b, 19 | string memory errorMessage 20 | ) internal pure returns (uint256) { 21 | require(b <= a, errorMessage); 22 | uint256 c = a - b; 23 | 24 | return c; 25 | } 26 | 27 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 28 | if (a == 0) { 29 | return 0; 30 | } 31 | 32 | uint256 c = a * b; 33 | require(c / a == b, "SafeMath: multiplication overflow"); 34 | 35 | return c; 36 | } 37 | 38 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 39 | return div(a, b, "SafeMath: division by zero"); 40 | } 41 | 42 | function div( 43 | uint256 a, 44 | uint256 b, 45 | string memory errorMessage 46 | ) internal pure returns (uint256) { 47 | require(b > 0, errorMessage); 48 | uint256 c = a / b; 49 | return c; 50 | } 51 | } 52 | 53 | interface IUniswapV2ERC20 { 54 | function totalSupply() external view returns (uint256); 55 | } 56 | 57 | interface IUniswapV2Pair is IUniswapV2ERC20 { 58 | function getReserves() 59 | external 60 | view 61 | returns ( 62 | uint112 reserve0, 63 | uint112 reserve1, 64 | uint32 blockTimestampLast 65 | ); 66 | 67 | function token0() external view returns (address); 68 | 69 | function token1() external view returns (address); 70 | } 71 | 72 | contract BtrflyOhmBondingCalculator{ 73 | 74 | using SafeMath for uint256; 75 | 76 | address immutable public BTRFLY; 77 | 78 | constructor( address _BTRFLY ){ 79 | BTRFLY = _BTRFLY; 80 | } 81 | 82 | function valuation(address _token, uint256 _amount) external view returns (uint256 value_){ 83 | 84 | address token0 = IUniswapV2Pair(_token).token0(); 85 | 86 | (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(_token).getReserves(); 87 | 88 | uint256 totalSupply = IUniswapV2Pair(_token).totalSupply(); 89 | uint256 reserve = token0 == BTRFLY ? reserve1 : reserve0; 90 | 91 | return _amount.mul(reserve).div(totalSupply); 92 | 93 | } 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /contracts/Mock/CRV.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | 5 | contract CRV is ERC20 { 6 | constructor() ERC20('DAI Stablecoin', 'DAI') {} 7 | 8 | function mint(address to) public { 9 | _mint(to, 100000 ether); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/Mock/CVX.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | 5 | contract CVX is ERC20 { 6 | constructor() ERC20('DAI Stablecoin', 'DAI') {} 7 | 8 | function mint(address to) public { 9 | _mint(to, 100000 ether); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/Mock/CurveHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | interface ICurveDeployer { 7 | function deploy_pool( 8 | string calldata name, 9 | string calldata symbol, 10 | address[2] calldata coins, 11 | uint256 a, 12 | uint256 gamma, 13 | uint256 midFee, 14 | uint256 outFee, 15 | uint256 extraProfit, 16 | uint256 feeGamma, 17 | uint256 adjustmentStep, 18 | uint256 adminFee, 19 | uint256 maHalfTime, 20 | uint256 initialPrice 21 | ) external payable; 22 | 23 | function find_pool_for_coins( 24 | address from, 25 | address to, 26 | uint256 i 27 | ) external view returns (address); 28 | } 29 | 30 | interface ICurveCryptoPool { 31 | function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) 32 | external 33 | payable; 34 | 35 | function calc_token_amount(uint256[2] calldata amounts) 36 | external 37 | view 38 | returns (uint256); 39 | 40 | // Would be replaced by Chainlink based oracle 41 | function price_oracle() external view returns (uint256); 42 | 43 | function token() external view returns (address); 44 | } 45 | 46 | interface IWETH { 47 | function deposit() external payable; 48 | 49 | function transfer(address to, uint256 amount) external returns (bool); 50 | 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | function balanceOf(address account) external view returns (uint256); 54 | } 55 | 56 | contract CurveHelper { 57 | ICurveDeployer public deployer; 58 | IWETH public weth; 59 | address public btrfly; 60 | 61 | constructor( 62 | address _deployer, 63 | address _btrfly, 64 | address _weth 65 | ) { 66 | deployer = ICurveDeployer(_deployer); 67 | weth = IWETH(_weth); 68 | btrfly = _btrfly; 69 | 70 | address[2] memory coins = [_weth, _btrfly]; 71 | 72 | deployer.deploy_pool( 73 | "ETH/BTRFLY", 74 | "ETHBTRFLY", 75 | coins, 76 | 400000, 77 | 145000000000000, 78 | 26000000, 79 | 45000000, 80 | 2000000000000, 81 | 230000000000000, 82 | 146000000000000, 83 | 5000000000, 84 | 600, 85 | 200000000000000000 86 | ); 87 | } 88 | 89 | function poolAddress() public view returns (address) { 90 | return deployer.find_pool_for_coins(address(weth), btrfly, 0); 91 | } 92 | 93 | function poolTokenAddress() public view returns (address) { 94 | address pool = poolAddress(); 95 | return ICurveCryptoPool(pool).token(); 96 | } 97 | 98 | function poolMinimumToken(uint256 amount1, uint256 amount2) external view returns (uint256) { 99 | address pool = poolAddress(); 100 | uint256[2] memory amounts = [amount1, amount2]; 101 | return ICurveCryptoPool(pool).calc_token_amount(amounts); 102 | } 103 | 104 | function poolTokenBalance(address account) external view returns (uint256) { 105 | address token = poolTokenAddress(); 106 | return IERC20(token).balanceOf(account); 107 | } 108 | 109 | function poolPrice() external view returns (uint256) { 110 | address pool = poolAddress(); 111 | return ICurveCryptoPool(pool).price_oracle(); 112 | } 113 | 114 | function initPool(uint256 amount1, uint256 amount2) external { 115 | address pool = poolAddress(); 116 | uint256[2] memory amounts = [amount1, amount2]; 117 | IERC20(btrfly).approve(pool, 2**256 - 1); 118 | IWETH(weth).approve(pool, 2**256 - 1); 119 | ICurveCryptoPool(pool).add_liquidity(amounts, 0); 120 | } 121 | 122 | // Mainly used for mocking WETH for treasury 123 | function wrapAndTransfer(address to, uint256 amount) external payable { 124 | weth.deposit{value: msg.value}(); 125 | weth.transfer(to, amount); 126 | } 127 | 128 | function wethBalance(address account) external view returns (uint256) { 129 | return IWETH(weth).balanceOf(account); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /contracts/OHMStake.sol: -------------------------------------------------------------------------------- 1 | //deposits IOU-OHM 2 | //withdraws OHM 3 | //uses staking helper to stake it 4 | //deposits sOHM 5 | //withdraws IOU-OHM 6 | //burns it 7 | 8 | interface IERC20 { 9 | function decimals() external view returns (uint8); 10 | 11 | function totalSupply() external view returns (uint256); 12 | 13 | function balanceOf(address account) external view returns (uint256); 14 | 15 | function transfer(address recipient, uint256 amount) external returns (bool); 16 | 17 | function allowance(address owner, address spender) external view returns (uint256); 18 | 19 | function approve(address spender, uint256 amount) external returns (bool); 20 | 21 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 22 | 23 | event Transfer(address indexed from, address indexed to, uint256 value); 24 | 25 | event Approval(address indexed owner, address indexed spender, uint256 value); 26 | } 27 | 28 | interface ITreasury { 29 | function deposit( uint _amount, address _token, uint _profit ) external returns ( uint ); 30 | function simp( uint _amount, address _token ) external; 31 | function valueOf( address _token, uint _amount ) external view returns ( uint value_ ); 32 | function manage( address _token, uint _amount ) external; 33 | function auditReserves() external; 34 | } 35 | 36 | interface IStaking { 37 | function stake( uint _amount, address _recipient ) external returns ( bool ); 38 | function claim( address _recipient ) external; 39 | } 40 | 41 | interface IReserveIOU { 42 | function mint(address recipient, uint256 amount) external; 43 | function burn(uint amount) external; 44 | } 45 | 46 | contract OHMStake { 47 | 48 | address public immutable OHM; 49 | address public immutable sOHM; 50 | address public immutable treasury; 51 | address public immutable ohmStaking; 52 | address public immutable OHMIOU; 53 | 54 | constructor( 55 | address _OHM, 56 | address _sOHM, 57 | address _treasury, 58 | address _ohmStaking, 59 | address _OHMIOU 60 | ){ 61 | OHM = _OHM; 62 | sOHM = _sOHM; 63 | treasury = _treasury; 64 | ohmStaking = _ohmStaking; 65 | OHMIOU = _OHMIOU; 66 | } 67 | 68 | function stakeOHMInTreasury() external{ 69 | // draw all of the treasury's OHM, using the IOU as a tool to avoid hitting excess reserve limits 70 | uint256 ohmBalance = IERC20(OHM).balanceOf(treasury); 71 | IReserveIOU(OHMIOU).mint(address(this),ohmBalance); 72 | IERC20(OHMIOU).approve(treasury,ohmBalance); 73 | ITreasury(treasury).simp(ohmBalance, OHMIOU); //simp is shorthand for deposit where all value is profit 74 | ITreasury(treasury).manage(OHM, ohmBalance); 75 | //staking stuff 76 | IERC20( OHM ).approve( ohmStaking, ohmBalance ); 77 | IStaking( ohmStaking ).stake( ohmBalance, treasury ); 78 | IStaking( ohmStaking ).claim( treasury ); 79 | //audit reserves to reflect new sOHM and rebasing 80 | ITreasury(treasury).auditReserves(); 81 | //burn the IOU 82 | ITreasury(treasury).manage(OHMIOU, ohmBalance); 83 | IReserveIOU(OHMIOU).burn(ohmBalance); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /contracts/PBTRFLY.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2021-01-21 3 | */ 4 | 5 | // SPDX-License-Identifier: AGPL-3.0-or-later 6 | pragma solidity 0.7.5; 7 | 8 | library SafeMath { 9 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 10 | uint256 c = a + b; 11 | require(c >= a, "SafeMath: addition overflow"); 12 | 13 | return c; 14 | } 15 | 16 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return sub(a, b, "SafeMath: subtraction overflow"); 18 | } 19 | 20 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 21 | require(b <= a, errorMessage); 22 | uint256 c = a - b; 23 | 24 | return c; 25 | } 26 | 27 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 28 | if (a == 0) { 29 | return 0; 30 | } 31 | 32 | uint256 c = a * b; 33 | require(c / a == b, "SafeMath: multiplication overflow"); 34 | 35 | return c; 36 | } 37 | 38 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 39 | return div(a, b, "SafeMath: division by zero"); 40 | } 41 | 42 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 43 | require(b > 0, errorMessage); 44 | uint256 c = a / b; 45 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 46 | 47 | return c; 48 | } 49 | 50 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 51 | return mod(a, b, "SafeMath: modulo by zero"); 52 | } 53 | 54 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 55 | require(b != 0, errorMessage); 56 | return a % b; 57 | } 58 | 59 | function sqrrt(uint256 a) internal pure returns (uint c) { 60 | if (a > 3) { 61 | c = a; 62 | uint b = add( div( a, 2), 1 ); 63 | while (b < c) { 64 | c = b; 65 | b = div( add( div( a, b ), b), 2 ); 66 | } 67 | } else if (a != 0) { 68 | c = 1; 69 | } 70 | } 71 | 72 | function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { 73 | return div( mul( total_, percentage_ ), 1000 ); 74 | } 75 | 76 | function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { 77 | return div( mul(part_, 100) , total_ ); 78 | } 79 | 80 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 81 | // (a + b) / 2 can overflow, so we distribute 82 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 83 | } 84 | 85 | function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { 86 | return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); 87 | } 88 | 89 | function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { 90 | return sqrrt( mul( multiplier_, payment_ ) ); 91 | } 92 | 93 | function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { 94 | return mul( multiplier_, supply_ ); 95 | } 96 | } 97 | 98 | library Context { 99 | function _msgSender() internal view returns (address payable) { 100 | return msg.sender; 101 | } 102 | 103 | function _msgData() internal view returns (bytes memory) { 104 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 105 | return msg.data; 106 | } 107 | } 108 | 109 | contract Ownable { 110 | 111 | address internal _owner; 112 | 113 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 114 | 115 | constructor () { 116 | address msgSender = Context._msgSender(); 117 | _owner = msgSender; 118 | emit OwnershipTransferred(address(0), msgSender); 119 | } 120 | 121 | function owner() public view returns (address) { 122 | return _owner; 123 | } 124 | 125 | modifier onlyOwner() { 126 | require(_owner == Context._msgSender(), "Ownable: caller is not the owner"); 127 | _; 128 | } 129 | 130 | function renounceOwnership() public virtual onlyOwner { 131 | emit OwnershipTransferred(_owner, address(0)); 132 | _owner = address(0); 133 | } 134 | 135 | function transferOwnership(address newOwner) public virtual onlyOwner { 136 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 137 | emit OwnershipTransferred(_owner, newOwner); 138 | _owner = newOwner; 139 | } 140 | } 141 | 142 | interface IERC20 { 143 | function totalSupply() external view returns (uint256); 144 | 145 | function balanceOf(address account) external view returns (uint256); 146 | 147 | function transfer(address recipient, uint256 amount) external returns (bool); 148 | 149 | function allowance(address owner, address spender) external view returns (uint256); 150 | 151 | function approve(address spender, uint256 amount) external returns (bool); 152 | 153 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 154 | 155 | event Transfer(address indexed from, address indexed to, uint256 value); 156 | 157 | event Approval(address indexed owner, address indexed spender, uint256 value); 158 | } 159 | 160 | abstract contract ERC20 is IERC20 { 161 | 162 | using SafeMath for uint256; 163 | 164 | 165 | mapping (address => uint256) internal _balances; 166 | 167 | mapping (address => mapping (address => uint256)) internal _allowances; 168 | 169 | uint256 internal _totalSupply; 170 | 171 | string internal _name; 172 | 173 | string internal _symbol; 174 | 175 | uint8 internal _decimals; 176 | 177 | constructor (string memory name_, string memory symbol_, uint8 decimals_) { 178 | _name = name_; 179 | _symbol = symbol_; 180 | _decimals = decimals_; 181 | } 182 | 183 | function name() public view returns (string memory) { 184 | return _name; 185 | } 186 | 187 | function symbol() public view returns (string memory) { 188 | return _symbol; 189 | } 190 | 191 | function decimals() public view returns (uint8) { 192 | return _decimals; 193 | } 194 | 195 | function totalSupply() public view override returns (uint256) { 196 | return _totalSupply; 197 | } 198 | 199 | function balanceOf(address account) public view override returns (uint256) { 200 | return _balances[account]; 201 | } 202 | 203 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 204 | _transfer(Context._msgSender(), recipient, amount); 205 | return true; 206 | } 207 | 208 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 209 | return _allowances[owner][spender]; 210 | } 211 | 212 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 213 | _approve(Context._msgSender(), spender, amount); 214 | return true; 215 | } 216 | 217 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 218 | _transfer(sender, recipient, amount); 219 | _approve(sender, Context._msgSender(), _allowances[sender][Context._msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); 220 | return true; 221 | } 222 | 223 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 224 | _approve(Context._msgSender(), spender, _allowances[Context._msgSender()][spender].add(addedValue)); 225 | return true; 226 | } 227 | 228 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 229 | _approve(Context._msgSender(), spender, _allowances[Context._msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 230 | return true; 231 | } 232 | 233 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 234 | require(sender != address(0), "ERC20: transfer from the zero address"); 235 | require(recipient != address(0), "ERC20: transfer to the zero address"); 236 | 237 | _beforeTokenTransfer(sender, recipient, amount); 238 | 239 | _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 240 | _balances[recipient] = _balances[recipient].add(amount); 241 | emit Transfer(sender, recipient, amount); 242 | } 243 | 244 | function _mint(address account_, uint256 ammount_) internal virtual { 245 | require(account_ != address(0), "ERC20: mint to the zero address"); 246 | _beforeTokenTransfer(address( this ), account_, ammount_); 247 | _totalSupply = _totalSupply.add(ammount_); 248 | _balances[account_] = _balances[account_].add(ammount_); 249 | emit Transfer(address( this ), account_, ammount_); 250 | } 251 | 252 | function _burn(address account, uint256 amount) internal virtual { 253 | require(account != address(0), "ERC20: burn from the zero address"); 254 | 255 | _beforeTokenTransfer(account, address(0), amount); 256 | 257 | _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); 258 | _totalSupply = _totalSupply.sub(amount); 259 | emit Transfer(account, address(0), amount); 260 | } 261 | 262 | function _approve(address owner, address spender, uint256 amount) internal virtual { 263 | require(owner != address(0), "ERC20: approve from the zero address"); 264 | require(spender != address(0), "ERC20: approve to the zero address"); 265 | 266 | _allowances[owner][spender] = amount; 267 | emit Approval(owner, spender, amount); 268 | } 269 | 270 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 271 | } 272 | 273 | abstract contract Divine is ERC20, Ownable { 274 | 275 | constructor ( string memory name_, string memory symbol_, uint8 decimals_ ) ERC20( name_, symbol_, decimals_ ) {} 276 | } 277 | 278 | contract PBTRFLY is Divine { 279 | 280 | using SafeMath for uint256; 281 | 282 | bool public requireSellerApproval; 283 | bool public allowMinting; 284 | 285 | mapping( address => bool ) public isApprovedSeller; 286 | 287 | constructor() Divine( "pBTRFLY", "pBTRFLY", 18 ) { 288 | uint256 initialSupply_ = 25 * 1000 * 1000 * 1e18; //writing out 25 million ether units the long way to be safe 289 | requireSellerApproval = true; 290 | allowMinting = true; 291 | _addApprovedSeller( address(this) ); 292 | _addApprovedSeller( msg.sender ); 293 | _mint( owner(), initialSupply_ ); 294 | } 295 | 296 | function allowOpenTrading() external onlyOwner() returns ( bool ) { 297 | requireSellerApproval = false; 298 | return requireSellerApproval; 299 | } 300 | 301 | function disableMinting() external onlyOwner() returns ( bool ) { 302 | allowMinting = false; 303 | return allowMinting; 304 | } 305 | 306 | function _addApprovedSeller( address approvedSeller_ ) internal { 307 | isApprovedSeller[approvedSeller_] = true; 308 | } 309 | 310 | function addApprovedSeller( address approvedSeller_ ) external onlyOwner() returns ( bool ) { 311 | _addApprovedSeller( approvedSeller_ ); 312 | return isApprovedSeller[approvedSeller_]; 313 | } 314 | 315 | function addApprovedSellers( address[] calldata approvedSellers_ ) external onlyOwner() returns ( bool ) { 316 | 317 | for( uint256 iteration_; approvedSellers_.length > iteration_; iteration_++ ) { 318 | _addApprovedSeller( approvedSellers_[iteration_] ); 319 | } 320 | return true; 321 | } 322 | 323 | function _removeApprovedSeller( address disapprovedSeller_ ) internal { 324 | isApprovedSeller[disapprovedSeller_] = false; 325 | } 326 | 327 | function removeApprovedSeller( address disapprovedSeller_ ) external onlyOwner() returns ( bool ) { 328 | _removeApprovedSeller( disapprovedSeller_ ); 329 | return isApprovedSeller[disapprovedSeller_]; 330 | } 331 | 332 | function removeApprovedSellers( address[] calldata disapprovedSellers_ ) external onlyOwner() returns ( bool ) { 333 | 334 | for( uint256 iteration_; disapprovedSellers_.length > iteration_; iteration_++ ) { 335 | _removeApprovedSeller( disapprovedSellers_[iteration_] ); 336 | } 337 | return true; 338 | } 339 | function _beforeTokenTransfer(address from_, address to_, uint256 amount_ ) internal override { 340 | require( (_balances[to_] > 0 || isApprovedSeller[from_] == true), "Account not approved to transfer pBTRFLY." ); 341 | } 342 | 343 | function mint( address recipient_, uint256 amount_) public virtual onlyOwner() { 344 | require( allowMinting, "Minting has been disabled." ); 345 | _mint( recipient_, amount_ ); 346 | } 347 | 348 | function burn(uint256 amount_) public virtual { 349 | _burn( msg.sender, amount_ ); 350 | } 351 | 352 | function burnFrom( address account_, uint256 amount_ ) public virtual { 353 | _burnFrom( account_, amount_ ); 354 | } 355 | 356 | function _burnFrom( address account_, uint256 amount_ ) internal virtual { 357 | uint256 decreasedAllowance_ = allowance( account_, msg.sender ).sub( amount_, "ERC20: burn amount exceeds allowance"); 358 | _approve( account_, msg.sender, decreasedAllowance_ ); 359 | _burn( account_, amount_ ); 360 | } 361 | } -------------------------------------------------------------------------------- /contracts/RedeemHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | interface IOwnable { 5 | function policy() external view returns (address); 6 | 7 | function renounceManagement() external; 8 | 9 | function pushManagement( address newOwner_ ) external; 10 | 11 | function pullManagement() external; 12 | } 13 | 14 | contract Ownable is IOwnable { 15 | 16 | address internal _owner; 17 | address internal _newOwner; 18 | 19 | event OwnershipPushed(address indexed previousOwner, address indexed newOwner); 20 | event OwnershipPulled(address indexed previousOwner, address indexed newOwner); 21 | 22 | constructor () { 23 | _owner = msg.sender; 24 | emit OwnershipPushed( address(0), _owner ); 25 | } 26 | 27 | function policy() public view override returns (address) { 28 | return _owner; 29 | } 30 | 31 | modifier onlyPolicy() { 32 | require( _owner == msg.sender, "Ownable: caller is not the owner" ); 33 | _; 34 | } 35 | 36 | function renounceManagement() public virtual override onlyPolicy() { 37 | emit OwnershipPushed( _owner, address(0) ); 38 | _owner = address(0); 39 | } 40 | 41 | function pushManagement( address newOwner_ ) public virtual override onlyPolicy() { 42 | require( newOwner_ != address(0), "Ownable: new owner is the zero address"); 43 | emit OwnershipPushed( _owner, newOwner_ ); 44 | _newOwner = newOwner_; 45 | } 46 | 47 | function pullManagement() public virtual override { 48 | require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); 49 | emit OwnershipPulled( _owner, _newOwner ); 50 | _owner = _newOwner; 51 | } 52 | } 53 | 54 | interface IBond { 55 | function redeem( address _recipient, bool _stake ) external returns ( uint ); 56 | function pendingPayoutFor( address _depositor ) external view returns ( uint pendingPayout_ ); 57 | } 58 | 59 | contract RedeemHelper is Ownable { 60 | 61 | address[] public bonds; 62 | 63 | function redeemAll( address _recipient, bool _stake ) external { 64 | for( uint i = 0; i < bonds.length; i++ ) { 65 | if ( bonds[i] != address(0) ) { 66 | if ( IBond( bonds[i] ).pendingPayoutFor( _recipient ) > 0 ) { 67 | IBond( bonds[i] ).redeem( _recipient, _stake ); 68 | } 69 | } 70 | } 71 | } 72 | 73 | function addBondContract( address _bond ) external onlyPolicy() { 74 | require( _bond != address(0) ); 75 | bonds.push( _bond ); 76 | } 77 | 78 | function removeBondContract( uint _index ) external onlyPolicy() { 79 | bonds[ _index ] = address(0); 80 | } 81 | } -------------------------------------------------------------------------------- /contracts/ReserveIOUERC20.sol: -------------------------------------------------------------------------------- 1 | // A unit of account token exclusively for the purpose of trading 2 | 3 | // SPDX-License-Identifier: AGPL-3.0-or-later 4 | pragma solidity 0.7.5; 5 | 6 | library SafeMath { 7 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 8 | uint256 c = a + b; 9 | require(c >= a, "SafeMath: addition overflow"); 10 | 11 | return c; 12 | } 13 | 14 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 15 | return sub(a, b, "SafeMath: subtraction overflow"); 16 | } 17 | 18 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 19 | require(b <= a, errorMessage); 20 | uint256 c = a - b; 21 | 22 | return c; 23 | } 24 | 25 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 26 | if (a == 0) { 27 | return 0; 28 | } 29 | 30 | uint256 c = a * b; 31 | require(c / a == b, "SafeMath: multiplication overflow"); 32 | 33 | return c; 34 | } 35 | 36 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 37 | return div(a, b, "SafeMath: division by zero"); 38 | } 39 | 40 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 41 | require(b > 0, errorMessage); 42 | uint256 c = a / b; 43 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 44 | 45 | return c; 46 | } 47 | 48 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 49 | return mod(a, b, "SafeMath: modulo by zero"); 50 | } 51 | 52 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 53 | require(b != 0, errorMessage); 54 | return a % b; 55 | } 56 | 57 | function sqrrt(uint256 a) internal pure returns (uint c) { 58 | if (a > 3) { 59 | c = a; 60 | uint b = add( div( a, 2), 1 ); 61 | while (b < c) { 62 | c = b; 63 | b = div( add( div( a, b ), b), 2 ); 64 | } 65 | } else if (a != 0) { 66 | c = 1; 67 | } 68 | } 69 | 70 | function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { 71 | return div( mul( total_, percentage_ ), 1000 ); 72 | } 73 | 74 | function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { 75 | return div( mul(part_, 100) , total_ ); 76 | } 77 | 78 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 79 | // (a + b) / 2 can overflow, so we distribute 80 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 81 | } 82 | 83 | function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { 84 | return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); 85 | } 86 | 87 | function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { 88 | return sqrrt( mul( multiplier_, payment_ ) ); 89 | } 90 | 91 | function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { 92 | return mul( multiplier_, supply_ ); 93 | } 94 | } 95 | 96 | library Context { 97 | function _msgSender() internal view returns (address payable) { 98 | return msg.sender; 99 | } 100 | 101 | function _msgData() internal view returns (bytes memory) { 102 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 103 | return msg.data; 104 | } 105 | } 106 | 107 | contract Ownable { 108 | 109 | address internal _owner; 110 | 111 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 112 | 113 | constructor () { 114 | address msgSender = Context._msgSender(); 115 | _owner = msgSender; 116 | emit OwnershipTransferred(address(0), msgSender); 117 | } 118 | 119 | function owner() public view returns (address) { 120 | return _owner; 121 | } 122 | 123 | modifier onlyOwner() { 124 | require(_owner == Context._msgSender(), "Ownable: caller is not the owner"); 125 | _; 126 | } 127 | 128 | function renounceOwnership() public virtual onlyOwner { 129 | emit OwnershipTransferred(_owner, address(0)); 130 | _owner = address(0); 131 | } 132 | 133 | function transferOwnership(address newOwner) public virtual onlyOwner { 134 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 135 | emit OwnershipTransferred(_owner, newOwner); 136 | _owner = newOwner; 137 | } 138 | } 139 | 140 | interface IERC20 { 141 | function totalSupply() external view returns (uint256); 142 | 143 | function balanceOf(address account) external view returns (uint256); 144 | 145 | function transfer(address recipient, uint256 amount) external returns (bool); 146 | 147 | function allowance(address owner, address spender) external view returns (uint256); 148 | 149 | function approve(address spender, uint256 amount) external returns (bool); 150 | 151 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 152 | 153 | event Transfer(address indexed from, address indexed to, uint256 value); 154 | 155 | event Approval(address indexed owner, address indexed spender, uint256 value); 156 | } 157 | 158 | abstract contract ERC20 is IERC20 { 159 | 160 | using SafeMath for uint256; 161 | 162 | 163 | mapping (address => uint256) internal _balances; 164 | 165 | mapping (address => mapping (address => uint256)) internal _allowances; 166 | 167 | uint256 internal _totalSupply; 168 | 169 | string internal _name; 170 | 171 | string internal _symbol; 172 | 173 | uint8 internal _decimals; 174 | 175 | constructor (string memory name_, string memory symbol_, uint8 decimals_) { 176 | _name = name_; 177 | _symbol = symbol_; 178 | _decimals = decimals_; 179 | } 180 | 181 | function name() public view returns (string memory) { 182 | return _name; 183 | } 184 | 185 | function symbol() public view returns (string memory) { 186 | return _symbol; 187 | } 188 | 189 | function decimals() public view returns (uint8) { 190 | return _decimals; 191 | } 192 | 193 | function totalSupply() public view override returns (uint256) { 194 | return _totalSupply; 195 | } 196 | 197 | function balanceOf(address account) public view override returns (uint256) { 198 | return _balances[account]; 199 | } 200 | 201 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 202 | _transfer(Context._msgSender(), recipient, amount); 203 | return true; 204 | } 205 | 206 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 207 | return _allowances[owner][spender]; 208 | } 209 | 210 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 211 | _approve(Context._msgSender(), spender, amount); 212 | return true; 213 | } 214 | 215 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 216 | _transfer(sender, recipient, amount); 217 | _approve(sender, Context._msgSender(), _allowances[sender][Context._msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); 218 | return true; 219 | } 220 | 221 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 222 | _approve(Context._msgSender(), spender, _allowances[Context._msgSender()][spender].add(addedValue)); 223 | return true; 224 | } 225 | 226 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 227 | _approve(Context._msgSender(), spender, _allowances[Context._msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 228 | return true; 229 | } 230 | 231 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 232 | require(sender != address(0), "ERC20: transfer from the zero address"); 233 | require(recipient != address(0), "ERC20: transfer to the zero address"); 234 | 235 | _beforeTokenTransfer(sender, recipient, amount); 236 | 237 | _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 238 | _balances[recipient] = _balances[recipient].add(amount); 239 | emit Transfer(sender, recipient, amount); 240 | } 241 | 242 | function _mint(address account_, uint256 ammount_) internal virtual { 243 | require(account_ != address(0), "ERC20: mint to the zero address"); 244 | _beforeTokenTransfer(address( this ), account_, ammount_); 245 | _totalSupply = _totalSupply.add(ammount_); 246 | _balances[account_] = _balances[account_].add(ammount_); 247 | emit Transfer(address( this ), account_, ammount_); 248 | } 249 | 250 | function _burn(address account, uint256 amount) internal virtual { 251 | require(account != address(0), "ERC20: burn from the zero address"); 252 | 253 | _beforeTokenTransfer(account, address(0), amount); 254 | 255 | _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); 256 | _totalSupply = _totalSupply.sub(amount); 257 | emit Transfer(account, address(0), amount); 258 | } 259 | 260 | function _approve(address owner, address spender, uint256 amount) internal virtual { 261 | require(owner != address(0), "ERC20: approve from the zero address"); 262 | require(spender != address(0), "ERC20: approve to the zero address"); 263 | 264 | _allowances[owner][spender] = amount; 265 | emit Approval(owner, spender, amount); 266 | } 267 | 268 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 269 | } 270 | 271 | library Counters { 272 | using SafeMath for uint256; 273 | 274 | struct Counter { 275 | uint256 _value; // default: 0 276 | } 277 | 278 | function current(Counter storage counter) internal view returns (uint256) { 279 | return counter._value; 280 | } 281 | 282 | function increment(Counter storage counter) internal { 283 | counter._value += 1; 284 | } 285 | 286 | function decrement(Counter storage counter) internal { 287 | counter._value = counter._value.sub(1); 288 | } 289 | } 290 | 291 | interface IERC2612Permit { 292 | 293 | function permit( 294 | address owner, 295 | address spender, 296 | uint256 amount, 297 | uint256 deadline, 298 | uint8 v, 299 | bytes32 r, 300 | bytes32 s 301 | ) external; 302 | 303 | function nonces(address owner) external view returns (uint256); 304 | } 305 | 306 | abstract contract ERC20Permit is ERC20, IERC2612Permit { 307 | using Counters for Counters.Counter; 308 | 309 | mapping(address => Counters.Counter) private _nonces; 310 | 311 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 312 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 313 | 314 | bytes32 public DOMAIN_SEPARATOR; 315 | 316 | constructor() { 317 | uint256 chainID; 318 | assembly { 319 | chainID := chainid() 320 | } 321 | 322 | DOMAIN_SEPARATOR = keccak256( 323 | abi.encode( 324 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 325 | keccak256(bytes(name())), 326 | keccak256(bytes("1")), // Version 327 | chainID, 328 | address(this) 329 | ) 330 | ); 331 | } 332 | 333 | function permit( 334 | address owner, 335 | address spender, 336 | uint256 amount, 337 | uint256 deadline, 338 | uint8 v, 339 | bytes32 r, 340 | bytes32 s 341 | ) public virtual override { 342 | require(block.timestamp <= deadline, "Permit: expired deadline"); 343 | 344 | bytes32 hashStruct = 345 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); 346 | 347 | bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); 348 | 349 | address signer = ecrecover(_hash, v, r, s); 350 | require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature"); 351 | 352 | _nonces[owner].increment(); 353 | _approve(owner, spender, amount); 354 | } 355 | 356 | function nonces(address owner) public view override returns (uint256) { 357 | return _nonces[owner].current(); 358 | } 359 | } 360 | 361 | 362 | abstract contract FrozenToken is Ownable, ERC20Permit { 363 | bool isTokenFrozen = true; 364 | mapping (address => bool ) isAuthorisedOperators; 365 | 366 | 367 | modifier onlyAuthorisedOperators () { 368 | require(!isTokenFrozen || isAuthorisedOperators[msg.sender], 'Frozen: token frozen or msg.sender not authorised'); 369 | _; 370 | } 371 | 372 | 373 | function unFreezeToken () external onlyOwner () { 374 | isTokenFrozen = false; 375 | } 376 | 377 | function changeAuthorisation (address operator, bool status) public onlyOwner { 378 | require(operator != address(0), "Frozen: new operator cant be zero address"); 379 | isAuthorisedOperators[operator] = status; 380 | } 381 | 382 | 383 | function addBatchAuthorisedOperators(address[] memory authorisedOperatorsArray) external onlyOwner { 384 | for (uint i = 0; i < authorisedOperatorsArray.length; i++) { 385 | changeAuthorisation(authorisedOperatorsArray[i],true); 386 | } 387 | } 388 | 389 | function transfer(address recipient, uint256 amount) public virtual override onlyAuthorisedOperators returns (bool) { 390 | super.transfer(recipient, amount); 391 | } 392 | 393 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override onlyAuthorisedOperators returns (bool){ 394 | super.transferFrom(sender,recipient,amount); 395 | } 396 | 397 | } 398 | 399 | contract ReserveIOU is FrozenToken { 400 | 401 | constructor ( string memory name_, string memory symbol_, uint8 decimals_ ) ERC20( name_, symbol_, decimals_ ) {} 402 | 403 | function mint(address recipient, uint256 amount) external onlyOwner{ 404 | _mint(recipient, amount); 405 | } 406 | 407 | function burn(uint amount) external{ 408 | _burn(msg.sender, amount); 409 | } 410 | 411 | } -------------------------------------------------------------------------------- /contracts/Staking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library SafeMath { 5 | /** 6 | * @dev Returns the addition of two unsigned integers, reverting on 7 | * overflow. 8 | * 9 | * Counterpart to Solidity's `+` operator. 10 | * 11 | * Requirements: 12 | * 13 | * - Addition cannot overflow. 14 | */ 15 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 16 | uint256 c = a + b; 17 | require(c >= a, "SafeMath: addition overflow"); 18 | 19 | return c; 20 | } 21 | 22 | /** 23 | * @dev Returns the subtraction of two unsigned integers, reverting on 24 | * overflow (when the result is negative). 25 | * 26 | * Counterpart to Solidity's `-` operator. 27 | * 28 | * Requirements: 29 | * 30 | * - Subtraction cannot overflow. 31 | */ 32 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 33 | return sub(a, b, "SafeMath: subtraction overflow"); 34 | } 35 | 36 | /** 37 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 38 | * overflow (when the result is negative). 39 | * 40 | * Counterpart to Solidity's `-` operator. 41 | * 42 | * Requirements: 43 | * 44 | * - Subtraction cannot overflow. 45 | */ 46 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 47 | require(b <= a, errorMessage); 48 | uint256 c = a - b; 49 | 50 | return c; 51 | } 52 | 53 | /** 54 | * @dev Returns the multiplication of two unsigned integers, reverting on 55 | * overflow. 56 | * 57 | * Counterpart to Solidity's `*` operator. 58 | * 59 | * Requirements: 60 | * 61 | * - Multiplication cannot overflow. 62 | */ 63 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 64 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 65 | // benefit is lost if 'b' is also tested. 66 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 67 | if (a == 0) { 68 | return 0; 69 | } 70 | 71 | uint256 c = a * b; 72 | require(c / a == b, "SafeMath: multiplication overflow"); 73 | 74 | return c; 75 | } 76 | 77 | /** 78 | * @dev Returns the integer division of two unsigned integers. Reverts on 79 | * division by zero. The result is rounded towards zero. 80 | * 81 | * Counterpart to Solidity's `/` operator. Note: this function uses a 82 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 83 | * uses an invalid opcode to revert (consuming all remaining gas). 84 | * 85 | * Requirements: 86 | * 87 | * - The divisor cannot be zero. 88 | */ 89 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 90 | return div(a, b, "SafeMath: division by zero"); 91 | } 92 | 93 | /** 94 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 95 | * division by zero. The result is rounded towards zero. 96 | * 97 | * Counterpart to Solidity's `/` operator. Note: this function uses a 98 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 99 | * uses an invalid opcode to revert (consuming all remaining gas). 100 | * 101 | * Requirements: 102 | * 103 | * - The divisor cannot be zero. 104 | */ 105 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 106 | require(b > 0, errorMessage); 107 | uint256 c = a / b; 108 | assert(a == b * c + a % b); // There is no case in which this doesn't hold 109 | 110 | return c; 111 | } 112 | } 113 | 114 | interface IERC20 { 115 | function decimals() external view returns (uint8); 116 | /** 117 | * @dev Returns the amount of tokens in existence. 118 | */ 119 | function totalSupply() external view returns (uint256); 120 | 121 | /** 122 | * @dev Returns the amount of tokens owned by `account`. 123 | */ 124 | function balanceOf(address account) external view returns (uint256); 125 | 126 | /** 127 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 128 | * 129 | * Returns a boolean value indicating whether the operation succeeded. 130 | * 131 | * Emits a {Transfer} event. 132 | */ 133 | function transfer(address recipient, uint256 amount) external returns (bool); 134 | 135 | /** 136 | * @dev Returns the remaining number of tokens that `spender` will be 137 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 138 | * zero by default. 139 | * 140 | * This value changes when {approve} or {transferFrom} are called. 141 | */ 142 | function allowance(address owner, address spender) external view returns (uint256); 143 | 144 | /** 145 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 146 | * 147 | * Returns a boolean value indicating whether the operation succeeded. 148 | * 149 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 150 | * that someone may use both the old and the new allowance by unfortunate 151 | * transaction ordering. One possible solution to mitigate this race 152 | * condition is to first reduce the spender's allowance to 0 and set the 153 | * desired value afterwards: 154 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 155 | * 156 | * Emits an {Approval} event. 157 | */ 158 | function approve(address spender, uint256 amount) external returns (bool); 159 | 160 | /** 161 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 162 | * allowance mechanism. `amount` is then deducted from the caller's 163 | * allowance. 164 | * 165 | * Returns a boolean value indicating whether the operation succeeded. 166 | * 167 | * Emits a {Transfer} event. 168 | */ 169 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 170 | 171 | /** 172 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 173 | * another (`to`). 174 | * 175 | * Note that `value` may be zero. 176 | */ 177 | event Transfer(address indexed from, address indexed to, uint256 value); 178 | 179 | /** 180 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 181 | * a call to {approve}. `value` is the new allowance. 182 | */ 183 | event Approval(address indexed owner, address indexed spender, uint256 value); 184 | } 185 | 186 | library Address { 187 | /** 188 | * @dev Returns true if `account` is a contract. 189 | * 190 | * [IMPORTANT] 191 | * ==== 192 | * It is unsafe to assume that an address for which this function returns 193 | * false is an externally-owned account (EOA) and not a contract. 194 | * 195 | * Among others, `isContract` will return false for the following 196 | * types of addresses: 197 | * 198 | * - an externally-owned account 199 | * - a contract in construction 200 | * - an address where a contract will be created 201 | * - an address where a contract lived, but was destroyed 202 | * ==== 203 | */ 204 | function isContract(address account) internal view returns (bool) { 205 | // This method relies in extcodesize, which returns 0 for contracts in 206 | // construction, since the code is only stored at the end of the 207 | // constructor execution. 208 | 209 | uint256 size; 210 | // solhint-disable-next-line no-inline-assembly 211 | assembly { size := extcodesize(account) } 212 | return size > 0; 213 | } 214 | 215 | /** 216 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 217 | * `recipient`, forwarding all available gas and reverting on errors. 218 | * 219 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 220 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 221 | * imposed by `transfer`, making them unable to receive funds via 222 | * `transfer`. {sendValue} removes this limitation. 223 | * 224 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 225 | * 226 | * IMPORTANT: because control is transferred to `recipient`, care must be 227 | * taken to not create reentrancy vulnerabilities. Consider using 228 | * {ReentrancyGuard} or the 229 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 230 | */ 231 | function sendValue(address payable recipient, uint256 amount) internal { 232 | require(address(this).balance >= amount, "Address: insufficient balance"); 233 | 234 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 235 | (bool success, ) = recipient.call{ value: amount }(""); 236 | require(success, "Address: unable to send value, recipient may have reverted"); 237 | } 238 | 239 | /** 240 | * @dev Performs a Solidity function call using a low level `call`. A 241 | * plain`call` is an unsafe replacement for a function call: use this 242 | * function instead. 243 | * 244 | * If `target` reverts with a revert reason, it is bubbled up by this 245 | * function (like regular Solidity function calls). 246 | * 247 | * Returns the raw returned data. To convert to the expected return value, 248 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 249 | * 250 | * Requirements: 251 | * 252 | * - `target` must be a contract. 253 | * - calling `target` with `data` must not revert. 254 | * 255 | * _Available since v3.1._ 256 | */ 257 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 258 | return functionCall(target, data, "Address: low-level call failed"); 259 | } 260 | 261 | /** 262 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 263 | * `errorMessage` as a fallback revert reason when `target` reverts. 264 | * 265 | * _Available since v3.1._ 266 | */ 267 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 268 | return _functionCallWithValue(target, data, 0, errorMessage); 269 | } 270 | 271 | /** 272 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 273 | * but also transferring `value` wei to `target`. 274 | * 275 | * Requirements: 276 | * 277 | * - the calling contract must have an ETH balance of at least `value`. 278 | * - the called Solidity function must be `payable`. 279 | * 280 | * _Available since v3.1._ 281 | */ 282 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 283 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 284 | } 285 | 286 | /** 287 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 288 | * with `errorMessage` as a fallback revert reason when `target` reverts. 289 | * 290 | * _Available since v3.1._ 291 | */ 292 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 293 | require(address(this).balance >= value, "Address: insufficient balance for call"); 294 | require(isContract(target), "Address: call to non-contract"); 295 | 296 | // solhint-disable-next-line avoid-low-level-calls 297 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 298 | return _verifyCallResult(success, returndata, errorMessage); 299 | } 300 | 301 | function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { 302 | require(isContract(target), "Address: call to non-contract"); 303 | 304 | // solhint-disable-next-line avoid-low-level-calls 305 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 306 | if (success) { 307 | return returndata; 308 | } else { 309 | // Look for revert reason and bubble it up if present 310 | if (returndata.length > 0) { 311 | // The easiest way to bubble the revert reason is using memory via assembly 312 | 313 | // solhint-disable-next-line no-inline-assembly 314 | assembly { 315 | let returndata_size := mload(returndata) 316 | revert(add(32, returndata), returndata_size) 317 | } 318 | } else { 319 | revert(errorMessage); 320 | } 321 | } 322 | } 323 | 324 | /** 325 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 326 | * but performing a static call. 327 | * 328 | * _Available since v3.3._ 329 | */ 330 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 331 | return functionStaticCall(target, data, "Address: low-level static call failed"); 332 | } 333 | 334 | /** 335 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 336 | * but performing a static call. 337 | * 338 | * _Available since v3.3._ 339 | */ 340 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 341 | require(isContract(target), "Address: static call to non-contract"); 342 | 343 | // solhint-disable-next-line avoid-low-level-calls 344 | (bool success, bytes memory returndata) = target.staticcall(data); 345 | return _verifyCallResult(success, returndata, errorMessage); 346 | } 347 | 348 | /** 349 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 350 | * but performing a delegate call. 351 | * 352 | * _Available since v3.3._ 353 | */ 354 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 355 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 356 | } 357 | 358 | /** 359 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 360 | * but performing a delegate call. 361 | * 362 | * _Available since v3.3._ 363 | */ 364 | function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 365 | require(isContract(target), "Address: delegate call to non-contract"); 366 | 367 | // solhint-disable-next-line avoid-low-level-calls 368 | (bool success, bytes memory returndata) = target.delegatecall(data); 369 | return _verifyCallResult(success, returndata, errorMessage); 370 | } 371 | 372 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 373 | if (success) { 374 | return returndata; 375 | } else { 376 | // Look for revert reason and bubble it up if present 377 | if (returndata.length > 0) { 378 | // The easiest way to bubble the revert reason is using memory via assembly 379 | 380 | // solhint-disable-next-line no-inline-assembly 381 | assembly { 382 | let returndata_size := mload(returndata) 383 | revert(add(32, returndata), returndata_size) 384 | } 385 | } else { 386 | revert(errorMessage); 387 | } 388 | } 389 | } 390 | 391 | function addressToString(address _address) internal pure returns(string memory) { 392 | bytes32 _bytes = bytes32(uint256(_address)); 393 | bytes memory HEX = "0123456789abcdef"; 394 | bytes memory _addr = new bytes(42); 395 | 396 | _addr[0] = '0'; 397 | _addr[1] = 'x'; 398 | 399 | for(uint256 i = 0; i < 20; i++) { 400 | _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; 401 | _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; 402 | } 403 | 404 | return string(_addr); 405 | 406 | } 407 | } 408 | 409 | library SafeERC20 { 410 | using SafeMath for uint256; 411 | using Address for address; 412 | 413 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 414 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 415 | } 416 | 417 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 418 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 419 | } 420 | 421 | /** 422 | * @dev Deprecated. This function has issues similar to the ones found in 423 | * {IERC20-approve}, and its usage is discouraged. 424 | * 425 | * Whenever possible, use {safeIncreaseAllowance} and 426 | * {safeDecreaseAllowance} instead. 427 | */ 428 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 429 | // safeApprove should only be called when setting an initial allowance, 430 | // or when resetting it to zero. To increase and decrease it, use 431 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 432 | // solhint-disable-next-line max-line-length 433 | require((value == 0) || (token.allowance(address(this), spender) == 0), 434 | "SafeERC20: approve from non-zero to non-zero allowance" 435 | ); 436 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 437 | } 438 | 439 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 440 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 441 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 442 | } 443 | 444 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 445 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); 446 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 447 | } 448 | 449 | /** 450 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 451 | * on the return value: the return value is optional (but if data is returned, it must not be false). 452 | * @param token The token targeted by the call. 453 | * @param data The call data (encoded using abi.encode or one of its variants). 454 | */ 455 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 456 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 457 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 458 | // the target address contains contract code and also asserts for success in the low-level call. 459 | 460 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 461 | if (returndata.length > 0) { // Return data is optional 462 | // solhint-disable-next-line max-line-length 463 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 464 | } 465 | } 466 | } 467 | 468 | interface IOwnable { 469 | function manager() external view returns (address); 470 | 471 | function renounceManagement() external; 472 | 473 | function pushManagement( address newOwner_ ) external; 474 | 475 | function pullManagement() external; 476 | } 477 | 478 | contract Ownable is IOwnable { 479 | 480 | address internal _owner; 481 | address internal _newOwner; 482 | 483 | event OwnershipPushed(address indexed previousOwner, address indexed newOwner); 484 | event OwnershipPulled(address indexed previousOwner, address indexed newOwner); 485 | 486 | constructor () { 487 | _owner = msg.sender; 488 | emit OwnershipPushed( address(0), _owner ); 489 | } 490 | 491 | function manager() public view override returns (address) { 492 | return _owner; 493 | } 494 | 495 | modifier onlyManager() { 496 | require( _owner == msg.sender, "Ownable: caller is not the owner" ); 497 | _; 498 | } 499 | 500 | function renounceManagement() public virtual override onlyManager() { 501 | emit OwnershipPushed( _owner, address(0) ); 502 | _owner = address(0); 503 | } 504 | 505 | function pushManagement( address newOwner_ ) public virtual override onlyManager() { 506 | require( newOwner_ != address(0), "Ownable: new owner is the zero address"); 507 | emit OwnershipPushed( _owner, newOwner_ ); 508 | _newOwner = newOwner_; 509 | } 510 | 511 | function pullManagement() public virtual override { 512 | require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); 513 | emit OwnershipPulled( _owner, _newOwner ); 514 | _owner = _newOwner; 515 | } 516 | } 517 | 518 | interface IsBTRFLY { 519 | function rebase( uint256 btrflyProfit_, uint epoch_) external returns (uint256); 520 | 521 | function circulatingSupply() external view returns (uint256); 522 | 523 | function balanceOf(address who) external view returns (uint256); 524 | 525 | function gonsForBalance( uint amount ) external view returns ( uint ); 526 | 527 | function balanceForGons( uint gons ) external view returns ( uint ); 528 | 529 | function index() external view returns ( uint ); 530 | } 531 | 532 | interface IWarmup { 533 | function retrieve( address staker_, uint amount_ ) external; 534 | } 535 | 536 | interface IDistributor { 537 | function distribute() external returns ( bool ); 538 | } 539 | 540 | contract REDACTEDStaking is Ownable { 541 | 542 | using SafeMath for uint256; 543 | using SafeERC20 for IERC20; 544 | 545 | address public immutable BTRFLY; 546 | address public immutable sBTRFLY; 547 | 548 | struct Epoch { 549 | uint length; 550 | uint number; 551 | uint endBlock; 552 | uint distribute; 553 | } 554 | Epoch public epoch; 555 | 556 | address public distributor; 557 | 558 | address public locker; 559 | uint public totalBonus; 560 | 561 | address public warmupContract; 562 | uint public warmupPeriod; 563 | 564 | constructor ( 565 | address _BTRFLY, 566 | address _sBTRFLY, 567 | uint _epochLength, 568 | uint _firstEpochNumber, 569 | uint _firstEpochBlock 570 | ) { 571 | require( _BTRFLY != address(0) ); 572 | BTRFLY = _BTRFLY; 573 | require( _sBTRFLY != address(0) ); 574 | sBTRFLY = _sBTRFLY; 575 | 576 | epoch = Epoch({ 577 | length: _epochLength, 578 | number: _firstEpochNumber, 579 | endBlock: _firstEpochBlock, 580 | distribute: 0 581 | }); 582 | } 583 | 584 | struct Claim { 585 | uint deposit; 586 | uint gons; 587 | uint expiry; 588 | bool lock; // prevents malicious delays 589 | } 590 | mapping( address => Claim ) public warmupInfo; 591 | 592 | /** 593 | @notice stake BTRFLY to enter warmup 594 | @param _amount uint 595 | @return bool 596 | */ 597 | function stake( uint _amount, address _recipient ) external returns ( bool ) { 598 | rebase(); 599 | 600 | IERC20( BTRFLY ).safeTransferFrom( msg.sender, address(this), _amount ); 601 | 602 | Claim memory info = warmupInfo[ _recipient ]; 603 | require( !info.lock, "Deposits for account are locked" ); 604 | 605 | warmupInfo[ _recipient ] = Claim ({ 606 | deposit: info.deposit.add( _amount ), 607 | gons: info.gons.add( IsBTRFLY( sBTRFLY ).gonsForBalance( _amount ) ), 608 | expiry: epoch.number.add( warmupPeriod ), 609 | lock: false 610 | }); 611 | 612 | IERC20( sBTRFLY ).safeTransfer( warmupContract, _amount ); 613 | return true; 614 | } 615 | 616 | /** 617 | @notice retrieve sBTRFLY from warmup 618 | @param _recipient address 619 | */ 620 | function claim ( address _recipient ) public { 621 | Claim memory info = warmupInfo[ _recipient ]; 622 | if ( epoch.number >= info.expiry && info.expiry != 0 ) { 623 | delete warmupInfo[ _recipient ]; 624 | IWarmup( warmupContract ).retrieve( _recipient, IsBTRFLY( sBTRFLY ).balanceForGons( info.gons ) ); 625 | } 626 | } 627 | 628 | /** 629 | @notice forfeit sBTRFLY in warmup and retrieve BTRFLY 630 | */ 631 | function forfeit() external { 632 | Claim memory info = warmupInfo[ msg.sender ]; 633 | delete warmupInfo[ msg.sender ]; 634 | 635 | IWarmup( warmupContract ).retrieve( address(this), IsBTRFLY( sBTRFLY ).balanceForGons( info.gons ) ); 636 | IERC20( BTRFLY ).safeTransfer( msg.sender, info.deposit ); 637 | } 638 | 639 | /** 640 | @notice prevent new deposits to address (protection from malicious activity) 641 | */ 642 | function toggleDepositLock() external { 643 | warmupInfo[ msg.sender ].lock = !warmupInfo[ msg.sender ].lock; 644 | } 645 | 646 | /** 647 | @notice redeem sBTRFLY for BTRFLY 648 | @param _amount uint 649 | @param _trigger bool 650 | */ 651 | function unstake( uint _amount, bool _trigger ) external { 652 | if ( _trigger ) { 653 | rebase(); 654 | } 655 | IERC20( sBTRFLY ).safeTransferFrom( msg.sender, address(this), _amount ); 656 | IERC20( BTRFLY ).safeTransfer( msg.sender, _amount ); 657 | } 658 | 659 | /** 660 | @notice returns the sBTRFLY index, which tracks rebase growth 661 | @return uint 662 | */ 663 | function index() public view returns ( uint ) { 664 | return IsBTRFLY( sBTRFLY ).index(); 665 | } 666 | 667 | /** 668 | @notice trigger rebase if epoch over 669 | */ 670 | function rebase() public { 671 | if( epoch.endBlock <= block.number ) { 672 | 673 | IsBTRFLY( sBTRFLY ).rebase( epoch.distribute, epoch.number ); 674 | 675 | epoch.endBlock = epoch.endBlock.add( epoch.length ); 676 | epoch.number++; 677 | 678 | if ( distributor != address(0) ) { 679 | IDistributor( distributor ).distribute(); 680 | } 681 | 682 | uint balance = contractBalance(); 683 | uint staked = IsBTRFLY( sBTRFLY ).circulatingSupply(); 684 | 685 | if( balance <= staked ) { 686 | epoch.distribute = 0; 687 | } else { 688 | epoch.distribute = balance.sub( staked ); 689 | } 690 | } 691 | } 692 | 693 | /** 694 | @notice returns contract BTRFLY holdings, including bonuses provided 695 | @return uint 696 | */ 697 | function contractBalance() public view returns ( uint ) { 698 | return IERC20( BTRFLY ).balanceOf( address(this) ).add( totalBonus ); 699 | } 700 | 701 | /** 702 | @notice provide bonus to locked staking contract 703 | @param _amount uint 704 | */ 705 | function giveLockBonus( uint _amount ) external { 706 | require( msg.sender == locker ); 707 | totalBonus = totalBonus.add( _amount ); 708 | IERC20( sBTRFLY ).safeTransfer( locker, _amount ); 709 | } 710 | 711 | /** 712 | @notice reclaim bonus from locked staking contract 713 | @param _amount uint 714 | */ 715 | function returnLockBonus( uint _amount ) external { 716 | require( msg.sender == locker ); 717 | totalBonus = totalBonus.sub( _amount ); 718 | IERC20( sBTRFLY ).safeTransferFrom( locker, address(this), _amount ); 719 | } 720 | 721 | enum CONTRACTS { DISTRIBUTOR, WARMUP, LOCKER } 722 | 723 | /** 724 | @notice sets the contract address for LP staking 725 | @param _contract address 726 | */ 727 | function setContract( CONTRACTS _contract, address _address ) external onlyManager() { 728 | if( _contract == CONTRACTS.DISTRIBUTOR ) { // 0 729 | distributor = _address; 730 | } else if ( _contract == CONTRACTS.WARMUP ) { // 1 731 | require( warmupContract == address( 0 ), "Warmup cannot be set more than once" ); 732 | warmupContract = _address; 733 | } else if ( _contract == CONTRACTS.LOCKER ) { // 2 734 | require( locker == address(0), "Locker cannot be set more than once" ); 735 | locker = _address; 736 | } 737 | } 738 | 739 | /** 740 | * @notice set warmup period for new stakers 741 | * @param _warmupPeriod uint 742 | */ 743 | function setWarmup( uint _warmupPeriod ) external onlyManager() { 744 | warmupPeriod = _warmupPeriod; 745 | } 746 | } -------------------------------------------------------------------------------- /contracts/StakingDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | 3 | pragma solidity 0.7.5; 4 | 5 | library SafeERC20 { 6 | using SafeMath for uint256; 7 | using Address for address; 8 | 9 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 10 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 11 | } 12 | 13 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 14 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 15 | } 16 | 17 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 18 | 19 | require((value == 0) || (token.allowance(address(this), spender) == 0), 20 | "SafeERC20: approve from non-zero to non-zero allowance" 21 | ); 22 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 23 | } 24 | 25 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 26 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 27 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 28 | } 29 | 30 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 31 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); 32 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 33 | } 34 | 35 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 36 | 37 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 38 | if (returndata.length > 0) { // Return data is optional 39 | // solhint-disable-next-line max-line-length 40 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 41 | } 42 | } 43 | } 44 | 45 | library SafeMath { 46 | 47 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 48 | uint256 c = a + b; 49 | require(c >= a, "SafeMath: addition overflow"); 50 | 51 | return c; 52 | } 53 | 54 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 55 | return sub(a, b, "SafeMath: subtraction overflow"); 56 | } 57 | 58 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 59 | require(b <= a, errorMessage); 60 | uint256 c = a - b; 61 | 62 | return c; 63 | } 64 | 65 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 66 | 67 | if (a == 0) { 68 | return 0; 69 | } 70 | 71 | uint256 c = a * b; 72 | require(c / a == b, "SafeMath: multiplication overflow"); 73 | 74 | return c; 75 | } 76 | 77 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 78 | return div(a, b, "SafeMath: division by zero"); 79 | } 80 | 81 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 82 | require(b > 0, errorMessage); 83 | uint256 c = a / b; 84 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 85 | 86 | return c; 87 | } 88 | 89 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 90 | return mod(a, b, "SafeMath: modulo by zero"); 91 | } 92 | 93 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 94 | require(b != 0, errorMessage); 95 | return a % b; 96 | } 97 | 98 | // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) 99 | function sqrrt(uint256 a) internal pure returns (uint c) { 100 | if (a > 3) { 101 | c = a; 102 | uint b = add( div( a, 2), 1 ); 103 | while (b < c) { 104 | c = b; 105 | b = div( add( div( a, b ), b), 2 ); 106 | } 107 | } else if (a != 0) { 108 | c = 1; 109 | } 110 | } 111 | 112 | function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { 113 | return div( mul( total_, percentage_ ), 1000 ); 114 | } 115 | 116 | function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { 117 | return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); 118 | } 119 | 120 | function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { 121 | return div( mul(part_, 100) , total_ ); 122 | } 123 | 124 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 125 | // (a + b) / 2 can overflow, so we distribute 126 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 127 | } 128 | 129 | function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { 130 | return sqrrt( mul( multiplier_, payment_ ) ); 131 | } 132 | 133 | function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { 134 | return mul( multiplier_, supply_ ); 135 | } 136 | } 137 | 138 | interface IERC20 { 139 | 140 | function totalSupply() external view returns (uint256); 141 | 142 | function balanceOf(address account) external view returns (uint256); 143 | 144 | function transfer(address recipient, uint256 amount) external returns (bool); 145 | 146 | function allowance(address owner, address spender) external view returns (uint256); 147 | 148 | function approve(address spender, uint256 amount) external returns (bool); 149 | 150 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 151 | 152 | event Transfer(address indexed from, address indexed to, uint256 value); 153 | 154 | event Approval(address indexed owner, address indexed spender, uint256 value); 155 | } 156 | 157 | library Address { 158 | 159 | function isContract(address account) internal view returns (bool) { 160 | // This method relies in extcodesize, which returns 0 for contracts in 161 | // construction, since the code is only stored at the end of the 162 | // constructor execution. 163 | 164 | uint256 size; 165 | // solhint-disable-next-line no-inline-assembly 166 | assembly { size := extcodesize(account) } 167 | return size > 0; 168 | } 169 | 170 | function sendValue(address payable recipient, uint256 amount) internal { 171 | require(address(this).balance >= amount, "Address: insufficient balance"); 172 | 173 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 174 | (bool success, ) = recipient.call{ value: amount }(""); 175 | require(success, "Address: unable to send value, recipient may have reverted"); 176 | } 177 | 178 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 179 | return functionCall(target, data, "Address: low-level call failed"); 180 | } 181 | 182 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 183 | return _functionCallWithValue(target, data, 0, errorMessage); 184 | } 185 | 186 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 187 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 188 | } 189 | 190 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 191 | require(address(this).balance >= value, "Address: insufficient balance for call"); 192 | require(isContract(target), "Address: call to non-contract"); 193 | 194 | // solhint-disable-next-line avoid-low-level-calls 195 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 196 | return _verifyCallResult(success, returndata, errorMessage); 197 | } 198 | 199 | function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { 200 | require(isContract(target), "Address: call to non-contract"); 201 | 202 | // solhint-disable-next-line avoid-low-level-calls 203 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 204 | if (success) { 205 | return returndata; 206 | } else { 207 | // Look for revert reason and bubble it up if present 208 | if (returndata.length > 0) { 209 | // The easiest way to bubble the revert reason is using memory via assembly 210 | 211 | // solhint-disable-next-line no-inline-assembly 212 | assembly { 213 | let returndata_size := mload(returndata) 214 | revert(add(32, returndata), returndata_size) 215 | } 216 | } else { 217 | revert(errorMessage); 218 | } 219 | } 220 | } 221 | 222 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 223 | return functionStaticCall(target, data, "Address: low-level static call failed"); 224 | } 225 | 226 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 227 | require(isContract(target), "Address: static call to non-contract"); 228 | 229 | // solhint-disable-next-line avoid-low-level-calls 230 | (bool success, bytes memory returndata) = target.staticcall(data); 231 | return _verifyCallResult(success, returndata, errorMessage); 232 | } 233 | 234 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 235 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 236 | } 237 | 238 | function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 239 | require(isContract(target), "Address: delegate call to non-contract"); 240 | (bool success, bytes memory returndata) = target.delegatecall(data); 241 | return _verifyCallResult(success, returndata, errorMessage); 242 | } 243 | 244 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 245 | if (success) { 246 | return returndata; 247 | } else { 248 | if (returndata.length > 0) { 249 | assembly { 250 | let returndata_size := mload(returndata) 251 | revert(add(32, returndata), returndata_size) 252 | } 253 | } else { 254 | revert(errorMessage); 255 | } 256 | } 257 | } 258 | 259 | function addressToString(address _address) internal pure returns(string memory) { 260 | bytes32 _bytes = bytes32(uint256(_address)); 261 | bytes memory HEX = "0123456789abcdef"; 262 | bytes memory _addr = new bytes(42); 263 | 264 | _addr[0] = '0'; 265 | _addr[1] = 'x'; 266 | 267 | for(uint256 i = 0; i < 20; i++) { 268 | _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; 269 | _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; 270 | } 271 | 272 | return string(_addr); 273 | 274 | } 275 | } 276 | 277 | 278 | interface IPolicy { 279 | 280 | function policy() external view returns (address); 281 | 282 | function renouncePolicy() external; 283 | 284 | function pushPolicy( address newPolicy_ ) external; 285 | 286 | function pullPolicy() external; 287 | } 288 | 289 | contract Policy is IPolicy { 290 | 291 | address internal _policy; 292 | address internal _newPolicy; 293 | 294 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 295 | 296 | constructor () { 297 | _policy = msg.sender; 298 | emit OwnershipTransferred( address(0), _policy ); 299 | } 300 | 301 | function policy() public view override returns (address) { 302 | return _policy; 303 | } 304 | 305 | modifier onlyPolicy() { 306 | require( _policy == msg.sender, "Ownable: caller is not the owner" ); 307 | _; 308 | } 309 | 310 | function renouncePolicy() public virtual override onlyPolicy() { 311 | emit OwnershipTransferred( _policy, address(0) ); 312 | _policy = address(0); 313 | } 314 | 315 | function pushPolicy( address newPolicy_ ) public virtual override onlyPolicy() { 316 | require( newPolicy_ != address(0), "Ownable: new owner is the zero address"); 317 | _newPolicy = newPolicy_; 318 | } 319 | 320 | function pullPolicy() public virtual override { 321 | require( msg.sender == _newPolicy ); 322 | emit OwnershipTransferred( _policy, _newPolicy ); 323 | _policy = _newPolicy; 324 | } 325 | } 326 | 327 | interface ITreasury { 328 | function mintRewards( address _recipient, uint _amount ) external; 329 | } 330 | 331 | contract Distributor is Policy { 332 | using SafeMath for uint; 333 | using SafeERC20 for IERC20; 334 | 335 | 336 | 337 | /* ====== VARIABLES ====== */ 338 | 339 | address public immutable BTRFLY; 340 | address public immutable treasury; 341 | 342 | uint public immutable epochLength; 343 | uint public nextEpochBlock; 344 | 345 | mapping( uint => Adjust ) public adjustments; 346 | 347 | 348 | /* ====== STRUCTS ====== */ 349 | 350 | struct Info { 351 | uint rate; // in ten-thousandths ( 5000 = 0.5% ) 352 | address recipient; 353 | } 354 | Info[] public info; 355 | 356 | struct Adjust { 357 | bool add; 358 | uint rate; 359 | uint target; 360 | } 361 | 362 | 363 | 364 | /* ====== CONSTRUCTOR ====== */ 365 | 366 | constructor( address _treasury, address _BTRFLY, uint _epochLength, uint _nextEpochBlock ) { 367 | require( _treasury != address(0) ); 368 | treasury = _treasury; 369 | require( _BTRFLY != address(0) ); 370 | BTRFLY = _BTRFLY; 371 | epochLength = _epochLength; 372 | nextEpochBlock = _nextEpochBlock; 373 | } 374 | 375 | 376 | 377 | /* ====== PUBLIC FUNCTIONS ====== */ 378 | 379 | /** 380 | @notice send epoch reward to staking contract 381 | */ 382 | function distribute() external returns ( bool ) { 383 | if ( nextEpochBlock <= block.number ) { 384 | nextEpochBlock = nextEpochBlock.add( epochLength ); // set next epoch block 385 | 386 | // distribute rewards to each recipient 387 | for ( uint i = 0; i < info.length; i++ ) { 388 | if ( info[ i ].rate > 0 ) { 389 | ITreasury( treasury ).mintRewards( // mint and send from treasury 390 | info[ i ].recipient, 391 | nextRewardAt( info[ i ].rate ) 392 | ); 393 | adjust( i ); // check for adjustment 394 | } 395 | } 396 | return true; 397 | } else { 398 | return false; 399 | } 400 | } 401 | 402 | 403 | 404 | /* ====== INTERNAL FUNCTIONS ====== */ 405 | 406 | /** 407 | @notice increment reward rate for collector 408 | */ 409 | function adjust( uint _index ) internal { 410 | Adjust memory adjustment = adjustments[ _index ]; 411 | if ( adjustment.rate != 0 ) { 412 | if ( adjustment.add ) { // if rate should increase 413 | info[ _index ].rate = info[ _index ].rate.add( adjustment.rate ); // raise rate 414 | if ( info[ _index ].rate >= adjustment.target ) { // if target met 415 | adjustments[ _index ].rate = 0; // turn off adjustment 416 | } 417 | } else { // if rate should decrease 418 | info[ _index ].rate = info[ _index ].rate.sub( adjustment.rate ); // lower rate 419 | if ( info[ _index ].rate <= adjustment.target ) { // if target met 420 | adjustments[ _index ].rate = 0; // turn off adjustment 421 | } 422 | } 423 | } 424 | } 425 | 426 | 427 | 428 | /* ====== VIEW FUNCTIONS ====== */ 429 | 430 | /** 431 | @notice view function for next reward at given rate 432 | @param _rate uint 433 | @return uint 434 | */ 435 | function nextRewardAt( uint _rate ) public view returns ( uint ) { 436 | return IERC20( BTRFLY ).totalSupply().mul( _rate ).div( 1000000 ); 437 | } 438 | 439 | /** 440 | @notice view function for next reward for specified address 441 | @param _recipient address 442 | @return uint 443 | */ 444 | function nextRewardFor( address _recipient ) public view returns ( uint ) { 445 | uint reward; 446 | for ( uint i = 0; i < info.length; i++ ) { 447 | if ( info[ i ].recipient == _recipient ) { 448 | reward = nextRewardAt( info[ i ].rate ); 449 | } 450 | } 451 | return reward; 452 | } 453 | 454 | 455 | 456 | /* ====== POLICY FUNCTIONS ====== */ 457 | 458 | /** 459 | @notice adds recipient for distributions 460 | @param _recipient address 461 | @param _rewardRate uint 462 | */ 463 | function addRecipient( address _recipient, uint _rewardRate ) external onlyPolicy() { 464 | require( _recipient != address(0) ); 465 | info.push( Info({ 466 | recipient: _recipient, 467 | rate: _rewardRate 468 | })); 469 | } 470 | 471 | /** 472 | @notice removes recipient for distributions 473 | @param _index uint 474 | @param _recipient address 475 | */ 476 | function removeRecipient( uint _index, address _recipient ) external onlyPolicy() { 477 | require( _recipient == info[ _index ].recipient ); 478 | info[ _index ].recipient = address(0); 479 | info[ _index ].rate = 0; 480 | } 481 | 482 | /** 483 | @notice set adjustment info for a collector's reward rate 484 | @param _index uint 485 | @param _add bool 486 | @param _rate uint 487 | @param _target uint 488 | */ 489 | function setAdjustment( uint _index, bool _add, uint _rate, uint _target ) external onlyPolicy() { 490 | adjustments[ _index ] = Adjust({ 491 | add: _add, 492 | rate: _rate, 493 | target: _target 494 | }); 495 | } 496 | } -------------------------------------------------------------------------------- /contracts/StakingHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | 5 | interface IERC20 { 6 | function decimals() external view returns (uint8); 7 | /** 8 | * @dev Returns the amount of tokens in existence. 9 | */ 10 | function totalSupply() external view returns (uint256); 11 | 12 | /** 13 | * @dev Returns the amount of tokens owned by `account`. 14 | */ 15 | function balanceOf(address account) external view returns (uint256); 16 | 17 | /** 18 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 19 | * 20 | * Returns a boolean value indicating whether the operation succeeded. 21 | * 22 | * Emits a {Transfer} event. 23 | */ 24 | function transfer(address recipient, uint256 amount) external returns (bool); 25 | 26 | /** 27 | * @dev Returns the remaining number of tokens that `spender` will be 28 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 29 | * zero by default. 30 | * 31 | * This value changes when {approve} or {transferFrom} are called. 32 | */ 33 | function allowance(address owner, address spender) external view returns (uint256); 34 | 35 | /** 36 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 37 | * 38 | * Returns a boolean value indicating whether the operation succeeded. 39 | * 40 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 41 | * that someone may use both the old and the new allowance by unfortunate 42 | * transaction ordering. One possible solution to mitigate this race 43 | * condition is to first reduce the spender's allowance to 0 and set the 44 | * desired value afterwards: 45 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 46 | * 47 | * Emits an {Approval} event. 48 | */ 49 | function approve(address spender, uint256 amount) external returns (bool); 50 | 51 | /** 52 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 53 | * allowance mechanism. `amount` is then deducted from the caller's 54 | * allowance. 55 | * 56 | * Returns a boolean value indicating whether the operation succeeded. 57 | * 58 | * Emits a {Transfer} event. 59 | */ 60 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 64 | * another (`to`). 65 | * 66 | * Note that `value` may be zero. 67 | */ 68 | event Transfer(address indexed from, address indexed to, uint256 value); 69 | 70 | /** 71 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 72 | * a call to {approve}. `value` is the new allowance. 73 | */ 74 | event Approval(address indexed owner, address indexed spender, uint256 value); 75 | } 76 | 77 | interface IStaking { 78 | function stake( uint _amount, address _recipient ) external returns ( bool ); 79 | function claim( address _recipient ) external; 80 | } 81 | 82 | contract StakingHelper { 83 | 84 | address public immutable staking; 85 | address public immutable BTRFLY; 86 | 87 | constructor ( address _staking, address _BTRFLY ) { 88 | require( _staking != address(0) ); 89 | staking = _staking; 90 | require( _BTRFLY != address(0) ); 91 | BTRFLY = _BTRFLY; 92 | } 93 | 94 | function stake( uint _amount ) external { 95 | IERC20( BTRFLY ).transferFrom( msg.sender, address(this), _amount ); 96 | IERC20( BTRFLY ).approve( staking, _amount ); 97 | IStaking( staking ).stake( _amount, msg.sender ); 98 | IStaking( staking ).claim( msg.sender ); 99 | } 100 | } -------------------------------------------------------------------------------- /contracts/StakingWarmup.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | 5 | interface IERC20 { 6 | function decimals() external view returns (uint8); 7 | /** 8 | * @dev Returns the amount of tokens in existence. 9 | */ 10 | function totalSupply() external view returns (uint256); 11 | 12 | /** 13 | * @dev Returns the amount of tokens owned by `account`. 14 | */ 15 | function balanceOf(address account) external view returns (uint256); 16 | 17 | /** 18 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 19 | * 20 | * Returns a boolean value indicating whether the operation succeeded. 21 | * 22 | * Emits a {Transfer} event. 23 | */ 24 | function transfer(address recipient, uint256 amount) external returns (bool); 25 | 26 | /** 27 | * @dev Returns the remaining number of tokens that `spender` will be 28 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 29 | * zero by default. 30 | * 31 | * This value changes when {approve} or {transferFrom} are called. 32 | */ 33 | function allowance(address owner, address spender) external view returns (uint256); 34 | 35 | /** 36 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 37 | * 38 | * Returns a boolean value indicating whether the operation succeeded. 39 | * 40 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 41 | * that someone may use both the old and the new allowance by unfortunate 42 | * transaction ordering. One possible solution to mitigate this race 43 | * condition is to first reduce the spender's allowance to 0 and set the 44 | * desired value afterwards: 45 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 46 | * 47 | * Emits an {Approval} event. 48 | */ 49 | function approve(address spender, uint256 amount) external returns (bool); 50 | 51 | /** 52 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 53 | * allowance mechanism. `amount` is then deducted from the caller's 54 | * allowance. 55 | * 56 | * Returns a boolean value indicating whether the operation succeeded. 57 | * 58 | * Emits a {Transfer} event. 59 | */ 60 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 64 | * another (`to`). 65 | * 66 | * Note that `value` may be zero. 67 | */ 68 | event Transfer(address indexed from, address indexed to, uint256 value); 69 | 70 | /** 71 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 72 | * a call to {approve}. `value` is the new allowance. 73 | */ 74 | event Approval(address indexed owner, address indexed spender, uint256 value); 75 | } 76 | 77 | contract StakingWarmup { 78 | 79 | address public immutable staking; 80 | address public immutable sBTRFLY; 81 | 82 | constructor ( address _staking, address _sBTRFLY ) { 83 | require( _staking != address(0) ); 84 | staking = _staking; 85 | require( _sBTRFLY != address(0) ); 86 | sBTRFLY = _sBTRFLY; 87 | } 88 | 89 | function retrieve( address _staker, uint _amount ) external { 90 | require( msg.sender == staking ); 91 | IERC20( sBTRFLY ).transfer( _staker, _amount ); 92 | } 93 | } -------------------------------------------------------------------------------- /contracts/StandardBondingCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library FullMath { 5 | function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { 6 | uint256 mm = mulmod(x, y, uint256(-1)); 7 | l = x * y; 8 | h = mm - l; 9 | if (mm < l) h -= 1; 10 | } 11 | 12 | function fullDiv( 13 | uint256 l, 14 | uint256 h, 15 | uint256 d 16 | ) private pure returns (uint256) { 17 | uint256 pow2 = d & -d; 18 | d /= pow2; 19 | l /= pow2; 20 | l += h * ((-pow2) / pow2 + 1); 21 | uint256 r = 1; 22 | r *= 2 - d * r; 23 | r *= 2 - d * r; 24 | r *= 2 - d * r; 25 | r *= 2 - d * r; 26 | r *= 2 - d * r; 27 | r *= 2 - d * r; 28 | r *= 2 - d * r; 29 | r *= 2 - d * r; 30 | return l * r; 31 | } 32 | 33 | function mulDiv( 34 | uint256 x, 35 | uint256 y, 36 | uint256 d 37 | ) internal pure returns (uint256) { 38 | (uint256 l, uint256 h) = fullMul(x, y); 39 | uint256 mm = mulmod(x, y, d); 40 | if (mm > l) h -= 1; 41 | l -= mm; 42 | require(h < d, 'FullMath::mulDiv: overflow'); 43 | return fullDiv(l, h, d); 44 | } 45 | } 46 | 47 | library Babylonian { 48 | 49 | function sqrt(uint256 x) internal pure returns (uint256) { 50 | if (x == 0) return 0; 51 | 52 | uint256 xx = x; 53 | uint256 r = 1; 54 | if (xx >= 0x100000000000000000000000000000000) { 55 | xx >>= 128; 56 | r <<= 64; 57 | } 58 | if (xx >= 0x10000000000000000) { 59 | xx >>= 64; 60 | r <<= 32; 61 | } 62 | if (xx >= 0x100000000) { 63 | xx >>= 32; 64 | r <<= 16; 65 | } 66 | if (xx >= 0x10000) { 67 | xx >>= 16; 68 | r <<= 8; 69 | } 70 | if (xx >= 0x100) { 71 | xx >>= 8; 72 | r <<= 4; 73 | } 74 | if (xx >= 0x10) { 75 | xx >>= 4; 76 | r <<= 2; 77 | } 78 | if (xx >= 0x8) { 79 | r <<= 1; 80 | } 81 | r = (r + x / r) >> 1; 82 | r = (r + x / r) >> 1; 83 | r = (r + x / r) >> 1; 84 | r = (r + x / r) >> 1; 85 | r = (r + x / r) >> 1; 86 | r = (r + x / r) >> 1; 87 | r = (r + x / r) >> 1; // Seven iterations should be enough 88 | uint256 r1 = x / r; 89 | return (r < r1 ? r : r1); 90 | } 91 | } 92 | 93 | library BitMath { 94 | 95 | function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { 96 | require(x > 0, 'BitMath::mostSignificantBit: zero'); 97 | 98 | if (x >= 0x100000000000000000000000000000000) { 99 | x >>= 128; 100 | r += 128; 101 | } 102 | if (x >= 0x10000000000000000) { 103 | x >>= 64; 104 | r += 64; 105 | } 106 | if (x >= 0x100000000) { 107 | x >>= 32; 108 | r += 32; 109 | } 110 | if (x >= 0x10000) { 111 | x >>= 16; 112 | r += 16; 113 | } 114 | if (x >= 0x100) { 115 | x >>= 8; 116 | r += 8; 117 | } 118 | if (x >= 0x10) { 119 | x >>= 4; 120 | r += 4; 121 | } 122 | if (x >= 0x4) { 123 | x >>= 2; 124 | r += 2; 125 | } 126 | if (x >= 0x2) r += 1; 127 | } 128 | } 129 | 130 | library FixedPoint { 131 | // range: [0, 2**112 - 1] 132 | // resolution: 1 / 2**112 133 | struct uq112x112 { 134 | uint224 _x; 135 | } 136 | 137 | // range: [0, 2**144 - 1] 138 | // resolution: 1 / 2**112 139 | struct uq144x112 { 140 | uint256 _x; 141 | } 142 | 143 | uint8 private constant RESOLUTION = 112; 144 | uint256 private constant Q112 = 0x10000000000000000000000000000; 145 | uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; 146 | uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) 147 | 148 | // decode a UQ112x112 into a uint112 by truncating after the radix point 149 | function decode(uq112x112 memory self) internal pure returns (uint112) { 150 | return uint112(self._x >> RESOLUTION); 151 | } 152 | 153 | // decode a uq112x112 into a uint with 18 decimals of precision 154 | function decode112with18(uq112x112 memory self) internal pure returns (uint) { 155 | return uint(self._x) / 5192296858534827; 156 | } 157 | 158 | function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { 159 | require(denominator > 0, 'FixedPoint::fraction: division by zero'); 160 | if (numerator == 0) return FixedPoint.uq112x112(0); 161 | 162 | if (numerator <= uint144(-1)) { 163 | uint256 result = (numerator << RESOLUTION) / denominator; 164 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 165 | return uq112x112(uint224(result)); 166 | } else { 167 | uint256 result = FullMath.mulDiv(numerator, Q112, denominator); 168 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 169 | return uq112x112(uint224(result)); 170 | } 171 | } 172 | 173 | // square root of a UQ112x112 174 | // lossy between 0/1 and 40 bits 175 | function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) { 176 | if (self._x <= uint144(-1)) { 177 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112))); 178 | } 179 | 180 | uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x); 181 | safeShiftBits -= safeShiftBits % 2; 182 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2))); 183 | } 184 | } 185 | 186 | library SafeMath { 187 | 188 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 189 | uint256 c = a + b; 190 | require(c >= a, "SafeMath: addition overflow"); 191 | 192 | return c; 193 | } 194 | 195 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 196 | return sub(a, b, "SafeMath: subtraction overflow"); 197 | } 198 | 199 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 200 | require(b <= a, errorMessage); 201 | uint256 c = a - b; 202 | 203 | return c; 204 | } 205 | 206 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 207 | 208 | if (a == 0) { 209 | return 0; 210 | } 211 | 212 | uint256 c = a * b; 213 | require(c / a == b, "SafeMath: multiplication overflow"); 214 | 215 | return c; 216 | } 217 | 218 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 219 | return div(a, b, "SafeMath: division by zero"); 220 | } 221 | 222 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 223 | require(b > 0, errorMessage); 224 | uint256 c = a / b; 225 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 226 | 227 | return c; 228 | } 229 | 230 | function sqrrt(uint256 a) internal pure returns (uint c) { 231 | if (a > 3) { 232 | c = a; 233 | uint b = add( div( a, 2), 1 ); 234 | while (b < c) { 235 | c = b; 236 | b = div( add( div( a, b ), b), 2 ); 237 | } 238 | } else if (a != 0) { 239 | c = 1; 240 | } 241 | } 242 | } 243 | 244 | interface IERC20 { 245 | function decimals() external view returns (uint8); 246 | } 247 | 248 | interface IUniswapV2ERC20 { 249 | function totalSupply() external view returns (uint); 250 | } 251 | 252 | interface IUniswapV2Pair is IUniswapV2ERC20 { 253 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 254 | function token0() external view returns ( address ); 255 | function token1() external view returns ( address ); 256 | } 257 | 258 | interface IBondingCalculator { 259 | function valuation( address pair_, uint amount_ ) external view returns ( uint _value ); 260 | } 261 | 262 | contract REDACTEDBondingCalculator is IBondingCalculator { 263 | 264 | using FixedPoint for *; 265 | using SafeMath for uint; 266 | using SafeMath for uint112; 267 | 268 | address public immutable BTRFLY; 269 | 270 | constructor( address _BTRFLY ) { 271 | require( _BTRFLY != address(0) ); 272 | BTRFLY = _BTRFLY; 273 | } 274 | 275 | function getKValue( address _pair ) public view returns( uint k_ ) { 276 | uint token0 = IERC20( IUniswapV2Pair( _pair ).token0() ).decimals(); 277 | uint token1 = IERC20( IUniswapV2Pair( _pair ).token1() ).decimals(); 278 | uint decimals = token0.add( token1 ).sub( IERC20( _pair ).decimals() ); 279 | 280 | (uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 281 | k_ = reserve0.mul(reserve1).div( 10 ** decimals ); 282 | } 283 | 284 | function getTotalValue( address _pair ) public view returns ( uint _value ) { 285 | _value = getKValue( _pair ).sqrrt().mul(2); 286 | } 287 | 288 | function valuation( address _pair, uint amount_ ) external view override returns ( uint _value ) { 289 | uint totalValue = getTotalValue( _pair ); 290 | uint totalSupply = IUniswapV2Pair( _pair ).totalSupply(); 291 | 292 | _value = totalValue.mul( FixedPoint.fraction( amount_, totalSupply ).decode112with18() ).div( 1e18 ); 293 | } 294 | 295 | function markdown( address _pair ) external view returns ( uint ) { 296 | ( uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 297 | 298 | uint reserve; 299 | if ( IUniswapV2Pair( _pair ).token0() == BTRFLY ) { 300 | reserve = reserve1; 301 | } else { 302 | reserve = reserve0; 303 | } 304 | return reserve.mul( 2 * ( 10 ** IERC20( BTRFLY ).decimals() ) ).div( getTotalValue( _pair ) ); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /contracts/Treasury.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library SafeMath { 5 | 6 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 7 | uint256 c = a + b; 8 | require(c >= a, "SafeMath: addition overflow"); 9 | 10 | return c; 11 | } 12 | 13 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 14 | return sub(a, b, "SafeMath: subtraction overflow"); 15 | } 16 | 17 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 18 | require(b <= a, errorMessage); 19 | uint256 c = a - b; 20 | 21 | return c; 22 | } 23 | 24 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 25 | if (a == 0) { 26 | return 0; 27 | } 28 | 29 | uint256 c = a * b; 30 | require(c / a == b, "SafeMath: multiplication overflow"); 31 | 32 | return c; 33 | } 34 | 35 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 36 | return div(a, b, "SafeMath: division by zero"); 37 | } 38 | 39 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 40 | require(b > 0, errorMessage); 41 | uint256 c = a / b; 42 | return c; 43 | } 44 | } 45 | 46 | library Address { 47 | 48 | function isContract(address account) internal view returns (bool) { 49 | // This method relies in extcodesize, which returns 0 for contracts in 50 | // construction, since the code is only stored at the end of the 51 | // constructor execution. 52 | 53 | uint256 size; 54 | // solhint-disable-next-line no-inline-assembly 55 | assembly { size := extcodesize(account) } 56 | return size > 0; 57 | } 58 | 59 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 60 | return _functionCallWithValue(target, data, 0, errorMessage); 61 | } 62 | 63 | function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { 64 | require(isContract(target), "Address: call to non-contract"); 65 | 66 | // solhint-disable-next-line avoid-low-level-calls 67 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 68 | if (success) { 69 | return returndata; 70 | } else { 71 | if (returndata.length > 0) { 72 | // solhint-disable-next-line no-inline-assembly 73 | assembly { 74 | let returndata_size := mload(returndata) 75 | revert(add(32, returndata), returndata_size) 76 | } 77 | } else { 78 | revert(errorMessage); 79 | } 80 | } 81 | } 82 | 83 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 84 | if (success) { 85 | return returndata; 86 | } else { 87 | if (returndata.length > 0) { 88 | // solhint-disable-next-line no-inline-assembly 89 | assembly { 90 | let returndata_size := mload(returndata) 91 | revert(add(32, returndata), returndata_size) 92 | } 93 | } else { 94 | revert(errorMessage); 95 | } 96 | } 97 | } 98 | } 99 | 100 | interface IOwnable { 101 | function manager() external view returns (address); 102 | 103 | function renounceManagement() external; 104 | 105 | function pushManagement( address newOwner_ ) external; 106 | 107 | function pullManagement() external; 108 | } 109 | 110 | contract Ownable is IOwnable { 111 | 112 | address internal _owner; 113 | address internal _newOwner; 114 | 115 | event OwnershipPushed(address indexed previousOwner, address indexed newOwner); 116 | event OwnershipPulled(address indexed previousOwner, address indexed newOwner); 117 | 118 | constructor () { 119 | _owner = msg.sender; 120 | emit OwnershipPushed( address(0), _owner ); 121 | } 122 | 123 | function manager() public view override returns (address) { 124 | return _owner; 125 | } 126 | 127 | modifier onlyManager() { 128 | require( _owner == msg.sender, "Ownable: caller is not the owner" ); 129 | _; 130 | } 131 | 132 | function renounceManagement() public virtual override onlyManager() { 133 | emit OwnershipPushed( _owner, address(0) ); 134 | _owner = address(0); 135 | } 136 | 137 | function pushManagement( address newOwner_ ) public virtual override onlyManager() { 138 | require( newOwner_ != address(0), "Ownable: new owner is the zero address"); 139 | emit OwnershipPushed( _owner, newOwner_ ); 140 | _newOwner = newOwner_; 141 | } 142 | 143 | function pullManagement() public virtual override { 144 | require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); 145 | emit OwnershipPulled( _owner, _newOwner ); 146 | _owner = _newOwner; 147 | } 148 | } 149 | 150 | interface IERC20 { 151 | function decimals() external view returns (uint8); 152 | 153 | function balanceOf(address account) external view returns (uint256); 154 | 155 | function transfer(address recipient, uint256 amount) external returns (bool); 156 | 157 | function approve(address spender, uint256 amount) external returns (bool); 158 | 159 | function totalSupply() external view returns (uint256); 160 | 161 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 162 | 163 | event Transfer(address indexed from, address indexed to, uint256 value); 164 | 165 | event Approval(address indexed owner, address indexed spender, uint256 value); 166 | } 167 | 168 | library SafeERC20 { 169 | using SafeMath for uint256; 170 | using Address for address; 171 | 172 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 173 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 174 | } 175 | 176 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 177 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 178 | } 179 | 180 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 181 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 182 | if (returndata.length > 0) { // Return data is optional 183 | // solhint-disable-next-line max-line-length 184 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 185 | } 186 | } 187 | } 188 | 189 | interface IERC20Mintable { 190 | function mint( uint256 amount_ ) external; 191 | 192 | function mint( address account_, uint256 ammount_ ) external; 193 | } 194 | 195 | interface IBTRFLYERC20 { 196 | function burnFrom(address account_, uint256 amount_) external; 197 | } 198 | 199 | interface IBondCalculator { 200 | function valuation( address pair_, uint amount_ ) external view returns ( uint _value ); 201 | } 202 | 203 | contract REDACTEDTreasury is Ownable { 204 | 205 | using SafeMath for uint; 206 | using SafeERC20 for IERC20; 207 | 208 | event Deposit( address indexed token, uint amount, uint value ); 209 | event Simp( address indexed token, uint amount, address niceGuy); 210 | event Withdrawal( address indexed token, uint amount, uint value ); 211 | event CreateDebt( address indexed debtor, address indexed token, uint amount, uint value ); 212 | event RepayDebt( address indexed debtor, address indexed token, uint amount, uint value ); 213 | event ReservesManaged( address indexed token, uint amount ); 214 | event ReservesUpdated( uint indexed totalReserves ); 215 | event ReservesAudited( uint indexed totalReserves ); 216 | event RewardsMinted( address indexed caller, address indexed recipient, uint amount ); 217 | event ChangeQueued( MANAGING indexed managing, address queued ); 218 | event ChangeActivated( MANAGING indexed managing, address activated, bool result ); 219 | event tokenValueSet(address token, uint value); 220 | 221 | enum MANAGING { RESERVEDEPOSITOR, RESERVESPENDER, RESERVETOKEN, RESERVEMANAGER, LIQUIDITYDEPOSITOR, LIQUIDITYTOKEN, LIQUIDITYMANAGER, DEBTOR, REWARDMANAGER, SBTRFLY } 222 | 223 | address public immutable BTRFLY; 224 | uint public blocksNeededForQueue; 225 | 226 | uint public immutable newBlocksNeededForQueue; 227 | 228 | address[] public reserveTokens; // Push only, beware false-positives. 229 | mapping( address => bool ) public isReserveToken; 230 | mapping( address => uint ) public reserveTokenQueue; // Delays changes to mapping. 231 | mapping( address => uint) public nativeFloorPrice; // With 9 decimals of precision 232 | 233 | address[] public reserveDepositors; // Push only, beware false-positives. Only for viewing. 234 | mapping( address => bool ) public isReserveDepositor; 235 | mapping( address => uint ) public reserveDepositorQueue; // Delays changes to mapping. 236 | 237 | address[] public reserveSpenders; // Push only, beware false-positives. Only for viewing. 238 | mapping( address => bool ) public isReserveSpender; 239 | mapping( address => uint ) public reserveSpenderQueue; // Delays changes to mapping. 240 | 241 | address[] public liquidityTokens; // Push only, beware false-positives. 242 | mapping( address => bool ) public isLiquidityToken; 243 | mapping( address => uint ) public LiquidityTokenQueue; // Delays changes to mapping. 244 | 245 | address[] public liquidityDepositors; // Push only, beware false-positives. Only for viewing. 246 | mapping( address => bool ) public isLiquidityDepositor; 247 | mapping( address => uint ) public LiquidityDepositorQueue; // Delays changes to mapping. 248 | 249 | mapping( address => address ) public bondCalculator; // bond calculator for liquidity token 250 | 251 | address[] public reserveManagers; // Push only, beware false-positives. Only for viewing. 252 | mapping( address => bool ) public isReserveManager; 253 | mapping( address => uint ) public ReserveManagerQueue; // Delays changes to mapping. 254 | 255 | address[] public liquidityManagers; // Push only, beware false-positives. Only for viewing. 256 | mapping( address => bool ) public isLiquidityManager; 257 | mapping( address => uint ) public LiquidityManagerQueue; // Delays changes to mapping. 258 | 259 | address[] public debtors; // Push only, beware false-positives. Only for viewing. 260 | mapping( address => bool ) public isDebtor; 261 | mapping( address => uint ) public debtorQueue; // Delays changes to mapping. 262 | mapping( address => uint ) public debtorBalance; 263 | 264 | address[] public rewardManagers; // Push only, beware false-positives. Only for viewing. 265 | mapping( address => bool ) public isRewardManager; 266 | mapping( address => uint ) public rewardManagerQueue; // Delays changes to mapping. 267 | 268 | address public sBTRFLY; 269 | uint public sBTRFLYQueue; // Delays change to sBTRFLY address 270 | 271 | uint public totalReserves; // Risk-free value of all assets 272 | uint public totalDebt; 273 | 274 | constructor ( 275 | address _BTRFLY, 276 | address _OHM, 277 | address _sOHM, 278 | address _CVX, 279 | address _CRV, 280 | address _BTRFLYOHM, 281 | uint _OHMFloor, 282 | uint _CVXFloor, 283 | uint _CRVFloor, 284 | uint _newBlocksNeededForQueue 285 | ) { 286 | require( _BTRFLY != address(0) ); 287 | BTRFLY = _BTRFLY; 288 | 289 | isReserveToken[ _OHM ] = true; 290 | reserveTokens.push( _OHM ); 291 | nativeFloorPrice[ _OHM ] = _OHMFloor; 292 | 293 | isReserveToken[ _sOHM ] = true; 294 | reserveTokens.push( _sOHM ); 295 | nativeFloorPrice[ _OHM ] = _OHMFloor; 296 | 297 | isReserveToken[ _CVX] = true; 298 | reserveTokens.push( _CVX ); 299 | nativeFloorPrice[ _CVX ] = _CVXFloor; 300 | 301 | isReserveToken[ _CRV] = true; 302 | reserveTokens.push( _CRV ); 303 | nativeFloorPrice[ _CRV ] = _CRVFloor; 304 | 305 | isLiquidityToken[ _BTRFLYOHM ] = true; 306 | liquidityTokens.push( _BTRFLYOHM ); 307 | 308 | blocksNeededForQueue = 0; 309 | newBlocksNeededForQueue = _newBlocksNeededForQueue; 310 | } 311 | 312 | function deactivateAndreMode() external onlyManager(){ 313 | blocksNeededForQueue = newBlocksNeededForQueue; 314 | } 315 | 316 | /** 317 | @notice allow approved address to deposit an asset for BTRFLY 318 | @param _amount uint 319 | @param _token address 320 | @param _profit uint 321 | @return send_ uint 322 | */ 323 | function deposit( uint _amount, address _token, uint _profit ) external returns ( uint send_ ) { 324 | require( isReserveToken[ _token ] || isLiquidityToken[ _token ], "Not accepted" ); 325 | IERC20( _token ).safeTransferFrom( msg.sender, address(this), _amount ); 326 | 327 | if ( isReserveToken[ _token ] ) { 328 | require( isReserveDepositor[ msg.sender ], "Not approved" ); 329 | } else { 330 | require( isLiquidityDepositor[ msg.sender ], "Not approved" ); 331 | } 332 | 333 | uint value = valueOf(_token, _amount); 334 | // mint BTRFLY needed and store amount of rewards for distribution 335 | send_ = value.sub( _profit ); 336 | IERC20Mintable( BTRFLY ).mint( msg.sender, send_ ); 337 | 338 | totalReserves = totalReserves.add( value ); 339 | emit ReservesUpdated( totalReserves ); 340 | 341 | emit Deposit( _token, _amount, value ); 342 | } 343 | 344 | function simp(uint _amount, address _token) external{ 345 | require( isReserveToken[ _token ] || isLiquidityToken[ _token ], "Not accepted" ); 346 | IERC20( _token ).safeTransferFrom( msg.sender, address(this), _amount ); 347 | uint value = valueOf(_token, _amount); 348 | totalReserves = totalReserves.add( value ); 349 | emit ReservesUpdated( totalReserves ); 350 | emit Simp(_token,_amount,msg.sender); 351 | } 352 | 353 | /** 354 | @notice allow approved address to burn BTRFLY for reserves 355 | @param _amount uint 356 | @param _token address 357 | */ 358 | function withdraw( uint _amount, address _token ) external { 359 | require( isReserveToken[ _token ], "Not accepted" ); // Only reserves can be used for redemptions 360 | require( isReserveSpender[ msg.sender ] == true, "Not approved" ); 361 | 362 | uint value = valueOf( _token, _amount ); 363 | IBTRFLYERC20( BTRFLY ).burnFrom( msg.sender, value ); 364 | 365 | totalReserves = totalReserves.sub( value ); 366 | emit ReservesUpdated( totalReserves ); 367 | 368 | IERC20( _token ).safeTransfer( msg.sender, _amount ); 369 | 370 | emit Withdrawal( _token, _amount, value ); 371 | } 372 | 373 | /** 374 | @notice allow approved address to borrow reserves 375 | @param _amount uint 376 | @param _token address 377 | */ 378 | function incurDebt( uint _amount, address _token ) external { 379 | require( isDebtor[ msg.sender ], "Not approved" ); 380 | require( isReserveToken[ _token ], "Not accepted" ); 381 | 382 | uint value = valueOf( _token, _amount ); 383 | 384 | uint maximumDebt = IERC20( sBTRFLY ).balanceOf( msg.sender ); // Can only borrow against sBTRFLY held 385 | uint availableDebt = maximumDebt.sub( debtorBalance[ msg.sender ] ); 386 | require( value <= availableDebt, "Exceeds debt limit" ); 387 | 388 | debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].add( value ); 389 | totalDebt = totalDebt.add( value ); 390 | 391 | totalReserves = totalReserves.sub( value ); 392 | emit ReservesUpdated( totalReserves ); 393 | 394 | IERC20( _token ).transfer( msg.sender, _amount ); 395 | 396 | emit CreateDebt( msg.sender, _token, _amount, value ); 397 | } 398 | 399 | /** 400 | @notice allow approved address to repay borrowed reserves with reserves 401 | @param _amount uint 402 | @param _token address 403 | */ 404 | function repayDebtWithReserve( uint _amount, address _token ) external { 405 | require( isDebtor[ msg.sender ], "Not approved" ); 406 | require( isReserveToken[ _token ], "Not accepted" ); 407 | 408 | IERC20( _token ).safeTransferFrom( msg.sender, address(this), _amount ); 409 | 410 | uint value = valueOf( _token, _amount ); 411 | debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].sub( value ); 412 | totalDebt = totalDebt.sub( value ); 413 | 414 | totalReserves = totalReserves.add( value ); 415 | emit ReservesUpdated( totalReserves ); 416 | 417 | emit RepayDebt( msg.sender, _token, _amount, value ); 418 | } 419 | 420 | /** 421 | @notice allow approved address to repay borrowed reserves with BTRFLY 422 | @param _amount uint 423 | */ 424 | function repayDebtWithBTRFLY( uint _amount ) external { 425 | require( isDebtor[ msg.sender ], "Not approved" ); 426 | 427 | IBTRFLYERC20( BTRFLY ).burnFrom( msg.sender, _amount ); 428 | 429 | debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].sub( _amount ); 430 | totalDebt = totalDebt.sub( _amount ); 431 | 432 | emit RepayDebt( msg.sender, BTRFLY, _amount, _amount ); 433 | } 434 | 435 | /** 436 | @notice allow approved address to withdraw assets 437 | @param _token address 438 | @param _amount uint 439 | */ 440 | function manage( address _token, uint _amount ) external { 441 | if( isLiquidityToken[ _token ] ) { 442 | require( isLiquidityManager[ msg.sender ], "Not approved" ); 443 | } else { 444 | require( isReserveManager[ msg.sender ], "Not approved" ); 445 | } 446 | 447 | uint value = valueOf(_token, _amount); 448 | require( value <= excessReserves(), "Insufficient reserves" ); 449 | 450 | totalReserves = totalReserves.sub( value ); 451 | emit ReservesUpdated( totalReserves ); 452 | 453 | IERC20( _token ).safeTransfer( msg.sender, _amount ); 454 | 455 | emit ReservesManaged( _token, _amount ); 456 | } 457 | 458 | /** 459 | @notice send epoch reward to staking contract 460 | */ 461 | function mintRewards( address _recipient, uint _amount ) external { 462 | require( isRewardManager[ msg.sender ], "Not approved" ); 463 | require( _amount <= excessReserves(), "Insufficient reserves" ); 464 | 465 | IERC20Mintable( BTRFLY ).mint( _recipient, _amount ); 466 | 467 | emit RewardsMinted( msg.sender, _recipient, _amount ); 468 | } 469 | 470 | /** 471 | @notice returns excess reserves not backing tokens 472 | @return uint 473 | */ 474 | function excessReserves() public view returns ( uint ) { 475 | return totalReserves.sub( IERC20( BTRFLY ).totalSupply().sub( totalDebt ) ); 476 | } 477 | 478 | /** 479 | @notice takes inventory of all tracked assets 480 | @notice always consolidate to recognized reserves before audit 481 | */ 482 | function auditReserves() external { 483 | uint reserves; 484 | for( uint i = 0; i < reserveTokens.length; i++ ) { 485 | reserves = reserves.add ( 486 | valueOf( reserveTokens[ i ], IERC20( reserveTokens[ i ] ).balanceOf( address(this) ) ) 487 | ); 488 | } 489 | for( uint i = 0; i < liquidityTokens.length; i++ ) { 490 | reserves = reserves.add ( 491 | valueOf( liquidityTokens[ i ], IERC20( liquidityTokens[ i ] ).balanceOf( address(this) ) ) 492 | ); 493 | } 494 | totalReserves = reserves; 495 | emit ReservesUpdated( reserves ); 496 | emit ReservesAudited( reserves ); 497 | } 498 | 499 | /** 500 | @notice returns BTRFLY valuation of asset 501 | @param _token address 502 | @param _amount uint 503 | @return value_ uint 504 | */ 505 | function valueOf( address _token, uint _amount ) public view returns ( uint value_ ) { 506 | if ( isReserveToken[ _token ] ) { 507 | // convert amount to match BTRFLY decimals and divide by number of tokens for 1 BTRFLY 508 | value_ = _amount.mul( 10 ** IERC20( BTRFLY ).decimals() ).div( 10 ** IERC20( _token ).decimals() ).mul(10 ** 9).div(nativeFloorPrice[_token]); 509 | } else if ( isLiquidityToken[ _token ] ) { 510 | value_ = IBondCalculator( bondCalculator[ _token ] ).valuation( _token, _amount ); 511 | } 512 | } 513 | 514 | /** 515 | @notice sets BTRFLY valuation of asset 516 | @param _token address 517 | @param _value uint 518 | */ 519 | function setFloor( address _token, uint _value ) external onlyManager() { 520 | require(nativeFloorPrice[_token] == 0, "Token floor value can only be set once"); 521 | nativeFloorPrice[_token] = _value; 522 | emit tokenValueSet(_token, _value); 523 | } 524 | 525 | function getFloor(address _token) external view returns(uint){ 526 | return nativeFloorPrice[_token]; 527 | } 528 | 529 | 530 | /** 531 | @notice queue address to change boolean in mapping 532 | @param _managing MANAGING 533 | @param _address address 534 | @return bool 535 | */ 536 | function queue( MANAGING _managing, address _address ) external onlyManager() returns ( bool ) { 537 | require( _address != address(0) ); 538 | if ( _managing == MANAGING.RESERVEDEPOSITOR ) { // 0 539 | reserveDepositorQueue[ _address ] = block.number.add( blocksNeededForQueue ); 540 | } else if ( _managing == MANAGING.RESERVESPENDER ) { // 1 541 | reserveSpenderQueue[ _address ] = block.number.add( blocksNeededForQueue ); 542 | } else if ( _managing == MANAGING.RESERVETOKEN ) { // 2 543 | reserveTokenQueue[ _address ] = block.number.add( blocksNeededForQueue ); 544 | } else if ( _managing == MANAGING.RESERVEMANAGER ) { // 3 545 | ReserveManagerQueue[ _address ] = block.number.add( blocksNeededForQueue.mul( 2 ) ); 546 | } else if ( _managing == MANAGING.LIQUIDITYDEPOSITOR ) { // 4 547 | LiquidityDepositorQueue[ _address ] = block.number.add( blocksNeededForQueue ); 548 | } else if ( _managing == MANAGING.LIQUIDITYTOKEN ) { // 5 549 | LiquidityTokenQueue[ _address ] = block.number.add( blocksNeededForQueue ); 550 | } else if ( _managing == MANAGING.LIQUIDITYMANAGER ) { // 6 551 | LiquidityManagerQueue[ _address ] = block.number.add( blocksNeededForQueue.mul( 2 ) ); 552 | } else if ( _managing == MANAGING.DEBTOR ) { // 7 553 | debtorQueue[ _address ] = block.number.add( blocksNeededForQueue ); 554 | } else if ( _managing == MANAGING.REWARDMANAGER ) { // 8 555 | rewardManagerQueue[ _address ] = block.number.add( blocksNeededForQueue ); 556 | } else if ( _managing == MANAGING.SBTRFLY ) { // 9 557 | sBTRFLYQueue = block.number.add( blocksNeededForQueue ); 558 | } else return false; 559 | 560 | emit ChangeQueued( _managing, _address ); 561 | return true; 562 | } 563 | 564 | /** 565 | @notice verify queue then set boolean in mapping 566 | @param _managing MANAGING 567 | @param _address address 568 | @param _calculator address 569 | @return bool 570 | */ 571 | function toggle( MANAGING _managing, address _address, address _calculator ) external onlyManager() returns ( bool ) { 572 | require( _address != address(0) ); 573 | bool result; 574 | if ( _managing == MANAGING.RESERVEDEPOSITOR ) { // 0 575 | if ( requirements( reserveDepositorQueue, isReserveDepositor, _address ) ) { 576 | reserveDepositorQueue[ _address ] = 0; 577 | if( !listContains( reserveDepositors, _address ) ) { 578 | reserveDepositors.push( _address ); 579 | } 580 | } 581 | result = !isReserveDepositor[ _address ]; 582 | isReserveDepositor[ _address ] = result; 583 | 584 | } else if ( _managing == MANAGING.RESERVESPENDER ) { // 1 585 | if ( requirements( reserveSpenderQueue, isReserveSpender, _address ) ) { 586 | reserveSpenderQueue[ _address ] = 0; 587 | if( !listContains( reserveSpenders, _address ) ) { 588 | reserveSpenders.push( _address ); 589 | } 590 | } 591 | result = !isReserveSpender[ _address ]; 592 | isReserveSpender[ _address ] = result; 593 | 594 | } else if ( _managing == MANAGING.RESERVETOKEN ) { // 2 595 | if ( requirements( reserveTokenQueue, isReserveToken, _address ) ) { 596 | reserveTokenQueue[ _address ] = 0; 597 | if( !listContains( reserveTokens, _address ) ) { 598 | reserveTokens.push( _address ); 599 | } 600 | } 601 | result = !isReserveToken[ _address ]; 602 | isReserveToken[ _address ] = result; 603 | 604 | } else if ( _managing == MANAGING.RESERVEMANAGER ) { // 3 605 | if ( requirements( ReserveManagerQueue, isReserveManager, _address ) ) { 606 | reserveManagers.push( _address ); 607 | ReserveManagerQueue[ _address ] = 0; 608 | if( !listContains( reserveManagers, _address ) ) { 609 | reserveManagers.push( _address ); 610 | } 611 | } 612 | result = !isReserveManager[ _address ]; 613 | isReserveManager[ _address ] = result; 614 | 615 | } else if ( _managing == MANAGING.LIQUIDITYDEPOSITOR ) { // 4 616 | if ( requirements( LiquidityDepositorQueue, isLiquidityDepositor, _address ) ) { 617 | liquidityDepositors.push( _address ); 618 | LiquidityDepositorQueue[ _address ] = 0; 619 | if( !listContains( liquidityDepositors, _address ) ) { 620 | liquidityDepositors.push( _address ); 621 | } 622 | } 623 | result = !isLiquidityDepositor[ _address ]; 624 | isLiquidityDepositor[ _address ] = result; 625 | 626 | } else if ( _managing == MANAGING.LIQUIDITYTOKEN ) { // 5 627 | if ( requirements( LiquidityTokenQueue, isLiquidityToken, _address ) ) { 628 | LiquidityTokenQueue[ _address ] = 0; 629 | if( !listContains( liquidityTokens, _address ) ) { 630 | liquidityTokens.push( _address ); 631 | } 632 | } 633 | result = !isLiquidityToken[ _address ]; 634 | isLiquidityToken[ _address ] = result; 635 | bondCalculator[ _address ] = _calculator; 636 | 637 | } else if ( _managing == MANAGING.LIQUIDITYMANAGER ) { // 6 638 | if ( requirements( LiquidityManagerQueue, isLiquidityManager, _address ) ) { 639 | LiquidityManagerQueue[ _address ] = 0; 640 | if( !listContains( liquidityManagers, _address ) ) { 641 | liquidityManagers.push( _address ); 642 | } 643 | } 644 | result = !isLiquidityManager[ _address ]; 645 | isLiquidityManager[ _address ] = result; 646 | 647 | } else if ( _managing == MANAGING.DEBTOR ) { // 7 648 | if ( requirements( debtorQueue, isDebtor, _address ) ) { 649 | debtorQueue[ _address ] = 0; 650 | if( !listContains( debtors, _address ) ) { 651 | debtors.push( _address ); 652 | } 653 | } 654 | result = !isDebtor[ _address ]; 655 | isDebtor[ _address ] = result; 656 | 657 | } else if ( _managing == MANAGING.REWARDMANAGER ) { // 8 658 | if ( requirements( rewardManagerQueue, isRewardManager, _address ) ) { 659 | rewardManagerQueue[ _address ] = 0; 660 | if( !listContains( rewardManagers, _address ) ) { 661 | rewardManagers.push( _address ); 662 | } 663 | } 664 | result = !isRewardManager[ _address ]; 665 | isRewardManager[ _address ] = result; 666 | 667 | } else if ( _managing == MANAGING.SBTRFLY ) { // 9 668 | sBTRFLYQueue = 0; 669 | sBTRFLY = _address; 670 | result = true; 671 | 672 | } else return false; 673 | 674 | emit ChangeActivated( _managing, _address, result ); 675 | return true; 676 | } 677 | 678 | /** 679 | @notice checks requirements and returns altered structs 680 | @param queue_ mapping( address => uint ) 681 | @param status_ mapping( address => bool ) 682 | @param _address address 683 | @return bool 684 | */ 685 | function requirements( 686 | mapping( address => uint ) storage queue_, 687 | mapping( address => bool ) storage status_, 688 | address _address 689 | ) internal view returns ( bool ) { 690 | if ( !status_[ _address ] ) { 691 | require( queue_[ _address ] != 0, "Must queue" ); 692 | require( queue_[ _address ] <= block.number, "Queue not expired" ); 693 | return true; 694 | } return false; 695 | } 696 | 697 | /** 698 | @notice checks array to ensure against duplicate 699 | @param _list address[] 700 | @param _token address 701 | @return bool 702 | */ 703 | function listContains( address[] storage _list, address _token ) internal view returns ( bool ) { 704 | for( uint i = 0; i < _list.length; i++ ) { 705 | if( _list[ i ] == _token ) { 706 | return true; 707 | } 708 | } 709 | return false; 710 | } 711 | } -------------------------------------------------------------------------------- /contracts/thecosomata/ThecosomataETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.0; 3 | 4 | import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; 5 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | interface IERC20Extended is IERC20 { 9 | function burn(uint256 amount) external; 10 | 11 | function decimals() external view returns (uint8); 12 | } 13 | 14 | interface IRedactedTreasury { 15 | function manage(address _token, uint256 _amount) external; 16 | } 17 | 18 | interface ICurveCryptoPool { 19 | function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) 20 | external 21 | payable; 22 | 23 | function calc_token_amount(uint256[2] calldata amounts) 24 | external 25 | view 26 | returns (uint256); 27 | 28 | function price_oracle() external view returns (uint256); 29 | 30 | function token() external view returns (address); 31 | } 32 | 33 | contract ThecosomataETH is AccessControl { 34 | using SafeERC20 for IERC20; 35 | 36 | bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE"); 37 | 38 | address public immutable BTRFLY; 39 | address public immutable WETH; 40 | address public immutable CURVEPOOL; 41 | address public immutable TREASURY; 42 | 43 | uint256 private immutable _btrflyDecimals; 44 | uint256 private immutable _wethDecimals; 45 | 46 | uint256 public slippage = 5; // in 1000th 47 | 48 | event AddLiquidity( 49 | uint256 wethLiquidity, 50 | uint256 btrflyLiquidity, 51 | uint256 btrflyBurned 52 | ); 53 | event Withdraw( 54 | address token, 55 | uint256 amount, 56 | address recipient 57 | ); 58 | event GrantKeeperRole(address keeper); 59 | event RevokeKeeperRole(address keeper); 60 | event SetSlippage(uint256 slippage); 61 | 62 | constructor( 63 | address _BTRFLY, 64 | address _WETH, 65 | address _TREASURY, 66 | address _CURVEPOOL 67 | ) { 68 | require(_BTRFLY != address(0), "Invalid BTRFLY address"); 69 | BTRFLY = _BTRFLY; 70 | 71 | require(_WETH != address(0), "Invalid WETH address"); 72 | WETH = _WETH; 73 | 74 | require(_CURVEPOOL != address(0), "Invalid POOL address"); 75 | CURVEPOOL = _CURVEPOOL; 76 | 77 | require(_TREASURY != address(0), "Invalid TREASURY address"); 78 | TREASURY = _TREASURY; 79 | 80 | // Approve for max capacity 81 | IERC20(_BTRFLY).approve(_CURVEPOOL, type(uint256).max); 82 | IERC20(_WETH).approve(_CURVEPOOL, type(uint256).max); 83 | 84 | _btrflyDecimals = IERC20Extended(_BTRFLY).decimals(); 85 | _wethDecimals = IERC20Extended(_WETH).decimals(); 86 | 87 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 88 | } 89 | 90 | // Update slippage percentage (in 1000th) 91 | function setSlippage(uint256 _slippage) 92 | external 93 | onlyRole(DEFAULT_ADMIN_ROLE) 94 | { 95 | // Make sure the slippage is not > 5% 96 | require(_slippage <= 50, "Slippage too high"); 97 | slippage = _slippage; 98 | 99 | emit SetSlippage(_slippage); 100 | } 101 | 102 | // Grant the keeper role for the specified address 103 | function grantKeeperRole(address keeper) 104 | external 105 | onlyRole(DEFAULT_ADMIN_ROLE) 106 | { 107 | require(keeper != address(0), "Invalid address"); 108 | _grantRole(KEEPER_ROLE, keeper); 109 | 110 | emit GrantKeeperRole(keeper); 111 | } 112 | 113 | // Revoke the keeper role from the specified address 114 | function revokeKeeperRole(address keeper) 115 | external 116 | onlyRole(DEFAULT_ADMIN_ROLE) 117 | { 118 | require(hasRole(KEEPER_ROLE, keeper), "Invalid address"); 119 | _revokeRole(KEEPER_ROLE, keeper); 120 | 121 | emit RevokeKeeperRole(keeper); 122 | } 123 | 124 | // Fetch the equivalent value of either specified BTRFLY/WETH amount 125 | function _calculateAmountRequiredForLP(uint256 amount, bool isBTRFLY) 126 | private 127 | view 128 | returns (uint256) 129 | { 130 | // Default price is based off "1 BTRFLY = X WETH", in 10^18 format 131 | uint256 priceOracle = ICurveCryptoPool(CURVEPOOL).price_oracle(); 132 | uint256 baseExp = 10**18; 133 | uint256 wethExp = 10**_wethDecimals; 134 | uint256 btrflyExp = 10**_btrflyDecimals; 135 | 136 | require(priceOracle != 0, "Invalid price oracle"); 137 | 138 | if (isBTRFLY) { 139 | return (((amount * priceOracle) / baseExp) * wethExp) / btrflyExp; 140 | } 141 | 142 | return (((amount * baseExp) / priceOracle) * btrflyExp) / wethExp; 143 | } 144 | 145 | // Return the currently available WETH and BTRFLY amounts 146 | function _getAvailableLiquidity() 147 | private 148 | view 149 | returns ( 150 | uint256 wethLiquidity, 151 | uint256 btrflyLiquidity 152 | ) 153 | { 154 | uint256 btrfly = IERC20Extended(BTRFLY).balanceOf(address(this)); 155 | uint256 wethAmount = _calculateAmountRequiredForLP(btrfly, true); 156 | uint256 wethCap = IERC20(WETH).balanceOf(TREASURY); 157 | wethLiquidity = wethCap > wethAmount ? wethAmount : wethCap; 158 | 159 | // Use BTRFLY balance if remaining capacity is enough, otherwise, calculate BTRFLY amount 160 | btrflyLiquidity = wethCap > wethAmount 161 | ? btrfly 162 | : _calculateAmountRequiredForLP(wethLiquidity, false); 163 | } 164 | 165 | // Perform the actual upkeep flow based on the available liquidity and expected LP token amounts 166 | function performUpkeep() external onlyRole(KEEPER_ROLE) { 167 | uint256 wethLiquidity; 168 | uint256 btrflyLiquidity; 169 | (wethLiquidity, btrflyLiquidity) = _getAvailableLiquidity(); 170 | 171 | require( 172 | wethLiquidity != 0 && btrflyLiquidity != 0, 173 | "Insufficient amounts" 174 | ); 175 | 176 | // Calculate the minimum amount of lp token expected using the specified amounts 177 | uint256[2] memory amounts = [wethLiquidity, btrflyLiquidity]; 178 | uint256 minimumLPAmount = ICurveCryptoPool(CURVEPOOL).calc_token_amount( 179 | amounts 180 | ); 181 | minimumLPAmount -= ((minimumLPAmount * slippage) / 1000); 182 | require(minimumLPAmount != 0, "Invalid slippage"); 183 | 184 | // Obtain WETH from the treasury 185 | IRedactedTreasury(TREASURY).manage(WETH, wethLiquidity); 186 | 187 | // Attempt to add liquidity with the specified amounts and minimum LP token to be received 188 | ICurveCryptoPool(CURVEPOOL).add_liquidity(amounts, minimumLPAmount); 189 | 190 | // Transfer out the pool token to treasury 191 | address token = ICurveCryptoPool(CURVEPOOL).token(); 192 | uint256 tokenBalance = IERC20(token).balanceOf(address(this)); 193 | IERC20(token).safeTransfer(TREASURY, tokenBalance); 194 | 195 | // Burn any excess/unused BTRFLY 196 | uint256 unusedBTRFLY = IERC20Extended(BTRFLY).balanceOf(address(this)); 197 | if (unusedBTRFLY != 0) { 198 | IERC20Extended(BTRFLY).burn(unusedBTRFLY); 199 | } 200 | 201 | emit AddLiquidity(wethLiquidity, btrflyLiquidity, unusedBTRFLY); 202 | } 203 | 204 | // Withdraw arbitrary token and amount owned by the contract 205 | function withdraw( 206 | address token, 207 | uint256 amount, 208 | address recipient 209 | ) external onlyRole(DEFAULT_ADMIN_ROLE) { 210 | require(token != address(0), "Invalid token"); 211 | require(recipient != address(0), "Invalid recipient"); 212 | require(amount != 0, "Invalid amount"); 213 | 214 | IERC20(token).safeTransfer(recipient, amount); 215 | 216 | emit Withdraw(token, amount, recipient); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv' 2 | 3 | import { HardhatUserConfig, task } from 'hardhat/config' 4 | import '@nomiclabs/hardhat-etherscan' 5 | import '@nomiclabs/hardhat-waffle' 6 | import '@typechain/hardhat' 7 | import 'hardhat-gas-reporter' 8 | import 'solidity-coverage' 9 | 10 | dotenv.config() 11 | 12 | // This is a sample Hardhat task. To learn how to create your own go to 13 | // https://hardhat.org/guides/create-task.html 14 | task('accounts', 'Prints the list of accounts', async (taskArgs, hre) => { 15 | const accounts = await hre.ethers.getSigners() 16 | 17 | for (const account of accounts) { 18 | console.log(account.address) 19 | } 20 | }) 21 | 22 | // You need to export an object to set up your config 23 | // Go to https://hardhat.org/config/ to learn more 24 | 25 | const config: HardhatUserConfig = { 26 | solidity: { 27 | compilers: [ 28 | { 29 | version: '0.7.5', 30 | settings: { 31 | optimizer: { 32 | enabled: true, 33 | runs: 200, 34 | }, 35 | }, 36 | }, 37 | { 38 | version: '0.8.0', 39 | settings: { 40 | optimizer: { 41 | enabled: true, 42 | runs: 200, 43 | }, 44 | }, 45 | }, 46 | ], 47 | }, 48 | networks: { 49 | ropsten: { 50 | url: process.env.ROPSTEN_URL || '', 51 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 52 | }, 53 | goerli: { 54 | url: '', 55 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 56 | }, 57 | hardhat: { 58 | forking: { 59 | url: process.env.MAINNET_URL !== undefined ? process.env.MAINNET_URL : '', 60 | enabled: true, 61 | }, 62 | accounts : { 63 | mnemonic : process.env.SEED 64 | }, 65 | }, 66 | }, 67 | gasReporter: { 68 | enabled: process.env.REPORT_GAS !== undefined, 69 | currency: 'USD', 70 | }, 71 | etherscan: { 72 | apiKey: process.env.ETHERSCAN_API_KEY, 73 | }, 74 | mocha: { 75 | timeout: 60000 76 | } 77 | } 78 | 79 | export default config 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "scripts": { 4 | "node": "npx hardhat node --network hardhat" 5 | }, 6 | "devDependencies": { 7 | "@nomiclabs/hardhat-ethers": "^2.0.0", 8 | "@nomiclabs/hardhat-etherscan": "^2.1.3", 9 | "@nomiclabs/hardhat-waffle": "^2.0.0", 10 | "@typechain/ethers-v5": "^7.0.1", 11 | "@typechain/hardhat": "^2.3.0", 12 | "@types/chai": "^4.2.21", 13 | "@types/mocha": "^9.0.0", 14 | "@types/node": "^12.0.0", 15 | "@typescript-eslint/eslint-plugin": "^4.29.1", 16 | "@typescript-eslint/parser": "^4.29.1", 17 | "chai": "^4.2.0", 18 | "dotenv": "^10.0.0", 19 | "eslint": "^7.29.0", 20 | "eslint-config-prettier": "^8.3.0", 21 | "eslint-config-standard": "^16.0.3", 22 | "eslint-plugin-import": "^2.23.4", 23 | "eslint-plugin-node": "^11.1.0", 24 | "eslint-plugin-prettier": "^3.4.0", 25 | "eslint-plugin-promise": "^5.1.0", 26 | "ethereum-waffle": "^3.0.0", 27 | "ethers": "^5.0.0", 28 | "hardhat": "^2.8.0", 29 | "hardhat-gas-reporter": "^1.0.4", 30 | "prettier": "^2.3.2", 31 | "prettier-plugin-solidity": "^1.0.0-beta.13", 32 | "solhint": "^3.3.6", 33 | "solidity-coverage": "^0.7.16", 34 | "ts-node": "^10.1.0", 35 | "typechain": "^5.1.2", 36 | "typescript": "^4.5.2" 37 | }, 38 | "dependencies": { 39 | "@openzeppelin/contracts": "^4.4.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node