├── .core.solcover.js ├── .env.example ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .yarn └── releases │ └── yarn-3.6.1.cjs ├── .yarnrc.yml ├── BUGBOUNTY.md ├── LICENSE ├── README.md ├── audits ├── certik-2021-06-02.pdf ├── peckshield-2021-06-28.pdf ├── peckshield-2021-09-10.pdf ├── peckshield-2021-12-24.pdf ├── peckshield-2022-05-20.pdf ├── peckshield-2022-10-30.pdf └── peckshield-2023-12-28.pdf ├── config.ts ├── contracts ├── anyswap │ └── AnyswapChess.sol ├── fund │ ├── FundRolesV2.sol │ ├── FundV3.sol │ ├── FundV4.sol │ ├── FundV5.sol │ ├── MaturityFund.sol │ ├── MaturityPrimaryMarket.sol │ ├── PrimaryMarketRouter.sol │ ├── PrimaryMarketRouterV2.sol │ ├── PrimaryMarketV3.sol │ ├── PrimaryMarketV5.sol │ ├── ShareStaking.sol │ ├── ShareStakingV2.sol │ ├── ShareV2.sol │ └── WstETHPrimaryMarketRouter.sol ├── governance │ ├── AddressWhitelist.sol │ ├── Briber.sol │ ├── Chess.sol │ ├── ChessControllerV6.sol │ ├── ChessRoles.sol │ ├── ChessSchedule.sol │ ├── ChessScheduleRelayer.sol │ ├── ChessSubSchedule.sol │ ├── ControllerBallotV2.sol │ ├── FeeConverter.sol │ ├── FeeDistributor.sol │ ├── InterestRateBallot.sol │ ├── InterestRateBallotV2.sol │ ├── InterestRateBallotV3.sol │ ├── RewardClaimer.sol │ ├── VestingEscrow.sol │ ├── VotingEscrowCallback.sol │ ├── VotingEscrowCheckpoint.sol │ └── VotingEscrowV4.sol ├── interfaces │ ├── IAnyswapV6ERC20.sol │ ├── IAprOracle.sol │ ├── IBallot.sol │ ├── IChessController.sol │ ├── IChessSchedule.sol │ ├── IControllerBallot.sol │ ├── IControllerBallotV2.sol │ ├── IFundForPrimaryMarketV3.sol │ ├── IFundForPrimaryMarketV4.sol │ ├── IFundForStrategy.sol │ ├── IFundForStrategyV2.sol │ ├── IFundV3.sol │ ├── IFundV4.sol │ ├── IFundV5.sol │ ├── ILiquidityGauge.sol │ ├── IPrimaryMarketRouter.sol │ ├── IPrimaryMarketRouterV2.sol │ ├── IPrimaryMarketV3.sol │ ├── IPrimaryMarketV5.sol │ ├── IShareV2.sol │ ├── IStableSwap.sol │ ├── ISwapRouter.sol │ ├── ITrancheIndexV2.sol │ ├── ITranchessSwapCallee.sol │ ├── ITwapOracle.sol │ ├── ITwapOracleV2.sol │ ├── IVotingEscrow.sol │ ├── IWrappedERC20.sol │ └── IWstETH.sol ├── keeper │ ├── BatchKeeperHelperBase.sol │ ├── CrossChainMintKeeperHelper.sol │ ├── CrossChainSyncKeeperHelper.sol │ └── FundKeeperHelper.sol ├── layerzero │ ├── LzApp.sol │ ├── NonblockingLzApp.sol │ ├── OFTCore.sol │ ├── ProxyOFT.sol │ ├── ProxyOFTPool.sol │ ├── UpgradeableLzApp.sol │ ├── UpgradeableNonblockingLzApp.sol │ ├── interfaces │ │ ├── ILayerZeroEndpoint.sol │ │ ├── ILayerZeroReceiver.sol │ │ ├── ILayerZeroUserApplicationConfig.sol │ │ └── IOFTCore.sol │ └── utils │ │ ├── BytesLib.sol │ │ └── ExcessivelySafeCall.sol ├── misc │ ├── BatchOperationHelper.sol │ ├── DataAggregator.sol │ ├── ExternalContracts.sol │ └── RewardCashier.sol ├── nonfungible │ └── NonfungibleRedemptionDescriptor.sol ├── oracle │ ├── AprOracle.sol │ ├── BscAprOracle.sol │ ├── BscAprOracleProxy.sol │ ├── ChainlinkTwapOracleV2.sol │ ├── ChainlinkTwapOracleV3.sol │ ├── ConstAprOracle.sol │ ├── ConstPriceOracle.sol │ ├── QueenRateProvider.sol │ └── WstETHPriceOracle.sol ├── strategy │ ├── BscStakingStrategy.sol │ ├── BscStakingStrategyV2.sol │ └── eth │ │ ├── BeaconStakingOracle.sol │ │ ├── EthPrimaryMarket.sol │ │ ├── EthStakingStrategy.sol │ │ ├── IWithdrawalManager.sol │ │ ├── NodeOperatorRegistry.sol │ │ ├── SafeStaking.sol │ │ ├── WithdrawalManager.sol │ │ ├── WithdrawalManagerFactory.sol │ │ └── WithdrawalManagerProxy.sol ├── swap │ ├── BalancerV2Router.sol │ ├── BishopStableSwapV2.sol │ ├── FlashSwapRouter.sol │ ├── FlashSwapRouterV2.sol │ ├── FlashSwapRouterV3.sol │ ├── FlashSwapRouterV3Helper.sol │ ├── LiquidityGauge.sol │ ├── LiquidityGaugeV2.sol │ ├── LiquidityGaugeV3.sol │ ├── QueenStableSwap.sol │ ├── StableSwap.sol │ ├── StableSwapV2.sol │ ├── StableSwapV3.sol │ ├── SwapBonus.sol │ ├── SwapBonusWrapper.sol │ ├── SwapRouter.sol │ ├── WstETHBishopStableSwap.sol │ ├── WstETHStableSwap.sol │ └── WstETHWrappingSwap.sol ├── test │ ├── AdvancedMathWrapper.sol │ ├── LZEndpointMock.sol │ ├── LzLib.sol │ ├── MockExternalRouter.sol │ ├── MockToken.sol │ ├── MockTwapOracle.sol │ ├── MockTwapOracleKeeper.sol │ ├── MockWrappedToken.sol │ └── MockWstETH.sol └── utils │ ├── AdvancedMath.sol │ ├── CarefulMath.sol │ ├── CoreUtility.sol │ ├── Exponential.sol │ ├── ExponentialNoError.sol │ ├── ManagedPausable.sol │ ├── ProxyUtility.sol │ └── SafeDecimalMath.sol ├── hardhat.config.ts ├── package.json ├── tasks ├── accounts.ts ├── address_file.ts ├── deploy_address_whitelist.ts ├── deploy_bsc_apr_oracle.ts ├── deploy_bsc_staking_strategy.ts ├── deploy_chess_controller_impl.ts ├── deploy_chess_pool.ts ├── deploy_chess_schedule_impl.ts ├── deploy_chess_schedule_relayer.ts ├── deploy_controller_ballot.ts ├── deploy_data_aggregator.ts ├── deploy_eth_staking_strategy.ts ├── deploy_fee_distributor.ts ├── deploy_flash_swap_router.ts ├── deploy_flash_swap_router_v3.ts ├── deploy_fund.ts ├── deploy_fund_eth.ts ├── deploy_fund_wsteth.ts ├── deploy_governance.ts ├── deploy_maturity_fund.ts ├── deploy_misc.ts ├── deploy_mock.ts ├── deploy_mock_twap_oracle.ts ├── deploy_stable_swap.ts ├── deploy_stable_swap_maturity.ts ├── deploy_stable_swap_wsteth.ts ├── deploy_sub_governance.ts ├── deploy_swap_router.ts ├── deploy_vesting.ts ├── deploy_voting_escrow_impl.ts ├── dev_deploy_curve.ts ├── dev_deploy_deposit_contract.ts ├── dev_deploy_lz.ts ├── dev_deploy_token_hub.ts ├── dev_redemption_nft_metadata.ts ├── signers.ts ├── test_deploy.ts └── utils.ts ├── test ├── advancedMath.ts ├── beaconStakingOracle.ts ├── bishopStableSwapV2.ts ├── bscStakingStrategy.ts ├── chainlinkTwapOracleV2.ts ├── chainlinkTwapOracleV3.ts ├── checkpointBypassAttack.ts ├── chessSchedule.ts ├── controllerBallotV2.ts ├── feeDistributor.ts ├── flashSwapRouter.ts ├── flashSwapRouterV3.ts ├── fundV3.ts ├── fundV4.ts ├── fundV5.ts ├── interestRateBallotV2.ts ├── liquidityGauge.ts ├── liquidityGaugeV2.ts ├── liquidityGaugeV3.ts ├── mock.ts ├── nodeOperatorRegistry.ts ├── primaryMarketV3.ts ├── queenStableSwap.ts ├── shareStaking.ts ├── shareStakingV2.ts ├── shareV2.ts ├── utils.ts ├── vestingEscrow.ts └── votingEscrowV4.ts ├── tsconfig.json └── yarn.lock /.core.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | norpc: true, 3 | testCommand: "npm test", 4 | compileCommand: "npm run compile", 5 | skipFiles: ["utils", "test", "interfaces", "misc"], 6 | }; 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Remote RPC endpoint and chain ID. 2 | # A network named "remote" is created when these two variables are set. 3 | # Either DEPLOYER_PK or DEPLOYER_HD_PATH is required for the remote network. 4 | # ETH_RPC=https://xxx/xxx 5 | # ETH_CHAIN_ID=5777 6 | 7 | # Private key of the deployer address. 8 | # DEPLOYER_PK=0x0000000000000000000000000000000000000000000000000000000000000000 9 | 10 | # HD path of the deployer address on Ledger. 11 | # DEPLOYER_HD_PATH=m/44'/60'/0'/0/0 12 | 13 | # Etherscan API key for contract code verification 14 | # ETHERSCAN_API_KEY= 15 | 16 | # Treasury address. Leave it empty to use the deployer address. 17 | # GOVERNANCE_TREASURY= 18 | 19 | # Timelock minimum delay in seconds. 20 | GOVERNANCE_TIMELOCK_DELAY=86400 21 | 22 | # Launch date (YYYY-MM-DD). It must be a Thursday. 23 | GOVERNANCE_LAUNCH_DATE=1970-01-01 24 | 25 | # LayerZero endpoint. 26 | LZ_ENDPOINT=0x0000000000000000000000000000000000000000; 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es2021: true, 4 | node: true, 5 | mocha: true, 6 | }, 7 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 8 | parser: "@typescript-eslint/parser", 9 | parserOptions: { 10 | ecmaVersion: 12, 11 | sourceType: "module", 12 | }, 13 | plugins: ["@typescript-eslint"], 14 | rules: { "no-constant-condition": "off" }, 15 | }; 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/releases/** binary 2 | /.yarn/plugins/** binary 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: [main, dev] 6 | pull_request: 7 | branches: [main, dev] 8 | 9 | jobs: 10 | test: 11 | name: Unit Test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Setup Node.js ${{ matrix.node-version }} environment 16 | uses: actions/setup-node@v2.1.4 17 | with: 18 | node-version: 20.x 19 | - name: Install dependencies 20 | run: yarn install --immutable 21 | - name: Run unit test 22 | run: yarn run test 23 | check: 24 | name: Lint and Format Check 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Setup Node.js environment 29 | uses: actions/setup-node@v2.1.4 30 | with: 31 | node-version: 20.x 32 | - name: Install dependencies 33 | run: yarn install --immutable 34 | - name: Check lint and format 35 | run: yarn run check 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .yarn/* 2 | !.yarn/patches 3 | !.yarn/releases 4 | !.yarn/plugins 5 | !.yarn/sdks 6 | !.yarn/versions 7 | .pnp.* 8 | /node_modules/ 9 | 10 | /cache/ 11 | /artifacts/ 12 | /deployed_addresses/ 13 | /exported_abi/ 14 | /coverage/ 15 | /coverage.json 16 | /.env 17 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.2.0 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 100, 4 | "useTabs": false 5 | } 6 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-3.6.1.cjs 4 | -------------------------------------------------------------------------------- /BUGBOUNTY.md: -------------------------------------------------------------------------------- 1 | # Bug Bounty README 2 | 3 | ## Overview 4 | 5 | This bug bounty document is to verify that **Tranchess** hosts a bug bounty on Immunefi at the address [https://immunefi.com/bounty/tranchess/](https://immunefi.com/bounty/tranchess/). 6 | 7 | If you have found a vulnerability in our project, it must be submitted through [Immunefi's platform](https://immunefi.com/). Immunefi will handle bug bounty communications. 8 | 9 | See the bounty page at Immunefi for more details on accepted vulnerabilities, payout amounts, and rules of participation. 10 | 11 | Users who violate the rules of participation will not receive bug bounty payouts and may be temporarily suspended or banned from the bug bounty program. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tranchess Core 2 | 3 | Tranchess core. 4 | 5 | # Local Development 6 | 7 | ## Install Dependencies 8 | 9 | `yarn install` 10 | 11 | ## Compile Contracts 12 | 13 | `yarn hardhat compile` 14 | 15 | ## Run Tests 16 | 17 | `yarn run test` 18 | 19 | ## Check Lint and Format 20 | 21 | `yarn run check` 22 | 23 | ## Deploy Contracts 24 | 25 | ### Deployed Contract Address 26 | 27 | Each of the following deployment tasks creates a JSON file under the `deployed_addresses` directory, 28 | writing address of all deployed contracts in it. 29 | 30 | ### Configuration 31 | 32 | Copy the file `.env.example` to `.env` and modify configurations in the file. 33 | 34 | This project depends on a few external contracts. On a public blockchain, please update 35 | their addresses in `.env`. On a private blockchain, deploy mock contracts using the following 36 | command. 37 | 38 | `yarn hardhat deploy_mock --network remote` 39 | 40 | ### Oracle Contracts 41 | 42 | `yarn hardhat deploy_twap_oracle --network remote --token --oracle-symbol BTC` 43 | 44 | `yarn hardhat deploy_bsc_apr_oracle --network remote --token --v-token ` 45 | 46 | On a private blockchain, you may want to modify address files generated by these tasks and 47 | change addresses to mock oracles. 48 | 49 | ### Governance Contracts 50 | 51 | `yarn hardhat deploy_governance --network remote` 52 | 53 | ### Fund Contracts 54 | 55 | `yarn hardhat deploy_fund --network remote --underlying-symbol --quote-symbol --admin-fee-rate 0.5` 56 | 57 | It reads address files generated by some previous tasks. 58 | 59 | ### Exchange Contracts 60 | 61 | `yarn hardhat deploy_exchange --network remote --underlying-symbol ` 62 | 63 | It reads address files generated by some previous tasks. 64 | 65 | ### Test the Deployment Tasks 66 | 67 | The Hardhat task `test_deploy` runs all the above deployment tasks on a temporary local 68 | Hardhat network. It can be used as a preliminary test. 69 | 70 | `yarn hardhat test_deploy` 71 | -------------------------------------------------------------------------------- /audits/certik-2021-06-02.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/certik-2021-06-02.pdf -------------------------------------------------------------------------------- /audits/peckshield-2021-06-28.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2021-06-28.pdf -------------------------------------------------------------------------------- /audits/peckshield-2021-09-10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2021-09-10.pdf -------------------------------------------------------------------------------- /audits/peckshield-2021-12-24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2021-12-24.pdf -------------------------------------------------------------------------------- /audits/peckshield-2022-05-20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2022-05-20.pdf -------------------------------------------------------------------------------- /audits/peckshield-2022-10-30.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2022-10-30.pdf -------------------------------------------------------------------------------- /audits/peckshield-2023-12-28.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranchess/contract-core/51cc41b768d05a3948e755a7ab4ec363b2050725/audits/peckshield-2023-12-28.pdf -------------------------------------------------------------------------------- /config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | export function endOfWeek(timestamp: number): number { 5 | const WEEK = 86400 * 7; 6 | const SETTLEMENT_TIME = 3600 * 14; 7 | return Math.floor((timestamp + WEEK - SETTLEMENT_TIME) / WEEK) * WEEK + SETTLEMENT_TIME; 8 | } 9 | 10 | export const ETH_RPC = process.env.ETH_RPC; 11 | export const ETH_CHAIN_ID = parseInt(process.env.ETH_CHAIN_ID ?? ""); 12 | export const DEPLOYER_PK = process.env.DEPLOYER_PK; 13 | export const DEPLOYER_HD_PATH = process.env.DEPLOYER_HD_PATH; 14 | 15 | export const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; 16 | 17 | export const GOVERNANCE_CONFIG = { 18 | CHESS_TOTAL_SUPPLY: "300000000", 19 | CHESS_SCHEDULE_MAX_SUPPLY: "120000000", 20 | TREASURY: process.env.GOVERNANCE_TREASURY, 21 | TIMELOCK_DELAY: parseInt(process.env.GOVERNANCE_TIMELOCK_DELAY ?? "3600"), 22 | LAUNCH_TIMESTAMP: endOfWeek( 23 | new Date(process.env.GOVERNANCE_LAUNCH_DATE ?? "1970-01-01").getTime() / 1000 24 | ), 25 | LZ_ENDPOINT: process.env.LZ_ENDPOINT, 26 | }; 27 | -------------------------------------------------------------------------------- /contracts/anyswap/AnyswapChess.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | import "../governance/ChessRoles.sol"; 8 | 9 | import "../interfaces/IAnyswapV6ERC20.sol"; 10 | 11 | contract AnyswapChess is IAnyswapV6ERC20, ERC20, ChessRoles, Ownable { 12 | address public constant override underlying = address(0); 13 | 14 | uint256 public immutable maxTotalSupply; 15 | 16 | constructor( 17 | string memory name_, 18 | string memory symbol_, 19 | uint256 maxTotalSupply_ 20 | ) public ERC20(name_, symbol_) { 21 | maxTotalSupply = maxTotalSupply_; 22 | } 23 | 24 | function addMinter(address account) external onlyOwner { 25 | _addMinter(account); 26 | } 27 | 28 | function removeMinter(address account) external onlyOwner { 29 | _removeMinter(account); 30 | } 31 | 32 | function mint(address to, uint256 amount) external override onlyMinter returns (bool) { 33 | _mint(to, amount); 34 | return true; 35 | } 36 | 37 | function burn(address from, uint256 amount) external override onlyMinter returns (bool) { 38 | _burn(from, amount); 39 | return true; 40 | } 41 | 42 | function _beforeTokenTransfer( 43 | address from, 44 | address, // to 45 | uint256 amount 46 | ) internal override { 47 | if (from == address(0)) { 48 | // When minting tokens 49 | require(totalSupply().add(amount) <= maxTotalSupply, "Max total supply exceeded"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/fund/PrimaryMarketRouterV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../fund/ShareStaking.sol"; 6 | 7 | import "../interfaces/IPrimaryMarketRouterV2.sol"; 8 | import "../interfaces/IPrimaryMarketV5.sol"; 9 | import "../interfaces/ISwapRouter.sol"; 10 | import "../interfaces/IStableSwap.sol"; 11 | import "../interfaces/IWrappedERC20.sol"; 12 | 13 | contract PrimaryMarketRouterV2 is IPrimaryMarketRouterV2, ITrancheIndexV2 { 14 | using SafeMath for uint256; 15 | using SafeERC20 for IERC20; 16 | 17 | IPrimaryMarketV5 public immutable primaryMarket; 18 | IFundV3 public immutable fund; 19 | IERC20 private immutable _tokenUnderlying; 20 | address private immutable _tokenB; 21 | 22 | constructor(address pm) public { 23 | primaryMarket = IPrimaryMarketV5(pm); 24 | IFundV3 fund_ = IFundV3(IPrimaryMarketV5(pm).fund()); 25 | fund = fund_; 26 | _tokenUnderlying = IERC20(fund_.tokenUnderlying()); 27 | _tokenB = fund_.tokenB(); 28 | } 29 | 30 | /// @dev Get redemption with StableSwap getQuoteOut interface. 31 | function getQuoteOut(uint256 baseIn) external view override returns (uint256 quoteOut) { 32 | (quoteOut, ) = primaryMarket.getRedemption(baseIn); 33 | } 34 | 35 | /// @dev Get creation for QUEEN with StableSwap getQuoteIn interface. 36 | function getQuoteIn(uint256 baseOut) external view override returns (uint256 quoteIn) { 37 | quoteIn = primaryMarket.getCreationForQ(baseOut); 38 | } 39 | 40 | /// @dev Get creation with StableSwap getBaseOut interface. 41 | function getBaseOut(uint256 quoteIn) external view override returns (uint256 baseOut) { 42 | baseOut = primaryMarket.getCreation(quoteIn); 43 | } 44 | 45 | /// @dev Get redemption for underlying with StableSwap getBaseIn interface. 46 | function getBaseIn(uint256 quoteOut) external view override returns (uint256 baseIn) { 47 | baseIn = primaryMarket.getRedemptionForUnderlying(quoteOut); 48 | } 49 | 50 | /// @dev Create QUEEN with StableSwap buy interface. 51 | /// Underlying should have already been sent to this contract 52 | function buy( 53 | uint256 version, 54 | uint256 baseOut, 55 | address recipient, 56 | bytes calldata 57 | ) external override returns (uint256 realBaseOut) { 58 | uint256 routerQuoteBalance = IERC20(_tokenUnderlying).balanceOf(address(this)); 59 | IERC20(_tokenUnderlying).safeTransfer(address(primaryMarket), routerQuoteBalance); 60 | realBaseOut = primaryMarket.create(recipient, baseOut, version); 61 | } 62 | 63 | /// @dev Redeem QUEEN with StableSwap sell interface. 64 | /// QUEEN should have already been sent to this contract 65 | function sell( 66 | uint256 version, 67 | uint256 quoteOut, 68 | address recipient, 69 | bytes calldata 70 | ) external override returns (uint256 realQuoteOut) { 71 | uint256 routerBaseBalance = fund.trancheBalanceOf(TRANCHE_Q, address(this)); 72 | realQuoteOut = primaryMarket.redeem(recipient, routerBaseBalance, quoteOut, version); 73 | } 74 | 75 | function create( 76 | address recipient, 77 | uint256 underlying, 78 | uint256 minOutQ, 79 | uint256 version 80 | ) public payable override returns (uint256 outQ) { 81 | if (msg.value > 0) { 82 | require(msg.value == underlying); // sanity check 83 | IWrappedERC20(address(_tokenUnderlying)).deposit{value: msg.value}(); 84 | _tokenUnderlying.safeTransfer(address(primaryMarket), msg.value); 85 | } else { 86 | IERC20(_tokenUnderlying).safeTransferFrom( 87 | msg.sender, 88 | address(primaryMarket), 89 | underlying 90 | ); 91 | } 92 | 93 | outQ = primaryMarket.create(recipient, minOutQ, version); 94 | } 95 | 96 | function createAndSplit( 97 | address recipient, 98 | uint256 underlying, 99 | uint256 minOutQ, 100 | uint256 version 101 | ) external payable override returns (uint256 outB, uint256 outR) { 102 | uint256 outQ = create(address(this), underlying, minOutQ, version); 103 | (outB, outR) = primaryMarket.split(recipient, outQ, version); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /contracts/fund/ShareV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/IFundV3.sol"; 5 | import "../interfaces/IShareV2.sol"; 6 | 7 | contract ShareV2 is IShareV2 { 8 | uint8 public constant decimals = 18; 9 | IFundV3 public immutable fund; 10 | uint256 public immutable tranche; 11 | 12 | string public name; 13 | string public symbol; 14 | 15 | constructor( 16 | string memory name_, 17 | string memory symbol_, 18 | address fund_, 19 | uint256 tranche_ 20 | ) public { 21 | name = name_; 22 | symbol = symbol_; 23 | fund = IFundV3(fund_); 24 | tranche = tranche_; 25 | } 26 | 27 | function totalSupply() external view override returns (uint256) { 28 | return fund.trancheTotalSupply(tranche); 29 | } 30 | 31 | function balanceOf(address account) external view override returns (uint256) { 32 | return fund.trancheBalanceOf(tranche, account); 33 | } 34 | 35 | function transfer(address recipient, uint256 amount) external override returns (bool) { 36 | fund.shareTransfer(msg.sender, recipient, amount); 37 | return true; 38 | } 39 | 40 | function allowance(address owner, address spender) external view override returns (uint256) { 41 | return fund.trancheAllowance(tranche, owner, spender); 42 | } 43 | 44 | function approve(address spender, uint256 amount) external override returns (bool) { 45 | fund.shareApprove(msg.sender, spender, amount); 46 | return true; 47 | } 48 | 49 | function transferFrom( 50 | address sender, 51 | address recipient, 52 | uint256 amount 53 | ) external override returns (bool) { 54 | fund.shareTransferFrom(msg.sender, sender, recipient, amount); 55 | return true; 56 | } 57 | 58 | function increaseAllowance(address spender, uint256 addedValue) external returns (bool) { 59 | fund.shareIncreaseAllowance(msg.sender, spender, addedValue); 60 | return true; 61 | } 62 | 63 | function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) { 64 | fund.shareDecreaseAllowance(msg.sender, spender, subtractedValue); 65 | return true; 66 | } 67 | 68 | modifier onlyFund() { 69 | require(msg.sender == address(fund), "Only fund"); 70 | _; 71 | } 72 | 73 | function fundEmitTransfer( 74 | address sender, 75 | address recipient, 76 | uint256 amount 77 | ) external override onlyFund { 78 | emit Transfer(sender, recipient, amount); 79 | } 80 | 81 | function fundEmitApproval( 82 | address owner, 83 | address spender, 84 | uint256 amount 85 | ) external override onlyFund { 86 | emit Approval(owner, spender, amount); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/fund/WstETHPrimaryMarketRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | 8 | import "../interfaces/IPrimaryMarketV5.sol"; 9 | import "../interfaces/IFundV3.sol"; 10 | import "../interfaces/IWstETH.sol"; 11 | import "../interfaces/ITrancheIndexV2.sol"; 12 | import "../interfaces/IStableSwap.sol"; 13 | 14 | contract WstETHPrimaryMarketRouter is IStableSwapCore, ITrancheIndexV2 { 15 | using SafeERC20 for IERC20; 16 | 17 | IPrimaryMarketV5 public immutable primaryMarket; 18 | IFundV3 public immutable fund; 19 | address private immutable _wstETH; 20 | address private immutable _stETH; 21 | address private immutable _tokenB; 22 | 23 | constructor(address pm) public { 24 | primaryMarket = IPrimaryMarketV5(pm); 25 | IFundV3 fund_ = IFundV3(IPrimaryMarketV5(pm).fund()); 26 | fund = fund_; 27 | _wstETH = fund_.tokenUnderlying(); 28 | _stETH = IWstETH(fund_.tokenUnderlying()).stETH(); 29 | _tokenB = fund_.tokenB(); 30 | } 31 | 32 | /// @dev Get redemption with StableSwap getQuoteOut interface. 33 | function getQuoteOut(uint256 baseIn) external view override returns (uint256 quoteOut) { 34 | (quoteOut, ) = primaryMarket.getRedemption(baseIn); 35 | } 36 | 37 | /// @dev Get creation for QUEEN with StableSwap getQuoteIn interface. 38 | function getQuoteIn(uint256 baseOut) external view override returns (uint256 quoteIn) { 39 | quoteIn = primaryMarket.getCreationForQ(baseOut); 40 | } 41 | 42 | /// @dev Get creation with StableSwap getBaseOut interface. 43 | function getBaseOut(uint256 quoteIn) external view override returns (uint256 baseOut) { 44 | baseOut = primaryMarket.getCreation(quoteIn); 45 | } 46 | 47 | /// @dev Get redemption for underlying with StableSwap getBaseIn interface. 48 | function getBaseIn(uint256 quoteOut) external view override returns (uint256 baseIn) { 49 | baseIn = primaryMarket.getRedemptionForUnderlying(quoteOut); 50 | } 51 | 52 | /// @dev Create QUEEN with StableSwap buy interface. 53 | /// Underlying should have already been sent to this contract 54 | function buy( 55 | uint256 version, 56 | uint256 baseOut, 57 | address recipient, 58 | bytes calldata 59 | ) external override returns (uint256 realBaseOut) { 60 | uint256 routerQuoteBalance = IERC20(_wstETH).balanceOf(address(this)); 61 | IERC20(_wstETH).safeTransfer(address(primaryMarket), routerQuoteBalance); 62 | realBaseOut = primaryMarket.create(recipient, baseOut, version); 63 | } 64 | 65 | /// @dev Redeem QUEEN with StableSwap sell interface. 66 | /// QUEEN should have already been sent to this contract 67 | function sell( 68 | uint256 version, 69 | uint256 quoteOut, 70 | address recipient, 71 | bytes calldata 72 | ) external override returns (uint256 realQuoteOut) { 73 | uint256 routerBaseBalance = fund.trancheBalanceOf(TRANCHE_Q, address(this)); 74 | realQuoteOut = primaryMarket.redeem(recipient, routerBaseBalance, quoteOut, version); 75 | } 76 | 77 | function create( 78 | address recipient, 79 | bool needWrap, 80 | uint256 underlying, 81 | uint256 minOutQ, 82 | uint256 version 83 | ) public returns (uint256 outQ) { 84 | if (needWrap) { 85 | IERC20(_stETH).safeTransferFrom(msg.sender, address(this), underlying); 86 | IERC20(_stETH).approve(_wstETH, underlying); 87 | underlying = IWstETH(_wstETH).wrap(underlying); 88 | IERC20(_wstETH).safeTransfer(address(primaryMarket), underlying); 89 | } else { 90 | IERC20(_wstETH).safeTransferFrom(msg.sender, address(primaryMarket), underlying); 91 | } 92 | 93 | outQ = primaryMarket.create(recipient, minOutQ, version); 94 | } 95 | 96 | function createAndSplit( 97 | address recipient, 98 | bool needWrap, 99 | uint256 underlying, 100 | uint256 minOutQ, 101 | uint256 version 102 | ) external returns (uint256 outB, uint256 outR) { 103 | uint256 outQ = create(address(this), needWrap, underlying, minOutQ, version); 104 | (outB, outR) = primaryMarket.split(recipient, outQ, version); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /contracts/governance/AddressWhitelist.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | contract AddressWhitelist { 5 | mapping(address => bool) public whitelist; 6 | 7 | constructor(address[] memory whitelistAccounts) public { 8 | for (uint256 i = 0; i < whitelistAccounts.length; i++) { 9 | whitelist[whitelistAccounts[i]] = true; 10 | } 11 | } 12 | 13 | function check(address account) external view returns (bool) { 14 | return whitelist[account]; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/governance/Briber.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 6 | 7 | import "./RewardClaimer.sol"; 8 | 9 | interface IBribeMarket { 10 | function BRIBE_VAULT() external view returns (address); 11 | 12 | function depositBribe( 13 | bytes32 proposal, 14 | address token, 15 | uint256 amount, 16 | uint256 maxTokensPerVote, 17 | uint256 periods 18 | ) external; 19 | } 20 | 21 | contract Briber is Ownable { 22 | using SafeERC20 for IERC20; 23 | 24 | // Hidden hands ve2 config 25 | uint256 public constant NO_MAX_TOKENS_PER_VOTE = 0; // No limit 26 | uint256 public constant ONE_PERIOD = 1; // 1 round 27 | 28 | IBribeMarket public immutable bribeMarket; 29 | RewardClaimer public immutable rewardClaimer; 30 | address public immutable token; 31 | 32 | constructor(address bribeMarket_, address rewardClaimer_, address token_) public { 33 | bribeMarket = IBribeMarket(bribeMarket_); 34 | rewardClaimer = RewardClaimer(rewardClaimer_); 35 | token = token_; 36 | } 37 | 38 | function bribe(address gauge) external onlyOwner { 39 | bytes32 proposal = keccak256(abi.encodePacked(gauge)); 40 | _bribe(proposal); 41 | } 42 | 43 | function bribe(bytes32 proposal) external onlyOwner { 44 | _bribe(proposal); 45 | } 46 | 47 | function _bribe(bytes32 proposal) private { 48 | rewardClaimer.claimRewards(); 49 | uint256 bribeAmount = IERC20(token).balanceOf(address(this)); 50 | IERC20(token).safeApprove(bribeMarket.BRIBE_VAULT(), bribeAmount); 51 | bribeMarket.depositBribe(proposal, token, bribeAmount, NO_MAX_TOKENS_PER_VOTE, ONE_PERIOD); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/governance/Chess.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract Chess is ERC20 { 7 | constructor(uint256 initialSupply) public ERC20("Chess", "CHESS") { 8 | _mint(msg.sender, initialSupply); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/governance/ChessRoles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 5 | 6 | abstract contract ChessRoles { 7 | using EnumerableSet for EnumerableSet.AddressSet; 8 | 9 | EnumerableSet.AddressSet private _minterMembers; 10 | 11 | event MinterAdded(address indexed minter); 12 | event MinterRemoved(address indexed minter); 13 | 14 | modifier onlyMinter() { 15 | require(isMinter(msg.sender), "Only minter"); 16 | _; 17 | } 18 | 19 | function isMinter(address account) public view returns (bool) { 20 | return _minterMembers.contains(account); 21 | } 22 | 23 | function getMinterMember(uint256 index) external view returns (address) { 24 | return _minterMembers.at(index); 25 | } 26 | 27 | function getMinterCount() external view returns (uint256) { 28 | return _minterMembers.length(); 29 | } 30 | 31 | function _addMinter(address minter) internal { 32 | if (_minterMembers.add(minter)) { 33 | emit MinterAdded(minter); 34 | } 35 | } 36 | 37 | function _removeMinter(address minter) internal { 38 | if (_minterMembers.remove(minter)) { 39 | emit MinterRemoved(minter); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/governance/ChessScheduleRelayer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 5 | 6 | import "../interfaces/IChessSchedule.sol"; 7 | import "../interfaces/IChessController.sol"; 8 | import "../utils/CoreUtility.sol"; 9 | import "../utils/SafeDecimalMath.sol"; 10 | import "../layerzero/NonblockingLzApp.sol"; 11 | import "../layerzero/interfaces/IOFTCore.sol"; 12 | 13 | contract ChessScheduleRelayer is CoreUtility, NonblockingLzApp { 14 | using SafeDecimalMath for uint256; 15 | using SafeERC20 for IERC20; 16 | 17 | event CrossChainMinted(uint256 chainID, uint256 amount); 18 | event CrossChainSynced(uint256 chainID, uint256 week, uint256 veSupply); 19 | 20 | uint16 public immutable subLzChainID; 21 | 22 | IChessSchedule public immutable chessSchedule; 23 | 24 | IChessController public immutable chessController; 25 | 26 | address public immutable chessPool; 27 | 28 | address public immutable chess; 29 | 30 | mapping(uint256 => uint256) public veSupplyPerWeek; 31 | 32 | uint256 public lastWeek; 33 | 34 | constructor( 35 | uint16 subLzChainID_, 36 | address chessSchedule_, 37 | address chessController_, 38 | address chessPool_, 39 | address endpoint_ 40 | ) public NonblockingLzApp(endpoint_) { 41 | subLzChainID = subLzChainID_; 42 | chessSchedule = IChessSchedule(chessSchedule_); 43 | chessController = IChessController(chessController_); 44 | chessPool = chessPool_; 45 | chess = IOFTCore(chessPool_).token(); 46 | } 47 | 48 | function crossChainMint(bytes memory adapterParams) external payable { 49 | uint256 startWeek = _endOfWeek(block.timestamp) - 1 weeks; 50 | require(startWeek > lastWeek, "Not a new week"); 51 | lastWeek = startWeek; 52 | uint256 amount = chessSchedule.getWeeklySupply(startWeek).multiplyDecimal( 53 | chessController.getFundRelativeWeight(address(this), startWeek) 54 | ); 55 | if (amount != 0) { 56 | chessSchedule.mint(chessPool, amount); 57 | } 58 | uint256 balance = IERC20(chess).balanceOf(address(this)); 59 | if (balance != 0) { 60 | // Additional CHESS rewards directly transferred to this contract 61 | IERC20(chess).safeTransfer(chessPool, balance); 62 | amount += balance; 63 | } 64 | if (amount != 0) { 65 | _checkGasLimit(subLzChainID, 0 /*type*/, adapterParams, 0 /*extraGas*/); 66 | _lzSend( 67 | subLzChainID, 68 | abi.encode(amount), 69 | msg.sender == tx.origin ? msg.sender : payable(owner()), // To avoid reentrancy 70 | address(0x0), 71 | adapterParams, 72 | msg.value 73 | ); 74 | emit CrossChainMinted(subLzChainID, amount); 75 | } 76 | } 77 | 78 | function _nonblockingLzReceive( 79 | uint16, 80 | bytes memory, 81 | uint64, 82 | bytes memory data 83 | ) internal override { 84 | (uint256 week, uint256 supply, uint256 nextWeekSupply) = abi.decode( 85 | data, 86 | (uint256, uint256, uint256) 87 | ); 88 | veSupplyPerWeek[week] = supply; 89 | veSupplyPerWeek[week + 1 weeks] = nextWeekSupply; 90 | emit CrossChainSynced(subLzChainID, week, supply); 91 | emit CrossChainSynced(subLzChainID, week + 1 weeks, nextWeekSupply); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /contracts/governance/FeeConverter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/IFundV5.sol"; 5 | import "../interfaces/IPrimaryMarketV5.sol"; 6 | import "../interfaces/ITrancheIndexV2.sol"; 7 | 8 | contract FeeConverter is ITrancheIndexV2 { 9 | IFundV5 public immutable fund; 10 | IPrimaryMarketV5 public immutable primaryMarket; 11 | address public immutable feeCollector; 12 | 13 | constructor(address primaryMarket_, address feeCollector_) public { 14 | primaryMarket = IPrimaryMarketV5(primaryMarket_); 15 | fund = IFundV5(IPrimaryMarketV5(primaryMarket_).fund()); 16 | feeCollector = feeCollector_; 17 | } 18 | 19 | function collectFee() external { 20 | uint256 fee = fund.trancheBalanceOf(TRANCHE_Q, address(this)); 21 | uint256 version = fund.getRebalanceSize(); 22 | primaryMarket.redeem(feeCollector, fee, 0, version); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/governance/RewardClaimer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | import "../interfaces/IChessSchedule.sol"; 7 | import "../interfaces/IChessController.sol"; 8 | import "../utils/CoreUtility.sol"; 9 | import "../utils/SafeDecimalMath.sol"; 10 | 11 | contract RewardClaimer is Ownable, CoreUtility { 12 | using SafeDecimalMath for uint256; 13 | 14 | event ClaimerUpdated(address newClaimer); 15 | 16 | IChessSchedule public immutable chessSchedule; 17 | IChessController public immutable chessController; 18 | 19 | address public rewardClaimer; 20 | uint256 public lastWeek; 21 | 22 | constructor(address chessSchedule_, address chessController_) public { 23 | chessSchedule = IChessSchedule(chessSchedule_); 24 | chessController = IChessController(chessController_); 25 | lastWeek = _endOfWeek(block.timestamp); 26 | } 27 | 28 | function updateClaimer(address newClaimer) external onlyOwner { 29 | rewardClaimer = newClaimer; 30 | emit ClaimerUpdated(newClaimer); 31 | } 32 | 33 | modifier onlyClaimer() { 34 | require(msg.sender == rewardClaimer, "Only reward claimer"); 35 | _; 36 | } 37 | 38 | function claimRewards() external onlyClaimer { 39 | uint256 amount = _checkpoint(); 40 | chessSchedule.mint(msg.sender, amount); 41 | } 42 | 43 | function _checkpoint() private returns (uint256 amount) { 44 | uint256 w = lastWeek; 45 | uint256 currWeek = _endOfWeek(block.timestamp) - 1 weeks; 46 | 47 | for (; w < block.timestamp; w += 1 weeks) { 48 | uint256 weeklySupply = chessSchedule.getWeeklySupply(w); 49 | if (weeklySupply == 0) { 50 | // CHESS emission may update in the middle of a week due to cross-chain lag, 51 | // so we have to revisit the zero value as long as it is in the current week. 52 | if (w == currWeek) break; 53 | continue; 54 | } 55 | 56 | uint256 weeklyWeight = chessController.getFundRelativeWeight(address(this), w); 57 | if (weeklyWeight == 0) { 58 | continue; 59 | } 60 | 61 | amount = amount.add(weeklySupply.multiplyDecimal(weeklyWeight)); 62 | } 63 | 64 | // Update global state 65 | lastWeek = w; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /contracts/governance/VestingEscrow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | /// @notice Vests `Chess` tokens for a single address 5 | 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 8 | import "@openzeppelin/contracts/access/Ownable.sol"; 9 | import "@openzeppelin/contracts/math/SafeMath.sol"; 10 | 11 | contract VestingEscrow is Ownable { 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | event Fund(uint256 amount); 16 | event Claim(uint256 amount); 17 | event ToggleDisable(bool disabled); 18 | 19 | address public immutable token; 20 | address public immutable recipient; 21 | uint256 public immutable startTime; 22 | uint256 public immutable endTime; 23 | bool public canDisable; 24 | 25 | uint256 public initialLocked; 26 | uint256 public vestedAtStart; 27 | uint256 public totalClaimed; 28 | uint256 public disabledAt; 29 | 30 | constructor( 31 | address token_, 32 | address recipient_, 33 | uint256 startTime_, 34 | uint256 endTime_, 35 | bool canDisable_ 36 | ) public { 37 | token = token_; 38 | recipient = recipient_; 39 | startTime = startTime_; 40 | endTime = endTime_; 41 | canDisable = canDisable_; 42 | } 43 | 44 | function initialize(uint256 amount, uint256 vestedAtStart_) external { 45 | require(amount != 0 && amount >= vestedAtStart_, "Invalid amount or vestedAtStart"); 46 | require(initialLocked == 0, "Already initialized"); 47 | 48 | IERC20(token).safeTransferFrom(msg.sender, address(this), amount); 49 | 50 | initialLocked = amount; 51 | vestedAtStart = vestedAtStart_; 52 | emit Fund(amount); 53 | } 54 | 55 | /// @notice Get the total number of tokens which have vested, that are held 56 | /// by this contract 57 | function vestedSupply() external view returns (uint256) { 58 | return _totalVestedOf(block.timestamp); 59 | } 60 | 61 | /// @notice Get the total number of tokens which are still locked 62 | /// (have not yet vested) 63 | function lockedSupply() external view returns (uint256) { 64 | return initialLocked.sub(_totalVestedOf(block.timestamp)); 65 | } 66 | 67 | /// @notice Get the number of unclaimed, vested tokens for a given address 68 | /// @param account address to check 69 | function balanceOf(address account) external view returns (uint256) { 70 | if (account != recipient) { 71 | return 0; 72 | } 73 | return _totalVestedOf(block.timestamp).sub(totalClaimed); 74 | } 75 | 76 | /// @notice Disable or re-enable a vested address's ability to claim tokens 77 | /// @dev When disabled, the address is only unable to claim tokens which are still 78 | /// locked at the time of this call. It is not possible to block the claim 79 | /// of tokens which have already vested. 80 | function toggleDisable() external onlyOwner { 81 | require(canDisable, "Cannot disable"); 82 | 83 | bool isDisabled = disabledAt == 0; 84 | if (isDisabled) { 85 | disabledAt = block.timestamp; 86 | } else { 87 | disabledAt = 0; 88 | } 89 | 90 | emit ToggleDisable(isDisabled); 91 | } 92 | 93 | /// @notice Disable the ability to call `toggleDisable` 94 | function disableCanDisable() external onlyOwner { 95 | canDisable = false; 96 | } 97 | 98 | /// @notice Claim tokens which have vested 99 | function claim() external { 100 | uint256 timestamp = disabledAt; 101 | if (timestamp == 0) { 102 | timestamp = block.timestamp; 103 | } 104 | uint256 claimable = _totalVestedOf(timestamp).sub(totalClaimed); 105 | totalClaimed = totalClaimed.add(claimable); 106 | IERC20(token).safeTransfer(recipient, claimable); 107 | 108 | emit Claim(claimable); 109 | } 110 | 111 | function _totalVestedOf(uint256 timestamp) internal view returns (uint256) { 112 | uint256 start = startTime; 113 | uint256 end = endTime; 114 | uint256 locked = initialLocked; 115 | if (timestamp < start) { 116 | return 0; 117 | } else if (timestamp > end) { 118 | return locked; 119 | } 120 | uint256 vestedAtStart_ = vestedAtStart; 121 | return 122 | locked.sub(vestedAtStart_).mul(timestamp - start).div(end - start).add(vestedAtStart_); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /contracts/governance/VotingEscrowCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 6 | 7 | import "../interfaces/IVotingEscrow.sol"; 8 | 9 | contract VotingEscrowCallback is IVotingEscrowCallback, Ownable { 10 | using EnumerableSet for EnumerableSet.AddressSet; 11 | 12 | event CallbackHandleAdded(address callbackHandle); 13 | event CallbackHandleRemoved(address callbackHandle); 14 | 15 | EnumerableSet.AddressSet private _handles; 16 | 17 | function getCallbackHandles() external view returns (address[] memory handles) { 18 | uint256 length = _handles.length(); 19 | handles = new address[](length); 20 | for (uint256 i = 0; i < length; i++) { 21 | handles[i] = _handles.at(i); 22 | } 23 | } 24 | 25 | function addCallbackHandle(address callbackHandle) external onlyOwner { 26 | if (_handles.add(callbackHandle)) { 27 | emit CallbackHandleAdded(callbackHandle); 28 | } 29 | } 30 | 31 | function removeCallbackHandle(address callbackHandle) external onlyOwner { 32 | if (_handles.remove(callbackHandle)) { 33 | emit CallbackHandleRemoved(callbackHandle); 34 | } 35 | } 36 | 37 | function syncWithVotingEscrow(address account) external override { 38 | uint256 count = _handles.length(); 39 | for (uint256 i = 0; i < count; i++) { 40 | IVotingEscrowCallback(_handles.at(i)).syncWithVotingEscrow(account); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/IAnyswapV6ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IAnyswapV6ERC20 is IERC20 { 7 | function underlying() external view returns (address); 8 | 9 | function mint(address to, uint256 amount) external returns (bool); 10 | 11 | function burn(address from, uint256 amount) external returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IAprOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IAprOracle { 5 | function capture() external returns (uint256 dailyRate); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IBallot.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IBallot { 5 | struct Voter { 6 | uint256 amount; 7 | uint256 unlockTime; 8 | uint256 weight; 9 | } 10 | 11 | function count(uint256 timestamp) external view returns (uint256); 12 | 13 | function syncWithVotingEscrow(address account) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/IChessController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IChessController { 5 | function getFundRelativeWeight(address account, uint256 timestamp) external returns (uint256); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IChessSchedule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IChessSchedule { 5 | function getWeeklySupply(uint256 timestamp) external view returns (uint256); 6 | 7 | function getRate(uint256 timestamp) external view returns (uint256); 8 | 9 | function mint(address account, uint256 amount) external; 10 | 11 | function addMinter(address account) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IControllerBallot.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IControllerBallot { 5 | function count( 6 | uint256 timestamp 7 | ) external view returns (uint256[] memory ratios, address[] memory funds); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interfaces/IControllerBallotV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IControllerBallotV2 { 5 | function totalSupplyAtWeek(uint256 week) external view returns (uint256); 6 | 7 | function sumAtWeek(address pool, uint256 week) external view returns (uint256); 8 | 9 | function count( 10 | uint256 week 11 | ) external view returns (uint256[] memory sums, address[] memory funds); 12 | 13 | function cast(uint256[] memory weights) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundForPrimaryMarketV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IFundForPrimaryMarketV3 { 5 | function primaryMarketMint( 6 | uint256 tranche, 7 | address account, 8 | uint256 amount, 9 | uint256 version 10 | ) external; 11 | 12 | function primaryMarketBurn( 13 | uint256 tranche, 14 | address account, 15 | uint256 amount, 16 | uint256 version 17 | ) external; 18 | 19 | function primaryMarketTransferUnderlying( 20 | address recipient, 21 | uint256 amount, 22 | uint256 fee 23 | ) external; 24 | 25 | function primaryMarketAddDebt(uint256 amount, uint256 fee) external; 26 | 27 | function primaryMarketPayDebt(uint256 amount) external; 28 | } 29 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundForPrimaryMarketV4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IFundForPrimaryMarketV4 { 5 | function primaryMarketMint( 6 | uint256 tranche, 7 | address account, 8 | uint256 amount, 9 | uint256 version 10 | ) external; 11 | 12 | function primaryMarketBurn( 13 | uint256 tranche, 14 | address account, 15 | uint256 amount, 16 | uint256 version 17 | ) external; 18 | 19 | function primaryMarketTransferUnderlying( 20 | address recipient, 21 | uint256 amount, 22 | uint256 feeQ 23 | ) external; 24 | 25 | function primaryMarketAddDebtAndFee(uint256 amount, uint256 feeQ) external; 26 | 27 | function primaryMarketPayDebt(uint256 amount) external; 28 | } 29 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundForStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IFundForStrategy { 5 | function transferToStrategy(uint256 amount) external; 6 | 7 | function transferFromStrategy(uint256 amount) external; 8 | 9 | function reportProfit(uint256 profit, uint256 performanceFee) external; 10 | 11 | function reportLoss(uint256 loss) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundForStrategyV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IFundForStrategyV2 { 5 | function transferToStrategy(uint256 amount) external; 6 | 7 | function transferFromStrategy(uint256 amount) external; 8 | 9 | function reportProfit( 10 | uint256 profit, 11 | uint256 totalFee, 12 | uint256 strategyFee 13 | ) external returns (uint256 outQ); 14 | 15 | function reportLoss(uint256 loss) external; 16 | } 17 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundV4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./IFundV3.sol"; 5 | 6 | interface IFundV4 is IFundV3 { 7 | function getRelativeIncome( 8 | uint256 day 9 | ) external view returns (uint256 incomeOverQ, uint256 incomeOverB); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/IFundV5.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./IFundV3.sol"; 5 | 6 | interface IFundV5 is IFundV3 { 7 | function weightB() external view returns (uint256); 8 | 9 | function getSettledDay() external view returns (uint256); 10 | 11 | function getEquivalentTotalR() external view returns (uint256); 12 | 13 | function frozen() external view returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/ILiquidityGauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | /** 7 | * @dev Interface of the ERC20 standard as defined in the EIP. 8 | */ 9 | interface ILiquidityGauge is IERC20 { 10 | function mint(address account, uint256 amount) external; 11 | 12 | function burnFrom(address account, uint256 amount) external; 13 | 14 | function workingSupply() external view returns (uint256); 15 | 16 | function workingBalanceOf(address account) external view returns (uint256); 17 | 18 | function claimableRewards( 19 | address account 20 | ) 21 | external 22 | returns ( 23 | uint256 chessAmount, 24 | uint256 bonusAmount, 25 | uint256 amountQ, 26 | uint256 amountB, 27 | uint256 amountR, 28 | uint256 quoteAmount 29 | ); 30 | 31 | function claimRewards(address account) external; 32 | 33 | function distribute( 34 | uint256 amountQ, 35 | uint256 amountB, 36 | uint256 amountR, 37 | uint256 quoteAmount, 38 | uint256 version 39 | ) external; 40 | } 41 | -------------------------------------------------------------------------------- /contracts/interfaces/IPrimaryMarketRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../interfaces/IFundV3.sol"; 6 | import "../interfaces/IStableSwap.sol"; 7 | 8 | interface IPrimaryMarketRouter is IStableSwapCore { 9 | function create( 10 | address recipient, 11 | uint256 underlying, 12 | uint256 minOutQ, 13 | uint256 version 14 | ) external payable returns (uint256 outQ); 15 | 16 | function createAndStake( 17 | uint256 underlying, 18 | uint256 minOutQ, 19 | address staking, 20 | uint256 version 21 | ) external payable; 22 | 23 | function createSplitAndStake( 24 | uint256 underlying, 25 | uint256 minOutQ, 26 | address router, 27 | address quoteAddress, 28 | uint256 minLpOut, 29 | address staking, 30 | uint256 version 31 | ) external payable; 32 | 33 | function splitAndStake( 34 | uint256 inQ, 35 | address router, 36 | address quoteAddress, 37 | uint256 minLpOut, 38 | address staking, 39 | uint256 version 40 | ) external; 41 | } 42 | -------------------------------------------------------------------------------- /contracts/interfaces/IPrimaryMarketRouterV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../interfaces/IFundV3.sol"; 6 | import "../interfaces/IStableSwap.sol"; 7 | 8 | interface IPrimaryMarketRouterV2 is IStableSwapCore { 9 | function create( 10 | address recipient, 11 | uint256 underlying, 12 | uint256 minOutQ, 13 | uint256 version 14 | ) external payable returns (uint256 outQ); 15 | 16 | function createAndSplit( 17 | address recipient, 18 | uint256 underlying, 19 | uint256 minOutQ, 20 | uint256 version 21 | ) external payable returns (uint256 outB, uint256 outR); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interfaces/IPrimaryMarketV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IPrimaryMarketV3 { 5 | function fund() external view returns (address); 6 | 7 | function getCreation(uint256 underlying) external view returns (uint256 outQ); 8 | 9 | function getCreationForQ(uint256 minOutQ) external view returns (uint256 underlying); 10 | 11 | function getRedemption(uint256 inQ) external view returns (uint256 underlying, uint256 fee); 12 | 13 | function getRedemptionForUnderlying(uint256 minUnderlying) external view returns (uint256 inQ); 14 | 15 | function getSplit(uint256 inQ) external view returns (uint256 outB); 16 | 17 | function getSplitForB(uint256 minOutB) external view returns (uint256 inQ); 18 | 19 | function getMerge(uint256 inB) external view returns (uint256 outQ, uint256 feeQ); 20 | 21 | function getMergeForQ(uint256 minOutQ) external view returns (uint256 inB); 22 | 23 | function canBeRemovedFromFund() external view returns (bool); 24 | 25 | function create( 26 | address recipient, 27 | uint256 minOutQ, 28 | uint256 version 29 | ) external returns (uint256 outQ); 30 | 31 | function redeem( 32 | address recipient, 33 | uint256 inQ, 34 | uint256 minUnderlying, 35 | uint256 version 36 | ) external returns (uint256 underlying); 37 | 38 | function redeemAndUnwrap( 39 | address recipient, 40 | uint256 inQ, 41 | uint256 minUnderlying, 42 | uint256 version 43 | ) external returns (uint256 underlying); 44 | 45 | function queueRedemption( 46 | address recipient, 47 | uint256 inQ, 48 | uint256 minUnderlying, 49 | uint256 version 50 | ) external returns (uint256 underlying, uint256 index); 51 | 52 | function claimRedemptions( 53 | address account, 54 | uint256[] calldata indices 55 | ) external returns (uint256 underlying); 56 | 57 | function claimRedemptionsAndUnwrap( 58 | address account, 59 | uint256[] calldata indices 60 | ) external returns (uint256 underlying); 61 | 62 | function split(address recipient, uint256 inQ, uint256 version) external returns (uint256 outB); 63 | 64 | function merge(address recipient, uint256 inB, uint256 version) external returns (uint256 outQ); 65 | 66 | function settle(uint256 day) external; 67 | } 68 | -------------------------------------------------------------------------------- /contracts/interfaces/IPrimaryMarketV5.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IPrimaryMarketV5 { 5 | function fund() external view returns (address); 6 | 7 | function getCreation(uint256 underlying) external view returns (uint256 outQ); 8 | 9 | function getCreationForQ(uint256 minOutQ) external view returns (uint256 underlying); 10 | 11 | function getRedemption(uint256 inQ) external view returns (uint256 underlying, uint256 fee); 12 | 13 | function getRedemptionForUnderlying(uint256 minUnderlying) external view returns (uint256 inQ); 14 | 15 | function getSplit(uint256 inQ) external view returns (uint256 outB, uint256 outR); 16 | 17 | function getSplitForR(uint256 minOutR) external view returns (uint256 inQ, uint256 outB); 18 | 19 | function getMerge(uint256 inB) external view returns (uint256 inR, uint256 outQ, uint256 feeQ); 20 | 21 | function getMergeByR( 22 | uint256 inR 23 | ) external view returns (uint256 inB, uint256 outQ, uint256 feeQ); 24 | 25 | function canBeRemovedFromFund() external view returns (bool); 26 | 27 | function create( 28 | address recipient, 29 | uint256 minOutQ, 30 | uint256 version 31 | ) external returns (uint256 outQ); 32 | 33 | function redeem( 34 | address recipient, 35 | uint256 inQ, 36 | uint256 minUnderlying, 37 | uint256 version 38 | ) external returns (uint256 underlying); 39 | 40 | function redeemAndUnwrap( 41 | address recipient, 42 | uint256 inQ, 43 | uint256 minUnderlying, 44 | uint256 version 45 | ) external returns (uint256 underlying); 46 | 47 | function redeemAndUnwrapWstETH( 48 | address recipient, 49 | uint256 inQ, 50 | uint256 minStETH, 51 | uint256 version 52 | ) external returns (uint256 stETHAmount); 53 | 54 | function queueRedemption( 55 | address recipient, 56 | uint256 inQ, 57 | uint256 minUnderlying, 58 | uint256 version 59 | ) external returns (uint256 underlying, uint256 index); 60 | 61 | function claimRedemptions( 62 | address account, 63 | uint256[] calldata indices 64 | ) external returns (uint256 underlying); 65 | 66 | function claimRedemptionsAndUnwrap( 67 | address account, 68 | uint256[] calldata indices 69 | ) external returns (uint256 underlying); 70 | 71 | function claimRedemptionsAndUnwrapWstETH( 72 | address account, 73 | uint256[] calldata indices 74 | ) external returns (uint256 stETHAmount); 75 | 76 | function split( 77 | address recipient, 78 | uint256 inQ, 79 | uint256 version 80 | ) external returns (uint256 outB, uint256 outR); 81 | 82 | function merge(address recipient, uint256 inB, uint256 version) external returns (uint256 outQ); 83 | 84 | function settle(uint256 day) external; 85 | } 86 | -------------------------------------------------------------------------------- /contracts/interfaces/IShareV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IShareV2 is IERC20 { 7 | function fundEmitTransfer(address sender, address recipient, uint256 amount) external; 8 | 9 | function fundEmitApproval(address owner, address spender, uint256 amount) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/IStableSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/IFundV3.sol"; 5 | 6 | interface IStableSwapCore { 7 | function getQuoteOut(uint256 baseIn) external view returns (uint256 quoteOut); 8 | 9 | function getQuoteIn(uint256 baseOut) external view returns (uint256 quoteIn); 10 | 11 | function getBaseOut(uint256 quoteIn) external view returns (uint256 baseOut); 12 | 13 | function getBaseIn(uint256 quoteOut) external view returns (uint256 baseIn); 14 | 15 | function buy( 16 | uint256 version, 17 | uint256 baseOut, 18 | address recipient, 19 | bytes calldata data 20 | ) external returns (uint256 realBaseOut); 21 | 22 | function sell( 23 | uint256 version, 24 | uint256 quoteOut, 25 | address recipient, 26 | bytes calldata data 27 | ) external returns (uint256 realQuoteOut); 28 | } 29 | 30 | interface IStableSwap is IStableSwapCore { 31 | function fund() external view returns (IFundV3); 32 | 33 | function baseTranche() external view returns (uint256); 34 | 35 | function baseAddress() external view returns (address); 36 | 37 | function quoteAddress() external view returns (address); 38 | 39 | function allBalances() external view returns (uint256, uint256); 40 | 41 | function getOraclePrice() external view returns (uint256); 42 | 43 | function getCurrentD() external view returns (uint256); 44 | 45 | function getCurrentPriceOverOracle() external view returns (uint256); 46 | 47 | function getCurrentPrice() external view returns (uint256); 48 | 49 | function getPriceOverOracleIntegral() external view returns (uint256); 50 | 51 | function addLiquidity(uint256 version, address recipient) external returns (uint256); 52 | 53 | function removeLiquidity( 54 | uint256 version, 55 | uint256 lpIn, 56 | uint256 minBaseOut, 57 | uint256 minQuoteOut 58 | ) external returns (uint256 baseOut, uint256 quoteOut); 59 | 60 | function removeLiquidityUnwrap( 61 | uint256 version, 62 | uint256 lpIn, 63 | uint256 minBaseOut, 64 | uint256 minQuoteOut 65 | ) external returns (uint256 baseOut, uint256 quoteOut); 66 | 67 | function removeBaseLiquidity( 68 | uint256 version, 69 | uint256 lpIn, 70 | uint256 minBaseOut 71 | ) external returns (uint256 baseOut); 72 | 73 | function removeQuoteLiquidity( 74 | uint256 version, 75 | uint256 lpIn, 76 | uint256 minQuoteOut 77 | ) external returns (uint256 quoteOut); 78 | 79 | function removeQuoteLiquidityUnwrap( 80 | uint256 version, 81 | uint256 lpIn, 82 | uint256 minQuoteOut 83 | ) external returns (uint256 quoteOut); 84 | } 85 | 86 | /// @dev The interface shares the same function names as in `IStableSwapCore`; 87 | /// all getters are defined as non-view functions in order to parse and 88 | /// return the internal revert messages 89 | interface IStableSwapCoreInternalRevertExpected { 90 | function getQuoteOut(uint256 baseIn) external returns (uint256 quoteOut); 91 | 92 | function getQuoteIn(uint256 baseOut) external returns (uint256 quoteIn); 93 | 94 | function getBaseOut(uint256 quoteIn) external returns (uint256 baseOut); 95 | 96 | function getBaseIn(uint256 quoteOut) external returns (uint256 baseIn); 97 | 98 | function buy( 99 | uint256 version, 100 | uint256 baseOut, 101 | address recipient, 102 | bytes calldata data 103 | ) external returns (uint256 realBaseOut); 104 | 105 | function sell( 106 | uint256 version, 107 | uint256 quoteOut, 108 | address recipient, 109 | bytes calldata data 110 | ) external returns (uint256 realQuoteOut); 111 | } 112 | -------------------------------------------------------------------------------- /contracts/interfaces/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./IStableSwap.sol"; 5 | 6 | interface ISwapRouter { 7 | function getSwap(address baseToken, address quoteToken) external view returns (IStableSwap); 8 | 9 | function getAmountsOut( 10 | uint256 amount, 11 | address[] memory path 12 | ) 13 | external 14 | view 15 | returns (uint256[] memory amounts, IStableSwap[] memory swaps, bool[] memory isBuy); 16 | 17 | function getAmountsIn( 18 | uint256 amount, 19 | address[] memory path 20 | ) 21 | external 22 | view 23 | returns (uint256[] memory amounts, IStableSwap[] memory swaps, bool[] memory isBuy); 24 | 25 | function addLiquidity( 26 | address baseToken, 27 | address quoteToken, 28 | uint256 baseDelta, 29 | uint256 quoteDelta, 30 | uint256 minMintAmount, 31 | uint256 version, 32 | uint256 deadline 33 | ) external payable; 34 | 35 | function swapExactTokensForTokens( 36 | uint256 amountIn, 37 | uint256 minAmountOut, 38 | address[] calldata path, 39 | address recipient, 40 | address staking, 41 | uint256[] calldata versions, 42 | uint256 deadline 43 | ) external payable returns (uint256[] memory amounts); 44 | 45 | function swapTokensForExactTokens( 46 | uint256 amountOut, 47 | uint256 maxAmountIn, 48 | address[] calldata path, 49 | address recipient, 50 | address staking, 51 | uint256[] calldata versions, 52 | uint256 deadline 53 | ) external payable returns (uint256[] memory amounts); 54 | 55 | function swapExactTokensForTokensUnwrap( 56 | uint256 amountIn, 57 | uint256 minAmountOut, 58 | address[] calldata path, 59 | address recipient, 60 | uint256[] calldata versions, 61 | uint256 deadline 62 | ) external returns (uint256[] memory amounts); 63 | 64 | function swapTokensForExactTokensUnwrap( 65 | uint256 amountOut, 66 | uint256 maxAmountIn, 67 | address[] calldata path, 68 | address recipient, 69 | uint256[] calldata versions, 70 | uint256 deadline 71 | ) external returns (uint256[] memory amounts); 72 | } 73 | -------------------------------------------------------------------------------- /contracts/interfaces/ITrancheIndexV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | /// @notice Amounts of QUEEN, BISHOP and ROOK are sometimes stored in a `uint256[3]` array. 5 | /// This contract defines index of each tranche in this array. 6 | /// 7 | /// Solidity does not allow constants to be defined in interfaces. So this contract follows 8 | /// the naming convention of interfaces but is implemented as an `abstract contract`. 9 | abstract contract ITrancheIndexV2 { 10 | uint256 internal constant TRANCHE_Q = 0; 11 | uint256 internal constant TRANCHE_B = 1; 12 | uint256 internal constant TRANCHE_R = 2; 13 | 14 | uint256 internal constant TRANCHE_COUNT = 3; 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/ITranchessSwapCallee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface ITranchessSwapCallee { 5 | function tranchessSwapCallback( 6 | uint256 baseDeltaOut, 7 | uint256 quoteDeltaOut, 8 | bytes calldata data 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/ITwapOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface ITwapOracle { 5 | enum UpdateType { 6 | PRIMARY, 7 | SECONDARY, 8 | OWNER, 9 | CHAINLINK, 10 | UNISWAP_V2 11 | } 12 | 13 | function getTwap(uint256 timestamp) external view returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/ITwapOracleV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./ITwapOracle.sol"; 5 | 6 | interface ITwapOracleV2 is ITwapOracle { 7 | function getLatest() external view returns (uint256); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interfaces/IVotingEscrow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface IAddressWhitelist { 6 | function check(address account) external view returns (bool); 7 | } 8 | 9 | interface IVotingEscrowCallback { 10 | function syncWithVotingEscrow(address account) external; 11 | } 12 | 13 | interface IVotingEscrow { 14 | struct LockedBalance { 15 | uint256 amount; 16 | uint256 unlockTime; 17 | } 18 | 19 | function token() external view returns (address); 20 | 21 | function maxTime() external view returns (uint256); 22 | 23 | function balanceOf(address account) external view returns (uint256); 24 | 25 | function totalSupply() external view returns (uint256); 26 | 27 | function balanceOfAtTimestamp( 28 | address account, 29 | uint256 timestamp 30 | ) external view returns (uint256); 31 | 32 | function getTimestampDropBelow( 33 | address account, 34 | uint256 threshold 35 | ) external view returns (uint256); 36 | 37 | function getLockedBalance(address account) external view returns (LockedBalance memory); 38 | } 39 | -------------------------------------------------------------------------------- /contracts/interfaces/IWrappedERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IWrappedERC20 is IERC20 { 7 | function deposit() external payable; 8 | 9 | function withdraw(uint256 wad) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/IWstETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IWstETH { 5 | function stETH() external view returns (address); 6 | 7 | function stEthPerToken() external view returns (uint256); 8 | 9 | function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256); 10 | 11 | function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256); 12 | 13 | function wrap(uint256 _stETHAmount) external returns (uint256); 14 | 15 | function unwrap(uint256 _wstETHAmount) external returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/keeper/BatchKeeperHelperBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 6 | import "@chainlink/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol"; 7 | 8 | contract BatchKeeperHelperBase is KeeperCompatibleInterface, Ownable { 9 | using EnumerableSet for EnumerableSet.AddressSet; 10 | 11 | event AllowlistAdded(address contractAddress); 12 | event AllowlistRemoved(address contractAddress); 13 | 14 | EnumerableSet.AddressSet private _allowlist; 15 | 16 | constructor(address[] memory contracts_) public { 17 | for (uint256 i = 0; i < contracts_.length; i++) { 18 | _allowlist.add(contracts_[i]); 19 | emit AllowlistAdded(contracts_[i]); 20 | } 21 | } 22 | 23 | function allowlist() external view returns (address[] memory list) { 24 | uint256 length = _allowlist.length(); 25 | list = new address[](length); 26 | for (uint256 i = 0; i < length; i++) { 27 | list[i] = _allowlist.at(i); 28 | } 29 | } 30 | 31 | function addAllowlist(address contractAddress) external onlyOwner { 32 | if (_allowlist.add(contractAddress)) { 33 | emit AllowlistAdded(contractAddress); 34 | } 35 | } 36 | 37 | function removeAllowlist(address contractAddress) external onlyOwner { 38 | if (_allowlist.remove(contractAddress)) { 39 | emit AllowlistRemoved(contractAddress); 40 | } 41 | } 42 | 43 | function checkUpkeep( 44 | bytes calldata 45 | ) external override returns (bool upkeepNeeded, bytes memory performData) { 46 | uint256 length = _allowlist.length(); 47 | for (uint256 i = 0; i < length; i++) { 48 | address contractAddress = _allowlist.at(i); 49 | if (_checkUpkeep(contractAddress)) { 50 | upkeepNeeded = true; 51 | performData = abi.encodePacked(performData, contractAddress); 52 | } 53 | } 54 | } 55 | 56 | function performUpkeep(bytes calldata performData) external override { 57 | uint256 contractLength = performData.length / 20; 58 | require(contractLength > 0); 59 | for (uint256 i = 0; i < contractLength; i++) { 60 | address contractAddress = _getContractAddr(i); 61 | require(_allowlist.contains(contractAddress), "Not allowlisted"); 62 | _performUpkeep(contractAddress); 63 | } 64 | } 65 | 66 | function _getContractAddr(uint256 index) private pure returns (address contractAddress) { 67 | assembly { 68 | // 0x38 = 0x4 + 0x20 + 0x14 69 | contractAddress := calldataload(add(0x38, mul(index, 0x14))) 70 | } 71 | } 72 | 73 | function _checkUpkeep(address contractAddress) internal virtual returns (bool) {} 74 | 75 | function _performUpkeep(address contractAddress) internal virtual {} 76 | } 77 | -------------------------------------------------------------------------------- /contracts/keeper/CrossChainMintKeeperHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@chainlink/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol"; 6 | import "../layerzero/interfaces/ILayerZeroEndpoint.sol"; 7 | import "../utils/CoreUtility.sol"; 8 | 9 | interface IScheduleRelayer { 10 | function lzEndpoint() external view returns (ILayerZeroEndpoint); 11 | 12 | function subLzChainID() external view returns (uint16); 13 | 14 | function lastWeek() external view returns (uint256); 15 | 16 | function crossChainMint(bytes memory adapterParams) external payable; 17 | } 18 | 19 | contract CrossChainMintKeeperHelper is KeeperCompatibleInterface, Ownable, CoreUtility { 20 | uint256 private constant DATA_LENGTH = 32; // abi.encode(uint256) 21 | uint256 private constant MINT_GAS_LIMIT = 140000; 22 | 23 | IScheduleRelayer public immutable relayer; 24 | uint16 public immutable subLzChainID; 25 | ILayerZeroEndpoint public immutable lzEndpoint; 26 | 27 | constructor(address relayer_) public { 28 | relayer = IScheduleRelayer(relayer_); 29 | subLzChainID = IScheduleRelayer(relayer_).subLzChainID(); 30 | lzEndpoint = IScheduleRelayer(relayer_).lzEndpoint(); 31 | } 32 | 33 | receive() external payable {} 34 | 35 | function withdraw(uint256 value) external onlyOwner { 36 | (bool success, ) = msg.sender.call{value: value}(""); 37 | require(success, "ETH transfer failed"); 38 | } 39 | 40 | function checkUpkeep( 41 | bytes calldata 42 | ) external override returns (bool upkeepNeeded, bytes memory) { 43 | uint256 startWeek = _endOfWeek(block.timestamp) - 1 weeks; 44 | uint256 lastWeek = relayer.lastWeek(); 45 | upkeepNeeded = (startWeek > lastWeek); 46 | } 47 | 48 | function performUpkeep(bytes calldata) external override { 49 | (uint256 srcFees, ) = lzEndpoint.estimateFees( 50 | subLzChainID, 51 | address(relayer), 52 | new bytes(DATA_LENGTH), 53 | false, 54 | abi.encodePacked(uint16(1), MINT_GAS_LIMIT) 55 | ); 56 | require(address(this).balance >= srcFees, "Not enough balance"); 57 | relayer.crossChainMint{value: srcFees}(abi.encodePacked(uint16(1), MINT_GAS_LIMIT)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/keeper/CrossChainSyncKeeperHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@chainlink/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol"; 6 | import "../layerzero/interfaces/ILayerZeroEndpoint.sol"; 7 | 8 | interface ISubSchedule { 9 | function lzEndpoint() external view returns (ILayerZeroEndpoint); 10 | 11 | function mainLzChainID() external view returns (uint16); 12 | 13 | function crossChainSync(bytes memory adapterParams) external payable; 14 | } 15 | 16 | contract CrossChainSyncKeeperHelper is KeeperCompatibleInterface, Ownable { 17 | uint256 private constant DATA_LENGTH = 96; // abi.encode(uint256,uint256,uint256) 18 | uint256 private constant SYNC_GAS_LIMIT = 90000; 19 | 20 | ISubSchedule public immutable subSchedule; 21 | uint16 public immutable mainLzChainID; 22 | ILayerZeroEndpoint public immutable lzEndpoint; 23 | 24 | uint256 public lastTimestamp; 25 | 26 | constructor(address subSchedule_) public { 27 | subSchedule = ISubSchedule(subSchedule_); 28 | mainLzChainID = ISubSchedule(subSchedule_).mainLzChainID(); 29 | lzEndpoint = ISubSchedule(subSchedule_).lzEndpoint(); 30 | _updateLastTimestamp(block.timestamp); 31 | } 32 | 33 | receive() external payable {} 34 | 35 | function withdraw(uint256 value) external onlyOwner { 36 | (bool success, ) = msg.sender.call{value: value}(""); 37 | require(success, "ETH transfer failed"); 38 | } 39 | 40 | function updateLastTimestamp(uint256 lastTimestamp_) external onlyOwner { 41 | _updateLastTimestamp(lastTimestamp_); 42 | } 43 | 44 | function checkUpkeep( 45 | bytes calldata 46 | ) external override returns (bool upkeepNeeded, bytes memory) { 47 | upkeepNeeded = (block.timestamp > lastTimestamp + 1 weeks); 48 | } 49 | 50 | function performUpkeep(bytes calldata) external override { 51 | uint256 lastTimestamp_ = lastTimestamp; 52 | require(block.timestamp > lastTimestamp_ + 1 weeks, "Not yet"); 53 | 54 | (uint256 srcFees, ) = lzEndpoint.estimateFees( 55 | mainLzChainID, 56 | address(subSchedule), 57 | new bytes(DATA_LENGTH), 58 | false, 59 | abi.encodePacked(uint16(1), SYNC_GAS_LIMIT) 60 | ); 61 | require(address(this).balance >= srcFees, "Not enough balance"); 62 | subSchedule.crossChainSync{value: srcFees}(abi.encodePacked(uint16(1), SYNC_GAS_LIMIT)); 63 | 64 | // Always skip to the lastest week 65 | _updateLastTimestamp( 66 | lastTimestamp_ + ((block.timestamp - lastTimestamp_ - 1) / 1 weeks) * 1 weeks 67 | ); 68 | } 69 | 70 | function _updateLastTimestamp(uint256 lastTimestamp_) private { 71 | lastTimestamp = lastTimestamp_; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/keeper/FundKeeperHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./BatchKeeperHelperBase.sol"; 5 | import "../interfaces/IFundV3.sol"; 6 | 7 | interface IFundSettlement is IFundV3 { 8 | function settle() external; 9 | } 10 | 11 | interface IDistributor { 12 | function checkpoint() external; 13 | } 14 | 15 | contract FundKeeperHelper is BatchKeeperHelperBase { 16 | uint256 public delay; 17 | 18 | constructor(address[] memory funds_, uint256 delay_) public BatchKeeperHelperBase(funds_) { 19 | delay = delay_; 20 | } 21 | 22 | function updateDelay(uint256 newDelay) external onlyOwner { 23 | delay = newDelay; 24 | } 25 | 26 | function _checkUpkeep(address contractAddress) internal override returns (bool) { 27 | IFundSettlement fund = IFundSettlement(contractAddress); 28 | uint256 currentDay = fund.currentDay(); 29 | uint256 price = fund.twapOracle().getTwap(currentDay); 30 | return (block.timestamp >= currentDay + delay && price != 0); 31 | } 32 | 33 | function _performUpkeep(address contractAddress) internal override { 34 | IFundSettlement(contractAddress).settle(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/layerzero/NonblockingLzApp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.10 <0.8.0; 4 | 5 | import "./LzApp.sol"; 6 | import "./utils/ExcessivelySafeCall.sol"; 7 | 8 | /* 9 | * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel 10 | * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking 11 | * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress) 12 | */ 13 | abstract contract NonblockingLzApp is LzApp { 14 | using ExcessivelySafeCall for address; 15 | 16 | constructor(address _endpoint) public LzApp(_endpoint) {} 17 | 18 | mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages; 19 | 20 | event MessageFailed( 21 | uint16 _srcChainId, 22 | bytes _srcAddress, 23 | uint64 _nonce, 24 | bytes _payload, 25 | bytes _reason 26 | ); 27 | event RetryMessageSuccess( 28 | uint16 _srcChainId, 29 | bytes _srcAddress, 30 | uint64 _nonce, 31 | bytes32 _payloadHash 32 | ); 33 | 34 | // overriding the virtual function in LzReceiver 35 | function _blockingLzReceive( 36 | uint16 _srcChainId, 37 | bytes memory _srcAddress, 38 | uint64 _nonce, 39 | bytes memory _payload 40 | ) internal virtual override { 41 | bool success = false; 42 | bytes memory reason; 43 | uint256 gas = gasleft(); 44 | if (gas >= 30000) { 45 | (success, reason) = address(this).excessivelySafeCall( 46 | gas - 30000, 47 | 150, 48 | abi.encodeWithSelector( 49 | this.nonblockingLzReceive.selector, 50 | _srcChainId, 51 | _srcAddress, 52 | _nonce, 53 | _payload 54 | ) 55 | ); 56 | } 57 | 58 | // try-catch all errors/exceptions 59 | if (!success) { 60 | _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason); 61 | } 62 | } 63 | 64 | function _storeFailedMessage( 65 | uint16 _srcChainId, 66 | bytes memory _srcAddress, 67 | uint64 _nonce, 68 | bytes memory _payload, 69 | bytes memory _reason 70 | ) internal virtual { 71 | failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); 72 | emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason); 73 | } 74 | 75 | function nonblockingLzReceive( 76 | uint16 _srcChainId, 77 | bytes calldata _srcAddress, 78 | uint64 _nonce, 79 | bytes calldata _payload 80 | ) public virtual { 81 | // only internal transaction 82 | require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp"); 83 | _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); 84 | } 85 | 86 | //@notice override this function 87 | function _nonblockingLzReceive( 88 | uint16 _srcChainId, 89 | bytes memory _srcAddress, 90 | uint64 _nonce, 91 | bytes memory _payload 92 | ) internal virtual; 93 | 94 | function retryMessage( 95 | uint16 _srcChainId, 96 | bytes calldata _srcAddress, 97 | uint64 _nonce, 98 | bytes calldata _payload 99 | ) public payable virtual { 100 | // assert there is message to retry 101 | bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; 102 | require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message"); 103 | require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload"); 104 | // clear the stored message 105 | failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0); 106 | // execute the message. revert if it fails again 107 | _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); 108 | emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /contracts/layerzero/ProxyOFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.10 <0.8.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./OFTCore.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 8 | 9 | contract ProxyOFT is OFTCore { 10 | using SafeERC20 for IERC20; 11 | 12 | IERC20 internal immutable innerToken; 13 | 14 | constructor(address _lzEndpoint, address _token) public OFTCore(_lzEndpoint) { 15 | innerToken = IERC20(_token); 16 | } 17 | 18 | function circulatingSupply() public view virtual override returns (uint256) { 19 | return innerToken.totalSupply() - innerToken.balanceOf(address(this)); 20 | } 21 | 22 | function token() public view virtual override returns (address) { 23 | return address(innerToken); 24 | } 25 | 26 | function _debitFrom( 27 | address _from, 28 | uint16, 29 | bytes memory, 30 | uint256 _amount 31 | ) internal virtual override returns (uint256) { 32 | require(_from == _msgSender(), "ProxyOFT: owner is not send caller"); 33 | uint256 before = innerToken.balanceOf(address(this)); 34 | innerToken.safeTransferFrom(_from, address(this), _amount); 35 | return innerToken.balanceOf(address(this)) - before; 36 | } 37 | 38 | function _creditTo( 39 | uint16, 40 | address _toAddress, 41 | uint256 _amount 42 | ) internal virtual override returns (uint256) { 43 | uint256 before = innerToken.balanceOf(_toAddress); 44 | innerToken.safeTransfer(_toAddress, _amount); 45 | return innerToken.balanceOf(_toAddress) - before; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/layerzero/ProxyOFTPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | pragma solidity >=0.6.10 <0.8.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | import "./ProxyOFT.sol"; 9 | 10 | import "../governance/ChessRoles.sol"; 11 | 12 | contract ProxyOFTPool is ProxyOFT, ChessRoles { 13 | constructor(address _lzEndpoint, address _token) public ProxyOFT(_lzEndpoint, _token) {} 14 | 15 | function addMinter(address account) external onlyOwner { 16 | _addMinter(account); 17 | } 18 | 19 | function removeMinter(address account) external onlyOwner { 20 | _removeMinter(account); 21 | } 22 | 23 | function withdrawUnderlying(uint256 amount) external onlyMinter { 24 | IERC20(token()).safeTransfer(msg.sender, amount); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/layerzero/UpgradeableNonblockingLzApp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.10 <0.8.0; 4 | 5 | import "./UpgradeableLzApp.sol"; 6 | import "./utils/ExcessivelySafeCall.sol"; 7 | 8 | /* 9 | * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel 10 | * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking 11 | * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress) 12 | */ 13 | abstract contract UpgradeableNonblockingLzApp is UpgradeableLzApp { 14 | using ExcessivelySafeCall for address; 15 | 16 | constructor(address _endpoint) public UpgradeableLzApp(_endpoint) {} 17 | 18 | mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages; 19 | 20 | event MessageFailed( 21 | uint16 _srcChainId, 22 | bytes _srcAddress, 23 | uint64 _nonce, 24 | bytes _payload, 25 | bytes _reason 26 | ); 27 | event RetryMessageSuccess( 28 | uint16 _srcChainId, 29 | bytes _srcAddress, 30 | uint64 _nonce, 31 | bytes32 _payloadHash 32 | ); 33 | 34 | // overriding the virtual function in LzReceiver 35 | function _blockingLzReceive( 36 | uint16 _srcChainId, 37 | bytes memory _srcAddress, 38 | uint64 _nonce, 39 | bytes memory _payload 40 | ) internal virtual override { 41 | bool success = false; 42 | bytes memory reason; 43 | uint256 gas = gasleft(); 44 | if (gas >= 30000) { 45 | (success, reason) = address(this).excessivelySafeCall( 46 | gas - 30000, 47 | 150, 48 | abi.encodeWithSelector( 49 | this.nonblockingLzReceive.selector, 50 | _srcChainId, 51 | _srcAddress, 52 | _nonce, 53 | _payload 54 | ) 55 | ); 56 | } 57 | 58 | // try-catch all errors/exceptions 59 | if (!success) { 60 | _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason); 61 | } 62 | } 63 | 64 | function _storeFailedMessage( 65 | uint16 _srcChainId, 66 | bytes memory _srcAddress, 67 | uint64 _nonce, 68 | bytes memory _payload, 69 | bytes memory _reason 70 | ) internal virtual { 71 | failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); 72 | emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason); 73 | } 74 | 75 | function nonblockingLzReceive( 76 | uint16 _srcChainId, 77 | bytes calldata _srcAddress, 78 | uint64 _nonce, 79 | bytes calldata _payload 80 | ) public virtual { 81 | // only internal transaction 82 | require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp"); 83 | _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); 84 | } 85 | 86 | //@notice override this function 87 | function _nonblockingLzReceive( 88 | uint16 _srcChainId, 89 | bytes memory _srcAddress, 90 | uint64 _nonce, 91 | bytes memory _payload 92 | ) internal virtual; 93 | 94 | function retryMessage( 95 | uint16 _srcChainId, 96 | bytes calldata _srcAddress, 97 | uint64 _nonce, 98 | bytes calldata _payload 99 | ) public payable virtual { 100 | // assert there is message to retry 101 | bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; 102 | require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message"); 103 | require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload"); 104 | // clear the stored message 105 | failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0); 106 | // execute the message. revert if it fails again 107 | _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); 108 | emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /contracts/layerzero/interfaces/ILayerZeroReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.5.0; 4 | 5 | interface ILayerZeroReceiver { 6 | // @notice LayerZero endpoint will invoke this function to deliver the message on the destination 7 | // @param _srcChainId - the source endpoint identifier 8 | // @param _srcAddress - the source sending contract address from the source chain 9 | // @param _nonce - the ordered message nonce 10 | // @param _payload - the signed payload is the UA bytes has encoded to be sent 11 | function lzReceive( 12 | uint16 _srcChainId, 13 | bytes calldata _srcAddress, 14 | uint64 _nonce, 15 | bytes calldata _payload 16 | ) external; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/layerzero/interfaces/ILayerZeroUserApplicationConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.5.0; 4 | 5 | interface ILayerZeroUserApplicationConfig { 6 | // @notice set the configuration of the LayerZero messaging library of the specified version 7 | // @param _version - messaging library version 8 | // @param _chainId - the chainId for the pending config change 9 | // @param _configType - type of configuration. every messaging library has its own convention. 10 | // @param _config - configuration in the bytes. can encode arbitrary content. 11 | function setConfig( 12 | uint16 _version, 13 | uint16 _chainId, 14 | uint256 _configType, 15 | bytes calldata _config 16 | ) external; 17 | 18 | // @notice set the send() LayerZero messaging library version to _version 19 | // @param _version - new messaging library version 20 | function setSendVersion(uint16 _version) external; 21 | 22 | // @notice set the lzReceive() LayerZero messaging library version to _version 23 | // @param _version - new messaging library version 24 | function setReceiveVersion(uint16 _version) external; 25 | 26 | // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload 27 | // @param _srcChainId - the chainId of the source chain 28 | // @param _srcAddress - the contract address of the source contract at the source chain 29 | function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external; 30 | } 31 | -------------------------------------------------------------------------------- /contracts/layerzero/interfaces/IOFTCore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.5.0; 4 | 5 | import "@openzeppelin/contracts/introspection/IERC165.sol"; 6 | 7 | /** 8 | * @dev Interface of the IOFT core standard 9 | */ 10 | interface IOFTCore is IERC165 { 11 | /** 12 | * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`) 13 | * _dstChainId - L0 defined chain id to send tokens too 14 | * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain 15 | * _amount - amount of the tokens to transfer 16 | * _useZro - indicates to use zro to pay L0 fees 17 | * _adapterParam - flexible bytes array to indicate messaging adapter services in L0 18 | */ 19 | function estimateSendFee( 20 | uint16 _dstChainId, 21 | bytes calldata _toAddress, 22 | uint256 _amount, 23 | bool _useZro, 24 | bytes calldata _adapterParams 25 | ) external view returns (uint256 nativeFee, uint256 zroFee); 26 | 27 | /** 28 | * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from` 29 | * `_from` the owner of token 30 | * `_dstChainId` the destination chain identifier 31 | * `_toAddress` can be any size depending on the `dstChainId`. 32 | * `_amount` the quantity of tokens in wei 33 | * `_refundAddress` the address LayerZero refunds if too much message fee is sent 34 | * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token) 35 | * `_adapterParams` is a flexible bytes array to indicate messaging adapter services 36 | */ 37 | function sendFrom( 38 | address _from, 39 | uint16 _dstChainId, 40 | bytes calldata _toAddress, 41 | uint256 _amount, 42 | address payable _refundAddress, 43 | address _zroPaymentAddress, 44 | bytes calldata _adapterParams 45 | ) external payable; 46 | 47 | /** 48 | * @dev returns the circulating amount of tokens on current chain 49 | */ 50 | function circulatingSupply() external view returns (uint256); 51 | 52 | /** 53 | * @dev returns the address of the ERC20 token 54 | */ 55 | function token() external view returns (address); 56 | 57 | /** 58 | * @dev Emitted when `_amount` tokens are moved from the `_sender` to (`_dstChainId`, `_toAddress`) 59 | * `_nonce` is the outbound nonce 60 | */ 61 | event SendToChain( 62 | uint16 indexed _dstChainId, 63 | address indexed _from, 64 | bytes _toAddress, 65 | uint256 _amount 66 | ); 67 | 68 | /** 69 | * @dev Emitted when `_amount` tokens are received from `_srcChainId` into the `_toAddress` on the local chain. 70 | * `_nonce` is the inbound nonce. 71 | */ 72 | event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint256 _amount); 73 | 74 | event SetUseCustomAdapterParams(bool _useCustomAdapterParams); 75 | } 76 | -------------------------------------------------------------------------------- /contracts/misc/BatchOperationHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/IVotingEscrow.sol"; 5 | 6 | interface IClaimRewards { 7 | function claimRewards(address account) external; 8 | 9 | function claimRewardsAndUnwrap(address account) external; 10 | } 11 | 12 | contract BatchOperationHelper { 13 | string public constant VERSION = "2.0.0"; 14 | 15 | function batchClaimRewards(address[] calldata contracts, address account) public { 16 | uint256 count = contracts.length; 17 | for (uint256 i = 0; i < count; i++) { 18 | IClaimRewards(contracts[i]).claimRewards(account); 19 | } 20 | } 21 | 22 | function batchClaimRewardsAndUnwrap( 23 | address[] calldata contracts, 24 | address[] calldata wrappedContracts, 25 | address account 26 | ) external { 27 | batchClaimRewards(contracts, account); 28 | uint256 count = wrappedContracts.length; 29 | for (uint256 i = 0; i < count; i++) { 30 | IClaimRewards(wrappedContracts[i]).claimRewardsAndUnwrap(account); 31 | } 32 | } 33 | 34 | function batchSyncWithVotingEscrow(address[] calldata contracts, address account) external { 35 | uint256 count = contracts.length; 36 | for (uint256 i = 0; i < count; i++) { 37 | IVotingEscrowCallback(contracts[i]).syncWithVotingEscrow(account); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/misc/ExternalContracts.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | // This file imports external contracts that are used by this project, 5 | // forcing Hardhat to compile them. 6 | 7 | import "@openzeppelin/contracts/access/TimelockController.sol"; 8 | import "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol"; 9 | import "@openzeppelin/contracts/proxy/ProxyAdmin.sol"; 10 | -------------------------------------------------------------------------------- /contracts/misc/RewardCashier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.0 <0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/cryptography/MerkleProof.sol"; 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | import "../utils/SafeDecimalMath.sol"; 10 | 11 | contract RewardCashier is Ownable { 12 | using SafeMath for uint256; 13 | using SafeDecimalMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | address public immutable token; 17 | uint256 public immutable deadline; 18 | 19 | mapping(uint256 => bytes32) public roots; 20 | mapping(uint256 => uint256) public ratios; 21 | mapping(address => uint256) public nextClaimableVersion; 22 | 23 | uint256 public currentVersion; 24 | 25 | constructor(address token_, uint256 deadline_) public { 26 | token = token_; 27 | deadline = deadline_; 28 | } 29 | 30 | function claim( 31 | uint256[] calldata amounts, 32 | uint256[] calldata versions, 33 | bytes32[][] calldata merkleProofs 34 | ) external returns (uint256) { 35 | require(block.timestamp < deadline, "Deadline passed"); 36 | require(versions.length > 0, "No version"); 37 | require(nextClaimableVersion[msg.sender] <= versions[0], "Already claimed"); 38 | require(versions[versions.length - 1] < currentVersion, "Invalid version"); 39 | 40 | uint256 reward = 0; 41 | for (uint256 i = 0; i < versions.length; i++) { 42 | if (i > 0) require(versions[i - 1] < versions[i], "Invalid version"); 43 | bytes32 leaf = keccak256( 44 | abi.encodePacked(keccak256(abi.encode(msg.sender, amounts[i], versions[i]))) 45 | ); 46 | require(MerkleProof.verify(merkleProofs[i], roots[versions[i]], leaf), "Invalid proof"); 47 | reward = reward.add(amounts[i].multiplyDecimal(ratios[versions[i]])); 48 | } 49 | 50 | nextClaimableVersion[msg.sender] = versions[versions.length - 1] + 1; 51 | IERC20(token).safeTransfer(msg.sender, reward); 52 | return reward; 53 | } 54 | 55 | function addNewRoot( 56 | bytes32 root, 57 | uint256 totalRewards, 58 | uint256 totalShares 59 | ) external onlyOwner { 60 | roots[currentVersion] = root; 61 | ratios[currentVersion] = totalRewards.divideDecimal(totalShares); 62 | currentVersion++; 63 | } 64 | 65 | function drain() external onlyOwner { 66 | require(block.timestamp >= deadline); 67 | IERC20(token).safeTransfer(owner(), IERC20(token).balanceOf(address(this))); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/oracle/AprOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | import "../interfaces/IAprOracle.sol"; 8 | import "../utils/SafeDecimalMath.sol"; 9 | import "../utils/Exponential.sol"; 10 | import "../utils/CoreUtility.sol"; 11 | 12 | // Compound 13 | interface CTokenInterface { 14 | function borrowIndex() external view returns (uint256); 15 | 16 | function borrowRatePerBlock() external view returns (uint256); 17 | 18 | function accrualBlockNumber() external view returns (uint256); 19 | } 20 | 21 | // Aave 22 | interface ILendingPool { 23 | function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); 24 | } 25 | 26 | contract AprOracle is IAprOracle, Exponential, CoreUtility { 27 | using SafeMath for uint256; 28 | using SafeDecimalMath for uint256; 29 | 30 | uint256 public constant DECIMAL = 10 ** 18; 31 | uint256 public constant COMPOUND_BORROW_MAX_MANTISSA = 0.0005e16; 32 | 33 | // Mainnet: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 34 | // Kovan: 0xe22da380ee6B445bb8273C81944ADEB6E8450422 35 | address public immutable usdc; 36 | 37 | // Kovan: 0x9FE532197ad76c5a68961439604C037EB79681F0 38 | address public immutable aaveUsdcLendingPool; 39 | 40 | // Mainnet: 0x39AA39c021dfbaE8faC545936693aC917d5E7563 41 | // Kovan: 0x4a92E71227D294F041BD82dd8f78591B75140d63 42 | address public immutable cUsdc; 43 | 44 | string public name; 45 | uint256 public compoundBorrowIndex; 46 | uint256 public aaveBorrowIndex; 47 | uint256 public timestamp; 48 | uint256 public currentDailyRate; 49 | 50 | constructor( 51 | string memory name_, 52 | address usdc_, 53 | address aaveUsdcLendingPool_, 54 | address cUsdc_ 55 | ) public { 56 | name = name_; 57 | usdc = usdc_; 58 | aaveUsdcLendingPool = aaveUsdcLendingPool_; 59 | cUsdc = cUsdc_; 60 | compoundBorrowIndex = getCompoundBorrowIndex(cUsdc_); 61 | aaveBorrowIndex = getAaveBorrowIndex(aaveUsdcLendingPool_, usdc_); 62 | timestamp = block.timestamp; 63 | } 64 | 65 | // Compound 66 | function getCompoundBorrowIndex(address cToken) public view returns (uint256 newBorrowIndex) { 67 | /* Calculate the current borrow interest rate */ 68 | uint256 borrowRateMantissa = CTokenInterface(cToken).borrowRatePerBlock(); 69 | require(borrowRateMantissa <= COMPOUND_BORROW_MAX_MANTISSA, "Borrow rate is absurdly high"); 70 | 71 | uint256 borrowIndexPrior = CTokenInterface(cToken).borrowIndex(); 72 | uint256 accrualBlockNumber = CTokenInterface(cToken).accrualBlockNumber(); 73 | 74 | (, uint256 blockDelta) = subUInt(block.number, accrualBlockNumber); 75 | 76 | (, Exp memory simpleInterestFactor) = mulScalar( 77 | Exp({mantissa: borrowRateMantissa}), 78 | blockDelta 79 | ); 80 | (, newBorrowIndex) = mulScalarTruncateAddUInt( 81 | simpleInterestFactor, 82 | borrowIndexPrior, 83 | borrowIndexPrior 84 | ); 85 | } 86 | 87 | // Aave 88 | function getAaveBorrowIndex( 89 | address aaveLendingPool, 90 | address token 91 | ) public view returns (uint256 newBorrowRate) { 92 | newBorrowRate = ILendingPool(aaveLendingPool).getReserveNormalizedVariableDebt(token); 93 | } 94 | 95 | function getAverageDailyRate() 96 | public 97 | view 98 | returns (uint256, uint256, uint256, uint256, uint256) 99 | { 100 | uint256 newCompoundBorrowIndex = getCompoundBorrowIndex(cUsdc); 101 | uint256 newAaveBorrowRate = getAaveBorrowIndex(aaveUsdcLendingPool, usdc); 102 | 103 | uint256 compoundPeriodicRate = newCompoundBorrowIndex 104 | .sub(compoundBorrowIndex) 105 | .divideDecimal(compoundBorrowIndex); 106 | uint256 aavePeriodicRate = newAaveBorrowRate.sub(aaveBorrowIndex).divideDecimal( 107 | aaveBorrowIndex 108 | ); 109 | 110 | uint256 dailyRate = compoundPeriodicRate.add(aavePeriodicRate).mul(1 days).div(2).div( 111 | block.timestamp.sub(timestamp) 112 | ); 113 | 114 | return ( 115 | newCompoundBorrowIndex, 116 | newAaveBorrowRate, 117 | compoundPeriodicRate, 118 | aavePeriodicRate, 119 | dailyRate 120 | ); 121 | } 122 | 123 | function capture() external override returns (uint256 dailyRate) { 124 | uint256 currentWeek = _endOfWeek(timestamp); 125 | if (currentWeek > block.timestamp) { 126 | return currentDailyRate; 127 | } 128 | 129 | (compoundBorrowIndex, aaveBorrowIndex, , , dailyRate) = getAverageDailyRate(); 130 | timestamp = block.timestamp; 131 | currentDailyRate = dailyRate; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /contracts/oracle/BscAprOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | 6 | import "../interfaces/IAprOracle.sol"; 7 | import "../utils/SafeDecimalMath.sol"; 8 | import "../utils/Exponential.sol"; 9 | import "../utils/CoreUtility.sol"; 10 | 11 | // Venus 12 | interface VTokenInterfaces { 13 | function borrowIndex() external view returns (uint256); 14 | 15 | function borrowRatePerBlock() external view returns (uint256); 16 | 17 | function accrualBlockNumber() external view returns (uint256); 18 | } 19 | 20 | contract BscAprOracle is IAprOracle, Exponential, CoreUtility { 21 | using SafeMath for uint256; 22 | using SafeDecimalMath for uint256; 23 | 24 | uint256 public constant VENUS_BORROW_MAX_MANTISSA = 0.0005e16; 25 | 26 | address public immutable vUsdc; 27 | 28 | string public name; 29 | uint256 public venusBorrowIndex; 30 | uint256 public timestamp; 31 | uint256 public currentDailyRate; 32 | 33 | constructor(string memory name_, address vUsdc_) public { 34 | name = name_; 35 | vUsdc = vUsdc_; 36 | venusBorrowIndex = getVenusBorrowIndex(vUsdc_); 37 | timestamp = block.timestamp; 38 | } 39 | 40 | // Venus 41 | function getVenusBorrowIndex(address vToken) public view returns (uint256 newBorrowIndex) { 42 | /* Calculate the current borrow interest rate */ 43 | uint256 borrowRateMantissa = VTokenInterfaces(vToken).borrowRatePerBlock(); 44 | require(borrowRateMantissa <= VENUS_BORROW_MAX_MANTISSA, "Borrow rate is absurdly high"); 45 | 46 | uint256 borrowIndexPrior = VTokenInterfaces(vToken).borrowIndex(); 47 | uint256 accrualBlockNumber = VTokenInterfaces(vToken).accrualBlockNumber(); 48 | 49 | (, uint256 blockDelta) = subUInt(block.number, accrualBlockNumber); 50 | 51 | (, Exp memory simpleInterestFactor) = mulScalar( 52 | Exp({mantissa: borrowRateMantissa}), 53 | blockDelta 54 | ); 55 | (, newBorrowIndex) = mulScalarTruncateAddUInt( 56 | simpleInterestFactor, 57 | borrowIndexPrior, 58 | borrowIndexPrior 59 | ); 60 | } 61 | 62 | function getAverageDailyRate() public view returns (uint256, uint256, uint256) { 63 | uint256 newVenusBorrowIndex = getVenusBorrowIndex(vUsdc); 64 | 65 | uint256 venusPeriodicRate = newVenusBorrowIndex.sub(venusBorrowIndex).divideDecimal( 66 | venusBorrowIndex 67 | ); 68 | 69 | uint256 dailyRate = venusPeriodicRate.mul(1 days).div(block.timestamp.sub(timestamp)); 70 | 71 | return (newVenusBorrowIndex, venusPeriodicRate, dailyRate); 72 | } 73 | 74 | function capture() external override returns (uint256 dailyRate) { 75 | uint256 currentWeek = _endOfWeek(timestamp); 76 | if (currentWeek > block.timestamp) { 77 | return currentDailyRate; 78 | } 79 | 80 | (venusBorrowIndex, , dailyRate) = getAverageDailyRate(); 81 | timestamp = block.timestamp; 82 | currentDailyRate = dailyRate; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /contracts/oracle/BscAprOracleProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/IAprOracle.sol"; 5 | import "../interfaces/IFundV3.sol"; 6 | import "../interfaces/IPrimaryMarketV3.sol"; 7 | import "../interfaces/ITrancheIndexV2.sol"; 8 | import "../fund/ShareStaking.sol"; 9 | 10 | // Issue: The ShareStaking contract's _checkpoint() function had a vulnerability where 11 | // it could be skipped if invoked multiple times in the same block, potentially leading to 12 | // discrepancies between total supplies and actual balances after a rebalance event. This 13 | // could be exploited by an attacker through a series of transactions involving 14 | // frontrunning the rebalance call, thus draining user funds by manipulating the spareAmount. 15 | 16 | // Fix: This new BscAprOracleProxy contract has been introduced to wrap around the existing 17 | // BscAprOracle. It checks for fund version changes on every capture() call. If a change is 18 | // detected, it deliberately interacts with the ShareStaking contract to expose the potential 19 | // vulnerability by comparing total supplies before and after. Any mismatch triggers a revert, 20 | // preventing the checkpoint bypass and ensuring the rebalance can only happen if _checkpoint() 21 | // is properly called and updated in the same block as Fund.settle(). 22 | 23 | // The fix is implemented as an external proxy to the immutable Fund contract to avoid the need 24 | // for updating the ShareStaking contract itself, thus maintaining the integrity of the protocol 25 | // and safeguarding user funds. 26 | 27 | // Known Issue: The fix could be used to delay rebalance, but it is not economically viable for 28 | // an attacker to do so over a prolonged period, as the costs would quickly outweigh the potential 29 | // benefits, which is basically next to zero. 30 | 31 | contract BscAprOracleProxy is IAprOracle, ITrancheIndexV2 { 32 | // Under extreme circumstances, there might not be enough amount of token to deposit; 33 | // we could always transfer more QUEEN to resolve the issue. 34 | uint256 public constant DEPOSIT_AMOUNT = 1e15; 35 | IAprOracle public immutable aprOracle; 36 | IFundV3 public immutable fund; 37 | ShareStaking public immutable shareStaking; 38 | 39 | uint256 public currentVersion; 40 | 41 | constructor(IAprOracle aprOracle_, IFundV3 fund_, ShareStaking shareStaking_) public { 42 | aprOracle = aprOracle_; 43 | fund = fund_; 44 | shareStaking = shareStaking_; 45 | currentVersion = fund_.getRebalanceSize(); 46 | _approveMax(fund_, address(shareStaking_)); 47 | } 48 | 49 | function capture() external override returns (uint256 dailyRate) { 50 | uint256 newVersion = fund.getRebalanceSize(); 51 | if (newVersion != currentVersion) { 52 | uint256 amountQ = fund.trancheBalanceOf(TRANCHE_Q, address(this)); 53 | if (amountQ > 0) { 54 | IPrimaryMarketV3 primaryMarket = IPrimaryMarketV3(fund.primaryMarket()); 55 | primaryMarket.split(address(this), amountQ, newVersion); 56 | _approveMax(fund, address(shareStaking)); 57 | } 58 | currentVersion = newVersion; 59 | uint256 oldStakingQ = shareStaking.totalSupply(TRANCHE_Q); 60 | shareStaking.deposit(TRANCHE_B, DEPOSIT_AMOUNT, address(this), newVersion); 61 | shareStaking.deposit(TRANCHE_R, DEPOSIT_AMOUNT, address(this), newVersion); 62 | uint256 newStakingQ = shareStaking.totalSupply(TRANCHE_Q); 63 | require(newStakingQ == oldStakingQ, "Rebalance check failed"); 64 | shareStaking.withdraw(TRANCHE_B, DEPOSIT_AMOUNT, newVersion); 65 | shareStaking.withdraw(TRANCHE_R, DEPOSIT_AMOUNT, newVersion); 66 | } 67 | return aprOracle.capture(); 68 | } 69 | 70 | function _approveMax(IFundV3 fund_, address spender) private { 71 | // Approve max BISHOP and ROOK to ShareStaking 72 | fund_.trancheApprove(TRANCHE_B, spender, type(uint256).max, fund_.getRebalanceSize()); 73 | fund_.trancheApprove(TRANCHE_R, spender, type(uint256).max, fund_.getRebalanceSize()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /contracts/oracle/ConstAprOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | import "../interfaces/IAprOracle.sol"; 7 | 8 | contract ConstAprOracle is IAprOracle, Ownable { 9 | event Updated(uint256 dailyRate); 10 | 11 | uint256 public dailyRate; 12 | 13 | constructor(uint256 dailyRate_) public { 14 | dailyRate = dailyRate_; 15 | emit Updated(dailyRate_); 16 | } 17 | 18 | function update(uint256 newRate) external onlyOwner { 19 | dailyRate = newRate; 20 | emit Updated(newRate); 21 | } 22 | 23 | function capture() external override returns (uint256) { 24 | return dailyRate; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/oracle/ConstPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/ITwapOracleV2.sol"; 5 | 6 | /// @title Constant Price oracle 7 | /// @author Tranchess 8 | contract ConstPriceOracle is ITwapOracleV2 { 9 | uint256 public immutable price; 10 | 11 | constructor(uint256 price_) public { 12 | price = price_; 13 | } 14 | 15 | /// @notice Return the constant price with 18 decimal places. 16 | function getLatest() external view override returns (uint256) { 17 | return price; 18 | } 19 | 20 | /// @notice For constant price oracle, we keep the `getTwap` interface 21 | /// compatible but it only returns the constant price. 22 | function getTwap(uint256) external view override returns (uint256) { 23 | return price; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/oracle/QueenRateProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | 6 | import "../utils/SafeDecimalMath.sol"; 7 | import "../interfaces/IFundV3.sol"; 8 | 9 | /// @title Queen Rate Provider 10 | /// @notice Returns the value of Queen in terms of the underlying 11 | contract QueenRateProvider { 12 | using SafeMath for uint256; 13 | using SafeDecimalMath for uint256; 14 | 15 | IFundV3 public immutable fund; 16 | /// @dev A multipler that normalizes a underlying asset balance to 18 decimal places. 17 | uint256 internal immutable _underlyingDecimalMultiplier; 18 | 19 | constructor(address fund_) public { 20 | fund = IFundV3(fund_); 21 | _underlyingDecimalMultiplier = IFundV3(fund_).underlyingDecimalMultiplier(); 22 | } 23 | 24 | /// @return the value of Queen in terms of the underlying 25 | function getRate() external view returns (uint256) { 26 | uint256 fundUnderlying = fund.getTotalUnderlying(); 27 | uint256 fundEquivalentTotalQ = fund.getEquivalentTotalQ(); 28 | return fundUnderlying.mul(_underlyingDecimalMultiplier).divideDecimal(fundEquivalentTotalQ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/oracle/WstETHPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/ITwapOracleV2.sol"; 5 | import "../interfaces/IWstETH.sol"; 6 | 7 | /// @title wstETH Price oracle 8 | /// @author Tranchess 9 | contract WstETHPriceOracle is ITwapOracleV2 { 10 | IWstETH public immutable wstETH; 11 | 12 | constructor(address wstETH_) public { 13 | wstETH = IWstETH(wstETH_); 14 | } 15 | 16 | /// @notice Return the latest price with 18 decimal places. 17 | function getLatest() external view override returns (uint256) { 18 | return wstETH.stEthPerToken(); 19 | } 20 | 21 | /// @notice For wstETH price oracle, we keep the `getTwap` interface 22 | /// compatible but it only returns the latest price. 23 | function getTwap(uint256) external view override returns (uint256) { 24 | return wstETH.stEthPerToken(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/strategy/eth/IWithdrawalManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | interface IWithdrawalManager { 5 | function getWithdrawalCredential() external view returns (bytes32); 6 | 7 | function transferToStrategy(uint256 amount) external; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/strategy/eth/WithdrawalManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; 5 | 6 | import "../../interfaces/IWrappedERC20.sol"; 7 | import "./IWithdrawalManager.sol"; 8 | import "./EthStakingStrategy.sol"; 9 | 10 | contract WithdrawalManager is IWithdrawalManager, Initializable { 11 | event EtherReceived(address indexed from, uint256 amount, uint256 time); 12 | 13 | EthStakingStrategy public immutable strategy; 14 | IWrappedERC20 private immutable _tokenUnderlying; 15 | 16 | uint256 public operatorID; 17 | 18 | constructor(address payable strategy_) public { 19 | strategy = EthStakingStrategy(strategy_); 20 | _tokenUnderlying = IWrappedERC20( 21 | IFundV3(EthStakingStrategy(strategy_).fund()).tokenUnderlying() 22 | ); 23 | } 24 | 25 | function initialize(uint256 operatorID_) external initializer { 26 | operatorID = operatorID_; 27 | } 28 | 29 | receive() external payable { 30 | emit EtherReceived(msg.sender, msg.value, block.timestamp); 31 | } 32 | 33 | function getWithdrawalCredential() external view override returns (bytes32) { 34 | return bytes32(uint256(address(payable(this))) | (1 << 248)); 35 | } 36 | 37 | function transferToStrategy(uint256 amount) external override onlyStrategy { 38 | (bool success, ) = address(strategy).call{value: amount}(""); 39 | require(success); 40 | } 41 | 42 | modifier onlyStrategy() { 43 | require(address(strategy) == msg.sender, "Only strategy"); 44 | _; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/strategy/eth/WithdrawalManagerFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | import "./WithdrawalManagerProxy.sol"; 7 | 8 | contract WithdrawalManagerFactory is Ownable { 9 | event ImplementationUpdated(address indexed newImplementation); 10 | 11 | address public implementation; 12 | 13 | constructor(address implementation_) public { 14 | _updateImplementation(implementation_); 15 | } 16 | 17 | function deployContract(uint256 id) external returns (address) { 18 | WithdrawalManagerProxy proxy = new WithdrawalManagerProxy(this, id); 19 | return address(proxy); 20 | } 21 | 22 | function updateImplementation(address newImplementation) external onlyOwner { 23 | _updateImplementation(newImplementation); 24 | } 25 | 26 | function _updateImplementation(address newImplementation) private { 27 | implementation = newImplementation; 28 | emit ImplementationUpdated(newImplementation); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/strategy/eth/WithdrawalManagerProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Address.sol"; 5 | import "@openzeppelin/contracts/proxy/Proxy.sol"; 6 | 7 | import "./WithdrawalManagerFactory.sol"; 8 | 9 | // An individual withdraw maanger for a node operator 10 | 11 | contract WithdrawalManagerProxy is Proxy { 12 | using Address for address; 13 | 14 | WithdrawalManagerFactory internal immutable withdrawalManagerFactory; 15 | 16 | constructor(WithdrawalManagerFactory withdrawalManagerFactory_, uint256 operatorID_) public { 17 | // Initialize withdrawalManagerFactory 18 | require(address(withdrawalManagerFactory_) != address(0x0), "Invalid factory address"); 19 | withdrawalManagerFactory = withdrawalManagerFactory_; 20 | // Check for contract existence 21 | address implAddress = withdrawalManagerFactory_.implementation(); 22 | require(implAddress.isContract(), "Delegate contract does not exist"); 23 | // Call Initialize on delegate 24 | (bool success, ) = implAddress.delegatecall( 25 | abi.encodeWithSignature("initialize(uint256)", operatorID_) 26 | ); 27 | if (!success) { 28 | revert("Failed delegatecall"); 29 | } 30 | } 31 | 32 | function _implementation() internal view override returns (address) { 33 | return withdrawalManagerFactory.implementation(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/swap/FlashSwapRouterV3Helper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | 6 | import "./FlashSwapRouterV3.sol"; 7 | 8 | contract FlashSwapRouterV3Helper { 9 | using SafeMath for uint256; 10 | 11 | FlashSwapRouterV3 public immutable flashSwapRouter; 12 | 13 | constructor(address flashSwapRouter_) public { 14 | flashSwapRouter = FlashSwapRouterV3(flashSwapRouter_); 15 | } 16 | 17 | /// @dev Only meant for an off-chain client to call with eth_call. 18 | /// This function uses binary search to find the maximum `outR` in the range `[minOutR, maxOutR)` 19 | /// such that `getBuyR(outR).quoteDelta <= inQuote`. When `inQuote` does not increase monotonically 20 | /// with `outR`, this function does not guarantee to return the optimal solution. 21 | /// 22 | /// Although `FlashSwapRouterV3.getBuyR` is not a view function, it typically does not alter any 23 | /// contract state. However, this function fails when `FlashSwapRouterV3.getBuyR` does modify some state. 24 | function getBuyRFromQuote( 25 | IFundV5 fund, 26 | bool needWrap, 27 | address queenSwapOrPrimaryMarketRouter, 28 | address tokenQuote, 29 | uint256 minOutR, 30 | uint256 maxOutR, 31 | uint256 precision, 32 | uint256 inQuote 33 | ) external returns (uint256 outR) { 34 | while (minOutR.add(precision) < maxOutR) { 35 | uint256 midOutR = minOutR / 2 + maxOutR / 2; 36 | (bool success, bytes memory data) = address(flashSwapRouter).call( 37 | abi.encodeWithSelector( 38 | FlashSwapRouterV3.getBuyR.selector, 39 | fund, 40 | needWrap, 41 | queenSwapOrPrimaryMarketRouter, 42 | tokenQuote, 43 | midOutR 44 | ) 45 | ); 46 | if (success) { 47 | (uint256 quoteDelta, ) = abi.decode(data, (uint256, uint256)); 48 | if (quoteDelta <= inQuote) { 49 | minOutR = midOutR; 50 | continue; 51 | } 52 | } 53 | maxOutR = midOutR; 54 | } 55 | return minOutR; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/swap/QueenStableSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../interfaces/ITrancheIndexV2.sol"; 5 | import "../utils/SafeDecimalMath.sol"; 6 | import "./StableSwap.sol"; 7 | 8 | contract QueenStableSwap is StableSwap, ITrancheIndexV2 { 9 | using SafeDecimalMath for uint256; 10 | 11 | constructor( 12 | address lpToken_, 13 | address fund_, 14 | uint256 quoteDecimals_, 15 | uint256 ampl_, 16 | address feeCollector_, 17 | uint256 feeRate_, 18 | uint256 adminFeeRate_ 19 | ) 20 | public 21 | StableSwap( 22 | lpToken_, 23 | fund_, 24 | TRANCHE_Q, 25 | IFundV3(fund_).tokenUnderlying(), 26 | quoteDecimals_, 27 | ampl_, 28 | feeCollector_, 29 | feeRate_, 30 | adminFeeRate_ 31 | ) 32 | { 33 | require(10 ** (18 - quoteDecimals_) == IFundV3(fund_).underlyingDecimalMultiplier()); 34 | } 35 | 36 | function _getRebalanceResult( 37 | uint256 38 | ) 39 | internal 40 | view 41 | override 42 | returns ( 43 | uint256 newBase, 44 | uint256 newQuote, 45 | uint256 excessiveQ, 46 | uint256 excessiveB, 47 | uint256 excessiveR, 48 | uint256 excessiveQuote, 49 | bool isRebalanced 50 | ) 51 | { 52 | return (baseBalance, quoteBalance, 0, 0, 0, 0, false); 53 | } 54 | 55 | function _handleRebalance( 56 | uint256 57 | ) internal override returns (uint256 newBase, uint256 newQuote) { 58 | return (baseBalance, quoteBalance); 59 | } 60 | 61 | function getOraclePrice() public view override returns (uint256) { 62 | uint256 fundUnderlying = fund.getTotalUnderlying(); 63 | uint256 fundEquivalentTotalQ = fund.getEquivalentTotalQ(); 64 | return fundUnderlying.mul(_quoteDecimalMultiplier).divideDecimal(fundEquivalentTotalQ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /contracts/swap/SwapBonus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/math/Math.sol"; 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | 10 | contract SwapBonus is Ownable { 11 | using Math for uint256; 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | address public immutable liquidityGauge; 16 | address public immutable bonusToken; 17 | uint256 public ratePerSecond; 18 | uint256 public startTimestamp; 19 | uint256 public endTimestamp; 20 | uint256 public lastTimestamp; 21 | 22 | constructor(address liquidityGauge_, address bonusToken_) public { 23 | liquidityGauge = liquidityGauge_; 24 | bonusToken = bonusToken_; 25 | } 26 | 27 | function updateBonus(uint256 amount, uint256 start, uint256 interval) external onlyOwner { 28 | require(start >= block.timestamp, "Start time in the past"); 29 | require( 30 | endTimestamp < block.timestamp && endTimestamp == lastTimestamp, 31 | "Last reward not yet expired" 32 | ); 33 | ratePerSecond = amount.div(interval); 34 | startTimestamp = start; 35 | endTimestamp = start.add(interval); 36 | lastTimestamp = startTimestamp; 37 | IERC20(bonusToken).safeTransferFrom(msg.sender, address(this), ratePerSecond.mul(interval)); 38 | } 39 | 40 | function getBonus() external returns (uint256) { 41 | require(msg.sender == liquidityGauge); 42 | uint256 currentTimestamp = endTimestamp.min(block.timestamp); 43 | uint256 reward = ratePerSecond.mul(currentTimestamp - lastTimestamp); 44 | lastTimestamp = currentTimestamp; 45 | if (reward > 0) { 46 | IERC20(bonusToken).safeTransfer(liquidityGauge, reward); 47 | } 48 | return reward; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/swap/SwapBonusWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | import "./LiquidityGaugeV3.sol"; 10 | import "./SwapBonus.sol"; 11 | 12 | contract SwapBonusWrapper is Ownable { 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | address public immutable swapBonus; 17 | address public immutable bonusToken; 18 | LiquidityGaugeV3 public immutable liquidityGauge; 19 | 20 | constructor(LiquidityGaugeV3 liquidityGauge_) public { 21 | liquidityGauge = liquidityGauge_; 22 | address swapBonus_ = liquidityGauge_.swapBonus(); 23 | swapBonus = swapBonus_; 24 | bonusToken = SwapBonus(swapBonus_).bonusToken(); 25 | } 26 | 27 | function updateBonus(uint256 amount, uint256 interval) external onlyOwner { 28 | liquidityGauge.syncWithVotingEscrow(address(0)); 29 | uint256 realAmount = amount.div(interval).mul(interval); 30 | IERC20(bonusToken).safeTransferFrom(msg.sender, address(this), realAmount); 31 | IERC20(bonusToken).approve(swapBonus, realAmount); 32 | SwapBonus(swapBonus).updateBonus(amount, block.timestamp, interval); 33 | } 34 | 35 | function transferOwnershipToAdmin() external onlyOwner { 36 | SwapBonus(swapBonus).transferOwnership(owner()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/swap/WstETHBishopStableSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./WstETHStableSwap.sol"; 5 | 6 | contract WstETHBishopStableSwap is WstETHStableSwap { 7 | event Rebalanced(uint256 base, uint256 quote, uint256 version); 8 | 9 | constructor( 10 | address lpToken_, 11 | address fund_, 12 | address quoteAddress_, 13 | uint256 quoteDecimals_, 14 | uint256 ampl_, 15 | address feeCollector_, 16 | uint256 feeRate_, 17 | uint256 adminFeeRate_ 18 | ) 19 | public 20 | WstETHStableSwap( 21 | lpToken_, 22 | fund_, 23 | TRANCHE_B, 24 | quoteAddress_, 25 | quoteDecimals_, 26 | ampl_, 27 | feeCollector_, 28 | feeRate_, 29 | adminFeeRate_ 30 | ) 31 | {} 32 | 33 | function _rebalanceBase( 34 | uint256 oldBase, 35 | uint256 fromVersion, 36 | uint256 toVersion 37 | ) internal view override returns (uint256 excessiveQ, uint256 newBase) { 38 | (excessiveQ, newBase, ) = fund.batchRebalance(0, oldBase, 0, fromVersion, toVersion); 39 | } 40 | 41 | function _getBaseNav() internal view override returns (uint256) { 42 | uint256 price = fund.twapOracle().getLatest(); 43 | (, uint256 navB, ) = fund.extrapolateNav(price); 44 | return navB; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/swap/WstETHWrappingSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | import "../interfaces/IStableSwap.sol"; 8 | import "../interfaces/IWstETH.sol"; 9 | import "../utils/SafeDecimalMath.sol"; 10 | 11 | contract WstETHWrappingSwap is IStableSwap { 12 | using SafeERC20 for IERC20; 13 | using SafeMath for uint256; 14 | using SafeDecimalMath for uint256; 15 | 16 | address public immutable wstETH; // Base 17 | address public immutable stETH; // Quote 18 | 19 | constructor(address wstETH_) public { 20 | wstETH = wstETH_; 21 | stETH = IWstETH(wstETH_).stETH(); 22 | } 23 | 24 | function getQuoteOut(uint256 baseIn) external view override returns (uint256 quoteOut) { 25 | quoteOut = IWstETH(wstETH).getStETHByWstETH(baseIn); 26 | } 27 | 28 | function getQuoteIn(uint256 baseOut) external view override returns (uint256 quoteIn) { 29 | quoteIn = IWstETH(wstETH).getStETHByWstETH(baseOut).add(1); 30 | } 31 | 32 | function getBaseOut(uint256 quoteIn) external view override returns (uint256 baseOut) { 33 | baseOut = IWstETH(wstETH).getWstETHByStETH(quoteIn); 34 | } 35 | 36 | function getBaseIn(uint256 quoteOut) external view override returns (uint256 baseIn) { 37 | baseIn = IWstETH(wstETH).getWstETHByStETH(quoteOut).add(1); 38 | } 39 | 40 | function buy( 41 | uint256, 42 | uint256, 43 | address recipient, 44 | bytes calldata 45 | ) external override returns (uint256 realBaseOut) { 46 | uint256 quoteIn = IERC20(stETH).balanceOf(address(this)); 47 | IERC20(stETH).approve(wstETH, quoteIn); 48 | realBaseOut = IWstETH(wstETH).wrap(quoteIn); 49 | IERC20(wstETH).safeTransfer(recipient, realBaseOut); 50 | } 51 | 52 | function sell( 53 | uint256, 54 | uint256, 55 | address recipient, 56 | bytes calldata 57 | ) external override returns (uint256 realQuoteOut) { 58 | uint256 baseIn = IERC20(wstETH).balanceOf(address(this)); 59 | realQuoteOut = IWstETH(wstETH).unwrap(baseIn); 60 | IERC20(stETH).safeTransfer(recipient, realQuoteOut); 61 | } 62 | 63 | function baseAddress() external view override returns (address) { 64 | return wstETH; 65 | } 66 | 67 | function quoteAddress() external view override returns (address) { 68 | return stETH; 69 | } 70 | 71 | function getOraclePrice() external view override returns (uint256) { 72 | return IWstETH(wstETH).stEthPerToken(); 73 | } 74 | 75 | function getCurrentPrice() external view override returns (uint256) { 76 | return IWstETH(wstETH).stEthPerToken(); 77 | } 78 | 79 | function fund() external view override returns (IFundV3) { 80 | revert("Not implemented"); 81 | } 82 | 83 | function baseTranche() external view override returns (uint256) { 84 | revert("Not implemented"); 85 | } 86 | 87 | function allBalances() external view override returns (uint256, uint256) { 88 | revert("Not implemented"); 89 | } 90 | 91 | function getCurrentD() external view override returns (uint256) { 92 | revert("Not implemented"); 93 | } 94 | 95 | function getCurrentPriceOverOracle() external view override returns (uint256) { 96 | revert("Not implemented"); 97 | } 98 | 99 | function getPriceOverOracleIntegral() external view override returns (uint256) { 100 | revert("Not implemented"); 101 | } 102 | 103 | function addLiquidity(uint256, address) external override returns (uint256) { 104 | revert("Not implemented"); 105 | } 106 | 107 | function removeLiquidity( 108 | uint256, 109 | uint256, 110 | uint256, 111 | uint256 112 | ) external override returns (uint256, uint256) { 113 | revert("Not implemented"); 114 | } 115 | 116 | function removeLiquidityUnwrap( 117 | uint256, 118 | uint256, 119 | uint256, 120 | uint256 121 | ) external override returns (uint256, uint256) { 122 | revert("Not implemented"); 123 | } 124 | 125 | function removeBaseLiquidity(uint256, uint256, uint256) external override returns (uint256) { 126 | revert("Not implemented"); 127 | } 128 | 129 | function removeQuoteLiquidity(uint256, uint256, uint256) external override returns (uint256) { 130 | revert("Not implemented"); 131 | } 132 | 133 | function removeQuoteLiquidityUnwrap( 134 | uint256, 135 | uint256, 136 | uint256 137 | ) external override returns (uint256) { 138 | revert("Not implemented"); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /contracts/test/AdvancedMathWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "../utils/AdvancedMath.sol"; 5 | 6 | contract AdvancedMathWrapper { 7 | using AdvancedMath for uint256; 8 | 9 | function sqrt(uint256 value) external pure returns (uint256) { 10 | return value.sqrt(); 11 | } 12 | 13 | function cbrt(uint256 value) external pure returns (uint256) { 14 | return value.cbrt(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/test/LzLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity >=0.6.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | library LzLib { 7 | // LayerZero communication 8 | struct CallParams { 9 | address payable refundAddress; 10 | address zroPaymentAddress; 11 | } 12 | 13 | //--------------------------------------------------------------------------- 14 | // Address type handling 15 | 16 | struct AirdropParams { 17 | uint256 airdropAmount; 18 | bytes32 airdropAddress; 19 | } 20 | 21 | function buildAdapterParams( 22 | LzLib.AirdropParams memory _airdropParams, 23 | uint256 _uaGasLimit 24 | ) internal pure returns (bytes memory adapterParams) { 25 | if (_airdropParams.airdropAmount == 0 && _airdropParams.airdropAddress == bytes32(0x0)) { 26 | adapterParams = buildDefaultAdapterParams(_uaGasLimit); 27 | } else { 28 | adapterParams = buildAirdropAdapterParams(_uaGasLimit, _airdropParams); 29 | } 30 | } 31 | 32 | // Build Adapter Params 33 | function buildDefaultAdapterParams(uint256 _uaGas) internal pure returns (bytes memory) { 34 | // txType 1 35 | // bytes [2 32 ] 36 | // fields [txType extraGas] 37 | return abi.encodePacked(uint16(1), _uaGas); 38 | } 39 | 40 | function buildAirdropAdapterParams( 41 | uint256 _uaGas, 42 | AirdropParams memory _params 43 | ) internal pure returns (bytes memory) { 44 | require(_params.airdropAmount > 0, "Airdrop amount must be greater than 0"); 45 | require(_params.airdropAddress != bytes32(0x0), "Airdrop address must be set"); 46 | 47 | // txType 2 48 | // bytes [2 32 32 bytes[] ] 49 | // fields [txType extraGas dstNativeAmt dstNativeAddress] 50 | return abi.encodePacked(uint16(2), _uaGas, _params.airdropAmount, _params.airdropAddress); 51 | } 52 | 53 | function getGasLimit(bytes memory _adapterParams) internal pure returns (uint256 gasLimit) { 54 | require(_adapterParams.length == 34 || _adapterParams.length > 66, "Invalid adapterParams"); 55 | assembly { 56 | gasLimit := mload(add(_adapterParams, 34)) 57 | } 58 | } 59 | 60 | // Decode Adapter Params 61 | function decodeAdapterParams( 62 | bytes memory _adapterParams 63 | ) 64 | internal 65 | pure 66 | returns ( 67 | uint16 txType, 68 | uint256 uaGas, 69 | uint256 airdropAmount, 70 | address payable airdropAddress 71 | ) 72 | { 73 | require(_adapterParams.length == 34 || _adapterParams.length > 66, "Invalid adapterParams"); 74 | assembly { 75 | txType := mload(add(_adapterParams, 2)) 76 | uaGas := mload(add(_adapterParams, 34)) 77 | } 78 | require(txType == 1 || txType == 2, "Unsupported txType"); 79 | require(uaGas > 0, "Gas too low"); 80 | 81 | if (txType == 2) { 82 | assembly { 83 | airdropAmount := mload(add(_adapterParams, 66)) 84 | airdropAddress := mload(add(_adapterParams, 86)) 85 | } 86 | } 87 | } 88 | 89 | //--------------------------------------------------------------------------- 90 | // Address type handling 91 | function bytes32ToAddress(bytes32 _bytes32Address) internal pure returns (address _address) { 92 | return address(uint160(uint256(_bytes32Address))); 93 | } 94 | 95 | function addressToBytes32(address _address) internal pure returns (bytes32 _bytes32Address) { 96 | return bytes32(uint256(uint160(_address))); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /contracts/test/MockExternalRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 5 | 6 | contract MockExternalRouter { 7 | using SafeERC20 for IERC20; 8 | 9 | // keccak256(path) => amountOut => amountIn 10 | mapping(bytes32 => mapping(uint256 => uint256)) public nextIn; 11 | 12 | // keccak256(path) => amountIn => amountOut 13 | mapping(bytes32 => mapping(uint256 => uint256)) public nextOut; 14 | 15 | function setNextSwap(address[] memory path, uint256 amountIn, uint256 amountOut) external { 16 | nextIn[keccak256(abi.encode(path))][amountOut] = amountIn; 17 | nextOut[keccak256(abi.encode(path))][amountIn] = amountOut; 18 | } 19 | 20 | function getAmountsIn( 21 | uint256 amountOut, 22 | address[] calldata path 23 | ) external view returns (uint256[] memory amounts) { 24 | uint256 amountIn = nextIn[keccak256(abi.encode(path))][amountOut]; 25 | require(amountIn != 0, "No mock for the swap"); 26 | amounts = new uint256[](path.length); 27 | amounts[amounts.length - 1] = amountOut; 28 | amounts[0] = nextIn[keccak256(abi.encode(path))][amountOut]; 29 | } 30 | 31 | function swapExactTokensForTokens( 32 | uint256 amountIn, 33 | uint256 amountOutMin, 34 | address[] calldata path, 35 | address to, 36 | uint256 deadline 37 | ) external returns (uint256[] memory amounts) { 38 | require(deadline >= block.timestamp, "Deadline"); 39 | uint256 amountOut = nextOut[keccak256(abi.encode(path))][amountIn]; 40 | require(amountOut != 0, "No mock for the swap"); 41 | require(amountOut >= amountOutMin, "MockExternalRouter: Insufficient output"); 42 | amounts = new uint256[](path.length); 43 | amounts[0] = amountIn; 44 | amounts[amounts.length - 1] = amountOut; 45 | nextIn[keccak256(abi.encode(path))][amountOut] = 0; 46 | nextOut[keccak256(abi.encode(path))][amountIn] = 0; 47 | IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); 48 | IERC20(path[path.length - 1]).safeTransfer(to, amountOut); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test/MockToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract MockToken is ERC20, Ownable { 8 | constructor( 9 | string memory name, 10 | string memory symbol, 11 | uint8 decimals 12 | ) public ERC20(name, symbol) { 13 | _setupDecimals(decimals); 14 | } 15 | 16 | function mint(address account, uint256 amount) external onlyOwner { 17 | _mint(account, amount); 18 | } 19 | 20 | function burn(address account, uint256 amount) external onlyOwner { 21 | _burn(account, amount); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/MockTwapOracleKeeper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "./MockTwapOracle.sol"; 5 | import "@chainlink/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol"; 6 | 7 | contract MockTwapOracleKeeper is KeeperCompatibleInterface, CoreUtility { 8 | MockTwapOracle private immutable mockTwap; 9 | 10 | constructor(address mockTwap_) public { 11 | mockTwap = MockTwapOracle(mockTwap_); 12 | } 13 | 14 | function checkUpkeep( 15 | bytes calldata /*checkData*/ 16 | ) external override returns (bool upkeepNeeded, bytes memory performData) { 17 | return (block.timestamp > _endOfDay(mockTwap.lastStoredEpoch()), bytes("")); 18 | } 19 | 20 | function performUpkeep(bytes calldata /*performData*/) external override { 21 | mockTwap.catchUp(); 22 | } 23 | 24 | function _endOfDay(uint256 timestamp) private pure returns (uint256) { 25 | return ((timestamp.add(1 days) - SETTLEMENT_TIME) / 1 days) * 1 days + SETTLEMENT_TIME; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/test/MockWrappedToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract MockWrappedToken is ERC20 { 7 | constructor(string memory name, string memory symbol) public ERC20(name, symbol) { 8 | _setupDecimals(18); 9 | } 10 | 11 | function deposit() external payable { 12 | _mint(msg.sender, msg.value); 13 | } 14 | 15 | function withdraw(uint256 wad) external { 16 | _burn(msg.sender, wad); 17 | msg.sender.transfer(wad); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/MockWstETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | import "../interfaces/IWstETH.sol"; 7 | 8 | import "../utils/SafeDecimalMath.sol"; 9 | 10 | contract MockWstETH is IWstETH, ERC20 { 11 | using SafeDecimalMath for uint256; 12 | 13 | address public immutable override stETH; 14 | 15 | uint256 public override stEthPerToken; 16 | 17 | constructor(address stETH_) public ERC20("Mock wstETH", "wstETH") { 18 | stETH = stETH_; 19 | _setupDecimals(18); 20 | } 21 | 22 | function update(uint256 rate) external { 23 | stEthPerToken = rate; 24 | } 25 | 26 | function getWstETHByStETH(uint256 _stETHAmount) public view override returns (uint256) { 27 | return _stETHAmount.divideDecimal(stEthPerToken); 28 | } 29 | 30 | function getStETHByWstETH(uint256 _wstETHAmount) public view override returns (uint256) { 31 | return _wstETHAmount.multiplyDecimal(stEthPerToken); 32 | } 33 | 34 | function wrap(uint256 _stETHAmount) external override returns (uint256) { 35 | require(_stETHAmount > 0, "wstETH: can't wrap zero stETH"); 36 | uint256 wstETHAmount = getWstETHByStETH(_stETHAmount); 37 | _mint(msg.sender, wstETHAmount); 38 | IERC20(stETH).transferFrom(msg.sender, address(this), _stETHAmount); 39 | return wstETHAmount; 40 | } 41 | 42 | function unwrap(uint256 _wstETHAmount) external override returns (uint256) { 43 | require(_wstETHAmount > 0, "wstETH: zero amount unwrap not allowed"); 44 | uint256 stETHAmount = getStETHByWstETH(_wstETHAmount); 45 | _burn(msg.sender, _wstETHAmount); 46 | IERC20(stETH).transfer(msg.sender, stETHAmount); 47 | return stETHAmount; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/utils/AdvancedMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | library AdvancedMath { 5 | /// @dev Calculate square root. 6 | /// 7 | /// Reference: https://en.wikipedia.org/wiki/Integer_square_root#Algorithm_using_Newton's_method 8 | function sqrt(uint256 s) internal pure returns (uint256) { 9 | if (s == 0) return 0; 10 | uint256 t = s; 11 | uint256 x0 = 2; 12 | if (t >= 1 << 128) { 13 | t >>= 128; 14 | x0 <<= 64; 15 | } 16 | if (t >= 1 << 64) { 17 | t >>= 64; 18 | x0 <<= 32; 19 | } 20 | if (t >= 1 << 32) { 21 | t >>= 32; 22 | x0 <<= 16; 23 | } 24 | if (t >= 1 << 16) { 25 | t >>= 16; 26 | x0 <<= 8; 27 | } 28 | if (t >= 1 << 8) { 29 | t >>= 8; 30 | x0 <<= 4; 31 | } 32 | if (t >= 1 << 4) { 33 | t >>= 4; 34 | x0 <<= 2; 35 | } 36 | if (t >= 1 << 2) { 37 | x0 <<= 1; 38 | } 39 | uint256 x1 = (x0 + s / x0) >> 1; 40 | while (x1 < x0) { 41 | x0 = x1; 42 | x1 = (x0 + s / x0) >> 1; 43 | } 44 | return x0; 45 | } 46 | 47 | /// @notice Calculate cubic root. 48 | function cbrt(uint256 s) internal pure returns (uint256) { 49 | if (s == 0) return 0; 50 | uint256 t = s; 51 | uint256 x0 = 2; 52 | if (t >= 1 << 192) { 53 | t >>= 192; 54 | x0 <<= 64; 55 | } 56 | if (t >= 1 << 96) { 57 | t >>= 96; 58 | x0 <<= 32; 59 | } 60 | if (t >= 1 << 48) { 61 | t >>= 48; 62 | x0 <<= 16; 63 | } 64 | if (t >= 1 << 24) { 65 | t >>= 24; 66 | x0 <<= 8; 67 | } 68 | if (t >= 1 << 12) { 69 | t >>= 12; 70 | x0 <<= 4; 71 | } 72 | if (t >= 1 << 6) { 73 | t >>= 6; 74 | x0 <<= 2; 75 | } 76 | if (t >= 1 << 3) { 77 | x0 <<= 1; 78 | } 79 | uint256 x1 = (2 * x0 + s / x0 / x0) / 3; 80 | while (x1 < x0) { 81 | x0 = x1; 82 | x1 = (2 * x0 + s / x0 / x0) / 3; 83 | } 84 | return x0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /contracts/utils/CarefulMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | // 3 | // Copyright 2020 Compound Labs, Inc. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | // 11 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | pragma solidity ^0.6.0; 15 | 16 | /** 17 | * @title Careful Math 18 | * @author Compound 19 | * @notice Derived from OpenZeppelin's SafeMath library 20 | * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol 21 | */ 22 | abstract contract CarefulMath { 23 | /** 24 | * @dev Possible error codes that we can return 25 | */ 26 | enum MathError { 27 | NO_ERROR, 28 | DIVISION_BY_ZERO, 29 | INTEGER_OVERFLOW, 30 | INTEGER_UNDERFLOW 31 | } 32 | 33 | /** 34 | * @dev Multiplies two numbers, returns an error on overflow. 35 | */ 36 | function mulUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { 37 | if (a == 0) { 38 | return (MathError.NO_ERROR, 0); 39 | } 40 | 41 | uint256 c = a * b; 42 | 43 | if (c / a != b) { 44 | return (MathError.INTEGER_OVERFLOW, 0); 45 | } else { 46 | return (MathError.NO_ERROR, c); 47 | } 48 | } 49 | 50 | /** 51 | * @dev Integer division of two numbers, truncating the quotient. 52 | */ 53 | function divUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { 54 | if (b == 0) { 55 | return (MathError.DIVISION_BY_ZERO, 0); 56 | } 57 | 58 | return (MathError.NO_ERROR, a / b); 59 | } 60 | 61 | /** 62 | * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). 63 | */ 64 | function subUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { 65 | if (b <= a) { 66 | return (MathError.NO_ERROR, a - b); 67 | } else { 68 | return (MathError.INTEGER_UNDERFLOW, 0); 69 | } 70 | } 71 | 72 | /** 73 | * @dev Adds two numbers, returns an error on overflow. 74 | */ 75 | function addUInt(uint256 a, uint256 b) internal pure returns (MathError, uint256) { 76 | uint256 c = a + b; 77 | 78 | if (c >= a) { 79 | return (MathError.NO_ERROR, c); 80 | } else { 81 | return (MathError.INTEGER_OVERFLOW, 0); 82 | } 83 | } 84 | 85 | /** 86 | * @dev add a and b and then subtract c 87 | */ 88 | function addThenSubUInt( 89 | uint256 a, 90 | uint256 b, 91 | uint256 c 92 | ) internal pure returns (MathError, uint256) { 93 | (MathError err0, uint256 sum) = addUInt(a, b); 94 | 95 | if (err0 != MathError.NO_ERROR) { 96 | return (err0, 0); 97 | } 98 | 99 | return subUInt(sum, c); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /contracts/utils/CoreUtility.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | 6 | abstract contract CoreUtility { 7 | using SafeMath for uint256; 8 | 9 | /// @dev UTC time of a day when the fund settles. 10 | uint256 internal constant SETTLEMENT_TIME = 14 hours; 11 | 12 | /// @dev Return end timestamp of the trading week containing a given timestamp. 13 | /// 14 | /// A trading week starts at UTC time `SETTLEMENT_TIME` on a Thursday (inclusive) 15 | /// and ends at the same time of the next Thursday (exclusive). 16 | /// @param timestamp The given timestamp 17 | /// @return End timestamp of the trading week. 18 | function _endOfWeek(uint256 timestamp) internal pure returns (uint256) { 19 | return ((timestamp.add(1 weeks) - SETTLEMENT_TIME) / 1 weeks) * 1 weeks + SETTLEMENT_TIME; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/utils/ManagedPausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.0 <0.8.0; 3 | 4 | /** 5 | * @dev Contract of an emergency stop mechanism that can be triggered by an authorized account. 6 | * 7 | * This module is modified based on Pausable in OpenZeppelin v3.3.0, adding public functions to 8 | * pause, unpause and manage the pauser role. It is also designed to be used by upgradable 9 | * contracts, like PausableUpgradable but with compact storage slots and no dependencies. 10 | */ 11 | abstract contract ManagedPausable { 12 | /** 13 | * @dev Emitted when the pause is triggered by `account`. 14 | */ 15 | event Paused(address account); 16 | 17 | /** 18 | * @dev Emitted when the pause is lifted by `account`. 19 | */ 20 | event Unpaused(address account); 21 | 22 | event PauserRoleTransferred(address indexed previousPauser, address indexed newPauser); 23 | 24 | uint256 private constant FALSE = 0; 25 | uint256 private constant TRUE = 1; 26 | 27 | uint256 private _initialized; 28 | 29 | uint256 private _paused; 30 | 31 | address private _pauser; 32 | 33 | function _initializeManagedPausable(address pauser_) internal { 34 | require(_initialized == FALSE); 35 | _initialized = TRUE; 36 | _paused = FALSE; 37 | _pauser = pauser_; 38 | } 39 | 40 | /** 41 | * @dev Returns true if the contract is paused, and false otherwise. 42 | */ 43 | function paused() public view returns (bool) { 44 | return _paused != FALSE; 45 | } 46 | 47 | function pauser() public view returns (address) { 48 | return _pauser; 49 | } 50 | 51 | function renouncePauserRole() external onlyPauser { 52 | emit PauserRoleTransferred(_pauser, address(0)); 53 | _pauser = address(0); 54 | } 55 | 56 | function transferPauserRole(address newPauser) external onlyPauser { 57 | require(newPauser != address(0)); 58 | emit PauserRoleTransferred(_pauser, newPauser); 59 | _pauser = newPauser; 60 | } 61 | 62 | modifier onlyPauser() { 63 | require(_pauser == msg.sender, "Pausable: only pauser"); 64 | _; 65 | } 66 | 67 | /** 68 | * @dev Modifier to make a function callable only when the contract is not paused. 69 | * 70 | * Requirements: 71 | * 72 | * - The contract must not be paused. 73 | */ 74 | modifier whenNotPaused() { 75 | require(_paused == FALSE, "Pausable: paused"); 76 | _; 77 | } 78 | 79 | /** 80 | * @dev Modifier to make a function callable only when the contract is paused. 81 | * 82 | * Requirements: 83 | * 84 | * - The contract must be paused. 85 | */ 86 | modifier whenPaused() { 87 | require(_paused != FALSE, "Pausable: not paused"); 88 | _; 89 | } 90 | 91 | /** 92 | * @dev Triggers stopped state. 93 | * 94 | * Requirements: 95 | * 96 | * - The contract must not be paused. 97 | */ 98 | function pause() external onlyPauser whenNotPaused { 99 | _paused = TRUE; 100 | emit Paused(msg.sender); 101 | } 102 | 103 | /** 104 | * @dev Returns to normal state. 105 | * 106 | * Requirements: 107 | * 108 | * - The contract must be paused. 109 | */ 110 | function unpause() external onlyPauser whenPaused { 111 | _paused = FALSE; 112 | emit Unpaused(msg.sender); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /contracts/utils/ProxyUtility.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.6.10 <0.8.0; 3 | 4 | abstract contract ProxyUtility { 5 | /// @dev Storage slot with the admin of the contract. 6 | bytes32 private constant _ADMIN_SLOT = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1); 7 | 8 | /// @dev Revert if the proxy admin is not the caller 9 | modifier onlyProxyAdmin() { 10 | bytes32 slot = _ADMIN_SLOT; 11 | address proxyAdmin; 12 | assembly { 13 | proxyAdmin := sload(slot) 14 | } 15 | require(msg.sender == proxyAdmin, "Only proxy admin"); 16 | _; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatUserConfig, NetworksUserConfig } from "hardhat/types"; 2 | import "@nomiclabs/hardhat-waffle"; 3 | import "@nomiclabs/hardhat-etherscan"; 4 | import "solidity-coverage"; 5 | import "./tasks/accounts"; 6 | import "./tasks/deploy_address_whitelist"; 7 | import "./tasks/deploy_chess_pool"; 8 | import "./tasks/deploy_bsc_apr_oracle"; 9 | import "./tasks/deploy_bsc_staking_strategy"; 10 | import "./tasks/deploy_chess_controller_impl"; 11 | import "./tasks/deploy_chess_schedule_impl"; 12 | import "./tasks/deploy_chess_schedule_relayer"; 13 | import "./tasks/deploy_controller_ballot"; 14 | import "./tasks/deploy_eth_staking_strategy"; 15 | import "./tasks/deploy_fee_distributor"; 16 | import "./tasks/deploy_fund"; 17 | import "./tasks/deploy_fund_eth"; 18 | import "./tasks/deploy_fund_wsteth"; 19 | import "./tasks/deploy_governance"; 20 | import "./tasks/deploy_maturity_fund"; 21 | import "./tasks/deploy_misc"; 22 | import "./tasks/deploy_mock"; 23 | import "./tasks/deploy_mock_twap_oracle"; 24 | import "./tasks/deploy_vesting"; 25 | import "./tasks/deploy_voting_escrow_impl"; 26 | import "./tasks/deploy_stable_swap"; 27 | import "./tasks/deploy_stable_swap_maturity"; 28 | import "./tasks/deploy_stable_swap_wsteth"; 29 | import "./tasks/deploy_sub_governance"; 30 | import "./tasks/deploy_swap_router"; 31 | import "./tasks/deploy_flash_swap_router"; 32 | import "./tasks/deploy_flash_swap_router_v3"; 33 | import "./tasks/deploy_data_aggregator"; 34 | import "./tasks/dev_deploy_lz"; 35 | import "./tasks/dev_deploy_curve"; 36 | import "./tasks/dev_deploy_deposit_contract"; 37 | import "./tasks/dev_deploy_token_hub"; 38 | import "./tasks/dev_redemption_nft_metadata"; 39 | import "./tasks/test_deploy"; 40 | import { ETH_RPC, ETH_CHAIN_ID, DEPLOYER_PK, DEPLOYER_HD_PATH, ETHERSCAN_API_KEY } from "./config"; 41 | import "hardhat-gas-reporter"; 42 | import "hardhat-abi-exporter"; 43 | 44 | const networks: NetworksUserConfig = { 45 | hardhat: { 46 | // Waffle's `changeEtherBalance` does not support the London hard fork yet. 47 | // See this issue for details: https://github.com/EthWorks/Waffle/issues/571 48 | hardfork: "berlin", 49 | }, 50 | localhost: {}, 51 | }; 52 | if (ETH_RPC && ETH_CHAIN_ID) { 53 | if (!DEPLOYER_PK && !DEPLOYER_HD_PATH) { 54 | throw new Error("Please set either DEPLOYER_PK or DEPLOYER_HD_PATH for the remote network"); 55 | } 56 | if (DEPLOYER_PK && DEPLOYER_HD_PATH) { 57 | throw new Error("Do not set both DEPLOYER_PK and DEPLOYER_HD_PATH"); 58 | } 59 | networks.remote = { 60 | url: ETH_RPC, 61 | chainId: ETH_CHAIN_ID, 62 | accounts: DEPLOYER_PK ? [DEPLOYER_PK] : [], 63 | timeout: 1000000, 64 | }; 65 | } 66 | 67 | const config: HardhatUserConfig = { 68 | networks: networks, 69 | solidity: { 70 | version: "0.6.12", 71 | settings: { 72 | optimizer: { 73 | enabled: true, 74 | runs: 200, 75 | }, 76 | }, 77 | }, 78 | etherscan: { 79 | apiKey: ETHERSCAN_API_KEY, 80 | }, 81 | // @see https://hardhat.org/plugins/hardhat-gas-reporter.html 82 | gasReporter: { 83 | enabled: process.env.REPORT_GAS ? true : false, 84 | excludeContracts: ["test/", "utils/", "misc/"], 85 | }, 86 | abiExporter: { 87 | path: "./exported_abi", 88 | runOnCompile: true, 89 | clear: true, 90 | }, 91 | }; 92 | export default config; 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tranchess-core", 3 | "scripts": { 4 | "check": "yarn run lint && prettier --ignore-path .gitignore -c .", 5 | "fmt": "prettier --ignore-path .gitignore -w .", 6 | "lint": "eslint --ext .js,.ts --ignore-path .gitignore .", 7 | "test": "hardhat test", 8 | "coverage": "hardhat coverage --solcoverjs ./.core.solcover.js", 9 | "cc": "hardhat clean && hardhat compile" 10 | }, 11 | "devDependencies": { 12 | "@chainlink/contracts": "^0.2.2", 13 | "@ethersproject/hardware-wallets": "^5.7.0", 14 | "@nomiclabs/hardhat-ethers": "^2.2.3", 15 | "@nomiclabs/hardhat-etherscan": "^3.1.7", 16 | "@nomiclabs/hardhat-waffle": "^2.0.6", 17 | "@openzeppelin/contracts": "^3.4.2", 18 | "@openzeppelin/contracts-upgradeable": "^3.4.2", 19 | "@types/chai": "^4.3.5", 20 | "@types/mocha": "^10.0.1", 21 | "@types/node": "*", 22 | "@types/readline-sync": "^1.4.4", 23 | "@typescript-eslint/eslint-plugin": "^6.2.0", 24 | "@typescript-eslint/parser": "^6.2.0", 25 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 26 | "@uniswap/v3-core": "^1.0.1", 27 | "@uniswap/v3-periphery": "^1.4.3", 28 | "chai": "^4.3.7", 29 | "dotenv": "^16.3.1", 30 | "eslint": "^8.45.0", 31 | "eslint-config-prettier": "^8.9.0", 32 | "eslint-config-standard": "^17.1.0", 33 | "eslint-plugin-import": "^2.28.0", 34 | "eslint-plugin-node": "^11.1.0", 35 | "eslint-plugin-promise": "^6.1.1", 36 | "ethereum-waffle": "^3.4.0", 37 | "ethers": "^5.7.2", 38 | "hardhat": "^2.17.0", 39 | "hardhat-abi-exporter": "^2.10.1", 40 | "hardhat-gas-reporter": "^1.0.9", 41 | "prettier": "^2.8.8", 42 | "prettier-plugin-solidity": "^1.1.3", 43 | "readline-sync": "^1.4.10", 44 | "solidity-coverage": "^0.8.4", 45 | "ts-node": "^10.9.1", 46 | "typescript": "^5.1.6" 47 | }, 48 | "packageManager": "yarn@3.6.1" 49 | } 50 | -------------------------------------------------------------------------------- /tasks/accounts.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { updateHreSigner } from "./signers"; 3 | 4 | task("accounts", "Prints the list of accounts", async (_args, hre) => { 5 | await updateHreSigner(hre); 6 | const accounts = await hre.ethers.getSigners(); 7 | for (const account of accounts) { 8 | console.log(account.address); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /tasks/address_file.ts: -------------------------------------------------------------------------------- 1 | import fs = require("fs"); 2 | import path = require("path"); 3 | import { strict as assert } from "assert"; 4 | import { execSync } from "child_process"; 5 | import { HardhatRuntimeEnvironment } from "hardhat/types"; 6 | 7 | const ADDRESS_FILE_DIR = path.join(__dirname, "..", "deployed_addresses"); 8 | 9 | export interface Addresses { 10 | time: string; 11 | gitVersion: string; 12 | chainId: number; 13 | } 14 | 15 | export function newAddresses(hre: HardhatRuntimeEnvironment): Addresses { 16 | let gitVersion; 17 | try { 18 | gitVersion = execSync("git rev-parse HEAD").toString().trim(); 19 | } catch (e) { 20 | gitVersion = "N/A"; 21 | } 22 | return { 23 | time: new Date().toJSON(), 24 | gitVersion, 25 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 26 | chainId: hre.network.config.chainId!, 27 | }; 28 | } 29 | 30 | let addressDir: string; 31 | 32 | export function getAddressDir(hre: HardhatRuntimeEnvironment): string { 33 | if (!addressDir) { 34 | let name = `${hre.network.name}_${hre.network.config.chainId}`; 35 | if (hre.network.name === "hardhat") { 36 | name += "_" + Math.floor(new Date().getTime() / 1000).toString(); 37 | } 38 | addressDir = path.join(ADDRESS_FILE_DIR, name); 39 | } 40 | return addressDir; 41 | } 42 | 43 | function newFilename(module: string): string { 44 | const now = new Date(); 45 | let s = now.toISOString(); 46 | s = s.split(".")[0]; 47 | s = s.replace("T", "_"); 48 | s = s.split("-").join(""); 49 | s = s.split(":").join(""); 50 | return `${module}_address_${s}.json`; 51 | } 52 | 53 | export function saveAddressFile( 54 | hre: HardhatRuntimeEnvironment, 55 | module: string, 56 | addresses: T 57 | ): void { 58 | const dir = getAddressDir(hre); 59 | if (!fs.existsSync(dir)) { 60 | fs.mkdirSync(dir, { recursive: true }); 61 | } 62 | const filename = path.join(dir, newFilename(module)); 63 | if (fs.existsSync(filename)) { 64 | throw new Error(`Address file '${filename}' already exists`); 65 | } 66 | fs.writeFileSync(filename, JSON.stringify(addresses, null, 4)); 67 | } 68 | 69 | export function listAddressFile(directory: string, module: string): string[] { 70 | if (fs.existsSync(directory) && fs.lstatSync(directory).isDirectory()) { 71 | const filenames = fs.readdirSync(directory); 72 | return filenames 73 | .filter((f) => f.endsWith(".json")) 74 | .filter((f) => f.startsWith(module + "_address_")) 75 | .sort(); 76 | } else { 77 | return []; 78 | } 79 | } 80 | 81 | export function loadAddressFile( 82 | hre: HardhatRuntimeEnvironment, 83 | module: string, 84 | filename?: string 85 | ): T { 86 | const dir = getAddressDir(hre); 87 | const candidates = listAddressFile(dir, module); 88 | if (filename !== undefined) { 89 | assert.ok( 90 | candidates.indexOf(filename) >= 0, 91 | `Specified address file '${filename}' does not exist` 92 | ); 93 | } else { 94 | assert.ok(candidates.length > 0, `No address file of module '${module}' is found`); 95 | assert.ok( 96 | candidates.length === 1, 97 | `Multiple address files of module '${module}' are found` 98 | ); 99 | filename = candidates[0]; 100 | } 101 | const addresses: T = JSON.parse(fs.readFileSync(path.join(dir, filename), "utf-8")); 102 | assert.ok(addresses.time, `Malformed address file '${filename}'`); 103 | assert.ok(addresses.gitVersion, `Malformed address file '${filename}'`); 104 | assert.strictEqual(addresses.chainId, hre.network.config.chainId, "Chain ID mismatched"); 105 | return addresses; 106 | } 107 | -------------------------------------------------------------------------------- /tasks/deploy_address_whitelist.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, newAddresses } from "./address_file"; 3 | import { updateHreSigner } from "./signers"; 4 | 5 | export interface AddressWhitelistAddresses extends Addresses { 6 | addressWhitelist: string; 7 | } 8 | 9 | task("deploy_address_whitelist", "Deploy AddressWhitelist") 10 | .addParam("whitelist", "Comma-separated addresses") 11 | .setAction(async function (args, hre) { 12 | await updateHreSigner(hre); 13 | const { ethers } = hre; 14 | await hre.run("compile"); 15 | 16 | const AddressWhitelist = await ethers.getContractFactory("AddressWhitelist"); 17 | const addressWhitelist = await AddressWhitelist.deploy(args.whitelist.split(",")); 18 | console.log(`AddressWhitelist: ${addressWhitelist.address}`); 19 | 20 | const addresses: AddressWhitelistAddresses = { 21 | ...newAddresses(hre), 22 | addressWhitelist: addressWhitelist.address, 23 | }; 24 | saveAddressFile(hre, "address_whitelist", addresses); 25 | }); 26 | -------------------------------------------------------------------------------- /tasks/deploy_bsc_apr_oracle.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, newAddresses } from "./address_file"; 3 | import { updateHreSigner } from "./signers"; 4 | 5 | export interface BscAprOracleAddresses extends Addresses { 6 | token: string; 7 | vToken: string; 8 | bscAprOracle: string; 9 | } 10 | 11 | task("deploy_bsc_apr_oracle", "Deploy BscAprOracle") 12 | .addParam("token", "Token contract address") 13 | .addParam("vToken", "VToken contract address") 14 | .setAction(async (args, hre) => { 15 | await updateHreSigner(hre); 16 | const { ethers } = hre; 17 | await hre.run("compile"); 18 | 19 | const token = await ethers.getContractAt("ERC20", args.token); 20 | const tokenSymbol: string = await token.symbol(); 21 | console.log("Token symbol:", tokenSymbol); 22 | const vToken: string = args.vToken; 23 | console.log("VToken:", vToken); 24 | 25 | const BscAprOracle = await ethers.getContractFactory("BscAprOracle"); 26 | const bscAprOracle = await BscAprOracle.deploy(tokenSymbol, vToken); 27 | console.log(`BscAprOracle: ${bscAprOracle.address}`); 28 | 29 | const addresses: BscAprOracleAddresses = { 30 | ...newAddresses(hre), 31 | token: token.address, 32 | vToken, 33 | bscAprOracle: bscAprOracle.address, 34 | }; 35 | saveAddressFile(hre, `bsc_apr_oracle_${tokenSymbol.toLowerCase()}`, addresses); 36 | }); 37 | -------------------------------------------------------------------------------- /tasks/deploy_bsc_staking_strategy.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, newAddresses, loadAddressFile } from "./address_file"; 3 | import { GOVERNANCE_CONFIG } from "../config"; 4 | import { updateHreSigner } from "./signers"; 5 | import { GovernanceAddresses } from "./deploy_governance"; 6 | 7 | export interface StrategyAddresses extends Addresses { 8 | strategy: string; 9 | } 10 | 11 | const STAKE_HUB_ADDR = "0x0000000000000000000000000000000000002002"; 12 | 13 | task("deploy_bsc_staking_strategy", "Deploy BscStakingStrategy") 14 | .addParam("fund", "Fund contract address") 15 | .addParam("performanceFeeRate", "Performance fee rate") 16 | .setAction(async function (args, hre) { 17 | await updateHreSigner(hre); 18 | const { ethers } = hre; 19 | const { parseEther } = ethers.utils; 20 | await hre.run("compile"); 21 | 22 | const performanceFeeRate = parseEther(args.performanceFeeRate); 23 | const governanceAddresses = loadAddressFile(hre, "governance"); 24 | 25 | const BscStakingStrategy = await ethers.getContractFactory("BscStakingStrategyV2"); 26 | const bscStakingStrategyImpl = await BscStakingStrategy.deploy(STAKE_HUB_ADDR, args.fund); 27 | console.log(`BscStakingStrategy implementation: ${bscStakingStrategyImpl.address}`); 28 | 29 | const initTx = await bscStakingStrategyImpl.populateTransaction.initialize( 30 | performanceFeeRate 31 | ); 32 | const TransparentUpgradeableProxy = await ethers.getContractFactory( 33 | "TransparentUpgradeableProxy" 34 | ); 35 | const bscStakingStrategyProxy = await TransparentUpgradeableProxy.deploy( 36 | bscStakingStrategyImpl.address, 37 | governanceAddresses.proxyAdmin, 38 | initTx.data, 39 | { gasLimit: 1e6 } // Gas estimation may fail 40 | ); 41 | const bscStakingStrategy = BscStakingStrategy.attach(bscStakingStrategyProxy.address); 42 | console.log(`BscStakingStrategy: ${bscStakingStrategy.address}`); 43 | 44 | if (GOVERNANCE_CONFIG.TREASURY) { 45 | console.log("Transfering ownership to treasury"); 46 | await bscStakingStrategy.transferOwnership(GOVERNANCE_CONFIG.TREASURY); 47 | } 48 | 49 | const addresses: StrategyAddresses = { 50 | ...newAddresses(hre), 51 | strategy: bscStakingStrategy.address, 52 | }; 53 | saveAddressFile(hre, "bsc_staking_strategy", addresses); 54 | }); 55 | -------------------------------------------------------------------------------- /tasks/deploy_chess_controller_impl.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { endOfWeek } from "../config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { ControllerBallotAddresses } from "./deploy_controller_ballot"; 5 | import { updateHreSigner } from "./signers"; 6 | import { waitForContract } from "./utils"; 7 | 8 | export interface ChessControllerImplAddresses extends Addresses { 9 | chessControllerImpl: string; 10 | } 11 | 12 | task("deploy_chess_controller_impl", "Deploy ChessControllerV4 implementation") 13 | .addParam("launchDate", "Launch date (YYYY-MM-DD)") 14 | .setAction(async function (args, hre) { 15 | await updateHreSigner(hre); 16 | const { ethers } = hre; 17 | await hre.run("compile"); 18 | 19 | const launchStart = endOfWeek(new Date(args.launchDate).getTime() / 1000); 20 | 21 | const controllerBallotAddress = loadAddressFile( 22 | hre, 23 | "controller_ballot" 24 | ).controllerBallot; 25 | 26 | const ChessController = await ethers.getContractFactory("ChessControllerV6"); 27 | const chessControllerImpl = await ChessController.deploy( 28 | ethers.constants.AddressZero, 29 | launchStart, 30 | controllerBallotAddress 31 | ); 32 | console.log(`ChessController implementation: ${chessControllerImpl.address}`); 33 | await waitForContract(hre, chessControllerImpl.address); 34 | 35 | const addresses: ChessControllerImplAddresses = { 36 | ...newAddresses(hre), 37 | chessControllerImpl: chessControllerImpl.address, 38 | }; 39 | saveAddressFile(hre, "chess_controller_v6_impl", addresses); 40 | }); 41 | -------------------------------------------------------------------------------- /tasks/deploy_chess_pool.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { GOVERNANCE_CONFIG } from "../config"; 3 | import { Addresses, loadAddressFile, newAddresses, saveAddressFile } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { updateHreSigner } from "./signers"; 6 | import { waitForContract } from "./utils"; 7 | 8 | export interface ChessPoolAddresses extends Addresses { 9 | chessPool: string; 10 | } 11 | 12 | task("deploy_chess_pool", "Deploy LzChessPool") 13 | .addOptionalParam("chess", "Chess contract address", "") 14 | .setAction(async function (args, hre) { 15 | await updateHreSigner(hre); 16 | const { ethers } = hre; 17 | await hre.run("compile"); 18 | 19 | const chessAddress = 20 | args.chess || loadAddressFile(hre, "governance").chess; 21 | 22 | const ChessPool = await ethers.getContractFactory("ProxyOFTPool"); 23 | const chessPool = await ChessPool.deploy(GOVERNANCE_CONFIG.LZ_ENDPOINT, chessAddress); 24 | console.log(`ChessPool: ${chessPool.address}`); 25 | await waitForContract(hre, chessPool.address); 26 | 27 | await (await chessPool.setUseCustomAdapterParams(true)).wait(); 28 | 29 | const addresses: ChessPoolAddresses = { 30 | ...newAddresses(hre), 31 | chessPool: chessPool.address, 32 | }; 33 | saveAddressFile(hre, `chess_pool`, addresses); 34 | }); 35 | -------------------------------------------------------------------------------- /tasks/deploy_chess_schedule_impl.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { GOVERNANCE_CONFIG } from "../config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { updateHreSigner } from "./signers"; 6 | 7 | export interface ChessScheduleImplAddresses extends Addresses { 8 | chessScheduleImpl: string; 9 | } 10 | 11 | task("deploy_chess_schedule_impl", "Deploy ChessSchedule implementation") 12 | .addOptionalParam("chess", "Chess contract address", "") 13 | .setAction(async function (args, hre) { 14 | await updateHreSigner(hre); 15 | const { ethers } = hre; 16 | await hre.run("compile"); 17 | 18 | const chessAddress = 19 | args.chess || loadAddressFile(hre, "governance").chess; 20 | 21 | const ChessSchedule = await ethers.getContractFactory("ChessSchedule"); 22 | const chessScheduleImpl = await ChessSchedule.deploy( 23 | chessAddress, 24 | GOVERNANCE_CONFIG.LAUNCH_TIMESTAMP 25 | ); 26 | console.log(`ChessSchedule implementation: ${chessScheduleImpl.address}`); 27 | 28 | const addresses: ChessScheduleImplAddresses = { 29 | ...newAddresses(hre), 30 | chessScheduleImpl: chessScheduleImpl.address, 31 | }; 32 | saveAddressFile(hre, "chess_schedule_impl", addresses); 33 | }); 34 | -------------------------------------------------------------------------------- /tasks/deploy_chess_schedule_relayer.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { GOVERNANCE_CONFIG } from "../config"; 6 | import { updateHreSigner } from "./signers"; 7 | 8 | export interface ChessScheduleRelayerAddresses extends Addresses { 9 | relayer: string; 10 | } 11 | 12 | task("deploy_chess_schedule_relayer", "Deploy ChessScheduleRelayer") 13 | .addFlag("dry", "Get contract address without deploying it") 14 | .addParam("lzChainId", "LayerZero sub chain ID") 15 | .addParam("subSchedule", "Address of ChessSubSchedule on the sub chain") 16 | .setAction(async function (args, hre) { 17 | await updateHreSigner(hre); 18 | const { ethers } = hre; 19 | await hre.run("compile"); 20 | const [deployer] = await ethers.getSigners(); 21 | 22 | if (args.dry) { 23 | const addr = ethers.utils.getContractAddress({ 24 | from: deployer.address, 25 | nonce: await deployer.getTransactionCount("pending"), 26 | }); 27 | console.log(`ChessScheduleRelayer with be deployed at ${addr}`); 28 | return; 29 | } 30 | 31 | const lzChainId = parseInt(args.lzChainId); 32 | assert.ok(lzChainId > 0 && lzChainId < 1e9, "Invalid sub chain ID"); 33 | const subSchedule = args.subSchedule; 34 | const governanceAddresses = loadAddressFile(hre, "governance"); 35 | 36 | const ChessScheduleRelayer = await ethers.getContractFactory("ChessScheduleRelayer"); 37 | const relayer = await ChessScheduleRelayer.deploy( 38 | lzChainId, 39 | governanceAddresses.chessSchedule, 40 | governanceAddresses.chessController, 41 | governanceAddresses.chessPool, 42 | GOVERNANCE_CONFIG.LZ_ENDPOINT 43 | ); 44 | console.log(`ChessScheduleRelayer: ${relayer.address}`); 45 | 46 | await relayer.setTrustedRemoteAddress(lzChainId, subSchedule); 47 | 48 | const addresses: ChessScheduleRelayerAddresses = { 49 | ...newAddresses(hre), 50 | relayer: relayer.address, 51 | }; 52 | saveAddressFile(hre, `chess_schedule_relayer_${lzChainId}`, addresses); 53 | }); 54 | -------------------------------------------------------------------------------- /tasks/deploy_controller_ballot.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import type { FundAddresses } from "./deploy_fund"; 6 | import type { StableSwapAddresses } from "./deploy_stable_swap"; 7 | import { updateHreSigner } from "./signers"; 8 | import { waitForContract } from "./utils"; 9 | 10 | export interface ControllerBallotAddresses extends Addresses { 11 | controllerBallot: string; 12 | } 13 | 14 | task("deploy_controller_ballot", "Deploy ControllerBallot") 15 | .addOptionalParam("votingEscrow", "VotingEscrow contract address", "") 16 | .addOptionalParam("underlyingSymbols", "Comma-separated fund underlying symbols", "") 17 | .setAction(async function (args, hre) { 18 | await updateHreSigner(hre); 19 | const { ethers } = hre; 20 | await hre.run("compile"); 21 | 22 | let votingEscrowAddress = args.votingEscrow; 23 | let timelockControllerAddress; 24 | if (!votingEscrowAddress) { 25 | const governanceAddresses = loadAddressFile(hre, "governance"); 26 | votingEscrowAddress = governanceAddresses.votingEscrow; 27 | timelockControllerAddress = governanceAddresses.timelockController; 28 | } 29 | const symbols: string[] = args.underlyingSymbols.split(",").filter(Boolean); 30 | for (const symbol of symbols) { 31 | assert.match(symbol, /^[a-zA-Z]+$/, "Invalid symbol"); 32 | } 33 | 34 | const ControllerBallot = await ethers.getContractFactory("ControllerBallotV2"); 35 | const controllerBallot = await ControllerBallot.deploy(votingEscrowAddress); 36 | console.log(`ControllerBallot: ${controllerBallot.address}`); 37 | await waitForContract(hre, controllerBallot.address); 38 | 39 | for (const symbol of symbols) { 40 | const fundAddresses = loadAddressFile( 41 | hre, 42 | `fund_${symbol.toLowerCase()}` 43 | ); 44 | if (fundAddresses.shareStaking) { 45 | console.log(`Adding ${symbol} staking`); 46 | await (await controllerBallot.addPool(fundAddresses.shareStaking)).wait(); 47 | } 48 | console.log(`Adding ${symbol} BISHOP stable swap's liquidity gauge`); 49 | const stableSwapAddresses = loadAddressFile( 50 | hre, 51 | `bishop_stable_swap_${symbol.toLowerCase()}` 52 | ); 53 | await (await controllerBallot.addPool(stableSwapAddresses.liquidityGauge)).wait(); 54 | } 55 | 56 | if (timelockControllerAddress) { 57 | console.log("Transfering ownership to TimelockController"); 58 | await (await controllerBallot.transferOwnership(timelockControllerAddress)).wait(); 59 | } else { 60 | console.log("NOTE: Please transfer ownership of ControllerBallot to Timelock later"); 61 | } 62 | 63 | const addresses: ControllerBallotAddresses = { 64 | ...newAddresses(hre), 65 | controllerBallot: controllerBallot.address, 66 | }; 67 | saveAddressFile(hre, "controller_ballot", addresses); 68 | }); 69 | -------------------------------------------------------------------------------- /tasks/deploy_data_aggregator.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, loadAddressFile, newAddresses, saveAddressFile } from "./address_file"; 3 | import type { FlashSwapRouterAddresses } from "./deploy_flash_swap_router"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import type { StableSwapAddresses } from "./deploy_stable_swap"; 6 | import type { SwapRouterAddresses } from "./deploy_swap_router"; 7 | import { updateHreSigner } from "./signers"; 8 | 9 | export interface DataAggregatorAddresses extends Addresses { 10 | dataAggregator: string; 11 | } 12 | 13 | task("deploy_data_aggregator", "Deploy data aggregator") 14 | .addParam("firstUnderlyingSymbol", "Fund0 underlying symbols") 15 | .setAction(async function (args, hre) { 16 | await updateHreSigner(hre); 17 | const { ethers } = hre; 18 | await hre.run("compile"); 19 | 20 | const firstUnderlyingSymbol = args.firstUnderlyingSymbol; 21 | 22 | const governanceAddresses = loadAddressFile(hre, "governance"); 23 | const swapRouterAddresses = loadAddressFile(hre, "swap_router"); 24 | const flashSwapRouterAddresses = loadAddressFile( 25 | hre, 26 | "flash_swap_router" 27 | ); 28 | const bishopStableSwapAddress = loadAddressFile( 29 | hre, 30 | `bishop_stable_swap_${firstUnderlyingSymbol.toLowerCase()}` 31 | ); 32 | 33 | const DataAggregator = await ethers.getContractFactory("DataAggregator"); 34 | const dataAggregator = await DataAggregator.deploy( 35 | governanceAddresses.votingEscrow, 36 | governanceAddresses.chessSchedule, 37 | governanceAddresses.controllerBallot, 38 | governanceAddresses.interestRateBallot, 39 | swapRouterAddresses.swapRouter, 40 | flashSwapRouterAddresses.flashSwapRouter, 41 | bishopStableSwapAddress.quote, 42 | ethers.constants.AddressZero, 43 | [] 44 | ); 45 | console.log(`Data Aggregator: ${dataAggregator.address}`); 46 | 47 | const addresses: DataAggregatorAddresses = { 48 | ...newAddresses(hre), 49 | dataAggregator: dataAggregator.address, 50 | }; 51 | saveAddressFile(hre, `data_aggregator`, addresses); 52 | }); 53 | -------------------------------------------------------------------------------- /tasks/deploy_eth_staking_strategy.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, newAddresses, loadAddressFile } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { updateHreSigner } from "./signers"; 6 | 7 | export interface EthStrategyAddresses extends Addresses { 8 | strategy: string; 9 | nodeOperatorRegistry: string; 10 | withdrawalManager: string; 11 | withdrawalManagerFactory: string; 12 | } 13 | 14 | task("deploy_eth_staking_strategy", "Deploy EthStakingStrategy") 15 | .addParam("fund", "Fund contract address") 16 | .addParam("depositContract", "Deposit contract address") 17 | .addParam("totalFeeRate", "Total fee rate") 18 | .addParam("operatorFeeRate", "Node operator fee rate") 19 | .setAction(async function (args, hre) { 20 | await updateHreSigner(hre); 21 | const { ethers } = hre; 22 | const { parseEther } = ethers.utils; 23 | await hre.run("compile"); 24 | const [deployer] = await ethers.getSigners(); 25 | 26 | const totalFeeRate = parseEther(args.totalFeeRate); 27 | const operatorFeeRate = parseEther(args.operatorFeeRate); 28 | 29 | // +0 EthStakingStrategy 30 | // +1 NodeOperatorRegistry 31 | // +2 WithdrawalManager 32 | // +3 WithdrawalManagerFactory 33 | const registryAddress = ethers.utils.getContractAddress({ 34 | from: deployer.address, 35 | nonce: (await deployer.getTransactionCount("pending")) + 1, 36 | }); 37 | const factoryAddress = ethers.utils.getContractAddress({ 38 | from: deployer.address, 39 | nonce: (await deployer.getTransactionCount("pending")) + 3, 40 | }); 41 | 42 | const EthStakingStrategy = await ethers.getContractFactory("EthStakingStrategy"); 43 | const ethStakingStrategy = await EthStakingStrategy.deploy( 44 | args.fund, 45 | args.depositContract, 46 | registryAddress, 47 | totalFeeRate, 48 | operatorFeeRate 49 | ); 50 | console.log(`EthStakingStrategy: ${ethStakingStrategy.address}`); 51 | 52 | const NodeOperatorRegistry = await ethers.getContractFactory("NodeOperatorRegistry"); 53 | const nodeOperatorRegistry = await NodeOperatorRegistry.deploy( 54 | ethStakingStrategy.address, 55 | factoryAddress 56 | ); 57 | assert.strictEqual(nodeOperatorRegistry.address, registryAddress); 58 | console.log(`NodeOperatorRegistry: ${nodeOperatorRegistry.address}`); 59 | 60 | const WithdrawalManager = await ethers.getContractFactory("WithdrawalManager"); 61 | const withdrawalManager = await WithdrawalManager.deploy(ethStakingStrategy.address); 62 | console.log(`WithdrawalManager: ${withdrawalManager.address}`); 63 | 64 | const WithdrawalManagerFactory = await ethers.getContractFactory( 65 | "WithdrawalManagerFactory" 66 | ); 67 | const withdrawalManagerFactory = await WithdrawalManagerFactory.deploy( 68 | withdrawalManager.address 69 | ); 70 | assert.strictEqual(withdrawalManagerFactory.address, factoryAddress); 71 | console.log(`WithdrawalManagerFactory: ${withdrawalManagerFactory.address}`); 72 | 73 | console.log("Initialize the WithdrawalManager implementation"); 74 | await withdrawalManager.initialize(ethers.BigNumber.from(1).shl(256).sub(1)); 75 | 76 | console.log("Transfering ownership to Timelock"); 77 | const governanceAddresses = loadAddressFile(hre, "governance"); 78 | await ethStakingStrategy.transferOwnership(governanceAddresses.timelockController); 79 | await nodeOperatorRegistry.transferOwnership(governanceAddresses.timelockController); 80 | await withdrawalManagerFactory.transferOwnership(governanceAddresses.timelockController); 81 | 82 | const addresses: EthStrategyAddresses = { 83 | ...newAddresses(hre), 84 | strategy: ethStakingStrategy.address, 85 | nodeOperatorRegistry: nodeOperatorRegistry.address, 86 | withdrawalManager: withdrawalManager.address, 87 | withdrawalManagerFactory: withdrawalManagerFactory.address, 88 | }; 89 | saveAddressFile(hre, "eth_staking_strategy", addresses); 90 | }); 91 | -------------------------------------------------------------------------------- /tasks/deploy_fee_distributor.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 3 | import type { GovernanceAddresses } from "./deploy_governance"; 4 | import { GOVERNANCE_CONFIG } from "../config"; 5 | import { updateHreSigner } from "./signers"; 6 | import { waitForContract } from "./utils"; 7 | 8 | export interface FeeDistrubtorAddresses extends Addresses { 9 | underlying: string; 10 | underlyingSymbol: string; 11 | feeDistributor: string; 12 | } 13 | 14 | task("deploy_fee_distributor", "Deploy fund contracts") 15 | .addParam("underlying", "Underlying token address") 16 | .addParam("adminFeeRate", "Admin fraction in the fee distributor") 17 | .setAction(async function (args, hre) { 18 | await updateHreSigner(hre); 19 | const { ethers } = hre; 20 | const { parseEther } = ethers.utils; 21 | await hre.run("compile"); 22 | 23 | const adminFeeRate = parseEther(args.adminFeeRate); 24 | 25 | const governanceAddresses = loadAddressFile(hre, "governance"); 26 | 27 | const underlying = await ethers.getContractAt("ERC20", args.underlying); 28 | const underlyingSymbol = await underlying.symbol(); 29 | 30 | const FeeDistributor = await ethers.getContractFactory("FeeDistributor"); 31 | const feeDistributor = await FeeDistributor.deploy( 32 | underlying.address, 33 | governanceAddresses.votingEscrow, 34 | GOVERNANCE_CONFIG.TREASURY || (await FeeDistributor.signer.getAddress()), // admin 35 | adminFeeRate 36 | ); 37 | console.log(`FeeDistributor: ${feeDistributor.address}`); 38 | await waitForContract(hre, feeDistributor.address); 39 | 40 | console.log("Transfering ownership to TimelockController"); 41 | await ( 42 | await feeDistributor.transferOwnership(governanceAddresses.timelockController) 43 | ).wait(); 44 | 45 | const addresses: FeeDistrubtorAddresses = { 46 | ...newAddresses(hre), 47 | underlying: underlying.address, 48 | underlyingSymbol: underlyingSymbol, 49 | feeDistributor: feeDistributor.address, 50 | }; 51 | saveAddressFile(hre, `fee_distributor_${underlyingSymbol.toLowerCase()}`, addresses); 52 | }); 53 | -------------------------------------------------------------------------------- /tasks/deploy_flash_swap_router.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 3 | import { updateHreSigner } from "./signers"; 4 | import { SwapRouterAddresses } from "./deploy_swap_router"; 5 | 6 | export interface FlashSwapRouterAddresses extends Addresses { 7 | flashSwapRouter: string; 8 | } 9 | 10 | task("deploy_flash_swap_router", "Deploy the flash swap router").setAction(async function ( 11 | args, 12 | hre 13 | ) { 14 | await updateHreSigner(hre); 15 | const { ethers } = hre; 16 | await hre.run("compile"); 17 | 18 | const swapRouterAddresses = loadAddressFile(hre, "swap_router"); 19 | const swapRouter = await ethers.getContractAt("SwapRouter", swapRouterAddresses.swapRouter); 20 | 21 | const FlashSwapRouter = await ethers.getContractFactory("FlashSwapRouter"); 22 | const flashSwapRouter = await FlashSwapRouter.deploy(swapRouter.address); 23 | console.log(`FlashSwapRouter: ${flashSwapRouter.address}`); 24 | 25 | const addresses: FlashSwapRouterAddresses = { 26 | ...newAddresses(hre), 27 | flashSwapRouter: flashSwapRouter.address, 28 | }; 29 | saveAddressFile(hre, `flash_swap_router`, addresses); 30 | }); 31 | -------------------------------------------------------------------------------- /tasks/deploy_flash_swap_router_v3.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 3 | import { updateHreSigner } from "./signers"; 4 | import { SwapRouterAddresses } from "./deploy_swap_router"; 5 | 6 | export interface FlashSwapRouterAddresses extends Addresses { 7 | flashSwapRouter: string; 8 | } 9 | 10 | task("deploy_flash_swap_router_v3", "Deploy FlashSwapRouterV3").setAction(async function ( 11 | _args, 12 | hre 13 | ) { 14 | await updateHreSigner(hre); 15 | const { ethers } = hre; 16 | await hre.run("compile"); 17 | 18 | const swapRouterAddresses = loadAddressFile(hre, "swap_router"); 19 | const swapRouter = await ethers.getContractAt("SwapRouter", swapRouterAddresses.swapRouter); 20 | 21 | const FlashSwapRouter = await ethers.getContractFactory("FlashSwapRouterV3"); 22 | const flashSwapRouter = await FlashSwapRouter.deploy(swapRouter.address); 23 | console.log(`FlashSwapRouter: ${flashSwapRouter.address}`); 24 | 25 | const addresses: FlashSwapRouterAddresses = { 26 | ...newAddresses(hre), 27 | flashSwapRouter: flashSwapRouter.address, 28 | }; 29 | saveAddressFile(hre, `flash_swap_router`, addresses); 30 | }); 31 | -------------------------------------------------------------------------------- /tasks/deploy_misc.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { keyInYNStrict } from "readline-sync"; 3 | import { Addresses, saveAddressFile, newAddresses } from "./address_file"; 4 | import { updateHreSigner } from "./signers"; 5 | 6 | export interface MiscAddresses extends Addresses { 7 | protocolDataProvier?: string; 8 | batchOperationHelper?: string; 9 | batchUpgradeTool?: string; 10 | } 11 | 12 | task("deploy_misc", "Deploy misc contracts interactively") 13 | .addFlag("silent", "Run non-interactively and only deploy contracts specified by --deploy-*") 14 | .addFlag("deployBatchOperationHelper", "Deploy BatchOperationHelper without prompt") 15 | .setAction(async function (args, hre) { 16 | await updateHreSigner(hre); 17 | const { ethers } = hre; 18 | await hre.run("compile"); 19 | 20 | const addresses: MiscAddresses = newAddresses(hre); 21 | if ( 22 | args.deployBatchOperationHelper || 23 | (!args.silent && 24 | keyInYNStrict("Deploy BatchOperationHelper implementation?", { guide: true })) 25 | ) { 26 | const BatchOperationHelper = await ethers.getContractFactory("BatchOperationHelper"); 27 | const batchOperationHelper = await BatchOperationHelper.deploy(); 28 | console.log(`BatchOperationHelper: ${batchOperationHelper.address}`); 29 | addresses.batchOperationHelper = batchOperationHelper.address; 30 | } 31 | saveAddressFile(hre, "misc", addresses); 32 | }); 33 | -------------------------------------------------------------------------------- /tasks/deploy_mock_twap_oracle.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, newAddresses } from "./address_file"; 4 | import { updateHreSigner } from "./signers"; 5 | 6 | export interface TwapOracleAddresses extends Addresses { 7 | token: string; 8 | oracleSymbol: string; 9 | twapOracle: string; 10 | } 11 | 12 | task("deploy_mock_twap_oracle", "Deploy TwapOracle") 13 | .addParam("token", "Token contract address") 14 | .addParam("oracleSymbol", "Symbol in the oracle contract") 15 | .addParam("initialTwap", "The initial twap of the mock oracle") 16 | .setAction(async (args, hre) => { 17 | await updateHreSigner(hre); 18 | const { ethers } = hre; 19 | const { parseEther } = ethers.utils; 20 | await hre.run("compile"); 21 | 22 | const token = await ethers.getContractAt("ERC20", args.token); 23 | const tokenSymbol: string = await token.symbol(); 24 | console.log("Token symbol:", tokenSymbol); 25 | const oracleSymbol: string = args.oracleSymbol; 26 | assert.match(oracleSymbol, /^[A-Z]+$/, "Invalid symbol"); 27 | assert.ok(tokenSymbol.includes(oracleSymbol)); 28 | 29 | const initialTwap = parseEther(args.initialTwap); 30 | 31 | const MockTwapOracle = await ethers.getContractFactory("MockTwapOracle"); 32 | const mockTwapOracle = await MockTwapOracle.deploy( 33 | initialTwap, 34 | ethers.constants.AddressZero, 35 | 0 36 | ); 37 | console.log(`MockTwapOracle: ${mockTwapOracle.address}`); 38 | 39 | const addresses: TwapOracleAddresses = { 40 | ...newAddresses(hre), 41 | token: token.address, 42 | oracleSymbol, 43 | twapOracle: mockTwapOracle.address, 44 | }; 45 | saveAddressFile(hre, `twap_oracle_${tokenSymbol.toLowerCase()}`, addresses); 46 | }); 47 | -------------------------------------------------------------------------------- /tasks/deploy_swap_router.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { updateHreSigner } from "./signers"; 6 | import { StableSwapAddresses } from "./deploy_stable_swap"; 7 | import { waitForContract } from "./utils"; 8 | 9 | export interface SwapRouterAddresses extends Addresses { 10 | swapRouter: string; 11 | } 12 | 13 | task("deploy_swap_router", "Deploy swap routers contracts") 14 | .addOptionalParam("wstWrappingSwap", "WstETHWrappingSwap address", "") 15 | .addParam("queenSwaps", "Comma-separated fund underlying symbols for QueenStableSwaps") 16 | .addParam("bishopSwaps", "Comma-separated fund underlying symbols for BishopStableSwaps") 17 | .setAction(async function (args, hre) { 18 | await updateHreSigner(hre); 19 | const { ethers } = hre; 20 | await hre.run("compile"); 21 | 22 | const queenSwaps: string[] = args.queenSwaps.split(",").filter(Boolean); 23 | for (const queenSwap of queenSwaps) { 24 | assert.match(queenSwap, /^[a-zA-Z]+$/, "Invalid symbol"); 25 | } 26 | const bishopSwaps: string[] = args.bishopSwaps.split(",").filter(Boolean); 27 | for (const bishopSwap of bishopSwaps) { 28 | assert.match(bishopSwap, /^[a-zA-Z]+$/, "Invalid symbol"); 29 | } 30 | 31 | let wstETHAddress = ethers.constants.AddressZero; 32 | let stETHAddress = ethers.constants.AddressZero; 33 | if (args.wstWrappingSwap) { 34 | const wstETHWrappingSwap = await ethers.getContractAt( 35 | "WstETHWrappingSwap", 36 | args.wstWrappingSwap 37 | ); 38 | wstETHAddress = await wstETHWrappingSwap.wstETH(); 39 | stETHAddress = await wstETHWrappingSwap.stETH(); 40 | const wstETH = await ethers.getContractAt("IWstETH", wstETHAddress); 41 | assert.strictEqual(await wstETH.stETH(), stETHAddress); 42 | } 43 | 44 | const governanceAddresses = loadAddressFile(hre, "governance"); 45 | 46 | const swapAddressesList: StableSwapAddresses[] = []; 47 | for (const queenSwap of queenSwaps) { 48 | const queenSwapAddresses = loadAddressFile( 49 | hre, 50 | `queen_stable_swap_${queenSwap.toLowerCase()}` 51 | ); 52 | swapAddressesList.push(queenSwapAddresses); 53 | } 54 | for (const bishopSwap of bishopSwaps) { 55 | const bishopSwapAddresses = loadAddressFile( 56 | hre, 57 | `bishop_stable_swap_${bishopSwap.toLowerCase()}` 58 | ); 59 | swapAddressesList.push(bishopSwapAddresses); 60 | } 61 | 62 | const SwapRouter = await ethers.getContractFactory("SwapRouter"); 63 | const swapRouter = await SwapRouter.deploy(wstETHAddress); 64 | console.log(`SwapRouter: ${swapRouter.address}`); 65 | await waitForContract(hre, swapRouter.address); 66 | 67 | if (args.wstWrappingSwap) { 68 | await ( 69 | await swapRouter.addSwap(wstETHAddress, stETHAddress, args.wstWrappingSwap) 70 | ).wait(); 71 | } 72 | for (const swapAddresses of swapAddressesList) { 73 | const { base, baseSymbol, quote, quoteSymbol, stableSwap } = swapAddresses; 74 | console.log(`Adding ${baseSymbol}-${quoteSymbol} to the swap router`); 75 | await (await swapRouter.addSwap(base, quote, stableSwap)).wait(); 76 | } 77 | 78 | console.log("Transfering ownership to TimelockController"); 79 | await (await swapRouter.transferOwnership(governanceAddresses.timelockController)).wait(); 80 | 81 | const addresses: SwapRouterAddresses = { 82 | ...newAddresses(hre), 83 | swapRouter: swapRouter.address, 84 | }; 85 | saveAddressFile(hre, `swap_router`, addresses); 86 | }); 87 | -------------------------------------------------------------------------------- /tasks/deploy_vesting.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { Addresses, saveAddressFile, loadAddressFile, newAddresses } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { GOVERNANCE_CONFIG } from "../config"; 6 | import { updateHreSigner } from "./signers"; 7 | 8 | export interface VestingAddresses extends Addresses { 9 | recipient: string; 10 | vestingEscrow: string; 11 | } 12 | 13 | task("deploy_vesting", "Deploy and fund a VestingEscrow") 14 | .addParam("amount", "Amount of locked tokens") 15 | .addParam("recipient", "Recipient of the tokens") 16 | .addParam("startWeek", "Locked time in weeks before the first token is vested") 17 | .addParam("durationWeek", "Locked time in weeks from the first token is vested to all vested") 18 | .addParam("cliffPercent", "Pencentage of tokens vested immediately at the beginning") 19 | .setAction(async (args, hre) => { 20 | await updateHreSigner(hre); 21 | const { ethers } = hre; 22 | const { parseEther, getAddress } = ethers.utils; 23 | await hre.run("compile"); 24 | const [deployer] = await ethers.getSigners(); 25 | 26 | const amount = parseEther(args.amount); 27 | const recipient = getAddress(args.recipient); 28 | const WEEK = 7 * 86400; 29 | const startTime = GOVERNANCE_CONFIG.LAUNCH_TIMESTAMP + parseInt(args.startWeek) * WEEK; 30 | const endTime = startTime + parseInt(args.durationWeek) * WEEK; 31 | const cliffAmount = amount.mul(parseInt(args.cliffPercent)).div(100); 32 | 33 | const governanceAddresses = loadAddressFile(hre, "governance"); 34 | 35 | const chess = await ethers.getContractAt("Chess", governanceAddresses.chess); 36 | assert.ok( 37 | amount.lte(await chess.balanceOf(deployer.address)), 38 | "Insufficient CHESS in the deployer's account" 39 | ); 40 | 41 | const VestingEscrow = await ethers.getContractFactory("VestingEscrow"); 42 | const vestingEscrow = await VestingEscrow.deploy( 43 | governanceAddresses.chess, 44 | recipient, 45 | startTime, 46 | endTime, 47 | true 48 | ); 49 | console.log(`VestingEscrow: ${vestingEscrow.address}`); 50 | 51 | console.log("Initializing the VestingEscrow"); 52 | await (await chess.approve(vestingEscrow.address, amount)).wait(); 53 | await vestingEscrow.initialize(amount, cliffAmount); 54 | 55 | const addresses: VestingAddresses = { 56 | ...newAddresses(hre), 57 | recipient, 58 | vestingEscrow: vestingEscrow.address, 59 | }; 60 | saveAddressFile(hre, "vesting", addresses); 61 | }); 62 | -------------------------------------------------------------------------------- /tasks/deploy_voting_escrow_impl.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { GOVERNANCE_CONFIG } from "../config"; 3 | import { Addresses, loadAddressFile, newAddresses, saveAddressFile } from "./address_file"; 4 | import type { GovernanceAddresses } from "./deploy_governance"; 5 | import { updateHreSigner } from "./signers"; 6 | import { waitForContract } from "./utils"; 7 | 8 | export interface VotingEscrowImplAddresses extends Addresses { 9 | votingEscrowImpl: string; 10 | } 11 | 12 | task("deploy_voting_escrow_impl", "Deploy VotingEscrow implementation") 13 | .addOptionalParam("chess", "Chess contract address", "") 14 | .addOptionalParam("chessPool", "ProxyOFTPool contract address", "") 15 | .setAction(async function (args, hre) { 16 | await updateHreSigner(hre); 17 | const { ethers } = hre; 18 | await hre.run("compile"); 19 | 20 | const chessAddress = 21 | args.chess || loadAddressFile(hre, "governance").chess; 22 | const chessPoolAddress = 23 | args.chessPool || loadAddressFile(hre, "governance").chessPool; 24 | 25 | const VotingEscrow = await ethers.getContractFactory("VotingEscrowV4"); 26 | const votingEscrowImpl = await VotingEscrow.deploy( 27 | chessAddress, 28 | 208 * 7 * 86400, // 208 weeks 29 | chessPoolAddress, 30 | GOVERNANCE_CONFIG.LZ_ENDPOINT 31 | ); 32 | console.log(`VotingEscrow implementation: ${votingEscrowImpl.address}`); 33 | await waitForContract(hre, votingEscrowImpl.address); 34 | 35 | console.log("Making VotingEscrow implementation unusable without proxy"); 36 | await (await votingEscrowImpl.initialize("", "", 0)).wait(); 37 | await (await votingEscrowImpl.renounceOwnership()).wait(); 38 | 39 | const addresses: VotingEscrowImplAddresses = { 40 | ...newAddresses(hre), 41 | votingEscrowImpl: votingEscrowImpl.address, 42 | }; 43 | saveAddressFile(hre, "voting_escrow_v4_impl", addresses); 44 | }); 45 | -------------------------------------------------------------------------------- /tasks/dev_deploy_token_hub.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | import { updateHreSigner } from "./signers"; 4 | 5 | const TOKEN_HUB_ADDR = "0x0000000000000000000000000000000000001004"; 6 | 7 | task("dev_deploy_token_hub", "Deploy a mock contract for BSC precompiled TokenHub") 8 | .addFlag("force", "Replace code if the address has code before") 9 | .setAction(async function (args, hre) { 10 | await updateHreSigner(hre); 11 | const { ethers, waffle } = hre; 12 | const { deployMockContract } = waffle; 13 | const [deployer] = await ethers.getSigners(); 14 | await hre.run("compile"); 15 | 16 | const previousCode = await ethers.provider.send("eth_getCode", [TOKEN_HUB_ADDR]); 17 | if (previousCode !== "0x") { 18 | assert.ok(args.force, "The TokenHub address is already a smart contract"); 19 | } 20 | // Make sure "hardhat_setCode" is available 21 | await ethers.provider.send("hardhat_setCode", [TOKEN_HUB_ADDR, "0x"]); 22 | 23 | const MockTokenHub = await ethers.getContractAt("ITokenHub", ethers.constants.AddressZero); 24 | const mockTokenHub = await deployMockContract( 25 | deployer, 26 | MockTokenHub.interface.format() as string[] 27 | ); 28 | const code = await ethers.provider.send("eth_getCode", [mockTokenHub.address]); 29 | console.log(`Setting contract code to address ${TOKEN_HUB_ADDR}`); 30 | await ethers.provider.send("hardhat_setCode", [TOKEN_HUB_ADDR, code]); 31 | 32 | // Send mock transactions 33 | const startBlock = await ethers.provider.getBlockNumber(); 34 | await mockTokenHub.mock.getMiniRelayFee.returns(ethers.utils.parseEther("0.002")); 35 | await mockTokenHub.mock.transferOut.returns(true); 36 | const endBlock = await ethers.provider.getBlockNumber(); 37 | 38 | // Replay mock transactions on TOKEN_HUB_ADDR 39 | console.log("Setting return values of mock functions"); 40 | for (let blockNumber = startBlock + 1; blockNumber <= endBlock; blockNumber++) { 41 | const block = await ethers.provider.getBlockWithTransactions(blockNumber); 42 | for (const tx of block.transactions) { 43 | if (tx.from === deployer.address && tx.to === mockTokenHub.address) { 44 | await deployer.sendTransaction({ 45 | to: TOKEN_HUB_ADDR, 46 | data: tx.data, 47 | }); 48 | } 49 | } 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /tasks/dev_redemption_nft_metadata.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | import { task } from "hardhat/config"; 3 | 4 | task("dev_redemption_nft_metadata", "Generate a redemption NFT and print its metadata") 5 | .addParam("qSymbol", "QUEEN token's symbol") 6 | .addParam("underlyingSymbol", "Underlying token's symbol") 7 | .addParam("amountQ", "QUEEN token amount") 8 | .addParam("amountUnderlying", "Underlying token amount") 9 | .addFlag("image", "Print its token image") 10 | .setAction(async function (args, hre) { 11 | const { ethers } = hre; 12 | const { parseEther, parseUnits } = ethers.utils; 13 | 14 | assert.strictEqual(hre.network.name, "hardhat"); 15 | 16 | const Descriptor = await ethers.getContractFactory("NonfungibleRedemptionDescriptor"); 17 | const descriptor = await Descriptor.deploy( 18 | args.qSymbol, 19 | args.underlyingSymbol, 20 | 6, 21 | 0x8968b4, 22 | 0x4956b7, 23 | 0x8aa0ee, 24 | parseEther("1") 25 | ); 26 | const params = [ 27 | 65432, // tokenId 28 | parseEther(args.amountQ), 29 | parseUnits(args.amountUnderlying, 6), 30 | ethers.BigNumber.from( 31 | "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdea" 32 | ), // seed 33 | ]; 34 | const uri = await descriptor.tokenURI(...params); 35 | 36 | const HEADER = "data:application/json;base64,"; 37 | assert.ok(uri.startsWith(HEADER)); 38 | const metadata = JSON.parse(Buffer.from(uri.substring(HEADER.length), "base64").toString()); 39 | const IMAGE_HEADER = "data:image/svg+xml;base64,"; 40 | assert.ok(metadata.image.startsWith(IMAGE_HEADER)); 41 | if (args.image) { 42 | console.log( 43 | Buffer.from(metadata.image.substring(IMAGE_HEADER.length), "base64").toString() 44 | ); 45 | } else { 46 | console.log("Name:"); 47 | console.log(metadata.name); 48 | console.log(); 49 | console.log("Description:"); 50 | console.log(metadata.description); 51 | console.log(); 52 | console.log("Gas cost of tokenURI():"); 53 | console.log((await descriptor.estimateGas.tokenURI(...params)).toNumber()); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /tasks/signers.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from "hardhat/types"; 2 | import { ethers } from "ethers"; 3 | import { LedgerSigner } from "@ethersproject/hardware-wallets"; 4 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; 5 | import { DEPLOYER_HD_PATH } from "../config"; 6 | 7 | export async function updateHreSigner(hre: HardhatRuntimeEnvironment): Promise { 8 | if (DEPLOYER_HD_PATH && hre.network.name !== "hardhat") { 9 | const ledger = new LedgerSigner(hre.ethers.provider, "hid", DEPLOYER_HD_PATH); 10 | 11 | const oldSignMessage = ledger.signMessage; 12 | ledger.signMessage = async function ( 13 | message: ethers.utils.Bytes | string 14 | ): Promise { 15 | console.log("Please sign the following message on Ledger:", message); 16 | return await oldSignMessage.apply(this, [message]); 17 | }; 18 | 19 | const oldSignTransaction = ledger.signTransaction; 20 | ledger.signTransaction = async function ( 21 | transaction: ethers.providers.TransactionRequest 22 | ): Promise { 23 | console.log("Please sign the following transaction on Ledger:", transaction); 24 | return await oldSignTransaction.apply(this, [transaction]); 25 | }; 26 | 27 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 28 | const ledgerWithAddress = await SignerWithAddress.create(ledger as any); 29 | hre.ethers.getSigners = async function () { 30 | return [ledgerWithAddress]; 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tasks/utils.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from "hardhat/types"; 2 | 3 | export async function waitForContract(hre: HardhatRuntimeEnvironment, addr: string): Promise { 4 | const { ethers } = hre; 5 | let delay = 1000; 6 | while ((await ethers.provider.getCode(addr)) === "0x") { 7 | console.log(`Waiting contract deployment at ${addr}`); 8 | await new Promise((r) => setTimeout(r, delay)); // Sleep 9 | if (delay < 16000) { 10 | delay *= 2; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/advancedMath.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { BigNumber, Contract } from "ethers"; 3 | import type { Fixture } from "ethereum-waffle"; 4 | import { waffle, ethers } from "hardhat"; 5 | const { loadFixture } = waffle; 6 | 7 | describe("AdvancedMath", function () { 8 | interface FixtureData { 9 | readonly advancedMathWrapper: Contract; 10 | } 11 | 12 | let currentFixture: Fixture; 13 | let fixtureData: FixtureData; 14 | 15 | let advancedMathWrapper: Contract; 16 | 17 | async function deployFixture(): Promise { 18 | const AdvancedMathWrapper = await ethers.getContractFactory("AdvancedMathWrapper"); 19 | const advancedMathWrapper = await AdvancedMathWrapper.deploy(); 20 | 21 | return { 22 | advancedMathWrapper: advancedMathWrapper, 23 | }; 24 | } 25 | 26 | before(function () { 27 | currentFixture = deployFixture; 28 | }); 29 | 30 | beforeEach(async function () { 31 | fixtureData = await loadFixture(currentFixture); 32 | advancedMathWrapper = fixtureData.advancedMathWrapper; 33 | }); 34 | 35 | describe("sqrt()", function () { 36 | it("Should find the square root of a small number", async function () { 37 | expect(await advancedMathWrapper.sqrt(0)).to.equal(0); 38 | expect(await advancedMathWrapper.sqrt(1)).to.equal(1); 39 | expect(await advancedMathWrapper.sqrt(4)).to.equal(2); 40 | }); 41 | 42 | it("Should find the square root of a big number", async function () { 43 | expect( 44 | await advancedMathWrapper.sqrt( 45 | BigNumber.from( 46 | "55186156870478567193644641351382124067713781048612400765092754877653207859685" 47 | ) 48 | ) 49 | ).to.equal(BigNumber.from("234917340506141792124551400965823811665")); 50 | }); 51 | }); 52 | 53 | describe("cbrt()", function () { 54 | it("Should find the cube root of a small number", async function () { 55 | expect(await advancedMathWrapper.cbrt(0)).to.equal(0); 56 | expect(await advancedMathWrapper.cbrt(1)).to.equal(1); 57 | expect(await advancedMathWrapper.cbrt(8)).to.equal(2); 58 | }); 59 | 60 | it("Should find the cube root of a big number", async function () { 61 | expect( 62 | await advancedMathWrapper.cbrt( 63 | BigNumber.from( 64 | "55186156870478567193644641351382124067713781048612400765092754877653207859685" 65 | ) 66 | ) 67 | ).to.equal(BigNumber.from("38072382092838690183991666")); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish, Wallet } from "ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | export const TRANCHE_Q = 0; 5 | export const TRANCHE_B = 1; 6 | export const TRANCHE_R = 2; 7 | export const HOUR = 3600; 8 | export const DAY = HOUR * 24; 9 | export const WEEK = DAY * 7; 10 | export const SETTLEMENT_TIME = HOUR * 14; // UTC time 14:00 every day 11 | 12 | export interface FixtureWalletMap { 13 | readonly [name: string]: Wallet; 14 | } 15 | 16 | export async function advanceBlockAtTime(time: number): Promise { 17 | await ethers.provider.send("evm_mine", [time]); 18 | } 19 | 20 | export async function setNextBlockTime(time: number): Promise { 21 | await ethers.provider.send("evm_setNextBlockTimestamp", [time]); 22 | } 23 | 24 | /** 25 | * Note that failed transactions are silently ignored when automining is disabled. 26 | */ 27 | export async function setAutomine(flag: boolean): Promise { 28 | await ethers.provider.send("evm_setAutomine", [flag]); 29 | } 30 | 31 | declare global { 32 | // eslint-disable-next-line @typescript-eslint/no-namespace 33 | export namespace Chai { 34 | // Fix type annotation in @ethereum-waffle/chai 35 | interface CloseTo { 36 | (expected: BigNumberish, delta: BigNumberish, message?: string): Assertion; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist" 8 | }, 9 | "include": ["./tasks", "./scripts", "./test"], 10 | "files": ["./hardhat.config.ts"] 11 | } 12 | --------------------------------------------------------------------------------