├── .eslintignore
├── .eslintrc.yaml
├── .gitignore
├── .prettierignore
├── .prettierrc.yaml
├── .solhint.json
├── .solhintignore
├── README.md
├── contracts
├── Aura.sol
├── AuraBalRewardPool.sol
├── AuraClaimZap.sol
├── AuraLocker.sol
├── AuraMath.sol
├── AuraMerkleDrop.sol
├── AuraMinter.sol
├── AuraPenaltyForwarder.sol
├── AuraStakingProxy.sol
├── AuraVestedEscrow.sol
├── BalLiquidityProvider.sol
├── ClaimFeesHelper.sol
├── CrvDepositorWrapper.sol
├── ExtraRewardsDistributor.sol
├── Interfaces.sol
└── mocks
│ ├── MockAuraMath.sol
│ ├── MockBalInvestor.sol
│ ├── MockVoteStorage.sol
│ ├── balancer
│ ├── MockBalancerPool.sol
│ ├── MockBalancerPoolToken.sol
│ ├── MockBalancerVault.sol
│ ├── MockFeeDistro.sol
│ ├── MockInvestmentPoolFactory.sol
│ ├── MockStablePoolFactory.sol
│ └── MockWeightedPool2TokensFactory.sol
│ └── curve
│ ├── MockCurveGauge.sol
│ ├── MockCurveMinter.sol
│ ├── MockCurveVoteEscrow.sol
│ ├── MockERC20.sol
│ ├── MockVoting.sol
│ └── MockWalletChecker.sol
├── convex-platform
└── contracts
│ ├── contracts
│ ├── ArbitartorVault.sol
│ ├── BaseRewardPool.sol
│ ├── BaseRewardPool4626.sol
│ ├── Booster.sol
│ ├── BoosterOwner.sol
│ ├── ConvexMasterChef.sol
│ ├── CrvDepositor.sol
│ ├── DepositToken.sol
│ ├── ExtraRewardStashV3.sol
│ ├── Interfaces.sol
│ ├── PoolManagerProxy.sol
│ ├── PoolManagerSecondaryProxy.sol
│ ├── PoolManagerV3.sol
│ ├── ProxyFactory.sol
│ ├── RewardFactory.sol
│ ├── RewardHook.sol
│ ├── StashFactoryV2.sol
│ ├── TokenFactory.sol
│ ├── VirtualBalanceRewardPool.sol
│ ├── VoterProxy.sol
│ ├── cCrv.sol
│ └── interfaces
│ │ ├── BoringMath.sol
│ │ ├── IERC20Metadata.sol
│ │ ├── IERC4626.sol
│ │ ├── IGaugeController.sol
│ │ ├── IProxyFactory.sol
│ │ ├── IRewardHook.sol
│ │ ├── IRewarder.sol
│ │ └── MathUtil.sol
│ └── package.json
├── discord-export
├── Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html
├── Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files
│ ├── 0-EB806.png
│ ├── 0ab6fddedfdca4dee0b54ab41ab4faad-C5611.png
│ ├── 1-B2132.png
│ ├── 1f389-5C738.svg
│ ├── 1f3c1-445DC.svg
│ ├── 1f3c6-621A1.svg
│ ├── 1f43a-EB486.svg
│ ├── 1f440-6C64D.svg
│ ├── 1f448-3DF32.svg
│ ├── 1f449-25CFB.svg
│ ├── 1f44b-8A059.svg
│ ├── 1f44c-59547.svg
│ ├── 1f44d-27259.svg
│ ├── 1f48c-A96CC.svg
│ ├── 1f49c-71A75.svg
│ ├── 1f4aa-2FD27.svg
│ ├── 1f4af-4CFF5.svg
│ ├── 1f4b8-E3468.svg
│ ├── 1f4c6-44E30.svg
│ ├── 1f4dc-AC641.svg
│ ├── 1f50d-195C0.svg
│ ├── 1f512-24F85.svg
│ ├── 1f604-BF863.svg
│ ├── 1f60d-BEAFF.svg
│ ├── 1f626-91074.svg
│ ├── 1f642-83E8A.svg
│ ├── 1f64c-7C820.svg
│ ├── 1f64f-22B8D.svg
│ ├── 1f680-A35CE.svg
│ ├── 1f6a8-A8AB3.svg
│ ├── 1f7e2-FC8EC.svg
│ ├── 1f911-F346C.svg
│ ├── 1f91e-2A114.svg
│ ├── 1f972-F415D.svg
│ ├── 1f9be-DDCD0.svg
│ ├── 1f9d1-5BC80.svg
│ ├── 1fac2-960B6.svg
│ ├── 20c8931564dd11b412a4fecc64bf9451-26B4C.png
│ ├── 23a7e58bdc28d6ad048874c84ad5fdb1-DE264.png
│ ├── 2665-8CDCE.svg
│ ├── 2696-15F4A.svg
│ ├── 2a9faff195fe333526cfe6ae6fce1420-927E9.png
│ ├── 423a533afdf07948f23af9a1ebceec37-235E9.png
│ ├── 5-C387C
│ ├── 5-E9BDB.png
│ ├── 568d22b97293cdd2d9b7006198d6adbc-9738F.png
│ ├── 5960c5502dd09d4875482231f43edbc5-F05FC.png
│ ├── 67594ee4b4d1fc03bca468327a0d145b-0C614.png
│ ├── 781415a7ef0ff20e2f8e85348fbd64c1-5F911.png
│ ├── 7fcd80e4d3c60a600235b03c75f682a9-75033.png
│ ├── 83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg
│ ├── 851893157188599838-C23B5.png
│ ├── 851893826846457866-1AD3D.png
│ ├── 851893827089727568-5FD38.png
│ ├── 851893827315826708-F59C0.png
│ ├── 910676187288846397-518CD.png
│ ├── 970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png
│ ├── C4-banner-7C19B.png
│ ├── FSB_4THVcAAXrSe-D0ED9.jpg
│ ├── Screenshot_20220523_113543_com.android.chr-1C166.jpg
│ ├── TKrF4ZUY_400x400-5F957.jpg
│ ├── a3543456a09e49cb364c91b15541774a-203F3.png
│ ├── ae70f07a06c1c7e983291bb14a1bed7f-B8F4A.png
│ ├── apple-touch-icon-192x192-E344C.png
│ ├── b07292365785e0b7bd0dab7dd7d9e09f-62888.png
│ ├── c9cb30134c634c9e02d0c64df4922803-1AAF2.png
│ ├── eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D
│ ├── fab1b54617157de0fdc01d6d30bc68b4-901C1.png
│ ├── ggsans-italic-400-E988B.woff2
│ ├── ggsans-italic-500-0777F.woff2
│ ├── ggsans-italic-600-CB411.woff2
│ ├── ggsans-italic-700-891AC.woff2
│ ├── ggsans-italic-800-D36B0.woff2
│ ├── ggsans-normal-400-1456D.woff2
│ ├── ggsans-normal-500-89CE5.woff2
│ ├── ggsans-normal-600-C1EA8.woff2
│ ├── ggsans-normal-700-1949A.woff2
│ ├── ggsans-normal-800-58487.woff2
│ ├── highlight.min-D8D27.js
│ ├── lottie.min-99657.js
│ ├── solarized-dark.min-BA98F.css
│ └── unknown-D6C62.png
├── Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt
└── Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files
│ ├── 5-C387C
│ ├── 83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg
│ ├── C4-banner-7C19B.png
│ ├── FSB_4THVcAAXrSe-D0ED9.jpg
│ ├── Screenshot_20220523_113543_com.android.chr-1C166.jpg
│ ├── eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D
│ └── unknown-D6C62.png
├── fork-test-output.txt
├── hardhat-cvx.config.ts
├── hardhat-fork.config.ts
├── hardhat.config.ts
├── index.ts
├── package.json
├── scripts
├── deployMocks.ts
└── deploySystem.ts
├── tasks
├── deploy
│ ├── index.ts
│ └── mainnet-config.ts
├── index.ts
└── utils
│ ├── deploy-utils.ts
│ ├── etherscan.ts
│ ├── index.ts
│ ├── networkAddressFactory.ts
│ ├── signerFactory.ts
│ └── tokens.ts
├── test-fork
├── BalEthSwap.spec.ts
└── FullDeployment.spec.ts
├── test-output.txt
├── test-utils
├── assertions.ts
├── constants.ts
├── fork.ts
├── index.ts
├── math.ts
├── merkle.ts
├── regex.ts
└── time.ts
├── test
├── Aura.spec.ts
├── AuraBalRewardPool.spec.ts
├── AuraClaimZap.spec.ts
├── AuraLocker.spec.ts
├── AuraLockerBalances.spec.ts
├── AuraMath.spec.ts
├── AuraMerkleDrop.spec.ts
├── AuraMinter.spec.ts
├── AuraPenaltyForwarder.spec.ts
├── AuraStakingProxy.spec.ts
├── AuraVestedEscrow.spec.ts
├── BalInvestor.spec.ts
├── BalLiquidityProvider.spec.ts
├── BaseRewardPool4626.spec.ts
├── Booster.spec.ts
├── ConvexMasterChef.spec.ts
├── CrvDepositor.spec.ts
├── ExtraRewardsDistributor.spec.ts
├── PoolManager.spec.ts
├── VoterProxy.spec.ts
└── auraLockerBalanceData.json
├── tsconfig.json
├── types
├── common.ts
└── index.ts
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | # directories
2 | .yarn/
3 | **/.coverage_artifacts
4 | **/.coverage_cache
5 | **/.coverage_contracts
6 | **/artifacts
7 | **/build
8 | **/cache
9 | **/coverage
10 | **/dist
11 | **/node_modules
12 | **/types
13 |
14 | # files
15 | *.env
16 | *.log
17 | .pnp.*
18 | coverage.json
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 |
24 | #convex
25 | **/convex-platform/**/*
--------------------------------------------------------------------------------
/.eslintrc.yaml:
--------------------------------------------------------------------------------
1 | extends:
2 | - "eslint:recommended"
3 | - "plugin:@typescript-eslint/eslint-recommended"
4 | - "plugin:@typescript-eslint/recommended"
5 | - "prettier"
6 | parser: "@typescript-eslint/parser"
7 | parserOptions:
8 | project: "tsconfig.json"
9 | plugins:
10 | - "@typescript-eslint"
11 | root: true
12 | rules:
13 | "@typescript-eslint/no-floating-promises":
14 | - error
15 | - ignoreIIFE: true
16 | ignoreVoid: true
17 | "@typescript-eslint/no-inferrable-types": "off"
18 | "@typescript-eslint/no-unused-vars":
19 | - error
20 | - argsIgnorePattern: "_"
21 | varsIgnorePattern: "_"
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_*
2 | # directories
3 | .yarn/*
4 | !.yarn/patches
5 | !.yarn/releases
6 | !.yarn/plugins
7 | !.yarn/sdks
8 | !.yarn/versions
9 | **/artifacts
10 | **/build
11 | **/cache
12 | /coverage
13 | **/.coverage_artifacts
14 | **/.coverage_cache
15 | **/.coverage_contracts
16 | **/dist
17 | **/node_modules
18 |
19 | # files
20 | *.env
21 | *.log
22 | .pnp.*
23 | coverage.json
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 | contracts.json
28 |
29 | # types
30 | types/generated/**/*
31 | _flat
32 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # directories
2 | .yarn/
3 | **/.coverage_artifacts
4 | **/.coverage_cache
5 | **/.coverage_contracts
6 | **/artifacts
7 | **/build
8 | **/cache
9 | **/coverage
10 | **/dist
11 | **/node_modules
12 | **/types
13 |
14 | # files
15 | *.env
16 | *.log
17 | .pnp.*
18 | coverage.json
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #Convex files
24 | convex-platform/contracts
25 | convex-platform/README.md
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | arrowParens: avoid
2 | bracketSpacing: true
3 | endOfLine: auto
4 | printWidth: 120
5 | singleQuote: false
6 | tabWidth: 4
7 | trailingComma: all
8 |
9 | overrides:
10 | - files: "*.sol"
11 | options:
12 | tabWidth: 4
13 |
--------------------------------------------------------------------------------
/.solhint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "solhint:recommended",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "code-complexity": ["error", 8],
6 | "compiler-version": ["error", ">=0.8.4"],
7 | "const-name-snakecase": "off",
8 | "constructor-syntax": "error",
9 | "func-visibility": ["error", { "ignoreConstructors": true }],
10 | "max-line-length": ["error", 120],
11 | "not-rely-on-time": "off",
12 | "prettier/prettier": [
13 | "error",
14 | {
15 | "endOfLine": "auto"
16 | }
17 | ],
18 | "reason-string": ["warn", { "maxLength": 64 }]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.solhintignore:
--------------------------------------------------------------------------------
1 | # directories
2 | **/artifacts
3 | **/node_modules
4 | contracts/mocks
5 |
--------------------------------------------------------------------------------
/contracts/Aura.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import { ERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
6 | import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
7 | import { Address } from "@openzeppelin/contracts-0.8/utils/Address.sol";
8 | import { AuraMath } from "./AuraMath.sol";
9 |
10 | interface IStaker {
11 | function operator() external view returns (address);
12 | }
13 |
14 | /**
15 | * @title AuraToken
16 | * @notice Basically an ERC20 with minting functionality operated by the "operator" of the VoterProxy (Booster).
17 | * @dev The minting schedule is based on the amount of CRV earned through staking and is
18 | * distirbuted along a supply curve (cliffs etc). Fork of ConvexToken.
19 | */
20 | contract AuraToken is ERC20 {
21 | using SafeERC20 for IERC20;
22 | using Address for address;
23 | using AuraMath for uint256;
24 |
25 | address public operator;
26 | address public immutable vecrvProxy;
27 |
28 | uint256 public constant EMISSIONS_MAX_SUPPLY = 5e25; // 50m
29 | uint256 public constant totalCliffs = 500;
30 | uint256 public immutable reductionPerCliff;
31 |
32 | address public minter;
33 | uint256 private minterMinted = type(uint256).max;
34 |
35 | /* ========== EVENTS ========== */
36 |
37 | event Initialised();
38 | event OperatorChanged(address indexed previousOperator, address indexed newOperator);
39 |
40 | /**
41 | * @param _proxy CVX VoterProxy
42 | * @param _nameArg Token name
43 | * @param _symbolArg Token symbol
44 | */
45 | constructor(
46 | address _proxy,
47 | string memory _nameArg,
48 | string memory _symbolArg
49 | ) ERC20(_nameArg, _symbolArg) {
50 | operator = msg.sender;
51 | vecrvProxy = _proxy;
52 | reductionPerCliff = EMISSIONS_MAX_SUPPLY.div(totalCliffs);
53 | }
54 |
55 | /**
56 | * @dev Initialise and mints initial supply of tokens.
57 | * @param _to Target address to mint.
58 | * @param _amount Amount of tokens to mint.
59 | * @param _minter The minter address.
60 | */
61 | function init(
62 | address _to,
63 | uint256 _amount,
64 | address _minter
65 | ) external {
66 | require(msg.sender == operator, "Only operator");
67 | require(totalSupply() == 0, "Only once");
68 | require(_amount > 0, "Must mint something");
69 | require(_minter != address(0), "Invalid minter");
70 |
71 | _mint(_to, _amount);
72 | updateOperator();
73 | minter = _minter;
74 | minterMinted = 0;
75 |
76 | emit Initialised();
77 | }
78 |
79 | /**
80 | * @dev This can be called if the operator of the voterProxy somehow changes.
81 | */
82 | function updateOperator() public {
83 | address newOperator = IStaker(vecrvProxy).operator();
84 | emit OperatorChanged(operator, newOperator);
85 | operator = newOperator;
86 | }
87 |
88 | /**
89 | * @dev Mints AURA to a given user based on the BAL supply schedule.
90 | */
91 | function mint(address _to, uint256 _amount) external {
92 | require(totalSupply() != 0, "Not initialised");
93 |
94 | if (msg.sender != operator) {
95 | // dont error just return. if a shutdown happens, rewards on old system
96 | // can still be claimed, just wont mint cvx
97 | return;
98 | }
99 |
100 | // e.g. emissionsMinted = 6e25 - 5e25 - 0 = 1e25;
101 | uint256 emissionsMinted = totalSupply() - EMISSIONS_MAX_SUPPLY - minterMinted;
102 | // e.g. reductionPerCliff = 5e25 / 500 = 1e23
103 | // e.g. cliff = 1e25 / 1e23 = 100
104 | uint256 cliff = emissionsMinted.div(reductionPerCliff);
105 |
106 | // e.g. 100 < 500
107 | if (cliff < totalCliffs) {
108 | // e.g. (new) reduction = (500 - 100) * 2.5 + 700 = 1700;
109 | // e.g. (new) reduction = (500 - 250) * 2.5 + 700 = 1325;
110 | // e.g. (new) reduction = (500 - 400) * 2.5 + 700 = 950;
111 | uint256 reduction = totalCliffs.sub(cliff).mul(5).div(2).add(700);
112 | // e.g. (new) amount = 1e19 * 1700 / 500 = 34e18;
113 | // e.g. (new) amount = 1e19 * 1325 / 500 = 26.5e18;
114 | // e.g. (new) amount = 1e19 * 950 / 500 = 19e17;
115 | uint256 amount = _amount.mul(reduction).div(totalCliffs);
116 | // e.g. amtTillMax = 5e25 - 1e25 = 4e25
117 | uint256 amtTillMax = EMISSIONS_MAX_SUPPLY.sub(emissionsMinted);
118 | if (amount > amtTillMax) {
119 | amount = amtTillMax;
120 | }
121 | _mint(_to, amount);
122 | }
123 | }
124 |
125 | /**
126 | * @dev Allows minter to mint to a specific address
127 | */
128 | function minterMint(address _to, uint256 _amount) external {
129 | require(msg.sender == minter, "Only minter");
130 | minterMinted += _amount;
131 | _mint(_to, _amount);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/contracts/AuraMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | /// @notice A library for performing overflow-/underflow-safe math,
5 | /// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
6 | library AuraMath {
7 | /**
8 | * @dev Returns the smallest of two numbers.
9 | */
10 | function min(uint256 a, uint256 b) internal pure returns (uint256) {
11 | return a < b ? a : b;
12 | }
13 |
14 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
15 | c = a + b;
16 | }
17 |
18 | function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
19 | c = a - b;
20 | }
21 |
22 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
23 | c = a * b;
24 | }
25 |
26 | function div(uint256 a, uint256 b) internal pure returns (uint256) {
27 | return a / b;
28 | }
29 |
30 | /**
31 | * @dev Returns the average of two numbers. The result is rounded towards
32 | * zero.
33 | */
34 | function average(uint256 a, uint256 b) internal pure returns (uint256) {
35 | // (a + b) / 2 can overflow, so we distribute.
36 | return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
37 | }
38 |
39 | function to224(uint256 a) internal pure returns (uint224 c) {
40 | require(a <= type(uint224).max, "AuraMath: uint224 Overflow");
41 | c = uint224(a);
42 | }
43 |
44 | function to128(uint256 a) internal pure returns (uint128 c) {
45 | require(a <= type(uint128).max, "AuraMath: uint128 Overflow");
46 | c = uint128(a);
47 | }
48 |
49 | function to112(uint256 a) internal pure returns (uint112 c) {
50 | require(a <= type(uint112).max, "AuraMath: uint112 Overflow");
51 | c = uint112(a);
52 | }
53 |
54 | function to96(uint256 a) internal pure returns (uint96 c) {
55 | require(a <= type(uint96).max, "AuraMath: uint96 Overflow");
56 | c = uint96(a);
57 | }
58 |
59 | function to32(uint256 a) internal pure returns (uint32 c) {
60 | require(a <= type(uint32).max, "AuraMath: uint32 Overflow");
61 | c = uint32(a);
62 | }
63 | }
64 |
65 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
66 | library AuraMath32 {
67 | function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
68 | c = a - b;
69 | }
70 | }
71 |
72 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint112.
73 | library AuraMath112 {
74 | function add(uint112 a, uint112 b) internal pure returns (uint112 c) {
75 | c = a + b;
76 | }
77 |
78 | function sub(uint112 a, uint112 b) internal pure returns (uint112 c) {
79 | c = a - b;
80 | }
81 | }
82 |
83 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint224.
84 | library AuraMath224 {
85 | function add(uint224 a, uint224 b) internal pure returns (uint224 c) {
86 | c = a + b;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/contracts/AuraMinter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { Ownable } from "@openzeppelin/contracts-0.8/access/Ownable.sol";
5 | import { AuraToken } from "./Aura.sol";
6 |
7 | /**
8 | * @title AuraMinter
9 | * @notice Wraps the AuraToken minterMint function and protects from inflation until
10 | * 4 years have passed.
11 | * @dev Ownership initially owned by the DAO, but likely transferred to smart contract
12 | * wrapper or additional value system at some stage as directed by token holders.
13 | */
14 | contract AuraMinter is Ownable {
15 | /// @dev Aura token
16 | AuraToken public immutable aura;
17 | /// @dev Timestamp upon which minting will be possible
18 | uint256 public immutable inflationProtectionTime;
19 |
20 | constructor(address _aura, address _dao) Ownable() {
21 | aura = AuraToken(_aura);
22 | _transferOwnership(_dao);
23 | inflationProtectionTime = block.timestamp + 156 weeks;
24 | }
25 |
26 | /**
27 | * @dev Mint function allows the owner of the contract to inflate AURA post protection time
28 | * @param _to Recipient address
29 | * @param _amount Amount of tokens
30 | */
31 | function mint(address _to, uint256 _amount) external onlyOwner {
32 | require(block.timestamp > inflationProtectionTime, "Inflation protected for now");
33 | aura.minterMint(_to, _amount);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/AuraPenaltyForwarder.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { IExtraRewardsDistributor } from "./Interfaces.sol";
5 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
6 | import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
7 |
8 | /**
9 | * @title AuraPenaltyForwarder
10 | * @dev Receives a given token and forwards it on to a distribution contract. Used during
11 | * the bootstrapping period to forward AURA rewards to the extra rewards distributor.
12 | */
13 | contract AuraPenaltyForwarder {
14 | using SafeERC20 for IERC20;
15 |
16 | IExtraRewardsDistributor public immutable distributor;
17 | IERC20 public immutable token;
18 |
19 | uint256 public immutable distributionDelay;
20 | uint256 public lastDistribution;
21 |
22 | event Forwarded(uint256 amount);
23 |
24 | /**
25 | * @dev During deployment approves the distributor to spend all tokens
26 | * @param _distributor Contract that will distribute tokens
27 | * @param _token Token to be distributed
28 | * @param _delay Delay between each distribution trigger
29 | */
30 | constructor(
31 | address _distributor,
32 | address _token,
33 | uint256 _delay
34 | ) {
35 | distributor = IExtraRewardsDistributor(_distributor);
36 | token = IERC20(_token);
37 | distributionDelay = _delay;
38 |
39 | lastDistribution = block.timestamp;
40 |
41 | token.safeApprove(address(distributor), type(uint256).max);
42 | }
43 |
44 | /**
45 | * @dev Forwards the complete balance of token in this contract to the distributor
46 | */
47 | function forward() public {
48 | require(block.timestamp > lastDistribution + distributionDelay, "!elapsed");
49 | lastDistribution = block.timestamp;
50 |
51 | uint256 bal = token.balanceOf(address(this));
52 | require(bal > 0, "!empty");
53 |
54 | distributor.addReward(address(token), bal);
55 |
56 | emit Forwarded(bal);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/contracts/BalLiquidityProvider.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { IVault } from "./Interfaces.sol";
5 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
6 | import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
7 |
8 | /**
9 | * @title BalLiquidityProvider
10 | * @notice Provides initial liquidity to a Balancer pool on behalf of a given DAO
11 | */
12 | contract BalLiquidityProvider {
13 | using SafeERC20 for IERC20;
14 |
15 | IERC20 public immutable startToken;
16 | IERC20 public immutable pairToken;
17 | uint256 public minPairAmount;
18 |
19 | address private immutable provider;
20 | address public immutable dao;
21 |
22 | IVault public immutable bVault;
23 |
24 | event LiquidityProvided(uint256[] input, uint256 output);
25 | event MinPairAmountChanged(uint256 oldMinPairAmount, uint256 newMinPairAmount);
26 |
27 | constructor(
28 | address _startToken,
29 | address _pairToken,
30 | uint256 _minPairAmount,
31 | address _dao,
32 | address _bVault
33 | ) {
34 | startToken = IERC20(_startToken);
35 | pairToken = IERC20(_pairToken);
36 | minPairAmount = _minPairAmount;
37 | provider = msg.sender;
38 | dao = _dao;
39 | bVault = IVault(_bVault);
40 | }
41 |
42 | /**
43 | * @dev Provides liquidity on behalf of the dao, in a non-custodial manner.
44 | * Has protections in place to ensure that no erroneous liquidity data gets added.
45 | */
46 | function provideLiquidity(bytes32 _poolId, IVault.JoinPoolRequest memory _request) public {
47 | require(msg.sender == provider, "!auth");
48 | require(_request.assets.length == 2 && _request.maxAmountsIn.length == 2, "!valid");
49 | require(pairToken.balanceOf(address(this)) > minPairAmount, "!minLiq");
50 |
51 | for (uint256 i = 0; i < 2; i++) {
52 | address asset = address(_request.assets[i]);
53 | require(asset == address(startToken) || asset == address(pairToken), "!asset");
54 |
55 | IERC20 tkn = IERC20(asset);
56 | uint256 bal = tkn.balanceOf(address(this));
57 | require(bal > 0 && bal == _request.maxAmountsIn[i], "!bal");
58 |
59 | tkn.safeApprove(address(bVault), 0);
60 | tkn.safeApprove(address(bVault), bal);
61 | }
62 |
63 | (address pool, ) = bVault.getPool(_poolId);
64 | uint256 supplyBefore = IERC20(pool).totalSupply();
65 | require(supplyBefore == 0, "!init");
66 |
67 | bVault.joinPool(_poolId, address(this), dao, _request);
68 |
69 | uint256 balAfter = IERC20(pool).balanceOf(dao);
70 | require(balAfter > 0, "!mint");
71 |
72 | emit LiquidityProvided(_request.maxAmountsIn, balAfter);
73 | }
74 |
75 | /**
76 | * @dev Allows the DAO to change the minimum amount of the pair token that must be added as liquidity
77 | */
78 | function changeMinPairAmount(uint256 _newAmount) external {
79 | require(msg.sender == dao, "!auth");
80 | emit MinPairAmountChanged(minPairAmount, _newAmount);
81 | minPairAmount = _newAmount;
82 | }
83 |
84 | /**
85 | * @dev Rescues a given token from the contract.
86 | * Only provider or DAO can call this function.
87 | */
88 | function rescueToken(address _erc20) external {
89 | require(msg.sender == provider || msg.sender == dao, "!auth");
90 | IERC20 tkn = IERC20(_erc20);
91 | tkn.safeTransfer(dao, tkn.balanceOf(address(this)));
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/contracts/ClaimFeesHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import { IFeeDistributor } from "./mocks/balancer/MockFeeDistro.sol";
6 |
7 | interface IBooster {
8 | function earmarkFees(address _feeDistro) external returns (bool);
9 | }
10 |
11 | /**
12 | * @title ClaimFeesHelper
13 | * @author ConvexFinance
14 | * @notice Claim vecrv fees and distribute
15 | * @dev Allows anyone to call `claimFees` that will basically collect any 3crv and distribute to cvxCrv
16 | * via the booster.
17 | */
18 | contract ClaimFeesHelper {
19 | IBooster public immutable booster;
20 | address public immutable voterProxy;
21 |
22 | mapping(address => uint256) public lastTokenTimes;
23 | IFeeDistributor public feeDistro;
24 |
25 | /**
26 | * @param _booster Booster.sol, e.g. 0xF403C135812408BFbE8713b5A23a04b3D48AAE31
27 | * @param _voterProxy CVX VoterProxy e.g. 0x989AEb4d175e16225E39E87d0D97A3360524AD80
28 | * @param _feeDistro FeeDistro e.g. 0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc
29 | */
30 | constructor(
31 | address _booster,
32 | address _voterProxy,
33 | address _feeDistro
34 | ) {
35 | booster = IBooster(_booster);
36 | voterProxy = _voterProxy;
37 | feeDistro = IFeeDistributor(_feeDistro);
38 | }
39 |
40 | /**
41 | * @dev Claims fees from fee claimer, and pings the booster to distribute
42 | */
43 | function claimFees(IERC20 _token) external {
44 | uint256 tokenTime = feeDistro.getTokenTimeCursor(_token);
45 | require(tokenTime > lastTokenTimes[address(_token)], "not time yet");
46 |
47 | uint256 bal = IERC20(_token).balanceOf(voterProxy);
48 | feeDistro.claimToken(voterProxy, _token);
49 |
50 | // Loop through until something is transferred
51 | while (IERC20(_token).balanceOf(voterProxy) <= bal) {
52 | feeDistro.claimToken(voterProxy, _token);
53 | }
54 |
55 | booster.earmarkFees(address(_token));
56 | lastTokenTimes[address(_token)] = tokenTime;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/contracts/CrvDepositorWrapper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
6 | import { IVault, IPriceOracle, ICrvDepositorWrapper, IAsset } from "./Interfaces.sol";
7 | import { IVault } from "./Interfaces.sol";
8 |
9 | interface ICrvDepositor {
10 | function depositFor(
11 | address to,
12 | uint256 _amount,
13 | bool _lock,
14 | address _stakeAddress
15 | ) external;
16 | }
17 |
18 | /**
19 | * @title BalInvestor
20 | * @notice Deposits $BAL into a BAL/WETH BPT. Hooks into TWAP to determine minOut.
21 | * @dev Abstract contract for depositing BAL -> balBPT -> auraBAL via crvDepositor
22 | */
23 | abstract contract BalInvestor {
24 | using SafeERC20 for IERC20;
25 |
26 | IVault public immutable BALANCER_VAULT;
27 | address public immutable BAL;
28 | address public immutable WETH;
29 | address public immutable BALANCER_POOL_TOKEN;
30 | bytes32 public immutable BAL_ETH_POOL_ID;
31 |
32 | constructor(
33 | IVault _balancerVault,
34 | address _bal,
35 | address _weth,
36 | bytes32 _balETHPoolId
37 | ) {
38 | (
39 | address poolAddress, /* */
40 |
41 | ) = _balancerVault.getPool(_balETHPoolId);
42 | require(poolAddress != address(0), "!poolAddress");
43 |
44 | BALANCER_VAULT = _balancerVault;
45 | BAL = _bal;
46 | WETH = _weth;
47 | BALANCER_POOL_TOKEN = poolAddress;
48 | BAL_ETH_POOL_ID = _balETHPoolId;
49 | }
50 |
51 | function _setApprovals() internal {
52 | IERC20(WETH).safeApprove(address(BALANCER_VAULT), type(uint256).max);
53 | IERC20(BAL).safeApprove(address(BALANCER_VAULT), type(uint256).max);
54 | }
55 |
56 | function _getBptPrice() internal view returns (uint256) {
57 | IPriceOracle.OracleAverageQuery[] memory queries = new IPriceOracle.OracleAverageQuery[](1);
58 |
59 | queries[0].variable = IPriceOracle.Variable.BPT_PRICE;
60 | queries[0].secs = 3600; // last hour
61 | queries[0].ago = 0; // now
62 |
63 | // Gets the balancer time weighted average price denominated in BAL
64 | return IPriceOracle(BALANCER_POOL_TOKEN).getTimeWeightedAverage(queries)[0];
65 | }
66 |
67 | function _getMinOut(uint256 amount, uint256 minOutBps) internal view returns (uint256) {
68 | // Gets the balancer time weighted average price denominated in BAL
69 | // e.g. if 1 BAL == 0.4 BPT, bptOraclePrice == 2.5
70 | uint256 bptOraclePrice = _getBptPrice();
71 | // e.g. minOut = (((100e18 * 1e18) / 2.5e18) * 9980) / 10000;
72 | // e.g. minout = 39.92e18
73 | uint256 minOut = (((amount * 1e18) / bptOraclePrice) * minOutBps) / 10000;
74 | return minOut;
75 | }
76 |
77 | function _investBalToPool(uint256 amount, uint256 minOut) internal {
78 | IERC20(BAL).safeTransferFrom(msg.sender, address(this), amount);
79 | IAsset[] memory assets = new IAsset[](2);
80 | assets[0] = IAsset(BAL);
81 | assets[1] = IAsset(WETH);
82 | uint256[] memory maxAmountsIn = new uint256[](2);
83 | maxAmountsIn[0] = amount;
84 | maxAmountsIn[1] = 0;
85 |
86 | BALANCER_VAULT.joinPool(
87 | BAL_ETH_POOL_ID,
88 | address(this),
89 | address(this),
90 | IVault.JoinPoolRequest(
91 | assets,
92 | maxAmountsIn,
93 | abi.encode(IVault.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, maxAmountsIn, minOut),
94 | false // Don't use internal balances
95 | )
96 | );
97 | }
98 | }
99 |
100 | /**
101 | * @title CrvDepositorWrapper
102 | * @notice Converts BAL -> balBPT and then wraps to auraBAL via the crvDepositor
103 | */
104 | contract CrvDepositorWrapper is ICrvDepositorWrapper, BalInvestor {
105 | address public immutable crvDeposit;
106 |
107 | constructor(
108 | address _crvDeposit,
109 | IVault _balancerVault,
110 | address _bal,
111 | address _weth,
112 | bytes32 _balETHPoolId
113 | ) BalInvestor(_balancerVault, _bal, _weth, _balETHPoolId) {
114 | crvDeposit = _crvDeposit;
115 | }
116 |
117 | function setApprovals() external {
118 | _setApprovals();
119 | require(IERC20(BALANCER_POOL_TOKEN).approve(crvDeposit, type(uint256).max), "!approval");
120 | }
121 |
122 | /**
123 | * @dev Gets minimum output based on BPT oracle price
124 | * @param _amount Units of BAL to deposit
125 | * @param _outputBps Multiplier where 100% == 10000, 99.5% == 9950 and 98% == 9800
126 | * @return minOut Units of BPT to expect as output
127 | */
128 | function getMinOut(uint256 _amount, uint256 _outputBps) external view returns (uint256) {
129 | return _getMinOut(_amount, _outputBps);
130 | }
131 |
132 | function deposit(
133 | uint256 _amount,
134 | uint256 _minOut,
135 | bool _lock,
136 | address _stakeAddress
137 | ) external {
138 | _investBalToPool(_amount, _minOut);
139 | uint256 bptBalance = IERC20(BALANCER_POOL_TOKEN).balanceOf(address(this));
140 | ICrvDepositor(crvDeposit).depositFor(msg.sender, bptBalance, _lock, _stakeAddress);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/contracts/Interfaces.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 | pragma abicoder v2;
4 |
5 | interface IPriceOracle {
6 | struct OracleAverageQuery {
7 | Variable variable;
8 | uint256 secs;
9 | uint256 ago;
10 | }
11 | enum Variable {
12 | PAIR_PRICE,
13 | BPT_PRICE,
14 | INVARIANT
15 | }
16 |
17 | function getTimeWeightedAverage(OracleAverageQuery[] memory queries)
18 | external
19 | view
20 | returns (uint256[] memory results);
21 | }
22 |
23 | interface IVault {
24 | enum PoolSpecialization {
25 | GENERAL,
26 | MINIMAL_SWAP_INFO,
27 | TWO_TOKEN
28 | }
29 | enum JoinKind {
30 | INIT,
31 | EXACT_TOKENS_IN_FOR_BPT_OUT,
32 | TOKEN_IN_FOR_EXACT_BPT_OUT,
33 | ALL_TOKENS_IN_FOR_EXACT_BPT_OUT
34 | }
35 |
36 | enum SwapKind {
37 | GIVEN_IN,
38 | GIVEN_OUT
39 | }
40 |
41 | struct SingleSwap {
42 | bytes32 poolId;
43 | SwapKind kind;
44 | IAsset assetIn;
45 | IAsset assetOut;
46 | uint256 amount;
47 | bytes userData;
48 | }
49 |
50 | struct FundManagement {
51 | address sender;
52 | bool fromInternalBalance;
53 | address payable recipient;
54 | bool toInternalBalance;
55 | }
56 |
57 | struct JoinPoolRequest {
58 | IAsset[] assets;
59 | uint256[] maxAmountsIn;
60 | bytes userData;
61 | bool fromInternalBalance;
62 | }
63 |
64 | function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);
65 |
66 | function getPoolTokens(bytes32 poolId)
67 | external
68 | view
69 | returns (
70 | address[] memory tokens,
71 | uint256[] memory balances,
72 | uint256 lastChangeBlock
73 | );
74 |
75 | function joinPool(
76 | bytes32 poolId,
77 | address sender,
78 | address recipient,
79 | JoinPoolRequest memory request
80 | ) external payable;
81 |
82 | function swap(
83 | SingleSwap memory singleSwap,
84 | FundManagement memory funds,
85 | uint256 limit,
86 | uint256 deadline
87 | ) external returns (uint256 amountCalculated);
88 |
89 | function exitPool(
90 | bytes32 poolId,
91 | address sender,
92 | address payable recipient,
93 | ExitPoolRequest memory request
94 | ) external;
95 |
96 | struct ExitPoolRequest {
97 | IAsset[] assets;
98 | uint256[] minAmountsOut;
99 | bytes userData;
100 | bool toInternalBalance;
101 | }
102 | enum ExitKind {
103 | EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,
104 | EXACT_BPT_IN_FOR_TOKENS_OUT,
105 | BPT_IN_FOR_EXACT_TOKENS_OUT,
106 | MANAGEMENT_FEE_TOKENS_OUT // for ManagedPool
107 | }
108 | }
109 |
110 | interface IAsset {
111 | // solhint-disable-previous-line no-empty-blocks
112 | }
113 |
114 | interface IAuraLocker {
115 | function lock(address _account, uint256 _amount) external;
116 |
117 | function checkpointEpoch() external;
118 |
119 | function epochCount() external view returns (uint256);
120 |
121 | function balanceAtEpochOf(uint256 _epoch, address _user) external view returns (uint256 amount);
122 |
123 | function totalSupplyAtEpoch(uint256 _epoch) external view returns (uint256 supply);
124 |
125 | function queueNewRewards(uint256 _rewards) external;
126 |
127 | function notifyRewardAmount(address _rewardsToken, uint256 reward) external;
128 |
129 | function getReward(address _account, bool _stake) external;
130 |
131 | function getReward(address _account) external;
132 | }
133 |
134 | interface IExtraRewardsDistributor {
135 | function addReward(address _token, uint256 _amount) external;
136 | }
137 |
138 | interface ICrvDepositorWrapper {
139 | function getMinOut(uint256, uint256) external view returns (uint256);
140 |
141 | function deposit(
142 | uint256,
143 | uint256,
144 | bool,
145 | address _stakeAddress
146 | ) external;
147 | }
148 |
--------------------------------------------------------------------------------
/contracts/mocks/MockAuraMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.11;
3 |
4 | import { AuraMath, AuraMath32, AuraMath112, AuraMath224 } from "../AuraMath.sol";
5 |
6 | // solhint-disable func-name-mixedcase
7 | contract MockAuraMath {
8 | constructor() {}
9 |
10 | function AuraMath_min(uint256 a, uint256 b) external pure returns (uint256) {
11 | return AuraMath.min(a, b);
12 | }
13 |
14 | function AuraMath_add(uint256 a, uint256 b) external pure returns (uint256) {
15 | return AuraMath.add(a, b);
16 | }
17 |
18 | function AuraMath_sub(uint256 a, uint256 b) external pure returns (uint256) {
19 | return AuraMath.sub(a, b);
20 | }
21 |
22 | function AuraMath_mul(uint256 a, uint256 b) external pure returns (uint256) {
23 | return AuraMath.mul(a, b);
24 | }
25 |
26 | function AuraMath_div(uint256 a, uint256 b) external pure returns (uint256) {
27 | return AuraMath.div(a, b);
28 | }
29 |
30 | function AuraMath_average(uint256 a, uint256 b) external pure returns (uint256) {
31 | return AuraMath.average(a, b);
32 | }
33 |
34 | function AuraMath_to224(uint256 a) external pure returns (uint224) {
35 | return AuraMath.to224(a);
36 | }
37 |
38 | function AuraMath_to128(uint256 a) external pure returns (uint128) {
39 | return AuraMath.to128(a);
40 | }
41 |
42 | function AuraMath_to112(uint256 a) external pure returns (uint112) {
43 | return AuraMath.to112(a);
44 | }
45 |
46 | function AuraMath_to96(uint256 a) external pure returns (uint96) {
47 | return AuraMath.to96(a);
48 | }
49 |
50 | function AuraMath_to32(uint256 a) external pure returns (uint32) {
51 | return AuraMath.to32(a);
52 | }
53 |
54 | function AuraMath32_sub(uint32 a, uint32 b) external pure returns (uint32) {
55 | return AuraMath32.sub(a, b);
56 | }
57 |
58 | function AuraMath112_add(uint112 a, uint112 b) external pure returns (uint112) {
59 | return AuraMath112.add(a, b);
60 | }
61 |
62 | function AuraMath112_sub(uint112 a, uint112 b) external pure returns (uint112) {
63 | return AuraMath112.sub(a, b);
64 | }
65 |
66 | function AuraMath224_add(uint224 a, uint224 b) external pure returns (uint224) {
67 | return AuraMath224.add(a, b);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/contracts/mocks/MockBalInvestor.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import "../CrvDepositorWrapper.sol";
6 |
7 | contract MockBalInvestor is BalInvestor {
8 | constructor(
9 | IVault _balancerVault,
10 | address _bal,
11 | address _weth,
12 | bytes32 _balETHPoolId
13 | ) BalInvestor(_balancerVault, _bal, _weth, _balETHPoolId) {}
14 |
15 | function approveToken() external {
16 | _setApprovals();
17 | }
18 |
19 | function getBptPrice() external view returns (uint256) {
20 | return _getBptPrice();
21 | }
22 |
23 | function getMinOut(uint256 _amount, uint256 _outputBps) public view returns (uint256) {
24 | return _getMinOut(_amount, _outputBps);
25 | }
26 |
27 | function addBalToPool(uint256 amount, uint256 _minOut) external {
28 | _investBalToPool(amount, _minOut);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/contracts/mocks/MockVoteStorage.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | contract MockVoteStorage {
5 | struct Vote {
6 | uint256 timestamp;
7 | uint256 choice;
8 | string version;
9 | string space;
10 | string voteType;
11 | }
12 |
13 | mapping(string => Vote) public proposals;
14 |
15 | function setProposal(
16 | uint256 choice,
17 | uint256 timestamp,
18 | string memory version,
19 | string memory proposal,
20 | string memory space,
21 | string memory voteType
22 | ) external {
23 | Vote memory vote = Vote(timestamp, choice, version, space, voteType);
24 | proposals[proposal] = vote;
25 | }
26 |
27 | function hash(string memory proposal) public view returns (bytes32) {
28 | Vote memory vote = proposals[proposal];
29 |
30 | // prettier-ignore
31 | return hashStr(string(abi.encodePacked(
32 | "{",
33 | '"version":"', vote.version, '",',
34 | '"timestamp":"', uint2str(vote.timestamp), '",',
35 | '"space":"', vote.space, '",',
36 | '"type":"', vote.voteType, '",',
37 | payloadStr(proposal, vote.choice),
38 | "}"
39 | )));
40 | }
41 |
42 | function payloadStr(string memory proposal, uint256 choice) internal pure returns (string memory) {
43 | // prettier-ignore
44 | return string(abi.encodePacked(
45 | '"payload":', "{",
46 | '"proposal":', '"', proposal, '",',
47 | '"choice":', uint2str(choice), ","
48 | '"metadata":', '"{}"',
49 | "}"
50 | ));
51 | }
52 |
53 | function hashStr(string memory str) internal pure returns (bytes32) {
54 | return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", uint2str(bytes(str).length), str));
55 | }
56 |
57 | function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) {
58 | if (_i == 0) {
59 | return "0";
60 | }
61 | uint256 j = _i;
62 | uint256 len;
63 | while (j != 0) {
64 | len++;
65 | j /= 10;
66 | }
67 | bytes memory bstr = new bytes(len);
68 | uint256 k = len;
69 | while (_i != 0) {
70 | k = k - 1;
71 | uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
72 | bytes1 b1 = bytes1(temp);
73 | bstr[k] = b1;
74 | _i /= 10;
75 | }
76 | return string(bstr);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockBalancerPool.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IBalancerPool {
7 | function getPoolId() external view returns (bytes32);
8 |
9 | function getNormalizedWeights() external view returns (uint256[] memory);
10 |
11 | function getSwapEnabled() external view returns (bool);
12 | }
13 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockBalancerPoolToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
5 |
6 | contract MockBalancerPoolToken is ERC20("MockBPT", "MockBPT") {
7 | struct OracleAverageQuery {
8 | Variable variable;
9 | uint256 secs;
10 | uint256 ago;
11 | }
12 |
13 | enum Variable {
14 | PAIR_PRICE,
15 | BPT_PRICE,
16 | INVARIANT
17 | }
18 |
19 | uint8 dec;
20 |
21 | uint256 public price;
22 |
23 | constructor(
24 | uint8 _decimals,
25 | address _initialRecipient,
26 | uint256 _initialMint
27 | ) {
28 | dec = _decimals;
29 | _mint(_initialRecipient, _initialMint * (10**uint256(_decimals)));
30 | }
31 |
32 | function mint(address to, uint256 amount) external {
33 | _mint(to, amount);
34 | }
35 |
36 | function setPrice(uint256 _price) external {
37 | price = _price;
38 | }
39 |
40 | function getTimeWeightedAverage(OracleAverageQuery[] memory) external view returns (uint256[] memory) {
41 | uint256[] memory results = new uint256[](1);
42 | results[0] = price;
43 | return results;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockBalancerVault.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import "./MockBalancerPoolToken.sol";
6 | import "../../Interfaces.sol";
7 |
8 | interface IBalancerVault {
9 | function joinPool(
10 | bytes32 poolId,
11 | address sender,
12 | address recipient,
13 | JoinPoolRequest memory request
14 | ) external payable;
15 |
16 | struct JoinPoolRequest {
17 | address[] assets;
18 | uint256[] maxAmountsIn;
19 | bytes userData;
20 | bool fromInternalBalance;
21 | }
22 | }
23 |
24 | contract MockBalancerVault {
25 | address public pool;
26 |
27 | address public poolToken;
28 |
29 | address public tokenA;
30 |
31 | address public tokenB;
32 |
33 | constructor(address _poolToken) {
34 | poolToken = _poolToken;
35 | }
36 |
37 | function setTokens(address _tokenA, address _tokenB) external {
38 | tokenA = _tokenA;
39 | tokenB = _tokenB;
40 | }
41 |
42 | function getPool(bytes32) external view returns (address, IVault.PoolSpecialization) {
43 | return (poolToken, IVault.PoolSpecialization.GENERAL);
44 | }
45 |
46 | function joinPool(
47 | bytes32, /* poolId */
48 | address, /* sender */
49 | address recipient,
50 | IVault.JoinPoolRequest memory request
51 | ) external payable {
52 | uint256 amount = request.maxAmountsIn[0];
53 | uint256 price = MockBalancerPoolToken(poolToken).price();
54 | MockBalancerPoolToken(poolToken).mint(recipient, (amount * 1e18) / price);
55 | }
56 |
57 | function swap(
58 | IVault.SingleSwap memory singleSwap,
59 | IVault.FundManagement memory funds,
60 | uint256, /* limit */
61 | uint256 /* deadline */
62 | ) external returns (uint256 amountCalculated) {
63 | require(address(singleSwap.assetOut) == tokenA || address(singleSwap.assetOut) == tokenB, "!token");
64 |
65 | if (address(singleSwap.assetOut) == tokenA) {
66 | // send tokenA
67 | IERC20(tokenB).transferFrom(funds.sender, address(this), singleSwap.amount);
68 | IERC20(tokenA).transfer(funds.recipient, singleSwap.amount);
69 | } else if (address(singleSwap.assetOut) == tokenB) {
70 | // send tokenB
71 | IERC20(tokenA).transferFrom(funds.sender, address(this), singleSwap.amount);
72 | IERC20(tokenB).transfer(funds.recipient, singleSwap.amount);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockFeeDistro.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IFeeDistributor {
7 | function claimToken(address user, IERC20 token) external returns (uint256);
8 |
9 | function claimTokens(address user, IERC20[] calldata tokens) external returns (uint256[] memory);
10 |
11 | function getTokenTimeCursor(IERC20 token) external view returns (uint256);
12 | }
13 |
14 | // @dev - Must be funded by transferring crv to this contract post deployment, as opposed to minting directly
15 | contract MockFeeDistributor is IFeeDistributor {
16 | mapping(address => uint256) private tokenRates;
17 |
18 | constructor(address[] memory _tokens, uint256[] memory _rates) {
19 | for (uint256 i = 0; i < _tokens.length; i++) {
20 | tokenRates[_tokens[i]] = _rates[i];
21 | }
22 | }
23 |
24 | function claimToken(address user, IERC20 token) external returns (uint256) {
25 | return _claimToken(user, token);
26 | }
27 |
28 | function _claimToken(address user, IERC20 token) internal returns (uint256) {
29 | uint256 rate = tokenRates[address(token)];
30 | if (rate > 0) {
31 | token.transfer(user, rate);
32 | }
33 | return rate;
34 | }
35 |
36 | function claimTokens(address user, IERC20[] calldata tokens) external returns (uint256[] memory) {
37 | uint256[] memory rates = new uint256[](tokens.length);
38 | for (uint256 i = 0; i < tokens.length; i++) {
39 | rates[i] = _claimToken(user, tokens[i]);
40 | }
41 | return rates;
42 | }
43 |
44 | function getTokenTimeCursor(IERC20 token) external view returns (uint256) {
45 | return 1;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockInvestmentPoolFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IInvestmentPoolFactory {
7 | function create(
8 | string memory name,
9 | string memory symbol,
10 | IERC20[] memory tokens,
11 | uint256[] memory weights,
12 | uint256 swapFeePercentage,
13 | address owner,
14 | bool swapEnabledOnStart,
15 | uint256 managementSwapFeePercentage
16 | ) external returns (address);
17 | }
18 |
19 | interface IInvestmentPool {
20 | function setSwapEnabled(bool swapEnabled) external;
21 |
22 | function updateWeightsGradually(
23 | uint256 startTime,
24 | uint256 endTime,
25 | uint256[] memory endWeights
26 | ) external;
27 | }
28 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockStablePoolFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IStablePoolFactory {
7 | function create(
8 | string memory name,
9 | string memory symbol,
10 | IERC20[] memory tokens,
11 | uint256 amplificationParameter,
12 | uint256 swapFeePercentage,
13 | address owner
14 | ) external returns (address);
15 | }
16 |
--------------------------------------------------------------------------------
/contracts/mocks/balancer/MockWeightedPool2TokensFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IWeightedPool2TokensFactory {
7 | function create(
8 | string memory name,
9 | string memory symbol,
10 | IERC20[] memory tokens,
11 | uint256[] memory weights,
12 | uint256 swapFeePercentage,
13 | bool oracleEnabled,
14 | address owner
15 | ) external returns (address);
16 | }
17 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockCurveGauge.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
5 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
6 |
7 | contract MockCurveGauge is ERC20 {
8 | address public lp_token;
9 |
10 | // V2 gauge
11 | address[] public reward_tokens;
12 |
13 | constructor(
14 | string memory _name,
15 | string memory _symbol,
16 | address _lptoken,
17 | address[] memory _rewardTokens
18 | ) ERC20(_name, _symbol) {
19 | lp_token = _lptoken;
20 | reward_tokens = _rewardTokens;
21 | }
22 |
23 | function deposit(uint256 amount) external {
24 | _mint(msg.sender, amount);
25 | IERC20(lp_token).transferFrom(msg.sender, address(this), amount);
26 | }
27 |
28 | function withdraw(uint256 amount) external {
29 | _burn(msg.sender, amount);
30 | IERC20(lp_token).transfer(msg.sender, amount);
31 | }
32 |
33 | function claim_rewards() external {
34 | uint256 amount = balanceOf(msg.sender);
35 |
36 | for (uint256 i = 0; i < reward_tokens.length; i++) {
37 | IERC20(reward_tokens[i]).transfer(msg.sender, amount);
38 | }
39 | }
40 |
41 | function claimable_reward(address, address) external view returns (uint256) {
42 | return 0;
43 | }
44 |
45 | function deposit_reward_token(address, uint256) external {}
46 |
47 | function add_reward(address _reward_token, address _distributor) external {}
48 | }
49 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockCurveMinter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 |
6 | interface IMinter {
7 | function mint(address) external;
8 | }
9 |
10 | // @dev - Must be funded by transferring crv to this contract post deployment, as opposed to minting directly
11 | contract MockCurveMinter is IMinter {
12 | IERC20 public immutable crv;
13 | uint256 public rate;
14 |
15 | constructor(address _crv, uint256 _rate) {
16 | crv = IERC20(_crv);
17 | rate = _rate;
18 | }
19 |
20 | function mint(address) external {
21 | crv.transfer(msg.sender, rate);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockCurveVoteEscrow.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
5 | import "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
6 | import "./MockWalletChecker.sol";
7 |
8 | contract MockCurveVoteEscrow is ERC20("MockVE", "MockVE") {
9 | address public smart_wallet_checker;
10 |
11 | address public token;
12 |
13 | mapping(address => uint256) public lockAmounts;
14 |
15 | mapping(address => uint256) public lockTimes;
16 |
17 | uint256 public constant MAX_LEN = 365 days;
18 |
19 | constructor(address _smart_wallet_checker, address _token) {
20 | smart_wallet_checker = _smart_wallet_checker;
21 | token = _token;
22 | }
23 |
24 | function transfer(
25 | address, /* recipient */
26 | uint256 /* amount */
27 | ) public virtual override returns (bool) {
28 | revert("Not transferrable");
29 | }
30 |
31 | function transferFrom(
32 | address, /* sender */
33 | address, /* recipient */
34 | uint256 /* amount */
35 | ) public virtual override returns (bool) {
36 | revert("Not transferrable");
37 | }
38 |
39 | function create_lock(uint256 amount, uint256 unlockTime) external {
40 | require(MockWalletChecker(smart_wallet_checker).check(msg.sender), "!contracts");
41 | require(lockAmounts[msg.sender] == 0, "Withdraw old tokens first");
42 | require(unlockTime < block.timestamp + MAX_LEN, "Lock too long");
43 | require(amount > 0, "!amount");
44 |
45 | lockAmounts[msg.sender] = amount;
46 | lockTimes[msg.sender] = unlockTime;
47 |
48 | IERC20(token).transferFrom(msg.sender, address(this), amount);
49 | _mint(msg.sender, amount);
50 | }
51 |
52 | function increase_amount(uint256 amount) external {
53 | require(lockAmounts[msg.sender] > 0, "Must have a lock");
54 | require(lockTimes[msg.sender] > block.timestamp, "Current lock expired");
55 | require(amount > 0, "!amount");
56 | lockAmounts[msg.sender] += amount;
57 |
58 | IERC20(token).transferFrom(msg.sender, address(this), amount);
59 | _mint(msg.sender, amount);
60 | }
61 |
62 | function increase_unlock_time(uint256 time) external {
63 | require(lockAmounts[msg.sender] > 0, "Must have a lock");
64 | require(lockTimes[msg.sender] > block.timestamp, "Current lock expired");
65 | require(time > lockTimes[msg.sender], "Future time must be greater");
66 | require(time < block.timestamp + MAX_LEN, "Lock too long");
67 | lockTimes[msg.sender] = time;
68 | }
69 |
70 | function withdraw() external {
71 | require(lockTimes[msg.sender] < block.timestamp, "!unlocked");
72 |
73 | uint256 amount = balanceOf(msg.sender);
74 |
75 | lockAmounts[msg.sender] = 0;
76 | lockTimes[msg.sender] = 0;
77 |
78 | IERC20(token).transfer(msg.sender, amount);
79 | _burn(msg.sender, amount);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | import "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
5 |
6 | contract MockERC20 is ERC20 {
7 | uint8 dec;
8 |
9 | constructor(
10 | string memory _name,
11 | string memory _symbol,
12 | uint8 _decimals,
13 | address _initialRecipient,
14 | uint256 _initialMint
15 | ) ERC20(_name, _symbol) {
16 | dec = _decimals;
17 | _mint(_initialRecipient, _initialMint * (10**uint256(_decimals)));
18 | }
19 |
20 | function decimals() public view override returns (uint8) {
21 | return dec;
22 | }
23 |
24 | function mint(uint256 amount) public {
25 | _mint(msg.sender, amount);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockVoting.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | contract MockVoting {
5 | mapping(address => uint256) public gaugeWeights;
6 |
7 | mapping(uint256 => uint256) public votesFor;
8 |
9 | mapping(uint256 => uint256) public votesAgainst;
10 |
11 | struct VotedSlope {
12 | uint256 slope;
13 | uint256 power;
14 | uint256 end;
15 | }
16 |
17 | function vote(
18 | uint256 voteId,
19 | bool support,
20 | bool
21 | ) external {
22 | if (support) {
23 | votesFor[voteId]++;
24 | } else {
25 | votesAgainst[voteId]++;
26 | }
27 | }
28 |
29 | function vote_for_gauge_weights(address gauge, uint256 weight) external {
30 | gaugeWeights[gauge] += weight;
31 | }
32 |
33 | function get_gauge_weight(address gauge) external view returns (uint256) {
34 | return gaugeWeights[gauge];
35 | }
36 |
37 | function vote_user_slopes(address user, address gauge) external view returns (VotedSlope memory) {
38 | return VotedSlope(0, 0, 0);
39 | }
40 |
41 | // Total vote power used by user
42 | function vote_user_power(address user) external view returns (uint256) {
43 | return 0;
44 | }
45 |
46 | // Last user vote's timestamp for each gauge address
47 | function last_user_vote(address user, address gauge) external view returns (uint256) {
48 | return 0;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/contracts/mocks/curve/MockWalletChecker.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.11;
3 |
4 | contract MockWalletChecker {
5 | mapping(address => bool) public wallets;
6 |
7 | function approveWallet(address wallet) external {
8 | wallets[wallet] = true;
9 | }
10 |
11 | function check(address wallet) external view returns (bool) {
12 | return wallets[wallet];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/ArbitartorVault.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
6 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
7 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
8 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
9 |
10 | /**
11 | * @title ArbitratorVault
12 | * @author ConvexFinance
13 | * @notice Hold extra reward tokens on behalf of pools that have the same token as a reward (e.g. stkAAVE fro multiple aave pools)
14 | * @dev Sits on top of the STASH to basically handle the re-distribution of rewards to multiple stashes.
15 | * Because anyone can call gauge.claim_rewards(address) for the convex staking contract, rewards
16 | * could be forced to the wrong pool. Hold tokens here and distribute fairly(or at least more fairly),
17 | * to both pools at a later timing.
18 | */
19 | contract ArbitratorVault{
20 | using SafeERC20 for IERC20;
21 | using Address for address;
22 | using SafeMath for uint256;
23 |
24 | address public operator;
25 | address public immutable depositor;
26 |
27 |
28 | /**
29 | * @param _depositor Booster address
30 | */
31 | constructor(address _depositor)public
32 | {
33 | operator = msg.sender;
34 | depositor = _depositor;
35 | }
36 |
37 | function setOperator(address _op) external {
38 | require(msg.sender == operator, "!auth");
39 | operator = _op;
40 | }
41 |
42 | /**
43 | * @notice Permissioned fn to distribute any accrued rewards to a relevant stash
44 | * @dev Only called by operator: ConvexMultisig
45 | */
46 | function distribute(address _token, uint256[] calldata _toPids, uint256[] calldata _amounts) external {
47 | require(msg.sender == operator, "!auth");
48 |
49 | for(uint256 i = 0; i < _toPids.length; i++){
50 | //get stash from pid
51 | (,,,,address stashAddress,bool shutdown) = IDeposit(depositor).poolInfo(_toPids[i]);
52 |
53 | //if sent to a shutdown pool, could get trapped
54 | require(shutdown==false,"pool closed");
55 |
56 | //transfer
57 | IERC20(_token).safeTransfer(stashAddress, _amounts[i]);
58 | }
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/DepositToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
6 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
7 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
8 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
9 | import "@openzeppelin/contracts-0.6/token/ERC20/ERC20.sol";
10 |
11 |
12 | /**
13 | * @title DepositToken
14 | * @author ConvexFinance
15 | * @notice Simply creates a token that can be minted and burned from the operator
16 | */
17 | contract DepositToken is ERC20 {
18 | using SafeERC20 for IERC20;
19 | using Address for address;
20 | using SafeMath for uint256;
21 |
22 | address public operator;
23 |
24 | /**
25 | * @param _operator Booster
26 | * @param _lptoken Underlying LP token for deposits
27 | * @param _namePostfix Postfixes lpToken name
28 | * @param _symbolPrefix Prefixed lpToken symbol
29 | */
30 | constructor(
31 | address _operator,
32 | address _lptoken,
33 | string memory _namePostfix,
34 | string memory _symbolPrefix
35 | )
36 | public
37 | ERC20(
38 | string(
39 | abi.encodePacked(ERC20(_lptoken).name(), _namePostfix)
40 | ),
41 | string(abi.encodePacked(_symbolPrefix, ERC20(_lptoken).symbol()))
42 | )
43 | {
44 | operator = _operator;
45 | }
46 |
47 | function mint(address _to, uint256 _amount) external {
48 | require(msg.sender == operator, "!authorized");
49 |
50 | _mint(_to, _amount);
51 | }
52 |
53 | function burn(address _from, uint256 _amount) external {
54 | require(msg.sender == operator, "!authorized");
55 |
56 | _burn(_from, _amount);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/PoolManagerProxy.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 |
6 | /**
7 | * @title PoolManagerProxy
8 | * @author ConvexFinance
9 | * @notice Immutable pool manager proxy to enforce that there are no multiple pools of the same gauge
10 | * as well as new lp tokens are not gauge tokens
11 | * @dev Called by PoolManagerShutdownProxy
12 | */
13 | contract PoolManagerProxy{
14 |
15 | address public immutable pools;
16 | address public owner;
17 | address public operator;
18 |
19 | /**
20 | * @param _pools Contract can call addPool currently Booster
21 | * @param _owner Contract owner currently multisig
22 | */
23 | constructor(
24 | address _pools,
25 | address _owner
26 | ) public {
27 | pools = _pools;
28 | owner = _owner;
29 | operator = msg.sender;
30 | }
31 |
32 | modifier onlyOwner() {
33 | require(owner == msg.sender, "!owner");
34 | _;
35 | }
36 |
37 | modifier onlyOperator() {
38 | require(operator == msg.sender, "!op");
39 | _;
40 | }
41 |
42 | //set owner - only OWNER
43 | function setOwner(address _owner) external onlyOwner{
44 | owner = _owner;
45 | }
46 |
47 | //set operator - only OWNER
48 | function setOperator(address _operator) external onlyOwner{
49 | operator = _operator;
50 | }
51 |
52 | // sealed to be immutable
53 | // function revertControl() external{
54 | // }
55 |
56 | //shutdown a pool - only OPERATOR
57 | function shutdownPool(uint256 _pid) external onlyOperator returns(bool){
58 | return IPools(pools).shutdownPool(_pid);
59 | }
60 |
61 | /**
62 | * @notice Add pool to system
63 | * @dev Only callable by the operator looks up the gauge from the gaugeMap in Booster to ensure
64 | * it hasn't already been added
65 | */
66 | function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external onlyOperator returns(bool){
67 |
68 | require(_gauge != address(0),"gauge is 0");
69 | require(_lptoken != address(0),"lp token is 0");
70 |
71 | //check if a pool with this gauge already exists
72 | bool gaugeExists = IPools(pools).gaugeMap(_gauge);
73 | require(!gaugeExists, "already registered gauge");
74 |
75 | //must also check that the lp token is not a registered gauge
76 | //because curve gauges are tokenized
77 | gaugeExists = IPools(pools).gaugeMap(_lptoken);
78 | require(!gaugeExists, "already registered lptoken");
79 |
80 | return IPools(pools).addPool(_lptoken,_gauge,_stashVersion);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/PoolManagerSecondaryProxy.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "./interfaces/IGaugeController.sol";
6 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
7 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
8 |
9 | /**
10 | * @title PoolManagerSecondaryProxy
11 | * @author ConvexFinance
12 | * @notice Basically a PoolManager that has a better shutdown and calls addPool on PoolManagerProxy.
13 | * Immutable pool manager proxy to enforce that when a pool is shutdown, the proper number
14 | * of lp tokens are returned to the booster contract for withdrawal.
15 | */
16 | contract PoolManagerSecondaryProxy{
17 | using SafeMath for uint256;
18 |
19 | address public immutable gaugeController;
20 | address public immutable pools;
21 | address public immutable booster;
22 | address public owner;
23 | address public operator;
24 | bool public isShutdown;
25 |
26 | mapping(address => bool) public usedMap;
27 |
28 | /**
29 | * @param _gaugeController Curve Gauge controller (0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB)
30 | * @param _pools PoolManagerProxy (0x5F47010F230cE1568BeA53a06eBAF528D05c5c1B)
31 | * @param _booster Booster
32 | * @param _owner Executoor
33 | */
34 | constructor(
35 | address _gaugeController,
36 | address _pools,
37 | address _booster,
38 | address _owner
39 | ) public {
40 | gaugeController = _gaugeController;
41 | pools = _pools;
42 | booster = _booster;
43 | owner = _owner;
44 | operator = msg.sender;
45 | }
46 |
47 | modifier onlyOwner() {
48 | require(owner == msg.sender, "!owner");
49 | _;
50 | }
51 |
52 | modifier onlyOperator() {
53 | require(operator == msg.sender, "!op");
54 | _;
55 | }
56 |
57 | //set owner - only OWNER
58 | function setOwner(address _owner) external onlyOwner{
59 | owner = _owner;
60 | }
61 |
62 | //set operator - only OWNER
63 | function setOperator(address _operator) external onlyOwner{
64 | operator = _operator;
65 | }
66 |
67 | //manual set an address to used state
68 | function setUsedAddress(address[] memory usedList) external onlyOwner{
69 | for(uint i=0; i < usedList.length; i++){
70 | usedMap[usedList[i]] = true;
71 | }
72 | }
73 |
74 | //shutdown pool management and disallow new pools. change is immutable
75 | function shutdownSystem() external onlyOwner{
76 | isShutdown = true;
77 | }
78 |
79 | /**
80 | * @notice Shutdown a pool - only OPERATOR
81 | * @dev Shutdowns a pool and ensures all the LP tokens are properly
82 | * withdrawn to the Booster contract
83 | */
84 | function shutdownPool(uint256 _pid) external onlyOperator returns(bool){
85 | //get pool info
86 | (address lptoken, address depositToken,,,,bool isshutdown) = IPools(booster).poolInfo(_pid);
87 | require(!isshutdown, "already shutdown");
88 |
89 | //shutdown pool and get before and after amounts
90 | uint256 beforeBalance = IERC20(lptoken).balanceOf(booster);
91 | IPools(pools).shutdownPool(_pid);
92 | uint256 afterBalance = IERC20(lptoken).balanceOf(booster);
93 |
94 | //check that proper amount of tokens were withdrawn(will also fail if already shutdown)
95 | require( afterBalance.sub(beforeBalance) >= IERC20(depositToken).totalSupply(), "supply mismatch");
96 |
97 | return true;
98 | }
99 |
100 | //add a new pool if it has weight on the gauge controller - only OPERATOR
101 | function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external onlyOperator returns(bool){
102 | //check that the pool as weight
103 | uint256 weight = IGaugeController(gaugeController).get_gauge_weight(_gauge);
104 | require(weight > 0, "must have weight");
105 |
106 | return _addPool(_lptoken, _gauge, _stashVersion);
107 | }
108 |
109 | //force add a new pool, but only for addresses that have never been used before - only OPERATOR
110 | function forceAddPool(address _lptoken, address _gauge, uint256 _stashVersion) external onlyOperator returns(bool){
111 | require(!usedMap[_lptoken] && !usedMap[_gauge], "cant force used pool");
112 |
113 | return _addPool(_lptoken, _gauge, _stashVersion);
114 | }
115 |
116 | //internal add pool and updated used list
117 | function _addPool(address _lptoken, address _gauge, uint256 _stashVersion) internal returns(bool){
118 | require(!isShutdown, "shutdown");
119 |
120 | usedMap[_lptoken] = true;
121 | usedMap[_gauge] = true;
122 |
123 | return IPools(pools).addPool(_lptoken,_gauge,_stashVersion);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/PoolManagerV3.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "./interfaces/IGaugeController.sol";
6 |
7 | /**
8 | * @title PoolManagerV3
9 | * @author ConvexFinance
10 | * @notice Pool Manager v3
11 | * PoolManagerV3 calls addPool on PoolManagerShutdownProxy which calls
12 | * addPool on PoolManagerProxy which calls addPool on Booster.
13 | * PoolManager-ception
14 | * @dev Add pools to the Booster contract
15 | */
16 | contract PoolManagerV3{
17 |
18 | address public immutable pools;
19 | address public immutable gaugeController;
20 | address public operator;
21 |
22 | bool public protectAddPool;
23 |
24 | /**
25 | * @param _pools Currently PoolManagerSecondaryProxy
26 | * @param _gaugeController Curve gauge controller e.g: (0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB)
27 | * @param _operator Convex multisig
28 | */
29 | constructor(
30 | address _pools,
31 | address _gaugeController,
32 | address _operator
33 | ) public {
34 | pools = _pools;
35 | gaugeController = _gaugeController;
36 | operator = _operator;
37 | protectAddPool = true;
38 | }
39 |
40 | function setOperator(address _operator) external {
41 | require(msg.sender == operator, "!auth");
42 | operator = _operator;
43 | }
44 |
45 | /**
46 | * @notice set if addPool is only callable by operator
47 | */
48 | function setProtectPool(bool _protectAddPool) external {
49 | require(msg.sender == operator, "!auth");
50 | protectAddPool = _protectAddPool;
51 | }
52 |
53 | /**
54 | * @notice Add a new curve pool to the system. (default stash to v3)
55 | */
56 | function addPool(address _gauge) external returns(bool){
57 | _addPool(_gauge,3);
58 | return true;
59 | }
60 |
61 | /**
62 | * @notice Add a new curve pool to the system
63 | */
64 | function addPool(address _gauge, uint256 _stashVersion) external returns(bool){
65 | _addPool(_gauge,_stashVersion);
66 | return true;
67 | }
68 |
69 | function _addPool(address _gauge, uint256 _stashVersion) internal{
70 | if(protectAddPool) {
71 | require(msg.sender == operator, "!auth");
72 | }
73 | //get lp token from gauge
74 | address lptoken = ICurveGauge(_gauge).lp_token();
75 |
76 | //gauge/lptoken address checks will happen in the next call
77 | IPools(pools).addPool(lptoken,_gauge,_stashVersion);
78 | }
79 |
80 | function forceAddPool(address _lptoken, address _gauge, uint256 _stashVersion) external returns(bool){
81 | require(msg.sender==operator, "!auth");
82 |
83 | //force add pool without weight checks (can only be used on new token and gauge addresses)
84 | return IPools(pools).forceAddPool(_lptoken, _gauge, _stashVersion);
85 | }
86 |
87 | function shutdownPool(uint256 _pid) external returns(bool){
88 | require(msg.sender==operator, "!auth");
89 |
90 | IPools(pools).shutdownPool(_pid);
91 | return true;
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/ProxyFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | /**
5 | * @title ProxyFactory
6 | * @author Optionality
7 | * @notice Simply clones a smart contract at a given address
8 | * @dev Original code https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
9 | */
10 | contract ProxyFactory {
11 | function clone(address target) external returns (address result) {
12 | bytes20 targetBytes = bytes20(target);
13 | assembly {
14 | let clone := mload(0x40)
15 | mstore(
16 | clone,
17 | 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
18 | )
19 | mstore(add(clone, 0x14), targetBytes)
20 | mstore(
21 | add(clone, 0x28),
22 | 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
23 | )
24 | result := create(0, clone, 0x37)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/RewardFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "./BaseRewardPool4626.sol";
6 | import "./VirtualBalanceRewardPool.sol";
7 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
8 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
9 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
10 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
11 |
12 |
13 | /**
14 | * @title RewardFactory
15 | * @author ConvexFinance
16 | * @notice Used to deploy reward pools when a new pool is added to the Booster
17 | * contract. This contract deploys two types of reward pools:
18 | * - BaseRewardPool handles CRV rewards for guages
19 | * - VirtualBalanceRewardPool for extra rewards
20 | */
21 | contract RewardFactory {
22 | using Address for address;
23 |
24 | address public immutable operator;
25 | address public immutable crv;
26 |
27 | mapping (address => bool) private rewardAccess;
28 | mapping(address => uint256[]) public rewardActiveList;
29 |
30 |
31 | event RewardPoolCreated(address rewardPool, uint256 _pid, address depositToken);
32 | event TokenRewardPoolCreated(address rewardPool, address token, address mainRewards, address operator);
33 |
34 | event AccessChanged(address stash, bool hasAccess);
35 |
36 | /**
37 | * @param _operator Contract operator is Booster
38 | * @param _crv CRV token address
39 | */
40 | constructor(address _operator, address _crv) public {
41 | operator = _operator;
42 | crv = _crv;
43 | }
44 |
45 | //stash contracts need access to create new Virtual balance pools for extra gauge incentives(ex. snx)
46 | function setAccess(address _stash, bool _status) external{
47 | require(msg.sender == operator, "!auth");
48 | rewardAccess[_stash] = _status;
49 |
50 | emit AccessChanged(_stash, _status);
51 | }
52 |
53 | /**
54 | * @notice Create a Managed Reward Pool to handle distribution of all crv mined in a pool
55 | */
56 | function CreateCrvRewards(uint256 _pid, address _depositToken, address _lptoken) external returns (address) {
57 | require(msg.sender == operator, "!auth");
58 |
59 | //operator = booster(deposit) contract so that new crv can be added and distributed
60 | //reward manager = this factory so that extra incentive tokens(ex. snx) can be linked to the main managed reward pool
61 | BaseRewardPool4626 rewardPool = new BaseRewardPool4626(_pid,_depositToken,crv,operator, address(this), _lptoken);
62 |
63 | emit RewardPoolCreated(address(rewardPool), _pid, _depositToken);
64 | return address(rewardPool);
65 | }
66 |
67 | /**
68 | * @notice Create a virtual balance reward pool that mimics the balance of a pool's main reward contract
69 | * used for extra incentive tokens(ex. snx) as well as vecrv fees
70 | */
71 | function CreateTokenRewards(address _token, address _mainRewards, address _operator) external returns (address) {
72 | require(msg.sender == operator || rewardAccess[msg.sender] == true, "!auth");
73 |
74 | //create new pool, use main pool for balance lookup
75 | VirtualBalanceRewardPool rewardPool = new VirtualBalanceRewardPool(_mainRewards,_token,_operator);
76 | address rAddress = address(rewardPool);
77 | //add the new pool to main pool's list of extra rewards, assuming this factory has "reward manager" role
78 | IRewards(_mainRewards).addExtraReward(rAddress);
79 |
80 | emit TokenRewardPoolCreated(rAddress, _token, _mainRewards, _operator);
81 | //return new pool's address
82 | return rAddress;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/RewardHook.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
5 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
6 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
7 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
8 |
9 |
10 | /**
11 | * @title RewardHook
12 | * @author ConvexFinance
13 | * @notice Example Reward hook for stash
14 | * @dev ExtraRewardStash contracts call this hook if it is set. This hook
15 | * can be used to pull rewards during a claim. For example pulling
16 | * rewards from master chef.
17 | */
18 | contract RewardHook{
19 | using SafeERC20 for IERC20;
20 | using Address for address;
21 | using SafeMath for uint256;
22 |
23 |
24 | address public immutable stash;
25 | address public immutable rewardToken;
26 |
27 |
28 | /**
29 | * @param _stash Address of the reward stash
30 | * @param _reward Reward token
31 | */
32 | constructor(address _stash, address _reward) public {
33 | stash = _stash;
34 | rewardToken = _reward;
35 | }
36 |
37 |
38 | /**
39 | * @dev Called when claimRewards is called in ExtraRewardStash can implement
40 | * logic to pull rewards i.e from a master chef contract. This is just an example
41 | * and assumes rewards are just sent directly to this hook contract
42 | */
43 | function onRewardClaim() external{
44 |
45 | //get balance
46 | uint256 bal = IERC20(rewardToken).balanceOf(address(this));
47 |
48 | //send
49 | IERC20(rewardToken).safeTransfer(stash,bal);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/StashFactoryV2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "./interfaces/IProxyFactory.sol";
6 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
7 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
8 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
9 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
10 |
11 | /**
12 | * @title StashFactoryV2
13 | * @author ConvexFinance
14 | * @notice Factory to deploy reward stash contracts that handle extra rewards
15 | */
16 | contract StashFactoryV2 {
17 | using Address for address;
18 |
19 | bytes4 private constant rewarded_token = 0x16fa50b1; //rewarded_token()
20 | bytes4 private constant reward_tokens = 0x54c49fe9; //reward_tokens(uint256)
21 | bytes4 private constant rewards_receiver = 0x01ddabf1; //rewards_receiver(address)
22 |
23 | address public immutable operator;
24 | address public immutable rewardFactory;
25 | address public immutable proxyFactory;
26 |
27 | address public v1Implementation;
28 | address public v2Implementation;
29 | address public v3Implementation;
30 |
31 | event StashCreated(address stash, uint256 stashVersion);
32 |
33 | /**
34 | * @param _operator Operator is Booster
35 | * @param _rewardFactory Factory that creates reward contract that are
36 | * VirtualBalanceRewardPool's used for extra pool rewards
37 | * @param _proxyFactory Deploy proxies with stash implementation
38 | */
39 | constructor(address _operator, address _rewardFactory, address _proxyFactory) public {
40 | operator = _operator;
41 | rewardFactory = _rewardFactory;
42 | proxyFactory = _proxyFactory;
43 | }
44 |
45 | function setImplementation(address _v1, address _v2, address _v3) external{
46 | require(msg.sender == IDeposit(operator).owner(),"!auth");
47 |
48 | v1Implementation = _v1;
49 | v2Implementation = _v2;
50 | v3Implementation = _v3;
51 | }
52 |
53 | //Create a stash contract for the given gauge.
54 | //function calls are different depending on the version of curve gauges so determine which stash type is needed
55 | function CreateStash(uint256 _pid, address _gauge, address _staker, uint256 _stashVersion) external returns(address){
56 | require(msg.sender == operator, "!authorized");
57 |
58 | if(_stashVersion == uint256(3) && IsV3(_gauge)){
59 | //v3
60 | require(v3Implementation!=address(0),"0 impl");
61 | address stash = IProxyFactory(proxyFactory).clone(v3Implementation);
62 | IStash(stash).initialize(_pid,operator,_staker,_gauge,rewardFactory);
63 | emit StashCreated(stash, _stashVersion);
64 | return stash;
65 | }else if(_stashVersion == uint256(1) && IsV1(_gauge)){
66 | //v1
67 | require(v1Implementation!=address(0),"0 impl");
68 | address stash = IProxyFactory(proxyFactory).clone(v1Implementation);
69 | IStash(stash).initialize(_pid,operator,_staker,_gauge,rewardFactory);
70 | emit StashCreated(stash, _stashVersion);
71 | return stash;
72 | }else if(_stashVersion == uint256(2) && !IsV3(_gauge) && IsV2(_gauge)){
73 | //v2
74 | require(v2Implementation!=address(0),"0 impl");
75 | address stash = IProxyFactory(proxyFactory).clone(v2Implementation);
76 | IStash(stash).initialize(_pid,operator,_staker,_gauge,rewardFactory);
77 | emit StashCreated(stash, _stashVersion);
78 | return stash;
79 | }
80 | bool isV1 = IsV1(_gauge);
81 | bool isV2 = IsV2(_gauge);
82 | bool isV3 = IsV3(_gauge);
83 | require(!isV1 && !isV2 && !isV3,"stash version mismatch");
84 | return address(0);
85 | }
86 |
87 | function IsV1(address _gauge) private returns(bool){
88 | bytes memory data = abi.encode(rewarded_token);
89 | (bool success,) = _gauge.call(data);
90 | return success;
91 | }
92 |
93 | function IsV2(address _gauge) private returns(bool){
94 | bytes memory data = abi.encodeWithSelector(reward_tokens,uint256(0));
95 | (bool success,) = _gauge.call(data);
96 | return success;
97 | }
98 |
99 | function IsV3(address _gauge) private returns(bool){
100 | bytes memory data = abi.encodeWithSelector(rewards_receiver,address(0));
101 | (bool success,) = _gauge.call(data);
102 | return success;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/TokenFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "./DepositToken.sol";
6 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
7 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
8 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
9 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
10 |
11 | /**
12 | * @title TokenFactory
13 | * @author ConvexFinance
14 | * @notice Token factory used to create Deposit Tokens. These are the tokenized
15 | * pool deposit tokens e.g cvx3crv
16 | */
17 | contract TokenFactory {
18 | using Address for address;
19 |
20 | address public immutable operator;
21 | string public namePostfix;
22 | string public symbolPrefix;
23 |
24 | event DepositTokenCreated(address token, address lpToken);
25 |
26 | /**
27 | * @param _operator Operator is Booster
28 | * @param _namePostfix Postfixes lpToken name
29 | * @param _symbolPrefix Prefixed lpToken symbol
30 | */
31 | constructor(
32 | address _operator,
33 | string memory _namePostfix,
34 | string memory _symbolPrefix
35 | ) public {
36 | operator = _operator;
37 | namePostfix = _namePostfix;
38 | symbolPrefix = _symbolPrefix;
39 | }
40 |
41 | function CreateDepositToken(address _lptoken) external returns(address){
42 | require(msg.sender == operator, "!authorized");
43 |
44 | DepositToken dtoken = new DepositToken(operator,_lptoken,namePostfix,symbolPrefix);
45 | emit DepositTokenCreated(address(dtoken), _lptoken);
46 | return address(dtoken);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/cCrv.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import "./Interfaces.sol";
5 | import "@openzeppelin/contracts-0.6/math/SafeMath.sol";
6 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
7 | import "@openzeppelin/contracts-0.6/utils/Address.sol";
8 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
9 | import "@openzeppelin/contracts-0.6/token/ERC20/ERC20.sol";
10 |
11 |
12 | /**
13 | * @title cvxCrvToken
14 | * @author ConvexFinance
15 | * @notice Dumb ERC20 token that allows the operator (crvDepositor) to mint and burn tokens
16 | */
17 | contract cvxCrvToken is ERC20 {
18 | using SafeERC20 for IERC20;
19 | using Address for address;
20 | using SafeMath for uint256;
21 |
22 | address public operator;
23 |
24 | constructor(string memory _nameArg, string memory _symbolArg)
25 | public
26 | ERC20(
27 | _nameArg,
28 | _symbolArg
29 | )
30 | {
31 | operator = msg.sender;
32 | }
33 |
34 | /**
35 | * @notice Allows the initial operator (deployer) to set the operator.
36 | * Note - crvDepositor has no way to change this back, so it's effectively immutable
37 | */
38 | function setOperator(address _operator) external {
39 | require(msg.sender == operator, "!auth");
40 | operator = _operator;
41 | }
42 |
43 | /**
44 | * @notice Allows the crvDepositor to mint
45 | */
46 | function mint(address _to, uint256 _amount) external {
47 | require(msg.sender == operator, "!authorized");
48 |
49 | _mint(_to, _amount);
50 | }
51 |
52 | /**
53 | * @notice Allows the crvDepositor to burn
54 | */
55 | function burn(address _from, uint256 _amount) external {
56 | require(msg.sender == operator, "!authorized");
57 |
58 | _burn(_from, _amount);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/BoringMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | /// @notice A library for performing overflow-/underflow-safe math,
5 | /// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
6 | library BoringMath {
7 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
8 | require((c = a + b) >= b, "BoringMath: Add Overflow");
9 | }
10 |
11 | function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
12 | require((c = a - b) <= a, "BoringMath: Underflow");
13 | }
14 |
15 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
16 | require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
17 | }
18 |
19 | function div(uint256 a, uint256 b) internal pure returns (uint256) {
20 | require(b > 0, "BoringMath: division by zero");
21 | return a / b;
22 | }
23 |
24 | function to128(uint256 a) internal pure returns (uint128 c) {
25 | require(a <= uint128(-1), "BoringMath: uint128 Overflow");
26 | c = uint128(a);
27 | }
28 |
29 | function to64(uint256 a) internal pure returns (uint64 c) {
30 | require(a <= uint64(-1), "BoringMath: uint64 Overflow");
31 | c = uint64(a);
32 | }
33 |
34 | function to32(uint256 a) internal pure returns (uint32 c) {
35 | require(a <= uint32(-1), "BoringMath: uint32 Overflow");
36 | c = uint32(a);
37 | }
38 |
39 | function to40(uint256 a) internal pure returns (uint40 c) {
40 | require(a <= uint40(-1), "BoringMath: uint40 Overflow");
41 | c = uint40(a);
42 | }
43 |
44 | function to112(uint256 a) internal pure returns (uint112 c) {
45 | require(a <= uint112(-1), "BoringMath: uint112 Overflow");
46 | c = uint112(a);
47 | }
48 |
49 | function to224(uint256 a) internal pure returns (uint224 c) {
50 | require(a <= uint224(-1), "BoringMath: uint224 Overflow");
51 | c = uint224(a);
52 | }
53 |
54 | function to208(uint256 a) internal pure returns (uint208 c) {
55 | require(a <= uint208(-1), "BoringMath: uint208 Overflow");
56 | c = uint208(a);
57 | }
58 |
59 | function to216(uint256 a) internal pure returns (uint216 c) {
60 | require(a <= uint216(-1), "BoringMath: uint216 Overflow");
61 | c = uint216(a);
62 | }
63 | }
64 |
65 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
66 | library BoringMath128 {
67 | function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
68 | require((c = a + b) >= b, "BoringMath: Add Overflow");
69 | }
70 |
71 | function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
72 | require((c = a - b) <= a, "BoringMath: Underflow");
73 | }
74 | }
75 |
76 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
77 | library BoringMath64 {
78 | function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
79 | require((c = a + b) >= b, "BoringMath: Add Overflow");
80 | }
81 |
82 | function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
83 | require((c = a - b) <= a, "BoringMath: Underflow");
84 | }
85 | }
86 |
87 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
88 | library BoringMath32 {
89 | function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
90 | require((c = a + b) >= b, "BoringMath: Add Overflow");
91 | }
92 |
93 | function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
94 | require((c = a - b) <= a, "BoringMath: Underflow");
95 | }
96 |
97 | function mul(uint32 a, uint32 b) internal pure returns (uint32 c) {
98 | require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
99 | }
100 |
101 | function div(uint32 a, uint32 b) internal pure returns (uint32) {
102 | require(b > 0, "BoringMath: division by zero");
103 | return a / b;
104 | }
105 | }
106 |
107 |
108 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint112.
109 | library BoringMath112 {
110 | function add(uint112 a, uint112 b) internal pure returns (uint112 c) {
111 | require((c = a + b) >= b, "BoringMath: Add Overflow");
112 | }
113 |
114 | function sub(uint112 a, uint112 b) internal pure returns (uint112 c) {
115 | require((c = a - b) <= a, "BoringMath: Underflow");
116 | }
117 |
118 | function mul(uint112 a, uint112 b) internal pure returns (uint112 c) {
119 | require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
120 | }
121 |
122 | function div(uint112 a, uint112 b) internal pure returns (uint112) {
123 | require(b > 0, "BoringMath: division by zero");
124 | return a / b;
125 | }
126 | }
127 |
128 | /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint224.
129 | library BoringMath224 {
130 | function add(uint224 a, uint224 b) internal pure returns (uint224 c) {
131 | require((c = a + b) >= b, "BoringMath: Add Overflow");
132 | }
133 |
134 | function sub(uint224 a, uint224 b) internal pure returns (uint224 c) {
135 | require((c = a - b) <= a, "BoringMath: Underflow");
136 | }
137 |
138 | function mul(uint224 a, uint224 b) internal pure returns (uint224 c) {
139 | require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
140 | }
141 |
142 | function div(uint224 a, uint224 b) internal pure returns (uint224) {
143 | require(b > 0, "BoringMath: division by zero");
144 | return a / b;
145 | }
146 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/IERC20Metadata.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | import { IERC20 } from "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
5 |
6 | /**
7 | * @dev Interface for the optional metadata functions from the ERC20 standard.
8 | *
9 | * _Available since v4.1._
10 | */
11 | interface IERC20Metadata is IERC20 {
12 | /**
13 | * @dev Returns the name of the token.
14 | */
15 | function name() external view returns (string memory);
16 |
17 | /**
18 | * @dev Returns the symbol of the token.
19 | */
20 | function symbol() external view returns (string memory);
21 |
22 | /**
23 | * @dev Returns the decimals places of the token.
24 | */
25 | function decimals() external view returns (uint8);
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/IGaugeController.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity 0.6.12;
4 |
5 | interface IGaugeController {
6 | function get_gauge_weight(address _gauge) external view returns(uint256);
7 | function vote_user_slopes(address,address) external view returns(uint256,uint256,uint256);//slope,power,end
8 | function vote_for_gauge_weights(address,uint256) external;
9 | function add_gauge(address,int128,uint256) external;
10 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/IProxyFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | interface IProxyFactory {
5 | function clone(address _target) external returns(address);
6 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/IRewardHook.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity 0.6.12;
4 |
5 | interface IRewardHook {
6 | function onRewardClaim() external;
7 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/IRewarder.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity 0.6.12;
4 | import "@openzeppelin/contracts-0.6/token/ERC20/IERC20.sol";
5 | import "@openzeppelin/contracts-0.6/token/ERC20/SafeERC20.sol";
6 |
7 | interface IRewarder {
8 | using SafeERC20 for IERC20;
9 | function onReward(uint256 pid, address user, address recipient, uint256 sushiAmount, uint256 newLpAmount) external;
10 | function pendingTokens(uint256 pid, address user, uint256 sushiAmount) external view returns (IERC20[] memory, uint256[] memory);
11 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/contracts/interfaces/MathUtil.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.6.12;
3 |
4 | /**
5 | * @dev Standard math utilities missing in the Solidity language.
6 | */
7 | library MathUtil {
8 | /**
9 | * @dev Returns the smallest of two numbers.
10 | */
11 | function min(uint256 a, uint256 b) internal pure returns (uint256) {
12 | return a < b ? a : b;
13 | }
14 | }
--------------------------------------------------------------------------------
/convex-platform/contracts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "deploy": "truffle migrate --network ganachecli --skip-dry-run",
4 | "compile": "truffle compile"
5 | },
6 | "dependencies": {
7 | "@openzeppelin/contracts-0.6": "npm:@openzeppelin/contracts@3.4.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/0-EB806.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/0-EB806.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/0ab6fddedfdca4dee0b54ab41ab4faad-C5611.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/0ab6fddedfdca4dee0b54ab41ab4faad-C5611.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1-B2132.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1-B2132.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f389-5C738.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f3c1-445DC.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f3c6-621A1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f43a-EB486.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f440-6C64D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f448-3DF32.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f449-25CFB.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f44b-8A059.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f44c-59547.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f44d-27259.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f48c-A96CC.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f49c-71A75.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f4aa-2FD27.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f4af-4CFF5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f4b8-E3468.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f4c6-44E30.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f4dc-AC641.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f50d-195C0.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f512-24F85.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f604-BF863.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f60d-BEAFF.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f626-91074.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f642-83E8A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f64c-7C820.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f64f-22B8D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f680-A35CE.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f6a8-A8AB3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f7e2-FC8EC.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f911-F346C.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f91e-2A114.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f972-F415D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f9be-DDCD0.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1f9d1-5BC80.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/1fac2-960B6.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/20c8931564dd11b412a4fecc64bf9451-26B4C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/20c8931564dd11b412a4fecc64bf9451-26B4C.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/23a7e58bdc28d6ad048874c84ad5fdb1-DE264.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/23a7e58bdc28d6ad048874c84ad5fdb1-DE264.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/2665-8CDCE.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/2696-15F4A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/2a9faff195fe333526cfe6ae6fce1420-927E9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/2a9faff195fe333526cfe6ae6fce1420-927E9.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/423a533afdf07948f23af9a1ebceec37-235E9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/423a533afdf07948f23af9a1ebceec37-235E9.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5-C387C:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5-C387C
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5-E9BDB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5-E9BDB.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/568d22b97293cdd2d9b7006198d6adbc-9738F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/568d22b97293cdd2d9b7006198d6adbc-9738F.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5960c5502dd09d4875482231f43edbc5-F05FC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/5960c5502dd09d4875482231f43edbc5-F05FC.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/67594ee4b4d1fc03bca468327a0d145b-0C614.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/67594ee4b4d1fc03bca468327a0d145b-0C614.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/781415a7ef0ff20e2f8e85348fbd64c1-5F911.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/781415a7ef0ff20e2f8e85348fbd64c1-5F911.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/7fcd80e4d3c60a600235b03c75f682a9-75033.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/7fcd80e4d3c60a600235b03c75f682a9-75033.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893157188599838-C23B5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893157188599838-C23B5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893826846457866-1AD3D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893826846457866-1AD3D.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893827089727568-5FD38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893827089727568-5FD38.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893827315826708-F59C0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/851893827315826708-F59C0.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/910676187288846397-518CD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/910676187288846397-518CD.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/C4-banner-7C19B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/C4-banner-7C19B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/FSB_4THVcAAXrSe-D0ED9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/FSB_4THVcAAXrSe-D0ED9.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/Screenshot_20220523_113543_com.android.chr-1C166.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/Screenshot_20220523_113543_com.android.chr-1C166.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/TKrF4ZUY_400x400-5F957.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/TKrF4ZUY_400x400-5F957.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/a3543456a09e49cb364c91b15541774a-203F3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/a3543456a09e49cb364c91b15541774a-203F3.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ae70f07a06c1c7e983291bb14a1bed7f-B8F4A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ae70f07a06c1c7e983291bb14a1bed7f-B8F4A.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/apple-touch-icon-192x192-E344C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/apple-touch-icon-192x192-E344C.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/b07292365785e0b7bd0dab7dd7d9e09f-62888.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/b07292365785e0b7bd0dab7dd7d9e09f-62888.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/c9cb30134c634c9e02d0c64df4922803-1AAF2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/c9cb30134c634c9e02d0c64df4922803-1AAF2.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/fab1b54617157de0fdc01d6d30bc68b4-901C1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/fab1b54617157de0fdc01d6d30bc68b4-901C1.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-400-E988B.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-400-E988B.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-500-0777F.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-500-0777F.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-600-CB411.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-600-CB411.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-700-891AC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-700-891AC.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-800-D36B0.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-italic-800-D36B0.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-400-1456D.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-400-1456D.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-500-89CE5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-500-89CE5.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-600-C1EA8.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-600-C1EA8.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-700-1949A.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-700-1949A.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-800-58487.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/ggsans-normal-800-58487.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/solarized-dark.min-BA98F.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496}.hljs-comment,.hljs-quote{color:#586e75}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/unknown-D6C62.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].html_Files/unknown-D6C62.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/5-C387C:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/5-C387C
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/83ffc16bfc2260f3df5a5a3ce44e59ab2c5de5e3_2-4FEEE.jpeg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/C4-banner-7C19B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/C4-banner-7C19B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/FSB_4THVcAAXrSe-D0ED9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/FSB_4THVcAAXrSe-D0ED9.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/Screenshot_20220523_113543_com.android.chr-1C166.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/Screenshot_20220523_113543_com.android.chr-1C166.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/eecc78c5-e4c8-4746-b464-3f4a2332c79b-1832D
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/unknown-D6C62.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-05-aura/e64e72526db40f6a1b5c7ebe36e17cbb71dc9b64/discord-export/Code4rena - ARCHIVE-Q2-2022 - ☑aura-may11 [973316471793025054].txt_Files/unknown-D6C62.png
--------------------------------------------------------------------------------
/hardhat-cvx.config.ts:
--------------------------------------------------------------------------------
1 | import hardhatConfig from "./hardhat.config";
2 |
3 | export default {
4 | ...hardhatConfig,
5 | defaultNetwork: "hardhat",
6 | gasReporter: {
7 | ...hardhatConfig.gasReporter,
8 | src: "./contracts",
9 | },
10 | paths: {
11 | ...hardhatConfig.paths,
12 | sources: "./convex-platform/contracts/contracts",
13 | },
14 | solidity: {
15 | version: "0.6.12",
16 | settings: {
17 | optimizer: {
18 | enabled: true,
19 | runs: 200,
20 | },
21 | },
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/hardhat-fork.config.ts:
--------------------------------------------------------------------------------
1 | import hardhatConfig from "./hardhat.config";
2 |
3 | export default {
4 | ...hardhatConfig,
5 | networks: {
6 | ...hardhatConfig.networks,
7 | hardhat: {
8 | allowUnlimitedContractSize: false,
9 | forking: {
10 | url: process.env.NODE_URL || "",
11 | },
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import "@nomiclabs/hardhat-ethers";
2 | import "@nomiclabs/hardhat-waffle";
3 | import "@typechain/hardhat";
4 | import "hardhat-gas-reporter";
5 | import "solidity-coverage";
6 | import "@nomiclabs/hardhat-etherscan";
7 |
8 | import { resolve } from "path";
9 |
10 | import { config as dotenvConfig } from "dotenv";
11 | import { HardhatUserConfig } from "hardhat/config";
12 |
13 | dotenvConfig({ path: resolve(__dirname, "./.env") });
14 |
15 | const chainIds = {
16 | goerli: 5,
17 | hardhat: 31337,
18 | kovan: 42,
19 | mainnet: 1,
20 | rinkeby: 4,
21 | ropsten: 3,
22 | };
23 |
24 | const compilerSettings = {
25 | metadata: {
26 | // Not including the metadata hash
27 | // https://github.com/paulrberg/solidity-template/issues/31
28 | bytecodeHash: "none",
29 | },
30 | // Disable the optimizer when debugging
31 | // https://hardhat.org/hardhat-network/#solidity-optimizer-support
32 | optimizer: {
33 | enabled: true,
34 | runs: 800,
35 | },
36 | };
37 |
38 | const config: HardhatUserConfig = {
39 | defaultNetwork: "hardhat",
40 | gasReporter: {
41 | currency: "USD",
42 | enabled: true,
43 | excludeContracts: [],
44 | src: "./contracts",
45 | },
46 | networks: {
47 | hardhat: {
48 | chainId: chainIds.hardhat,
49 | allowUnlimitedContractSize: true,
50 | },
51 | mainnet: {
52 | url: process.env.NODE_URL || "",
53 | },
54 | kovan: {
55 | url: process.env.NODE_URL || "",
56 | gasPrice: 3000000000,
57 | },
58 | forking: {
59 | url: process.env.NODE_URL || "",
60 | },
61 | rinkeby: { url: process.env.NODE_URL || "", gasPrice: 3000000000 },
62 | },
63 | paths: {
64 | artifacts: "./artifacts",
65 | cache: "./cache",
66 | sources: "./contracts",
67 | tests: "./test",
68 | },
69 | solidity: {
70 | compilers: [
71 | {
72 | version: "0.6.12",
73 | settings: compilerSettings,
74 | },
75 | {
76 | version: "0.8.11",
77 | settings: compilerSettings,
78 | },
79 | ],
80 | },
81 | typechain: {
82 | outDir: "types/generated",
83 | target: "ethers-v5",
84 | },
85 | etherscan: {
86 | apiKey: process.env.ETHERSCAN_KEY,
87 | },
88 | };
89 |
90 | export default config;
91 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | // Export functions from the package/module
2 | export * from "./test-utils";
3 |
4 | // Export types
5 | export * from "./types";
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aura-contracts-lite",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "compile": "yarn compile:cvx && yarn compile:aura && yarn run compile:ts",
7 | "compile:aura": "yarn hardhat compile --config hardhat.config.ts",
8 | "compile:cvx": "yarn hardhat compile --config hardhat-cvx.config.ts",
9 | "compile:ts": "npx tsc",
10 | "lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
11 | "lint:sol": "solhint --config ./.solhint.json \"contracts/**/*.sol\"",
12 | "lint:ts": "eslint --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .",
13 | "prettier": "prettier --config ./.prettierrc.yaml --write \"**/*.{js,json,md,sol,ts}\"",
14 | "prettier:check": "prettier --check --config ./.prettierrc.yaml \"**/*.{js,json,md,sol,ts}\"",
15 | "test": "hardhat test",
16 | "test:fork": "yarn hardhat --config hardhat-fork.config.ts test",
17 | "test:fork:all": "yarn hardhat --config hardhat-fork.config.ts test ./test-fork/*.spec.ts",
18 | "typechain": "yarn hardhat typechain"
19 | },
20 | "author": "",
21 | "license": "ISC",
22 | "devDependencies": {
23 | "@balancer-labs/balancer-js": "^1.0.1",
24 | "@codechecks/client": "^0.1.12",
25 | "@commitlint/cli": "^14.1.0",
26 | "@commitlint/config-conventional": "^14.1.0",
27 | "@ethersproject/abi": "5.5.0",
28 | "@ethersproject/abstract-signer": "5.5.0",
29 | "@ethersproject/bignumber": "5.5.0",
30 | "@ethersproject/bytes": "5.5.0",
31 | "@ethersproject/hash": "5.5.0",
32 | "@ethersproject/providers": "5.5.0",
33 | "@nomiclabs/hardhat-ethers": "2.0.5",
34 | "@nomiclabs/hardhat-etherscan": "^3.0.3",
35 | "@nomiclabs/hardhat-waffle": "^2.0.1",
36 | "@openzeppelin/contracts-0.6": "npm:@openzeppelin/contracts@3.4.0",
37 | "@openzeppelin/contracts-0.8": "npm:@openzeppelin/contracts@4.4.2",
38 | "@snapshot-labs/snapshot.js": "^0.3.53",
39 | "@typechain/ethers-v5": "^8.0.5",
40 | "@typechain/hardhat": "^3.0.0",
41 | "@types/chai": "^4.2.22",
42 | "@types/fs-extra": "^9.0.13",
43 | "@types/mocha": "^9.0.0",
44 | "@types/node": "^16.11.10",
45 | "@typescript-eslint/eslint-plugin": "^5.4.0",
46 | "@typescript-eslint/parser": "^5.4.0",
47 | "chai": "^4.3.4",
48 | "commitizen": "^4.2.4",
49 | "cross-env": "^7.0.3",
50 | "cz-conventional-changelog": "^3.3.0",
51 | "diff2html": "^3.4.16",
52 | "diffchecker": "^0.2.8",
53 | "dotenv": "^10.0.0",
54 | "eslint": "^8.3.0",
55 | "eslint-config-prettier": "^8.3.0",
56 | "ethereum-waffle": "^3.4.0",
57 | "ethers": "5.5.4",
58 | "fs-extra": "^10.0.0",
59 | "hardhat": "2.9.2",
60 | "hardhat-gas-reporter": "^1.0.4",
61 | "husky": "^7.0.4",
62 | "koa": "^2.13.4",
63 | "lint-staged": "^11.2.6",
64 | "merkletreejs": "^0.2.31",
65 | "mocha": "^9.1.3",
66 | "node-jq": "^2.3.3",
67 | "pinst": "^2.1.6",
68 | "prettier": "^2.6.2",
69 | "prettier-plugin-solidity": "^1.0.0-beta.19",
70 | "shelljs": "^0.8.4",
71 | "shx": "^0.3.3",
72 | "solhint": "^3.3.7",
73 | "solhint-plugin-prettier": "^0.0.5",
74 | "solidity-coverage": "^0.7.17",
75 | "ts-generator": "^0.1.1",
76 | "ts-node": "^10.4.0",
77 | "typechain": "^6.0.5",
78 | "typescript": "^4.5.2"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tasks/deploy/index.ts:
--------------------------------------------------------------------------------
1 | import "./mainnet-config";
2 |
--------------------------------------------------------------------------------
/tasks/index.ts:
--------------------------------------------------------------------------------
1 | import "./deploy";
2 |
--------------------------------------------------------------------------------
/tasks/utils/deploy-utils.ts:
--------------------------------------------------------------------------------
1 | import { HardhatRuntimeEnvironment } from "hardhat/types";
2 | import { Contract, ContractFactory, ContractReceipt, ContractTransaction, Overrides } from "ethers";
3 | import { formatUnits } from "@ethersproject/units";
4 | import { ExtSystemConfig } from "../../scripts/deploySystem";
5 |
6 | export const deployContract = async (
7 | hre: HardhatRuntimeEnvironment,
8 | contractFactory: ContractFactory,
9 | contractName = "Contract",
10 | constructorArgs: Array = [],
11 | overrides: Overrides = {},
12 | debug = true,
13 | waitForBlocks = undefined,
14 | ): Promise => {
15 | const contract = (await contractFactory.deploy(...constructorArgs, overrides)) as T;
16 | if (debug) {
17 | console.log(
18 | `\nDeploying ${contractName} contract with hash ${contract.deployTransaction.hash} from ${
19 | contract.deployTransaction.from
20 | } with gas price ${contract.deployTransaction.gasPrice?.toNumber() || 0 / 1e9} Gwei`,
21 | );
22 | }
23 | const receipt = await contract.deployTransaction.wait(waitForBlocks);
24 | const txCost = receipt.gasUsed.mul(contract.deployTransaction.gasPrice || 0);
25 | const abiEncodedConstructorArgs = contract.interface.encodeDeploy(constructorArgs);
26 |
27 | if (debug) {
28 | console.log(
29 | `\nDeployed ${contractName} to ${contract.address} in block ${receipt.blockNumber}, using ${
30 | receipt.gasUsed
31 | } gas costing ${formatUnits(txCost)} ETH`,
32 | );
33 | console.log(`ABI encoded args: ${abiEncodedConstructorArgs.slice(2)}`);
34 | }
35 |
36 | // await verifyEtherscan(hre, {
37 | // address: contract.address,
38 | // constructorArguments: constructorArgs,
39 | // });
40 |
41 | return contract;
42 | };
43 |
44 | export const logTxDetails = async (tx: ContractTransaction, method: string): Promise => {
45 | console.log(
46 | `Sent ${method} transaction with hash ${tx.hash} from ${tx.from} with gas price ${
47 | tx.gasPrice?.toNumber() || 0 / 1e9
48 | } Gwei`,
49 | );
50 | const receipt = await tx.wait();
51 |
52 | // Calculate tx cost in Wei
53 | const txCost = receipt.gasUsed.mul(tx.gasPrice ?? 0);
54 | console.log(
55 | `Processed ${method} tx in block ${receipt.blockNumber}, using ${receipt.gasUsed} gas costing ${formatUnits(
56 | txCost,
57 | )} ETH`,
58 | );
59 |
60 | return receipt;
61 | };
62 |
63 | export function logExtSystem(system: ExtSystemConfig) {
64 | const keys = Object.keys(system);
65 | console.log(`\n~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
66 | console.log(`~~~~~~~ EXT SYSTEM ~~~~~~~`);
67 | console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~\n`);
68 | keys.map(k => {
69 | console.log(`${k}:\t${system[k]}`);
70 | });
71 | }
72 |
73 | export function logContracts(contracts: { [key: string]: { address: string } }) {
74 | const keys = Object.keys(contracts);
75 | console.log(`\n~~~~~~~~~~~~~~~~~~~~~~~~~~~`);
76 | console.log(`~~~~ SYSTEM DEPLOYMENT ~~~~`);
77 | console.log(`~~~~~~~~~~~~~~~~~~~~~~~~~~~\n`);
78 | keys.map(k => {
79 | if (Array.isArray(contracts[k])) {
80 | console.log(`${k}:\t[${(contracts[k] as any as [{ address: string }]).map(i => i.address)}]`);
81 | } else {
82 | console.log(`${k}:\t${contracts[k].address}`);
83 | }
84 | });
85 | }
86 |
87 | export async function waitForTx(
88 | tx: ContractTransaction,
89 | debug = false,
90 | waitForBlocks = undefined,
91 | ): Promise {
92 | const receipt = await tx.wait(waitForBlocks);
93 | if (debug) {
94 | console.log(`\nTRANSACTION: ${receipt.transactionHash}`);
95 | console.log(`to:: ${tx.to}`);
96 | console.log(`txData:: ${tx.data}`);
97 | }
98 | return receipt;
99 | }
100 |
--------------------------------------------------------------------------------
/tasks/utils/etherscan.ts:
--------------------------------------------------------------------------------
1 | import { HardhatRuntimeEnvironment } from "hardhat/types";
2 |
3 | interface VerifyEtherscan {
4 | address: string;
5 | contract?: string;
6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
7 | constructorArguments?: any[];
8 | libraries?: {
9 | [libraryName: string]: string;
10 | };
11 | }
12 |
13 | export const verifyEtherscan = async (hre: HardhatRuntimeEnvironment, contract: VerifyEtherscan): Promise => {
14 | if (
15 | hre.network.name == "mainnet" &&
16 | !["tasks-fork.config.ts", "hardhat-fork.config.ts"].includes(hre.hardhatArguments.config)
17 | ) {
18 | console.log(`About to verify ${contract.address} on Etherscan`);
19 | await hre.run("verify:verify", contract);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/tasks/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./signerFactory";
2 | export * from "./deploy-utils";
3 | export * from "./tokens";
4 | export * from "./etherscan";
5 |
--------------------------------------------------------------------------------
/tasks/utils/signerFactory.ts:
--------------------------------------------------------------------------------
1 | import { Signer, Wallet } from "ethers";
2 | import { ethereumAddress, privateKey } from "../../test-utils/regex";
3 | import { impersonate } from "../../test-utils/fork";
4 | import { Account } from "types";
5 | import { getChain, getChainAddress, HardhatRuntime, resolveAddress } from "./networkAddressFactory";
6 |
7 | let signerInstance: Signer;
8 |
9 | export const getSigner = async (hre: HardhatRuntime = {}, useCache = true, key?: string): Promise => {
10 | // If already initiated a signer, just return the singleton instance
11 | if (useCache && signerInstance) return signerInstance;
12 |
13 | const pk = key || process.env.PRIVATE_KEY;
14 | if (pk) {
15 | if (!pk.match(privateKey)) {
16 | throw Error(`Invalid format of private key`);
17 | }
18 | const wallet = new Wallet(pk, hre.ethers.provider);
19 | console.log(`Using signer ${await wallet.getAddress()} from private key`);
20 | return wallet;
21 | }
22 |
23 | // If connecting to a forked chain
24 | if (["tasks-fork.config.ts", "hardhat-fork.config.ts"].includes(hre?.hardhatArguments.config)) {
25 | const chain = getChain(hre);
26 | // If IMPERSONATE environment variable has been set
27 | if (process.env.IMPERSONATE) {
28 | let address = process.env.IMPERSONATE;
29 | if (!address.match(ethereumAddress)) {
30 | address = resolveAddress(process.env.IMPERSONATE, chain);
31 | if (!address)
32 | throw Error(`Environment variable IMPERSONATE is an invalid Ethereum address or contract name`);
33 | }
34 | console.log(`Impersonating account ${address} from IMPERSONATE environment variable`);
35 | signerInstance = await impersonate(address);
36 | return signerInstance;
37 | }
38 | const address = getChainAddress("Deployer", chain);
39 | if (address) {
40 | console.log(`Impersonating account ${address} resolved from "Deployer"`);
41 | signerInstance = await impersonate(address);
42 | return signerInstance;
43 | }
44 | // Return a random account with no Ether
45 | signerInstance = Wallet.createRandom().connect(hre.ethers.provider);
46 | console.log(`Impersonating random account ${await signerInstance.getAddress()}`);
47 | return signerInstance;
48 | }
49 |
50 | // Return a random account with no Ether.
51 | // This is typically used for readonly tasks. eg reports
52 | signerInstance = Wallet.createRandom().connect(hre.ethers.provider);
53 | return signerInstance;
54 | };
55 |
56 | export const getSignerAccount = async (hre: HardhatRuntime = {}): Promise => {
57 | const signer = await getSigner(hre);
58 | return {
59 | signer,
60 | address: await signer.getAddress(),
61 | };
62 | };
63 |
--------------------------------------------------------------------------------
/tasks/utils/tokens.ts:
--------------------------------------------------------------------------------
1 | export enum Chain {
2 | mainnet,
3 | rinkeby,
4 | kovan,
5 | local,
6 | }
7 |
8 | export interface Token {
9 | symbol: string;
10 | address: string;
11 | chain: Chain;
12 | decimals: number;
13 | }
14 |
15 | export const assetAddressTypes = ["address"] as const;
16 | export type AssetAddressTypes = typeof assetAddressTypes[number];
17 |
18 | export const tokens: Array = [];
19 |
--------------------------------------------------------------------------------
/test-fork/BalEthSwap.spec.ts:
--------------------------------------------------------------------------------
1 | import { simpleToExactAmount } from "../test-utils/math";
2 | import hre, { ethers, network } from "hardhat";
3 | import { expect } from "chai";
4 | import { MockBalInvestor, MockBalInvestor__factory, ERC20__factory, ERC20 } from "../types/generated";
5 | import { deployContract } from "../tasks/utils";
6 | import { impersonateAccount, fullScale } from "../test-utils";
7 | import { Signer } from "ethers";
8 |
9 | const debug = false;
10 |
11 | const ALCHEMY_API_KEY = process.env.NODE_URL;
12 |
13 | const BALWhale = "0xff052381092420b7f24cc97fded9c0c17b2cbbb9";
14 |
15 | describe("TestBalEth", () => {
16 | let testEthBal: MockBalInvestor;
17 | let balToken: ERC20;
18 | let signer: Signer;
19 |
20 | const amount = ethers.utils.parseEther("100");
21 |
22 | before(async () => {
23 | await network.provider.request({
24 | method: "hardhat_reset",
25 | params: [
26 | {
27 | forking: {
28 | jsonRpcUrl: ALCHEMY_API_KEY,
29 | blockNumber: 14370000,
30 | },
31 | },
32 | ],
33 | });
34 |
35 | await impersonateAccount(BALWhale);
36 |
37 | signer = await ethers.getSigner(BALWhale);
38 |
39 | const poolId = "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014";
40 | const vault = "0xba12222222228d8ba445958a75a0704d566bf2c8";
41 | const weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
42 | const bal = "0xba100000625a3754423978a60c9317c58a424e3D";
43 |
44 | balToken = ERC20__factory.connect(bal, signer);
45 |
46 | testEthBal = await deployContract(
47 | hre,
48 | new MockBalInvestor__factory(signer),
49 | "testEthBal",
50 | [vault, bal, weth, poolId],
51 | {},
52 | debug,
53 | );
54 | });
55 |
56 | describe("join BAL:ETH 80/20 pool with BAL", () => {
57 | it("transfer BAL to contract", async () => {
58 | const tx = await balToken.approve(testEthBal.address, amount);
59 | await tx.wait();
60 | });
61 |
62 | it("add BAL to pool", async () => {
63 | const bptAddress = await testEthBal.BALANCER_POOL_TOKEN();
64 | const bpt = ERC20__factory.connect(bptAddress, signer);
65 |
66 | const bptBalanceBefore = await bpt.balanceOf(testEthBal.address);
67 |
68 | let tx = await testEthBal.approveToken();
69 | await tx.wait();
70 |
71 | const minOut = await testEthBal.getMinOut(amount, 9980);
72 | tx = await testEthBal.addBalToPool(amount.toString(), minOut);
73 | await tx.wait();
74 |
75 | const bptBalanceAfter = await bpt.balanceOf(testEthBal.address);
76 | const bptBalanceDelta = bptBalanceAfter.sub(bptBalanceBefore);
77 |
78 | const bptPrice = await testEthBal.getBptPrice();
79 |
80 | const bptBalValue = bptPrice.mul(bptBalanceDelta).div(fullScale);
81 | const minAmount = amount.mul("9950").div("10000");
82 | expect(bptBalValue).gt(minAmount);
83 | });
84 |
85 | it("fails if incorrect minout passed", async () => {
86 | const tx = await balToken.approve(testEthBal.address, amount);
87 | await tx.wait();
88 |
89 | let minOut = await testEthBal.getMinOut(amount, 10005);
90 |
91 | await expect(testEthBal.addBalToPool(amount.toString(), minOut)).to.be.revertedWith("BAL#208");
92 |
93 | minOut = await testEthBal.getMinOut(amount, 9980);
94 |
95 | await testEthBal.addBalToPool(amount.toString(), minOut);
96 | });
97 |
98 | it("fails if slippage not met (large deposit)", async () => {
99 | const tx = await balToken.approve(testEthBal.address, simpleToExactAmount(1, 24));
100 | await tx.wait();
101 |
102 | const minOut = await testEthBal.getMinOut(simpleToExactAmount(1, 24), 9980);
103 |
104 | await expect(testEthBal.addBalToPool(simpleToExactAmount(1, 24), minOut)).to.be.revertedWith("BAL#208");
105 | });
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/test-utils/assertions.ts:
--------------------------------------------------------------------------------
1 | import { assert } from "chai";
2 | import { BN, simpleToExactAmount } from "./math";
3 | import { fullScale } from "./constants";
4 |
5 | /**
6 | * Convenience method to assert that two BN.js instances are within 100 units of each other.
7 | * @param actual The BN.js instance you received
8 | * @param expected The BN.js amount you expected to receive, allowing a varience of +/- 10 units
9 | */
10 | export const assertBNClose = (
11 | actual: BN | string,
12 | expected: BN,
13 | variance: BN | number = BN.from(10),
14 | reason: string = null,
15 | ): void => {
16 | const actualBN = BN.from(actual);
17 | const actualDelta = actualBN.gt(expected) ? actualBN.sub(expected) : expected.sub(actualBN);
18 |
19 | const str = reason ? `\n\tReason: ${reason}\n\t${actualBN.toString()} vs ${expected.toString()}` : "";
20 | assert.ok(
21 | actualBN.gte(expected.sub(variance)),
22 | `Number is too small to be close (Delta between actual and expected is ${actualDelta.toString()}, but variance was only ${variance.toString()}${str}`,
23 | );
24 | assert.ok(
25 | actualBN.lte(expected.add(variance)),
26 | `Number is too large to be close (Delta between actual and expected is ${actualDelta.toString()}, but variance was only ${variance.toString()})${str}`,
27 | );
28 | };
29 |
30 | /**
31 | * Convenience method to assert that two BN.js instances are within 100 units of each other.
32 | * @param actual The BN.js instance you received
33 | * @param expected The BN.js amount you expected to receive, allowing a varience of +/- 10 units
34 | */
35 | export const assertBNClosePercent = (a: BN, b: BN, variance: string | number = "0.02", reason: string = null): void => {
36 | if (a.eq(b)) return;
37 | const varianceBN = simpleToExactAmount(variance.toString().substr(0, 6), 16);
38 | const diff = a.sub(b).abs().mul(2).mul(fullScale).div(a.add(b));
39 | const str = reason ? `\n\tReason: ${reason}\n\t${a.toString()} vs ${b.toString()}` : "";
40 | assert.ok(
41 | diff.lte(varianceBN),
42 | `Numbers exceed ${variance}% diff (Delta between a and b is ${diff.toString()}%, but variance was only ${varianceBN.toString()})${str}`,
43 | );
44 | };
45 |
46 | /**
47 | * Convenience method to assert that one BN.js instance is GTE the other
48 | * @param actual The BN.js instance you received
49 | * @param expected The operant to compare against
50 | */
51 | export const assertBnGte = (actual: BN, comparison: BN): void => {
52 | assert.ok(
53 | actual.gte(comparison),
54 | `Number must be GTE comparitor, got: ${actual.toString()}; comparitor: ${comparison.toString()}`,
55 | );
56 | };
57 |
58 | /**
59 | * Convenience method to assert that one BN.js number is eq to, or greater than an expected value by some small amount
60 | * @param actual The BN.js instance you received
61 | * @param equator The BN.js to equate to
62 | * @param maxActualShouldExceedExpected Upper limit for the growth
63 | * @param mustBeGreater Fail if the operands are equal
64 | */
65 | export const assertBNSlightlyGT = (
66 | actual: BN,
67 | equator: BN,
68 | maxActualShouldExceedExpected = BN.from(100),
69 | mustBeGreater = false,
70 | reason: string = null,
71 | ): void => {
72 | const actualDelta = actual.gt(equator) ? actual.sub(equator) : equator.sub(actual);
73 |
74 | const str = reason ? `\n\t${reason}\n\t${actual.toString()} vs ${equator.toString()}` : "";
75 |
76 | assert.ok(
77 | mustBeGreater ? actual.gt(equator) : actual.gte(equator),
78 | `Actual value should be greater than the expected value ${str}`,
79 | );
80 | assert.ok(
81 | actual.lte(equator.add(maxActualShouldExceedExpected)),
82 | `Actual value should not exceed ${maxActualShouldExceedExpected.toString()} units greater than expected. Variance was ${actualDelta.toString()} ${str}`,
83 | );
84 | };
85 |
86 | /**
87 | * Convenience method to assert that one BN.js number is eq to, or greater than an expected value by some small amount
88 | * @param actual The BN.js instance you received
89 | * @param equator The BN.js to equate to
90 | * @param maxActualShouldExceedExpected Percentage amount of increase, as a string (1% = 1)
91 | * @param mustBeGreater Fail if the operands are equal
92 | */
93 | export const assertBNSlightlyGTPercent = (
94 | actual: BN,
95 | equator: BN,
96 | maxPercentIncrease = "0.1",
97 | mustBeGreater = false,
98 | ): void => {
99 | const maxIncreaseBN = simpleToExactAmount(maxPercentIncrease, 16);
100 | const maxIncreaseUnits = equator.mul(maxIncreaseBN).div(fullScale);
101 | // const actualDelta = actual.gt(equator) ? actual.sub(equator) : equator.sub(actual);
102 |
103 | assert.ok(
104 | mustBeGreater ? actual.gt(equator) : actual.gte(equator),
105 | `Actual value should be greater than the expected value`,
106 | );
107 | assert.ok(
108 | actual.lte(equator.add(maxIncreaseUnits)),
109 | `Actual value should not exceed ${maxPercentIncrease}% greater than expected`,
110 | );
111 | };
112 |
--------------------------------------------------------------------------------
/test-utils/constants.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 | import { BigNumber as BN } from "ethers";
3 |
4 | export const fullScale: BN = BN.from(10).pow(18);
5 |
6 | export const DEFAULT_DECIMALS = 18;
7 |
8 | export const DEAD_ADDRESS = "0x0000000000000000000000000000000000000001";
9 | export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
10 | export const ZERO_KEY = "0x0000000000000000000000000000000000000000000000000000000000000000";
11 |
12 | export const MAX_UINT256 = BN.from(2).pow(256).sub(1);
13 | export const MAX_INT128 = BN.from(2).pow(127).sub(1);
14 | export const MIN_INT128 = BN.from(2).pow(127).mul(-1);
15 |
16 | export const ZERO = BN.from(0);
17 | export const ONE_MIN = BN.from(60);
18 | export const TEN_MINS = BN.from(60 * 10);
19 | export const ONE_HOUR = BN.from(60 * 60);
20 | export const ONE_DAY = BN.from(60 * 60 * 24);
21 | export const FIVE_DAYS = BN.from(60 * 60 * 24 * 5);
22 | export const TEN_DAYS = BN.from(60 * 60 * 24 * 10);
23 | export const ONE_WEEK = BN.from(60 * 60 * 24 * 7);
24 | export const ONE_YEAR = BN.from(60 * 60 * 24 * 365);
25 |
--------------------------------------------------------------------------------
/test-utils/fork.ts:
--------------------------------------------------------------------------------
1 | import { Signer } from "ethers";
2 | import { Account } from "types";
3 |
4 | // impersonates a specific account
5 | export const impersonate = async (addr: string, fund = true): Promise => {
6 | // Dynamic import hardhat module to avoid importing while hardhat config is being defined.
7 | // The error this avoids is:
8 | // Error HH9: Error while loading Hardhat's configuration.
9 | // You probably tried to import the "hardhat" module from your config or a file imported from it.
10 | // This is not possible, as Hardhat can't be initialized while its config is being defined.
11 | const { network, ethers } = await import("hardhat");
12 | await network.provider.request({
13 | method: "hardhat_impersonateAccount",
14 | params: [addr],
15 | });
16 | if (fund) {
17 | // Give the account 10 Ether
18 | await network.provider.request({
19 | method: "hardhat_setBalance",
20 | params: [addr, "0x8AC7230489E80000"],
21 | });
22 | }
23 | return ethers.provider.getSigner(addr);
24 | };
25 |
26 | export const impersonateAccount = async (address: string, fund = true): Promise => {
27 | const signer = await impersonate(address, fund);
28 | return {
29 | signer,
30 | address,
31 | };
32 | };
33 |
--------------------------------------------------------------------------------
/test-utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./assertions";
2 | export * from "./constants";
3 | export * from "./fork";
4 | export * from "./math";
5 | export * from "./time";
6 | export * from "./regex";
7 | export * from "./merkle";
8 |
--------------------------------------------------------------------------------
/test-utils/math.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber as BN } from "ethers";
2 | import { DEFAULT_DECIMALS } from "./constants";
3 |
4 | export { BN };
5 |
6 | // Converts an unscaled number to scaled number with the specified number of decimals
7 | // eg convert 3 to 3000000000000000000 with 18 decimals
8 | export const simpleToExactAmount = (amount: number | string | BN, decimals: number | BN = DEFAULT_DECIMALS): BN => {
9 | // Code is largely lifted from the guts of web3 toWei here:
10 | // https://github.com/ethjs/ethjs-unit/blob/master/src/index.js
11 | let amountString = amount.toString();
12 | const decimalsBN = BN.from(decimals);
13 |
14 | if (decimalsBN.gt(100)) {
15 | throw new Error(`Invalid decimals amount`);
16 | }
17 |
18 | const scale = BN.from(10).pow(decimals);
19 | const scaleString = scale.toString();
20 |
21 | // Is it negative?
22 | const negative = amountString.substring(0, 1) === "-";
23 | if (negative) {
24 | amountString = amountString.substring(1);
25 | }
26 |
27 | if (amountString === ".") {
28 | throw new Error(`Error converting number ${amountString} to precise unit, invalid value`);
29 | }
30 |
31 | // Split it into a whole and fractional part
32 | // eslint-disable-next-line prefer-const
33 | let [whole, fraction, ...rest] = amountString.split(".");
34 | if (rest.length > 0) {
35 | throw new Error(`Error converting number ${amountString} to precise unit, too many decimal points`);
36 | }
37 |
38 | if (!whole) {
39 | whole = "0";
40 | }
41 | if (!fraction) {
42 | fraction = "0";
43 | }
44 |
45 | if (fraction.length > scaleString.length - 1) {
46 | throw new Error(`Error converting number ${amountString} to precise unit, too many decimal places`);
47 | }
48 |
49 | while (fraction.length < scaleString.length - 1) {
50 | fraction += "0";
51 | }
52 |
53 | const wholeBN = BN.from(whole);
54 | const fractionBN = BN.from(fraction);
55 | let result = wholeBN.mul(scale).add(fractionBN);
56 |
57 | if (negative) {
58 | result = result.mul("-1");
59 | }
60 |
61 | return result;
62 | };
63 |
64 | export const percentToWeight = (percent: number | string | BN): BN => simpleToExactAmount(percent, 16);
65 |
66 | // Returns the smaller number
67 | export const minimum = (a: BN, b: BN): BN => (a.lte(b) ? a : b);
68 |
69 | // Returns the bigger number
70 | export const maximum = (a: BN, b: BN): BN => (a.gte(b) ? a : b);
71 |
72 | // Returns the square root of a big number, solution taken from https://github.com/ethers-io/ethers.js/issues/1182#issuecomment-744142921
73 | export const sqrt = (value: BN | number): BN => {
74 | const x = BN.from(value);
75 | let z = x.add(1).div(2);
76 | let y = x;
77 | while (z.sub(y).isNegative()) {
78 | y = z;
79 | z = x.div(z).add(z).div(2);
80 | }
81 | return y;
82 | };
83 |
84 | export const sum = (a: BN, b: BN): BN => a.add(b);
85 |
--------------------------------------------------------------------------------
/test-utils/merkle.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from "ethers";
2 | import { MerkleTree } from "merkletreejs";
3 | import { keccak256, solidityKeccak256 } from "ethers/lib/utils";
4 |
5 | const hashFn = (data: string) => keccak256(data).slice(2);
6 |
7 | export const createTreeWithAccounts = (accounts: Record): MerkleTree => {
8 | const elements = Object.entries(accounts).map(([account, balance]) =>
9 | solidityKeccak256(["address", "uint256"], [account, balance.toString()]),
10 | );
11 | return new MerkleTree(elements, hashFn, { sort: true });
12 | };
13 |
14 | export const getAccountBalanceProof = (tree: MerkleTree, account: string, balance: BigNumber) => {
15 | const element = solidityKeccak256(["address", "uint256"], [account, balance.toString()]);
16 | return tree.getHexProof(element);
17 | };
18 |
--------------------------------------------------------------------------------
/test-utils/regex.ts:
--------------------------------------------------------------------------------
1 | export const bytes = /^0x([A-Fa-f0-9]{1,})$/;
2 |
3 | export const bytesFixed = (x: number): RegExp => new RegExp(`^0x([A-Fa-f0-9]{${x * 2}})$`);
4 |
5 | export const bytes32 = bytesFixed(32);
6 | export const ethereumAddress = bytesFixed(20);
7 | export const transactionHash = bytes32;
8 |
9 | export const privateKey = /^[A-Fa-f0-9]{1,64}$/;
10 |
--------------------------------------------------------------------------------
/test-utils/time.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 | import { Block } from "@ethersproject/abstract-provider";
3 | import { BigNumberish } from "@ethersproject/bignumber";
4 | import { BN } from "./math";
5 | import { ONE_WEEK } from "./constants";
6 |
7 | export const advanceBlock = async (blocks?: BN | number): Promise => {
8 | if (blocks === undefined) {
9 | await ethers.provider.send("evm_mine", []);
10 | } else {
11 | await ethers.provider.send("hardhat_mine", [ethers.utils.hexlify(blocks)]);
12 | // work around for issue [hardhat_mine produces a failed tx when running in Coverage](https://github.com/NomicFoundation/hardhat/issues/2467)
13 | await ethers.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0x0"]);
14 | }
15 | };
16 |
17 | export const increaseTime = async (length: BN | number): Promise => {
18 | await ethers.provider.send("evm_increaseTime", [BN.from(length).toNumber()]);
19 | await advanceBlock();
20 | };
21 | export const latestBlock = async (): Promise => ethers.provider.getBlock(await ethers.provider.getBlockNumber());
22 |
23 | export const getTimestamp = async (): Promise => BN.from((await latestBlock()).timestamp);
24 |
25 | export const increaseTimeTo = async (target: BN | number): Promise => {
26 | const now = await getTimestamp();
27 | const later = BN.from(target);
28 | if (later.lt(now))
29 | throw Error(`Cannot increase current time (${now.toNumber()}) to a moment in the past (${later.toNumber()})`);
30 | const diff = later.sub(now);
31 | await increaseTime(diff);
32 | await advanceBlock();
33 | };
34 |
35 | export const sleep = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms));
36 |
37 | export const startWeek = (epochSeconds: BigNumberish): BN => BN.from(epochSeconds).div(ONE_WEEK).mul(ONE_WEEK);
38 | export const startCurrentWeek = async (): Promise => startWeek(await getTimestamp());
39 |
40 | export const weekEpoch = (epochSeconds: BigNumberish): BN => BN.from(epochSeconds).div(ONE_WEEK);
41 | export const currentWeekEpoch = async (): Promise => weekEpoch(await getTimestamp());
42 |
--------------------------------------------------------------------------------
/test/AuraClaimZap.spec.ts:
--------------------------------------------------------------------------------
1 | import hre, { ethers } from "hardhat";
2 | import { Signer } from "ethers";
3 | import { expect } from "chai";
4 | import { deployMocks, DeployMocksResult, getMockDistro, getMockMultisigs } from "../scripts/deployMocks";
5 | import { SystemDeployed, deployPhase1, deployPhase2, deployPhase3, deployPhase4 } from "../scripts/deploySystem";
6 | import { increaseTime } from "../test-utils/time";
7 | import { ONE_WEEK } from "../test-utils/constants";
8 | import { simpleToExactAmount } from "../test-utils/math";
9 | import { BaseRewardPool__factory } from "../types/generated";
10 |
11 | describe("AuraClaimZap", () => {
12 | let accounts: Signer[];
13 | let mocks: DeployMocksResult;
14 | let deployer: Signer;
15 | let contracts: SystemDeployed;
16 | let alice: Signer;
17 | let aliceAddress: string;
18 |
19 | before(async () => {
20 | accounts = await ethers.getSigners();
21 |
22 | alice = accounts[1];
23 | aliceAddress = await alice.getAddress();
24 |
25 | deployer = accounts[0];
26 | mocks = await deployMocks(hre, deployer);
27 | const multisigs = await getMockMultisigs(accounts[0], accounts[0], accounts[0]);
28 | const distro = getMockDistro();
29 | const phase1 = await deployPhase1(hre, deployer, mocks.addresses);
30 | const phase2 = await deployPhase2(
31 | hre,
32 | deployer,
33 | phase1,
34 | distro,
35 | multisigs,
36 | mocks.namingConfig,
37 | mocks.addresses,
38 | );
39 | const phase3 = await deployPhase3(hre, deployer, phase2, multisigs, mocks.addresses);
40 | await phase3.poolManager.setProtectPool(false);
41 | contracts = await deployPhase4(hre, deployer, phase3, mocks.addresses);
42 |
43 | await mocks.crv.transfer(aliceAddress, simpleToExactAmount(1));
44 | await mocks.crv.transfer(mocks.balancerVault.address, simpleToExactAmount(10));
45 | await contracts.cvxCrv.transfer(mocks.balancerVault.address, simpleToExactAmount(10));
46 |
47 | await mocks.balancerVault.setTokens(contracts.cvxCrv.address, mocks.crv.address);
48 | });
49 |
50 | it("set approval for deposits", async () => {
51 | await contracts.claimZap.setApprovals();
52 | expect(await mocks.crv.allowance(contracts.claimZap.address, contracts.crvDepositorWrapper.address)).gte(
53 | ethers.constants.MaxUint256,
54 | );
55 | expect(await contracts.cvxCrv.allowance(contracts.claimZap.address, contracts.cvxCrvRewards.address)).gte(
56 | ethers.constants.MaxUint256,
57 | );
58 | expect(await contracts.cvx.allowance(contracts.claimZap.address, contracts.cvxLocker.address)).gte(
59 | ethers.constants.MaxUint256,
60 | );
61 | });
62 |
63 | it("claim rewards from cvxCrvStaking", async () => {
64 | const lock = true;
65 | const stakeAddress = contracts.cvxCrvRewards.address;
66 | const balance = await mocks.crv.balanceOf(aliceAddress);
67 |
68 | const minOut = await contracts.crvDepositorWrapper.connect(alice).getMinOut(balance, "10000");
69 | await mocks.crv.connect(alice).approve(contracts.crvDepositorWrapper.address, balance);
70 | await contracts.crvDepositorWrapper.connect(alice).deposit(balance, minOut, lock, stakeAddress);
71 |
72 | const rewardBalance = await contracts.cvxCrvRewards.balanceOf(aliceAddress);
73 | expect(rewardBalance).eq(minOut);
74 |
75 | await contracts.booster.earmarkRewards(0);
76 |
77 | await increaseTime(ONE_WEEK.mul("4"));
78 |
79 | const expectedRewards = await contracts.cvxCrvRewards.earned(aliceAddress);
80 |
81 | await mocks.crv.connect(alice).approve(contracts.claimZap.address, ethers.constants.MaxUint256);
82 | const option = 1 + 16 + 8;
83 | const minBptAmountOut = await contracts.crvDepositorWrapper.getMinOut(expectedRewards, 10000);
84 | await contracts.claimZap
85 | .connect(alice)
86 | .claimRewards([], [], [], [], expectedRewards, minBptAmountOut, 0, option);
87 |
88 | const newRewardBalance = await contracts.cvxCrvRewards.balanceOf(aliceAddress);
89 | expect(newRewardBalance).eq(minBptAmountOut.add(rewardBalance));
90 | });
91 |
92 | it("claim from lp staking pool", async () => {
93 | const stake = true;
94 | const amount = ethers.utils.parseEther("10");
95 | await mocks.lptoken.transfer(aliceAddress, amount);
96 | await mocks.lptoken.connect(alice).approve(contracts.booster.address, amount);
97 | await contracts.booster.connect(alice).deposit(0, amount, stake);
98 |
99 | await contracts.booster.earmarkRewards(0);
100 | const pool = await contracts.booster.poolInfo(0);
101 | const crvRewards = BaseRewardPool__factory.connect(pool.crvRewards, deployer);
102 | await increaseTime(ONE_WEEK.mul("2"));
103 |
104 | const balanceBefore = await mocks.crv.balanceOf(aliceAddress);
105 | const expectedRewards = await crvRewards.earned(aliceAddress);
106 |
107 | const options = 0;
108 | await contracts.claimZap.connect(alice).claimRewards([pool.crvRewards], [], [], [], 0, 0, 0, options);
109 |
110 | const balanceAfter = await mocks.crv.balanceOf(aliceAddress);
111 | expect(balanceAfter.sub(balanceBefore)).eq(expectedRewards);
112 | });
113 | });
114 |
--------------------------------------------------------------------------------
/test/AuraMinter.spec.ts:
--------------------------------------------------------------------------------
1 | import hre, { ethers } from "hardhat";
2 | import { Signer } from "ethers";
3 | import { expect } from "chai";
4 | import { deployPhase1, deployPhase2, deployPhase3, deployPhase4 } from "../scripts/deploySystem";
5 | import { deployMocks, DeployMocksResult, getMockDistro, getMockMultisigs } from "../scripts/deployMocks";
6 | import { AuraToken, AuraMinter } from "../types/generated";
7 | import { simpleToExactAmount, getTimestamp, increaseTime, ONE_WEEK } from "../test-utils";
8 |
9 | describe("AuraMinter", () => {
10 | let accounts: Signer[];
11 | let cvx: AuraToken;
12 | let minter: AuraMinter;
13 | let mocks: DeployMocksResult;
14 | let deployer: Signer;
15 | let alice: Signer;
16 | let aliceAddress: string;
17 |
18 | before(async () => {
19 | accounts = await ethers.getSigners();
20 |
21 | deployer = accounts[0];
22 | mocks = await deployMocks(hre, deployer);
23 | const multisigs = await getMockMultisigs(accounts[0], accounts[0], accounts[0]);
24 | const distro = getMockDistro();
25 | const phase1 = await deployPhase1(hre, deployer, mocks.addresses);
26 | const phase2 = await deployPhase2(
27 | hre,
28 | deployer,
29 | phase1,
30 | distro,
31 | multisigs,
32 | mocks.namingConfig,
33 | mocks.addresses,
34 | );
35 | const phase3 = await deployPhase3(hre, deployer, phase2, multisigs, mocks.addresses);
36 | await phase3.poolManager.setProtectPool(false);
37 | const contracts = await deployPhase4(hre, deployer, phase3, mocks.addresses);
38 |
39 | alice = accounts[1];
40 | aliceAddress = await alice.getAddress();
41 | cvx = contracts.cvx;
42 | minter = contracts.minter;
43 | });
44 |
45 | it("initial configuration is correct", async () => {
46 | expect(await minter.aura()).to.equal(cvx.address);
47 | expect(await minter.inflationProtectionTime()).to.gt((await getTimestamp()).add(ONE_WEEK.mul(155)));
48 | expect(await minter.inflationProtectionTime()).to.lt((await getTimestamp()).add(ONE_WEEK.mul(157)));
49 | expect(await minter.owner()).to.equal(await deployer.getAddress());
50 | });
51 | describe("@method AuraMinter.mint fails if", async () => {
52 | it("sender is not the dao", async () => {
53 | await expect(minter.connect(alice).mint(aliceAddress, simpleToExactAmount(1))).to.revertedWith(
54 | "Ownable: caller is not the owner",
55 | );
56 | });
57 | it("inflation protection time has not expired", async () => {
58 | await expect(minter.connect(deployer).mint(aliceAddress, simpleToExactAmount(1))).to.revertedWith(
59 | "Inflation protected for now",
60 | );
61 | });
62 | });
63 |
64 | describe("@method AuraMinter.mint mints when", async () => {
65 | it("protects inflation up to 155", async () => {
66 | await increaseTime(ONE_WEEK.mul(155));
67 | await expect(minter.connect(deployer).mint(aliceAddress, simpleToExactAmount(1))).to.revertedWith(
68 | "Inflation protected for now",
69 | );
70 | });
71 | it("@method AuraMinter.mint mints tokens", async () => {
72 | await increaseTime(ONE_WEEK.mul(2));
73 | const beforeBalance = await cvx.balanceOf(aliceAddress);
74 | const beforeTotalSupply = await cvx.totalSupply();
75 | await minter.mint(aliceAddress, 1000);
76 | const afterBalance = await cvx.balanceOf(aliceAddress);
77 | const afterTotalSupply = await cvx.totalSupply();
78 | expect(beforeBalance, "balance increases").to.lt(afterBalance);
79 | expect(beforeTotalSupply, "total supply increases").to.lt(afterTotalSupply);
80 | });
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/BalInvestor.spec.ts:
--------------------------------------------------------------------------------
1 | import hre, { ethers } from "hardhat";
2 | import { expect } from "chai";
3 | import { MockBalInvestor, MockBalInvestor__factory, ERC20__factory, ERC20 } from "../types/generated";
4 | import { deployContract } from "../tasks/utils";
5 | import { fullScale } from "../test-utils";
6 | import { Signer } from "ethers";
7 | import { deployMocks } from "../scripts/deployMocks";
8 |
9 | const debug = false;
10 |
11 | describe("TestBalEth", () => {
12 | let testEthBal: MockBalInvestor;
13 | let balToken: ERC20;
14 | let signer: Signer;
15 |
16 | const amount = ethers.utils.parseEther("100");
17 |
18 | before(async () => {
19 | const accounts = await ethers.getSigners();
20 |
21 | signer = accounts[0];
22 | const mocks = await deployMocks(hre, signer, debug);
23 |
24 | const poolId = "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014";
25 | const vault = mocks.balancerVault.address;
26 | const weth = mocks.weth.address;
27 | const bal = mocks.bal.address;
28 |
29 | balToken = ERC20__factory.connect(bal, signer);
30 |
31 | testEthBal = await deployContract(
32 | hre,
33 | new MockBalInvestor__factory(signer),
34 | "testEthBal",
35 | [vault, bal, weth, poolId],
36 | {},
37 | debug,
38 | );
39 | });
40 |
41 | describe("join BAL:ETH 80/20 pool with BAL", () => {
42 | it("approve BAL for contract", async () => {
43 | const tx = await balToken.approve(testEthBal.address, amount);
44 | await tx.wait();
45 | });
46 |
47 | it("add BAL to pool", async () => {
48 | const bptAddress = await testEthBal.BALANCER_POOL_TOKEN();
49 | const bpt = ERC20__factory.connect(bptAddress, signer);
50 |
51 | const bptBalanceBefore = await bpt.balanceOf(testEthBal.address);
52 |
53 | let tx = await testEthBal.approveToken();
54 | await tx.wait();
55 |
56 | const minOut = await testEthBal.getMinOut(amount, 9980);
57 | tx = await testEthBal.addBalToPool(amount, minOut);
58 | await tx.wait();
59 |
60 | const bptBalanceAfter = await bpt.balanceOf(testEthBal.address);
61 | const bptBalanceDelta = bptBalanceAfter.sub(bptBalanceBefore);
62 |
63 | const bptPrice = await testEthBal.getBptPrice();
64 |
65 | const bptBalValue = bptPrice.mul(bptBalanceDelta).div(fullScale);
66 | const minAmount = amount.mul("9950").div("10000");
67 | expect(bptBalValue).gt(minAmount);
68 | });
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/PoolManager.spec.ts:
--------------------------------------------------------------------------------
1 | import hre, { ethers } from "hardhat";
2 | import { expect } from "chai";
3 | import { deployPhase1, deployPhase2, deployPhase3 } from "../scripts/deploySystem";
4 | import { deployMocks, DeployMocksResult, getMockMultisigs, getMockDistro } from "../scripts/deployMocks";
5 | import { Booster, MockCurveGauge__factory, PoolManagerV3, MockCurveGauge } from "../types/generated";
6 | import { deployContract } from "../tasks/utils";
7 | import { Signer } from "ethers";
8 |
9 | describe("PoolManagerV3", () => {
10 | let booster: Booster;
11 | let poolManager: PoolManagerV3;
12 | let mocks: DeployMocksResult;
13 | let accounts: Signer[];
14 |
15 | let alice: Signer;
16 |
17 | before(async () => {
18 | accounts = await ethers.getSigners();
19 |
20 | mocks = await deployMocks(hre, accounts[0]);
21 | const multisigs = await getMockMultisigs(accounts[0], accounts[0], accounts[0]);
22 | const distro = getMockDistro();
23 |
24 | alice = accounts[5];
25 |
26 | const phase1 = await deployPhase1(hre, accounts[0], mocks.addresses);
27 | const phase2 = await deployPhase2(
28 | hre,
29 | accounts[0],
30 | phase1,
31 | distro,
32 | multisigs,
33 | mocks.namingConfig,
34 | mocks.addresses,
35 | );
36 | const contracts = await deployPhase3(hre, accounts[0], phase2, multisigs, mocks.addresses);
37 |
38 | booster = contracts.booster;
39 | poolManager = contracts.poolManager;
40 | });
41 |
42 | describe("@method addPool", async () => {
43 | let badGauge: MockCurveGauge;
44 |
45 | before(async () => {
46 | const badLptoken = "0x0000000000000000000000000000000000000000";
47 | badGauge = await deployContract(
48 | hre,
49 | new MockCurveGauge__factory(accounts[0]),
50 | "MockCurveGauge",
51 | ["BadGauge", "badGauge", badLptoken, []],
52 | {},
53 | false,
54 | );
55 | });
56 |
57 | it("addPool called by operator", async () => {
58 | const gauge = mocks.gauges[0];
59 | const tx = await poolManager["addPool(address)"](gauge.address);
60 | await tx.wait();
61 |
62 | const lptoken = await gauge.lp_token();
63 | const pool = await booster.poolInfo(0);
64 | expect(pool.lptoken).to.equal(lptoken);
65 | });
66 |
67 | it("reverts if pool weight is 0", async () => {
68 | const failedTx = poolManager["addPool(address)"](badGauge.address);
69 | await expect(failedTx).to.revertedWith("must have weight");
70 | });
71 |
72 | it("reverts if lptoken address is 0", async () => {
73 | const tx = await mocks.voting.vote_for_gauge_weights(badGauge.address, 1);
74 | await tx.wait();
75 |
76 | const failedTx = poolManager["addPool(address)"](badGauge.address);
77 | await expect(failedTx).to.revertedWith("lp token is 0");
78 | });
79 |
80 | it("reverts if gauge has already been added", async () => {
81 | const failedTx = poolManager["addPool(address)"](mocks.gauges[0].address);
82 | await expect(failedTx).to.revertedWith("already registered gauge");
83 | });
84 | });
85 |
86 | describe("@method shutdownPool", () => {
87 | it("reverts if not called by operator", async () => {
88 | const failedTx = poolManager.connect(accounts[2]).shutdownPool(0);
89 | await expect(failedTx).to.revertedWith("!auth");
90 | });
91 |
92 | it("happy path", async () => {
93 | const tx = await poolManager.shutdownPool(0);
94 | await tx.wait();
95 |
96 | const pool = await booster.poolInfo(0);
97 | expect(pool.shutdown).to.equal(true);
98 | });
99 | });
100 |
101 | describe("@method setProtectPool", () => {
102 | it("protectPool defaults to true", async () => {
103 | const startValue = await poolManager.protectAddPool();
104 | expect(startValue).to.equal(true);
105 | });
106 |
107 | it("reverts if addPool is protected and caller is not operator", async () => {
108 | const resp = poolManager.connect(alice)["addPool(address)"](mocks.gauges[1].address);
109 | await expect(resp).to.be.revertedWith("!auth");
110 | });
111 |
112 | it("reverts if setProtectPool caller is not operator", async () => {
113 | const resp = poolManager.connect(alice).setProtectPool(false);
114 | await expect(resp).to.be.revertedWith("!auth");
115 | });
116 |
117 | it("setProtectPool update protectAddPool", async () => {
118 | await poolManager.setProtectPool(false);
119 | const newValue = await poolManager.protectAddPool();
120 | expect(newValue).to.equal(false);
121 | });
122 |
123 | it("addPool can be called by anyone", async () => {
124 | const gauge = mocks.gauges[1];
125 | await poolManager.connect(alice)["addPool(address)"](gauge.address);
126 |
127 | const lptoken = await gauge.lp_token();
128 | const pool = await booster.poolInfo(1);
129 | expect(pool.lptoken).to.equal(lptoken);
130 | });
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "declaration": true,
5 | "declarationMap": true,
6 | "emitDecoratorMetadata": true,
7 | "esModuleInterop": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "lib": ["es6"],
11 | "module": "commonjs",
12 | "moduleResolution": "node",
13 | "noImplicitAny": false,
14 | "removeComments": true,
15 | "resolveJsonModule": true,
16 | "sourceMap": true,
17 | "rootDir": ".",
18 | "strictNullChecks": false,
19 | "strict": true,
20 | "target": "es6",
21 | "outDir": "./dist/"
22 | },
23 | "exclude": ["node_modules"],
24 | "include": [
25 | "tasks/**/*",
26 | "./test/*",
27 | "./test/**/*",
28 | "./test-fork/*",
29 | "./test-fork/**/*",
30 | "types/**/*",
31 | "test-utils/**/*",
32 | "scripts/**/*"
33 | ],
34 | "files": [
35 | "./index.ts",
36 | "./hardhat.config.ts",
37 | "./hardhat-fork.config.ts",
38 | "./hardhat-cvx.config.ts"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/types/common.ts:
--------------------------------------------------------------------------------
1 | import { Signer } from "ethers"
2 |
3 | export type EthAddress = string
4 | export type Bytes32 = string
5 |
6 | export interface Account {
7 | signer: Signer
8 | address: string
9 | }
10 |
--------------------------------------------------------------------------------
/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./common"
2 | export * from "./generated"
3 |
--------------------------------------------------------------------------------