├── .commitlintrc.js ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── deployment_task.md │ └── feature_request.md └── workflows │ ├── coverage.yaml │ ├── lint-sol.yaml │ ├── lint-ts.yaml │ ├── test-emissions.yaml │ ├── test-feeders.yaml │ ├── test-governance.yaml │ ├── test-masset.yaml │ ├── test-polygon.yaml │ ├── test-rewards.yaml │ ├── test-savings.yaml │ └── test-shared.yaml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .prettierrc.json ├── .solcover.js ├── .solhint.json ├── .solhintignore ├── LICENSE ├── README.md ├── contracts ├── buy-and-make │ ├── Collector.sol │ ├── GaugeBriber.sol │ ├── IBPool.sol │ ├── RevenueBuyBack.sol │ ├── RevenueForwarder.sol │ ├── RevenueRecipient.sol │ └── RevenueSplitBuyBack.sol ├── emissions │ ├── BalRewardsForwarder.sol │ ├── BasicRewardsForwarder.sol │ ├── BridgeForwarder.sol │ ├── DisperseForwarder.sol │ ├── EmissionsController.sol │ ├── L2BridgeRecipient.sol │ ├── L2EmissionsController.sol │ ├── README.md │ └── VotiumBribeForwarder.sol ├── feeders │ ├── FeederLogic.sol │ ├── FeederManager.sol │ ├── FeederPool.sol │ ├── InterestValidator.sol │ ├── NonPeggedFeederPool.sol │ └── peripheral │ │ ├── FeederRouter.sol │ │ └── FeederWrapper.sol ├── governance │ ├── ClaimableGovernor.sol │ ├── DelayedClaimableGovernor.sol │ ├── Governable.sol │ ├── IncentivisedVotingLockup.sol │ └── staking │ │ ├── GamifiedToken.sol │ │ ├── GamifiedVotingToken.sol │ │ ├── QuestManager.sol │ │ ├── StakedToken.sol │ │ ├── StakedTokenBPT.sol │ │ ├── StakedTokenBatcher.sol │ │ ├── StakedTokenMTA.sol │ │ ├── deps │ │ ├── GamifiedTokenStructs.sol │ │ └── SignatureVerifier.sol │ │ └── interfaces │ │ ├── IBVault.sol │ │ ├── IGovernanceHook.sol │ │ ├── ILockedERC20.sol │ │ ├── IQuestManager.sol │ │ └── IStakedToken.sol ├── interfaces │ ├── IBoostDirector.sol │ ├── IBoostedDualVaultWithLockup.sol │ ├── IBoostedVaultWithLockup.sol │ ├── IDisperse.sol │ ├── IERC4626Vault.sol │ ├── IEjector.sol │ ├── IEmissionsController.sol │ ├── IFAssetRedemptionPriceGetter.sol │ ├── IFeederPool.sol │ ├── IIncentivisedVotingLockup.sol │ ├── IInvariantValidator.sol │ ├── IMasset.sol │ ├── INexus.sol │ ├── IPlatformIntegration.sol │ ├── IRevenueRecipient.sol │ ├── IRewardsDistributionRecipient.sol │ ├── IRootChainManager.sol │ ├── ISavingsContract.sol │ ├── ISavingsManager.sol │ ├── IStakingRewardsWithPlatformToken.sol │ ├── IUnwrapper.sol │ ├── IVotes.sol │ └── IVotiumBribe.sol ├── legacy-upgraded │ ├── imbtc-mainnet-22.sol │ ├── imusd-mainnet-22.sol │ └── imusd-polygon-22.sol ├── masset │ ├── Masset.sol │ ├── MassetLogic.sol │ ├── MassetManager.sol │ ├── MassetStructs.sol │ ├── liquidator │ │ ├── ILiquidator.sol │ │ ├── Liquidator.sol │ │ └── Unliquidator.sol │ ├── peripheral │ │ ├── AaveV2Integration.sol │ │ ├── AbstractIntegration.sol │ │ ├── AlchemixIntegration.sol │ │ ├── CompoundInegration.sol │ │ ├── DudIntegration.sol │ │ ├── DudPlatform.sol │ │ ├── IDudPlatform.sol │ │ └── RenWrapper.sol │ └── versions │ │ ├── BasketManager.json │ │ ├── MV1.sol │ │ ├── MV1Migrator.sol │ │ ├── MV2.sol │ │ ├── MusdEth.json │ │ └── MusdLegacy.json ├── nexus │ └── Nexus.sol ├── peripheral │ ├── Aave │ │ ├── AaveStakedTokenV2.json │ │ ├── IAave.sol │ │ └── IAaveIncentivesController.sol │ ├── Alchemix │ │ └── IAlchemixStakingPools.sol │ ├── Balancer │ │ ├── IBalancerGauge.sol │ │ └── IChildChainStreamer.sol │ ├── Compound │ │ ├── Comptroller.json │ │ └── ICERC20.sol │ ├── Curve │ │ ├── CurveRegistryExchange.json │ │ └── ICurve.sol │ ├── ENS │ │ └── EnsEthRegistrarController.json │ ├── RAI │ │ └── IRedemptionPriceSnap.sol │ ├── Uniswap │ │ ├── BytesLib.sol │ │ ├── IUniswapV2Router02.sol │ │ ├── IUniswapV3Quoter.sol │ │ ├── IUniswapV3SwapRouter.sol │ │ └── Path.sol │ └── dydx │ │ ├── DyDx.sol │ │ └── DyDxFlashLoan.sol ├── polygon │ ├── IClaimRewards.sol │ ├── IPLiquidator.sol │ ├── PAaveIntegration.sol │ └── PLiquidator.sol ├── rewards │ ├── BoostDirector.sol │ ├── BoostDirectorV2.sol │ ├── InitializableRewardsDistributionRecipient.sol │ ├── README.md │ ├── RewardsDistributionRecipient.sol │ ├── RewardsDistributor.sol │ ├── RewardsDistributorEth.json │ ├── boosted-staking │ │ ├── BoostedDualVault.sol │ │ ├── BoostedTokenWrapper.sol │ │ └── BoostedVault.sol │ └── staking │ │ ├── HeadlessStakingRewards.sol │ │ ├── PlatformTokenVendor.sol │ │ ├── PlatformTokenVendorFactory.sol │ │ ├── StakingRewards.sol │ │ ├── StakingRewardsWithPlatformToken.sol │ │ └── StakingTokenWrapper.sol ├── savings │ ├── Poker.sol │ ├── SavingsContract.sol │ ├── SavingsManager.sol │ └── peripheral │ │ ├── IConnector.sol │ │ ├── SaveWrapper.sol │ │ └── Unwrapper.sol ├── shared │ ├── @openzeppelin-2.5 │ │ ├── Context.sol │ │ ├── ERC205.sol │ │ └── Initializable.sol │ ├── @openzeppelin │ │ └── InstantProxyAdmin.sol │ ├── GovernedMinterRole.sol │ ├── IBasicToken.sol │ ├── IERC20WithCheckpointing.sol │ ├── ImmutableModule.sol │ ├── InitializableERC20Detailed.sol │ ├── InitializableReentrancyGuard.sol │ ├── InitializableToken.sol │ ├── MassetHelpers.sol │ ├── MetaToken.sol │ ├── ModuleKeys.sol │ ├── ModuleKeysStorage.sol │ ├── PausableModule.sol │ ├── Root.sol │ ├── SafeCastExtended.sol │ ├── SlotFiller51.sol │ ├── StableMath.sol │ └── YieldValidator.sol ├── upgradability │ ├── DelayedProxyAdmin.sol │ └── Proxies.sol └── z_mocks │ ├── buy-and-make │ ├── CRP.sol │ ├── CRPFactory.sol │ ├── MockBPool.sol │ └── RecipientV1.sol │ ├── emissions │ ├── MockChildChainStreamer.sol │ ├── MockDisperse.sol │ └── MockVotiumBribe.sol │ ├── feeder │ ├── ExposedFeederLogic.sol │ ├── ExposedFeederPool.sol │ ├── ExposedNonPeggedFeederPool.sol │ └── RedemptionPriceSnapMock.sol │ ├── governance │ ├── MockBPT.sol │ ├── MockBPTGauge.sol │ ├── MockBVault.sol │ ├── MockEmissionController.sol │ ├── MockStakedTokenWithPrice.sol │ └── stakedTokenWrapper.sol │ ├── masset │ ├── ExposedMasset.sol │ ├── ExposedMassetLogic.sol │ ├── MaliciousAaveIntegration.sol │ ├── MockAave.sol │ ├── MockCompound.sol │ ├── MockMasset.sol │ ├── MockPlatformIntegration.sol │ ├── MockPlatformIntegrationWithToken.sol │ ├── MockRewardToken.sol │ ├── ValidatorWithTVLCap.sol │ └── migrate2 │ │ ├── IBasketManager.sol │ │ ├── IMassetV1.sol │ │ ├── InitializableModuleV1.sol │ │ ├── MassetStructsV1.sol │ │ ├── MusdV2Rebalance.sol │ │ └── MusdV2RebalanceSusd.sol │ ├── nexus │ └── MockNexus.sol │ ├── rewards │ ├── MockRewardsDistributionRecipient.sol │ └── MockRootChainManager.sol │ ├── savings │ ├── MockBoostedSavingsVault.sol │ ├── MockRevenueRecipient.sol │ ├── MockSavingsContract.sol │ ├── MockSavingsManager.sol │ ├── MockStakingContract.sol │ └── connectors │ │ ├── MockConnector.sol │ │ ├── MockErroneousConnector1.sol │ │ ├── MockErroneousConnector2.sol │ │ ├── MockLendingConnector.sol │ │ └── MockVaultConnector.sol │ └── shared │ ├── DeadToken.sol │ ├── IMStableVoterProxy.sol │ ├── MockAaveIncentivesController.sol │ ├── MockERC20.sol │ ├── MockERC20WithFee.sol │ ├── MockGovernable.sol │ ├── MockInitializableToken.sol │ ├── MockInitializableTokenWithFee.sol │ ├── MockRoot.sol │ ├── MockStakedAave.sol │ ├── MockTrigger.sol │ ├── MockUniswap.sol │ ├── MockUniswapV3.sol │ └── PublicStableMath.sol ├── docs └── emissions │ ├── EmissionsController.png │ ├── EmissionsController.svg │ ├── EmissionsControllerPolygon.png │ ├── EmissionsControllerPolygon.svg │ ├── EmissionsSchedule.png │ ├── buyBackForStakers.png │ ├── polygonBridge_balancer.png │ ├── polygonBridge_frax.png │ ├── polygonBridge_vimUSD.png │ ├── polygonContractDependencies.png │ └── weeklyEmissions.png ├── hardhat-fork-polygon.config.ts ├── hardhat-fork.config.ts ├── hardhat.config.ts ├── index.ts ├── package.json ├── security ├── run-flattener.sh ├── run-securify.sh └── run-slither.sh ├── tasks-fork-polygon.config.ts ├── tasks-fork.config.ts ├── tasks.config.ts ├── tasks ├── SaveWrapper.ts ├── balancer-mta-rewards │ ├── 20210720.json │ ├── 20210728.json │ ├── 20210803.json │ ├── 20210810.json │ ├── 20210817.json │ ├── 20210824.json │ └── 20210831.json ├── bridge.ts ├── deployBriber.ts ├── deployEmissionsController.ts ├── deployFeeders.ts ├── deployIntegration.ts ├── deployMV3.ts ├── deployMbtc.ts ├── deployPolygon.ts ├── deployRevenueForwarder.ts ├── deployRevenueRecipient.ts ├── deployRewards.ts ├── deploySavingsContract4626.ts ├── deploySavingsManager.ts ├── deployUnwrapper.ts ├── dials.ts ├── emissions.ts ├── ens.ts ├── feeder.ts ├── ironBankMigration.ts ├── mBTC.ts ├── mUSD.ts ├── masset.ts ├── ops.ts ├── poker.ts ├── prepareAccountFork.ts ├── rewards.ts ├── save.ts ├── stakingV1.ts ├── stakingV2.ts ├── token.ts ├── utils │ ├── deploy-utils.ts │ ├── emission-disperse-bal.ts │ ├── emissions-split-buy-back.ts │ ├── emissions-utils.ts │ ├── etherscan.ts │ ├── feederUtils.ts │ ├── flashbots.ts │ ├── index.ts │ ├── networkAddressFactory.ts │ ├── params.ts │ ├── quantity-formatters.ts │ ├── quest-utils.ts │ ├── rates-utils.ts │ ├── rewardsUtils.ts │ ├── signerFactory.ts │ ├── snap-utils.ts │ ├── storage-utils.ts │ └── tokens.ts └── vault.ts ├── tenderly.yaml ├── test-fork ├── buy-and-make │ ├── SavingsManager.json │ ├── gauge-briber.spec.ts │ ├── recipient-upgrade.spec.ts │ └── revenue-split.spec.ts ├── emissions │ ├── emissions-mainnet-deployment.spec.ts │ ├── emissions-mainnet-upgrade.spec.ts │ └── emissions-polygon-deployment.spec.ts ├── feeders │ ├── feeders-deployment.spec.ts │ ├── feeders-dudintegration.spec.ts │ ├── feeders-frax.spec.ts │ ├── feeders-musd-alchemix.spec.ts │ └── feeders-musd-cream.spec.ts ├── governance │ ├── bpt-staked-token-upgrade.spec.ts │ └── staked-token-deploy.spec.ts ├── mUSD │ ├── BasketManagerV1.json │ ├── MassetV1.json │ ├── SavingsManager.json │ ├── aave2-migration.spec.ts │ └── mUSD-migrate.spec.ts.parked └── savings │ ├── liquidator.spec.ts │ ├── poker.spec.ts │ ├── sc22-mainnet-upgrade.spec.ts │ ├── sc22-polygon-upgrade.spec.ts │ ├── sm14-upgrade.spec.ts │ ├── unliquidator.spec.ts │ └── unwrapper.spec.ts ├── test-utils ├── assertions.ts ├── btcConstants.ts ├── constants.ts ├── deploy.ts ├── fork.ts ├── index.ts ├── machines │ ├── feederMachine.ts │ ├── index.ts │ ├── mAssetMachine.ts │ └── standardAccounts.ts ├── math.ts ├── mstable-objects.ts ├── peripheral │ └── uniswap.ts ├── regex.ts ├── time.ts └── validator-data │ ├── convertCsvTestFiles.ts │ ├── cross │ ├── crossIntegrationData.json │ └── index.ts │ ├── feeder │ ├── fPoolIntegrationData.json │ ├── fPoolMintData.json │ ├── fPoolMintMultiData.json │ ├── fPoolRedeemData.json │ ├── fPoolRedeemMultiData.json │ ├── fPoolRedeemProportionalData.json │ ├── fPoolSwapData.json │ └── index.ts │ ├── index.ts │ └── masset │ ├── index.ts │ ├── integrationData.json │ ├── mintMultiTestData.json │ ├── mintTestData.json │ ├── redeemExactTestData.json │ ├── redeemMassetTestData.json │ ├── redeemTestData.json │ └── swapTestData.json ├── test ├── buy-and-make │ ├── revenue-buy-back.spec.ts │ ├── revenue-forwarder.spec.ts │ ├── revenue-recipient.spec.ts │ └── revenue-split-buy-back.spec.ts ├── emissions │ ├── bal-rewards-forwarder.spec.ts │ ├── basic-rewards-forwarder.spec.ts │ ├── disperse-forwarder.spec.ts │ ├── emission-controller-polygon.spec.ts │ ├── emission-controller.spec.ts │ └── votium-bribe-forwarder.spec.ts ├── feeders │ ├── admin.spec.ts │ ├── basic-fns.spec.ts │ ├── from-data │ │ ├── cross.spec.ts │ │ ├── integration.spec.ts │ │ └── single.spec.ts │ ├── mint.spec.ts │ ├── redeem.spec.ts │ └── swap.spec.ts ├── governance │ ├── ClaimableGovernor.behaviour.ts │ ├── DelayedClaimableGovernor.behaviour.ts │ ├── Governable.behaviour.ts │ ├── claimable-governor.spec.ts │ ├── delayed-governor.spec.ts │ ├── governable.spec.ts │ ├── incentivised-voting-lockup-gov.spec.ts │ ├── incentivised-voting-lockup-rewards.spec.ts │ └── staking │ │ ├── boost-director-v2.spec.ts │ │ ├── staked-token-batcher.spec.ts │ │ ├── staked-token-bpt.spec.ts │ │ ├── staked-token-mta.spec.ts │ │ └── staked-token.spec.ts ├── masset │ ├── admin.spec.ts │ ├── extra │ │ ├── erc20.spec.ts │ │ ├── large-basket.spec.ts │ │ └── mint-swap-redeem.spec.ts │ ├── from-data │ │ ├── integration.spec.ts │ │ └── single.spec.ts │ ├── liquidator.spec.ts │ ├── mint.spec.ts │ ├── peripheral │ │ ├── aavev2.spec.ts │ │ └── compound.spec.ts │ ├── recol.spec.ts │ ├── redeem.spec.ts │ ├── swap.spec.ts │ └── validator-etc.spec.ts ├── nexus │ └── nexus.spec.ts ├── polygon │ └── pliquidator.spec.ts ├── rewards │ ├── boost-director.spec.ts │ ├── boosted-dual-vault.spec.ts │ ├── boosted-vault.spec.ts │ ├── rewards-distributor.spec.ts │ ├── staking-rewards-with-platform-token.spec.ts │ └── staking-rewards.spec.ts ├── savings │ ├── savings-contract.spec.ts │ └── savings-manager.spec.ts └── shared │ ├── ERC20.behaviour.ts │ ├── ERC20Burnable.behaviour.ts │ ├── ERC4626.behaviour.ts │ ├── Module.behaviour.ts │ ├── PausableModule.behaviour.ts │ ├── RewardsDistributionRecipient.behaviour.ts │ ├── root.spec.ts │ └── stable-math.spec.ts ├── tsconfig.json ├── types ├── common.ts ├── index.ts ├── machines.ts └── stakedToken.ts └── yarn.lock /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | rules: { 4 | "scope-case": [2, "always", "lower-case"], 5 | "subject-case": [2, "always", "sentence-case"], 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | dist 5 | # don't lint nyc coverage output 6 | coverage 7 | # no generated types 8 | types/generated 9 | test-utils/env_setup.ts -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["airbnb-typescript", "plugin:@typescript-eslint/recommended", "prettier", "prettier/@typescript-eslint"], 3 | env: { 4 | node: true, 5 | browser: true, 6 | jest: true, 7 | }, 8 | parserOptions: { 9 | project: "./tsconfig.json", 10 | }, 11 | settings: { 12 | "import/resolver": { 13 | alias: { 14 | map: [ 15 | ["@task", "./task"], 16 | ["@forks", "./test-forks"], 17 | ["@utils", "./test-utils"], 18 | ["types/generated", "./types/generated/index", "types/contracts"], 19 | ], 20 | extensions: [".ts", ".d.ts", ".js", ".jsx", ".json"], 21 | }, 22 | }, 23 | }, 24 | rules: { 25 | "import/no-extraneous-dependencies": "off", 26 | "no-console": "off", 27 | "import/prefer-default-export": "off", 28 | "no-nested-ternary": 1, 29 | "no-await-in-loop": 1, 30 | "no-restricted-syntax": 1, 31 | "@typescript-eslint/dot-notation": 1, 32 | "@typescript-eslint/no-use-before-define": 1, 33 | "@typescript-eslint/no-loop-func": 1, 34 | "@typescript-eslint/no-unused-expressions": 1, 35 | "lines-between-class-members": 0, 36 | "prefer-destructuring": [ 37 | 1, 38 | { 39 | array: false, 40 | object: false, 41 | }, 42 | { 43 | enforceForRenamedProperties: false, 44 | }, 45 | ], 46 | "no-plusplus": ["error", { allowForLoopAfterthoughts: true }], 47 | }, 48 | overrides: [ 49 | { 50 | files: [ 51 | "./types/contracts.ts", 52 | "./types/interfaces.d.ts", 53 | "./types/**/*.ts", 54 | "./scripts/**/*.ts", 55 | "./test/**/*.ts", 56 | "./test-utils/**/*.ts", 57 | ], 58 | }, 59 | ], 60 | } 61 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | *Note: mStable has an active [bug bounty](https://docs.mstable.org/protocol/security/mstable-bug-bounty). If the bug affects running code then you should follow the responsible disclosure guidelines and submit it directly to the team in order to be eligible* 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **File and line** 16 | A link to the line containing the bug. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/deployment_task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Deployment 3 | about: Description 4 | issue: Issue LINK 5 | 6 | --- 7 | 8 | ## About 9 | A clear and concise description including contracts and networks.[...] 10 | 11 | ## Checklist 12 | The following tasks must be completed previous to the deployment. 13 | - [ ] Development 14 | - [ ] if upgraded, verify no collisions on storage layout 15 | - [ ] Feature branch passed all CI requirements. 16 | - [ ] Unit Tests passed 17 | - [ ] Fork Tests passed, (functional tests, integration tests, deployment tests, upgrade tests) 18 | - [ ] Code Coverage passed at https://coveralls.io/ 19 | - [ ] Deployment hardhat tasks created. 20 | - [ ] Double check constructor arguments, initialize functions. 21 | - [ ] Deployment 22 | - [ ] Proposed upgrade to smart contract (write down encodeFunctionData) 23 | - [ ] Accept upgrade to smart contract 24 | - [ ] Verify Etherscan 25 | - [ ] Request labels for Etherscan 26 | 27 | 28 | ## Artifacts 29 | **Contracts** 30 | - contracts/** 31 | 32 | **Unit Tests** 33 | - test/** 34 | 35 | **Fork Tests** 36 | - test-fork/** 37 | 38 | **Tasks** 39 | - tasks/** 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Coverage 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn install 23 | - run: yarn compile 24 | - run: yarn coverage 25 | 26 | - name: Coveralls 27 | uses: coverallsapp/github-action@master 28 | with: 29 | github-token: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/lint-sol.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Lint Solidity 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn install 23 | - run: yarn lint-sol 24 | -------------------------------------------------------------------------------- /.github/workflows/lint-ts.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Lint Typescript 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn install 23 | - run: yarn compile 24 | - run: yarn lint-ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-emissions.yaml: -------------------------------------------------------------------------------- 1 | on: ["push", "pull_request"] 2 | name: Test-Emissions 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/emissions/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-feeders.yaml: -------------------------------------------------------------------------------- 1 | on: ["push", "pull_request"] 2 | name: Test-Feeders 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/feeders/**/*.spec.ts ./test/feeders/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-governance.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Governance 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/governance/*.spec.ts ./test/governance/**/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-masset.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Masset 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/masset/*.spec.ts ./test/masset/**/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-polygon.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Polygon 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/polygon/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-rewards.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Rewards 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/rewards/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-savings.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Savings 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/savings/*.spec.ts 25 | -------------------------------------------------------------------------------- /.github/workflows/test-shared.yaml: -------------------------------------------------------------------------------- 1 | on: ["push"] 2 | name: Test-Peripheral 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: 14.16.1 11 | - name: Get yarn cache directory path 12 | id: yarn-cache-dir-path 13 | run: echo "::set-output name=dir::$(yarn cache dir)" 14 | 15 | - uses: actions/cache@v2 16 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 17 | with: 18 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-yarn- 22 | - run: yarn 23 | - run: yarn compile 24 | - run: yarn test:file ./test/buy-and-make/*.spec.ts ./test/nexus/*.spec.ts ./test/shared/*.spec.ts 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Directory for instrumented libs generated by jscoverage/JSCover 9 | lib-cov 10 | 11 | # Coverage directory used by tools like istanbul 12 | coverage 13 | 14 | # Dependency directories 15 | node_modules/ 16 | jspm_packages/ 17 | 18 | # TypeScript v1 declaration files 19 | typings/ 20 | 21 | # Optional npm cache directory 22 | .npm 23 | 24 | # Optional eslint cache 25 | .eslintcache 26 | 27 | 28 | # dotenv environment variables file 29 | .env 30 | 31 | # next.js build output 32 | .next 33 | 34 | # Hardhat 35 | abis 36 | cache 37 | build 38 | # Typechain 39 | /types/generated 40 | 41 | # VS Code Solidity Extension 42 | bin/contracts 43 | 44 | # TSC 45 | dist 46 | 47 | package-lock.json 48 | 49 | # .vscode 50 | .vscode/ 51 | 52 | # WebStorm 53 | .idea/ 54 | 55 | # solcover 56 | .coverage_cache/ 57 | .coverage_artifacts/ 58 | .coverage_contracts/ 59 | coverage.json 60 | cache 61 | 62 | # sol-merger 63 | flat 64 | _flat 65 | 66 | # test data 67 | /test-utils/validator-data/*.csv 68 | testoutput.txt -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint:fix 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "useTabs": false, 6 | "singleQuote": false, 7 | "printWidth": 140, 8 | "overrides": [ 9 | { 10 | "files": "*.ts", 11 | "options": { 12 | "parser": "typescript", 13 | "trailingComma": "all", 14 | "arrowParens": "always" 15 | } 16 | }, 17 | { 18 | "files": "*.sol", 19 | "options": { 20 | "printWidth": 100, 21 | "bracketSpacing": true, 22 | "explicitTypes": "always" 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | "interfaces", 4 | "integrations", 5 | "z_mocks", 6 | "shared/InitializableReentrancyGuard.sol", 7 | "integrations", 8 | "masset/peripheral", 9 | "masset/versions", 10 | "masset/liquidator/Unliquidator.sol", 11 | "peripheral", 12 | "savings/peripheral", 13 | "upgradability", 14 | "legacy", 15 | "legacy-upgraded", 16 | ], 17 | mocha: { 18 | grep: "@skip-on-coverage", // Find everything with this tag 19 | invert: true, // Run the grep's inverse set. 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "avoid-suicide": "error", 6 | "avoid-sha3": "warn", 7 | "compiler-version": ["warn", "^0.8.6"], 8 | "func-visibility": ["warn", { "ignoreConstructors": true }], 9 | "not-rely-on-time": "off", 10 | "no-empty-blocks": "off" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/z_mocks/**/* -------------------------------------------------------------------------------- /contracts/buy-and-make/Collector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ISavingsManager } from "../interfaces/ISavingsManager.sol"; 5 | import { ImmutableModule } from "../shared/ImmutableModule.sol"; 6 | 7 | /** 8 | * @title Collector 9 | * @dev Distributes unallocated interest across multiple mAssets via savingsManager 10 | */ 11 | contract Collector is ImmutableModule { 12 | constructor(address _nexus) ImmutableModule(_nexus) {} 13 | 14 | /** 15 | * @dev Distributes the interest accrued across multiple mAssets, optionally 16 | * calling collectAndDistribute beforehand. 17 | */ 18 | function distributeInterest(address[] calldata _mAssets, bool _collectFirst) external { 19 | ISavingsManager savingsManager = ISavingsManager(_savingsManager()); 20 | uint256 len = _mAssets.length; 21 | require(len > 0, "Invalid inputs"); 22 | for (uint256 i = 0; i < len; i++) { 23 | if (_collectFirst) savingsManager.collectAndDistributeInterest(_mAssets[i]); 24 | 25 | savingsManager.distributeUnallocatedInterest(_mAssets[i]); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/buy-and-make/IBPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IBPool { 5 | function joinswapExternAmountIn( 6 | address tokenIn, 7 | uint256 tokenAmountIn, 8 | uint256 minPoolAmountOut 9 | ) external returns (uint256 poolAmountOut); 10 | 11 | function swapExactAmountIn( 12 | address tokenIn, 13 | uint256 tokenAmountIn, 14 | address tokenOut, 15 | uint256 minAmountOut, 16 | uint256 maxPrice 17 | ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/emissions/BalRewardsForwarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IChildChainStreamer } from "../peripheral/Balancer/IChildChainStreamer.sol"; 5 | import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol"; 6 | import { InitializableRewardsDistributionRecipient } from "../rewards/InitializableRewardsDistributionRecipient.sol"; 7 | import { Initializable } from "../shared/@openzeppelin-2.5/Initializable.sol"; 8 | import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 9 | import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; 10 | import { BasicRewardsForwarder } from "./BasicRewardsForwarder.sol"; 11 | 12 | /** 13 | * @title BalRewardsForwarder 14 | * @author mStable 15 | * @notice Transfers any received reward tokens to another contract and notifies it. 16 | * @dev VERSION: 1.0 17 | * DATE: 2022-06-16 18 | */ 19 | contract BalRewardsForwarder is BasicRewardsForwarder { 20 | using SafeERC20 for IERC20; 21 | 22 | /** 23 | * @param _nexus mStable system Nexus address 24 | * @param _rewardsToken Token that is being distributed as a reward. eg MTA 25 | */ 26 | constructor(address _nexus, address _rewardsToken) 27 | BasicRewardsForwarder(_nexus, _rewardsToken) 28 | {} 29 | 30 | /** 31 | * @notice Called by the Emissions Controller to trigger the processing of the weekly BAL rewards. 32 | * @dev The Emissions Controller has already transferred the MTA to this contract. 33 | * @param _rewards Units of reward tokens that were distributed to this contract 34 | */ 35 | function notifyRewardAmount(uint256 _rewards) 36 | external 37 | override(BasicRewardsForwarder) 38 | onlyRewardsDistributor 39 | { 40 | // Send the rewards to the end recipient 41 | REWARDS_TOKEN.safeTransfer(endRecipient, _rewards); 42 | // Notify the end recipient of the rewards 43 | IChildChainStreamer(endRecipient).notify_reward_amount(address(REWARDS_TOKEN)); 44 | emit RewardsReceived(_rewards); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/emissions/L2BridgeRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | 6 | /** 7 | * @title L2BridgeRecipient 8 | * @author mStable 9 | * @notice Deployed on Polygon (or other L2's), this contract receives bridged tokens and gives the root emissions 10 | * controller permission to forward them. 11 | * @dev VERSION: 1.0 12 | * DATE: 2021-10-28 13 | */ 14 | contract L2BridgeRecipient { 15 | using SafeERC20 for IERC20; 16 | 17 | /// @notice Bridged rewards token on the Polygon chain. 18 | IERC20 public immutable REWARDS_TOKEN; 19 | /// @notice Polygon contract that will distribute bridged rewards on the Polygon chain. 20 | address public immutable L2_EMISSIONS_CONTROLLER; 21 | 22 | /** 23 | * @param _rewardsToken Bridged rewards token on the Polygon chain. 24 | * @param _l2EmissionsController Polygon contract that will distribute bridged rewards on the Polygon chain. 25 | */ 26 | constructor(address _rewardsToken, address _l2EmissionsController) { 27 | require(_rewardsToken != address(0), "Invalid Rewards token"); 28 | require(_l2EmissionsController != address(0), "Invalid Emissions Controller"); 29 | 30 | REWARDS_TOKEN = IERC20(_rewardsToken); 31 | L2_EMISSIONS_CONTROLLER = _l2EmissionsController; 32 | 33 | // Approve the Polygon PoS Bridge to transfer reward tokens from this contract 34 | IERC20(_rewardsToken).safeApprove(_l2EmissionsController, type(uint256).max); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/feeders/peripheral/FeederRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | pragma abicoder v2; 4 | 5 | /** 6 | * @title FeederRouter 7 | * @author mStable 8 | * @notice Routes trades efficiently between n Feeder Pools 9 | * Routes: 10 | * 1) multiMint and multiRedeem from fPool (fp) to mAsset pools (mp) 11 | * e.g. 12 | * - mintMulti (fAsset / mpAsset) 13 | * - redeemExact (fAsset / mpAsset) 14 | * 2) swaps between all fPools (fp) 15 | * e.g. 16 | * - fp a -> fp b 17 | * @dev ToDo 18 | */ 19 | contract FeederRouter { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /contracts/governance/DelayedClaimableGovernor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ClaimableGovernor } from "./ClaimableGovernor.sol"; 5 | 6 | /** 7 | * @title DelayedClaimableGovernor 8 | * @author mStable 9 | * @notice Current Governor can initiate governance change request. 10 | * After a defined delay, proposed Governor can claim governance 11 | * ownership. 12 | * VERSION: 1.1 13 | * DATE: 2021-04-15 14 | */ 15 | contract DelayedClaimableGovernor is ClaimableGovernor { 16 | uint256 public delay = 0; 17 | uint256 public requestTime = 0; 18 | 19 | /** 20 | * @dev Initializes the contract with given delay 21 | * @param _governorAddr Initial governor 22 | * @param _delay Delay in seconds for 2 way handshake 23 | */ 24 | constructor(address _governorAddr, uint256 _delay) ClaimableGovernor(_governorAddr) { 25 | require(_delay > 0, "Delay must be greater than zero"); 26 | delay = _delay; 27 | } 28 | 29 | /** 30 | * @dev Requests change of governor and logs request time 31 | * @param _proposedGovernor Address of the new governor 32 | */ 33 | function requestGovernorChange(address _proposedGovernor) public override onlyGovernor { 34 | requestTime = block.timestamp; 35 | super.requestGovernorChange(_proposedGovernor); 36 | } 37 | 38 | /** 39 | * @dev Cancels an outstanding governor change request by resetting request time 40 | */ 41 | function cancelGovernorChange() public override onlyGovernor { 42 | requestTime = 0; 43 | super.cancelGovernorChange(); 44 | } 45 | 46 | /** 47 | * @dev Proposed governor claims new position, callable after time elapsed 48 | */ 49 | function claimGovernorChange() public override onlyProposedGovernor { 50 | require(block.timestamp >= (requestTime + delay), "Delay not over"); 51 | super.claimGovernorChange(); 52 | requestTime = 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/governance/Governable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title Governable 6 | * @author mStable 7 | * @notice Simple contract implementing an Ownable pattern. 8 | * @dev Derives from V2.3.0 @openzeppelin/contracts/ownership/Ownable.sol 9 | * Modified to have custom name and features 10 | * - Removed `renounceOwnership` 11 | * - Changes `_owner` to `_governor` 12 | * VERSION: 1.1 13 | * DATE: 2021-04-15 14 | */ 15 | contract Governable { 16 | event GovernorChanged(address indexed previousGovernor, address indexed newGovernor); 17 | 18 | address private _governor; 19 | 20 | /** 21 | * @dev Initializes the contract setting the deployer as the initial Governor. 22 | */ 23 | constructor() { 24 | _governor = msg.sender; 25 | emit GovernorChanged(address(0), _governor); 26 | } 27 | 28 | /** 29 | * @dev Returns the address of the current Governor. 30 | */ 31 | function governor() public view virtual returns (address) { 32 | return _governor; 33 | } 34 | 35 | /** 36 | * @dev Throws if called by any account other than the Governor. 37 | */ 38 | modifier onlyGovernor() { 39 | require(isGovernor(), "GOV: caller is not the Governor"); 40 | _; 41 | } 42 | 43 | /** 44 | * @dev Returns true if the caller is the current Governor. 45 | */ 46 | function isGovernor() public view returns (bool) { 47 | return msg.sender == _governor; 48 | } 49 | 50 | /** 51 | * @dev Transfers Governance of the contract to a new account (`newGovernor`). 52 | * Can only be called by the current Governor. 53 | * @param _newGovernor Address of the new Governor 54 | */ 55 | function changeGovernor(address _newGovernor) external virtual onlyGovernor { 56 | _changeGovernor(_newGovernor); 57 | } 58 | 59 | /** 60 | * @dev Change Governance of the contract to a new account (`newGovernor`). 61 | * @param _newGovernor Address of the new Governor 62 | */ 63 | function _changeGovernor(address _newGovernor) internal { 64 | require(_newGovernor != address(0), "GOV: new Governor is address(0)"); 65 | emit GovernorChanged(_governor, _newGovernor); 66 | _governor = _newGovernor; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/governance/staking/StakedTokenBatcher.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IStakedToken } from "./interfaces/IStakedToken.sol"; 5 | 6 | /** 7 | * @title StakedTokenBatcher 8 | * @dev Batch transactions for staking a given staked token. 9 | */ 10 | contract StakedTokenBatcher { 11 | /** 12 | * @dev Called by anyone to poke the timestamp of an array of accounts. This allows users to 13 | * effectively 'claim' any new timeMultiplier, but will revert if any of the accounts has no changes. 14 | * It is recommend to validate off-chain the accounts before calling this function. 15 | * @param _stakedToken Address of user the staked token. 16 | * @param _accounts Array of account addresses to update. 17 | */ 18 | function reviewTimestamp(address _stakedToken, address[] calldata _accounts) external { 19 | IStakedToken stakedToken = IStakedToken(_stakedToken); 20 | uint256 len = _accounts.length; 21 | require(len > 0, "Invalid inputs"); 22 | for (uint256 i = 0; i < len; ) { 23 | stakedToken.reviewTimestamp(_accounts[i]); 24 | unchecked { 25 | ++i; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/governance/staking/deps/GamifiedTokenStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | struct Balance { 5 | /// units of staking token that has been deposited and consequently wrapped 6 | uint88 raw; 7 | /// (block.timestamp - weightedTimestamp) represents the seconds a user has had their full raw balance wrapped. 8 | /// If they deposit or withdraw, the weightedTimestamp is dragged towards block.timestamp proportionately 9 | uint32 weightedTimestamp; 10 | /// multiplier awarded for staking for a long time 11 | uint8 timeMultiplier; 12 | /// multiplier duplicated from QuestManager 13 | uint8 questMultiplier; 14 | /// Time at which the relative cooldown began 15 | uint32 cooldownTimestamp; 16 | /// Units up for cooldown 17 | uint88 cooldownUnits; 18 | } 19 | 20 | struct QuestBalance { 21 | /// last timestamp at which the user made a write action to this contract 22 | uint32 lastAction; 23 | /// permanent multiplier applied to an account, awarded for PERMANENT QuestTypes 24 | uint8 permMultiplier; 25 | /// multiplier that decays after each "season" (~9 months) by 75%, to avoid multipliers getting out of control 26 | uint8 seasonMultiplier; 27 | } 28 | 29 | /// @notice Quests can either give permanent rewards or only for the season 30 | enum QuestType { 31 | PERMANENT, 32 | SEASONAL 33 | } 34 | 35 | /// @notice Quests can be turned off by the questMaster. All those who already completed remain 36 | enum QuestStatus { 37 | ACTIVE, 38 | EXPIRED 39 | } 40 | struct Quest { 41 | /// Type of quest rewards 42 | QuestType model; 43 | /// Multiplier, from 1 == 1.01x to 100 == 2.00x 44 | uint8 multiplier; 45 | /// Is the current quest valid? 46 | QuestStatus status; 47 | /// Expiry date in seconds for the quest 48 | uint32 expiry; 49 | } 50 | -------------------------------------------------------------------------------- /contracts/governance/staking/interfaces/IBVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | struct ExitPoolRequest { 5 | address[] assets; 6 | uint256[] minAmountsOut; 7 | bytes userData; 8 | bool toInternalBalance; 9 | } 10 | 11 | interface IBVault { 12 | function exitPool( 13 | bytes32 poolId, 14 | address sender, 15 | address payable recipient, 16 | ExitPoolRequest memory request 17 | ) external; 18 | 19 | function getPoolTokens(bytes32 poolId) 20 | external 21 | view 22 | returns ( 23 | address[] memory tokens, 24 | uint256[] memory balances, 25 | uint256 lastChangeBlock 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /contracts/governance/staking/interfaces/IGovernanceHook.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IGovernanceHook { 5 | function moveVotingPowerHook( 6 | address from, 7 | address to, 8 | uint256 amount 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/governance/staking/interfaces/ILockedERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface for the optional metadata functions from the ERC20 standard. 7 | * 8 | * _Available since v4.1._ 9 | */ 10 | interface ILockedERC20 { 11 | /** 12 | * @dev Returns the name of the token. 13 | */ 14 | function name() external view returns (string memory); 15 | 16 | /** 17 | * @dev Returns the symbol of the token. 18 | */ 19 | function symbol() external view returns (string memory); 20 | 21 | /** 22 | * @dev Returns the decimals places of the token. 23 | */ 24 | function decimals() external view returns (uint8); 25 | 26 | /** 27 | * @dev Returns the amount of tokens in existence. 28 | */ 29 | function totalSupply() external view returns (uint256); 30 | 31 | /** 32 | * @dev Returns the amount of tokens owned by `account`. 33 | */ 34 | function balanceOf(address account) external view returns (uint256); 35 | 36 | /** 37 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 38 | * another (`to`). 39 | * 40 | * Note that `value` may be zero. 41 | */ 42 | event Transfer(address indexed from, address indexed to, uint256 value); 43 | 44 | /** 45 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 46 | * a call to {approve}. `value` is the new allowance. 47 | */ 48 | event Approval(address indexed owner, address indexed spender, uint256 value); 49 | } 50 | -------------------------------------------------------------------------------- /contracts/governance/staking/interfaces/IQuestManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../deps/GamifiedTokenStructs.sol"; 5 | 6 | interface IQuestManager { 7 | event QuestAdded( 8 | address questMaster, 9 | uint256 id, 10 | QuestType model, 11 | uint16 multiplier, 12 | QuestStatus status, 13 | uint32 expiry 14 | ); 15 | event QuestCompleteQuests(address indexed user, uint256[] ids); 16 | event QuestCompleteUsers(uint256 indexed questId, address[] accounts); 17 | event QuestExpired(uint16 indexed id); 18 | event QuestMaster(address oldQuestMaster, address newQuestMaster); 19 | event QuestSeasonEnded(); 20 | event QuestSigner(address oldQuestSigner, address newQuestSigner); 21 | event StakedTokenAdded(address stakedToken); 22 | 23 | // GETTERS 24 | function balanceData(address _account) external view returns (QuestBalance memory); 25 | 26 | function getQuest(uint256 _id) external view returns (Quest memory); 27 | 28 | function hasCompleted(address _account, uint256 _id) external view returns (bool); 29 | 30 | function questMaster() external view returns (address); 31 | 32 | function seasonEpoch() external view returns (uint32); 33 | 34 | // ADMIN 35 | function addQuest( 36 | QuestType _model, 37 | uint8 _multiplier, 38 | uint32 _expiry 39 | ) external; 40 | 41 | function addStakedToken(address _stakedToken) external; 42 | 43 | function expireQuest(uint16 _id) external; 44 | 45 | function setQuestMaster(address _newQuestMaster) external; 46 | 47 | function setQuestSigner(address _newQuestSigner) external; 48 | 49 | function startNewQuestSeason() external; 50 | 51 | // USER 52 | function completeUserQuests( 53 | address _account, 54 | uint256[] memory _ids, 55 | bytes calldata _signature 56 | ) external; 57 | 58 | function completeQuestUsers( 59 | uint256 _questId, 60 | address[] memory _accounts, 61 | bytes calldata _signature 62 | ) external; 63 | 64 | function checkForSeasonFinish(address _account) external returns (uint8 newQuestMultiplier); 65 | } 66 | -------------------------------------------------------------------------------- /contracts/interfaces/IBoostDirector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IBoostDirector { 5 | function getBalance(address _user) external returns (uint256); 6 | 7 | function setDirection( 8 | address _old, 9 | address _new, 10 | bool _pokeNew 11 | ) external; 12 | 13 | function whitelistVaults(address[] calldata _vaults) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interfaces/IDisperse.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | 6 | interface IDisperse { 7 | function disperseTokenSimple(IERC20 token, address[] memory recipients, uint256[] memory values) external; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interfaces/IEjector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title IEjector 6 | * @dev interface is used for Hardhat task integration 7 | */ 8 | interface IEjector { 9 | function ejectMany(address[] calldata _users) external; 10 | 11 | function votingLockup() external view returns (address); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IEmissionsController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IVotes } from "../interfaces/IVotes.sol"; 5 | import { DialData } from "../emissions/EmissionsController.sol"; 6 | 7 | /** 8 | * @title IEmissionsController 9 | * @dev Emissions Controller interface used for by RevenueBuyBack 10 | */ 11 | interface IEmissionsController { 12 | function getDialRecipient(uint256 dialId) external returns (address recipient); 13 | 14 | function donate(uint256[] memory _dialIds, uint256[] memory _amounts) external; 15 | 16 | function stakingContracts(uint256 dialId) external returns (address); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interfaces/IFAssetRedemptionPriceGetter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IFAssetRedemptionPriceGetter { 5 | function snappedRedemptionPrice() external view returns (uint256); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IIncentivisedVotingLockup.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20WithCheckpointing } from "../shared/IERC20WithCheckpointing.sol"; 5 | 6 | abstract contract IIncentivisedVotingLockup is IERC20WithCheckpointing { 7 | function getLastUserPoint(address _addr) 8 | external 9 | view 10 | virtual 11 | returns ( 12 | int128 bias, 13 | int128 slope, 14 | uint256 ts 15 | ); 16 | 17 | function createLock(uint256 _value, uint256 _unlockTime) external virtual; 18 | 19 | function withdraw() external virtual; 20 | 21 | function increaseLockAmount(uint256 _value) external virtual; 22 | 23 | function increaseLockLength(uint256 _unlockTime) external virtual; 24 | 25 | function eject(address _user) external virtual; 26 | 27 | function expireContract() external virtual; 28 | 29 | function claimReward() public virtual; 30 | 31 | function earned(address _account) public view virtual returns (uint256); 32 | 33 | function exit() external virtual; 34 | } 35 | -------------------------------------------------------------------------------- /contracts/interfaces/IInvariantValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../masset/MassetStructs.sol"; 5 | 6 | abstract contract IInvariantValidator { 7 | // Mint 8 | function computeMint( 9 | BassetData[] calldata _bAssets, 10 | uint8 _i, 11 | uint256 _rawInput, 12 | InvariantConfig memory _config 13 | ) external view virtual returns (uint256); 14 | 15 | function computeMintMulti( 16 | BassetData[] calldata _bAssets, 17 | uint8[] calldata _indices, 18 | uint256[] calldata _rawInputs, 19 | InvariantConfig memory _config 20 | ) external view virtual returns (uint256); 21 | 22 | // Swap 23 | function computeSwap( 24 | BassetData[] calldata _bAssets, 25 | uint8 _i, 26 | uint8 _o, 27 | uint256 _rawInput, 28 | uint256 _feeRate, 29 | InvariantConfig memory _config 30 | ) external view virtual returns (uint256, uint256); 31 | 32 | // Redeem 33 | function computeRedeem( 34 | BassetData[] calldata _bAssets, 35 | uint8 _i, 36 | uint256 _mAssetQuantity, 37 | InvariantConfig memory _config 38 | ) external view virtual returns (uint256); 39 | 40 | function computeRedeemExact( 41 | BassetData[] calldata _bAssets, 42 | uint8[] calldata _indices, 43 | uint256[] calldata _rawOutputs, 44 | InvariantConfig memory _config 45 | ) external view virtual returns (uint256); 46 | 47 | function computePrice(BassetData[] memory _bAssets, InvariantConfig memory _config) 48 | public 49 | pure 50 | virtual 51 | returns (uint256 price, uint256 k); 52 | } 53 | -------------------------------------------------------------------------------- /contracts/interfaces/INexus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title INexus 6 | * @dev Basic interface for interacting with the Nexus i.e. SystemKernel 7 | */ 8 | interface INexus { 9 | function governor() external view returns (address); 10 | 11 | function getModule(bytes32 key) external view returns (address); 12 | 13 | function proposeModule(bytes32 _key, address _addr) external; 14 | 15 | function cancelProposedModule(bytes32 _key) external; 16 | 17 | function acceptProposedModule(bytes32 _key) external; 18 | 19 | function acceptProposedModules(bytes32[] calldata _keys) external; 20 | 21 | function requestLockModule(bytes32 _key) external; 22 | 23 | function cancelLockModule(bytes32 _key) external; 24 | 25 | function lockModule(bytes32 _key) external; 26 | } 27 | -------------------------------------------------------------------------------- /contracts/interfaces/IPlatformIntegration.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title Platform interface to integrate with lending platform like Compound, AAVE etc. 6 | */ 7 | interface IPlatformIntegration { 8 | /** 9 | * @dev Deposit the given bAsset to Lending platform 10 | * @param _bAsset bAsset address 11 | * @param _amount Amount to deposit 12 | */ 13 | function deposit( 14 | address _bAsset, 15 | uint256 _amount, 16 | bool isTokenFeeCharged 17 | ) external returns (uint256 quantityDeposited); 18 | 19 | /** 20 | * @dev Withdraw given bAsset from Lending platform 21 | */ 22 | function withdraw( 23 | address _receiver, 24 | address _bAsset, 25 | uint256 _amount, 26 | bool _hasTxFee 27 | ) external; 28 | 29 | /** 30 | * @dev Withdraw given bAsset from Lending platform 31 | */ 32 | function withdraw( 33 | address _receiver, 34 | address _bAsset, 35 | uint256 _amount, 36 | uint256 _totalAmount, 37 | bool _hasTxFee 38 | ) external; 39 | 40 | /** 41 | * @dev Withdraw given bAsset from the cache 42 | */ 43 | function withdrawRaw( 44 | address _receiver, 45 | address _bAsset, 46 | uint256 _amount 47 | ) external; 48 | 49 | /** 50 | * @dev Returns the current balance of the given bAsset 51 | */ 52 | function checkBalance(address _bAsset) external returns (uint256 balance); 53 | } 54 | -------------------------------------------------------------------------------- /contracts/interfaces/IRevenueRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IRevenueRecipient { 5 | /** @dev Recipient */ 6 | function notifyRedistributionAmount(address _mAsset, uint256 _amount) external; 7 | 8 | function depositToPool(address[] calldata _mAssets, uint256[] calldata _percentages) external; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/IRewardsDistributionRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IRewardsDistributionRecipient { 7 | function notifyRewardAmount(uint256 reward) external; 8 | 9 | function getRewardToken() external view returns (IERC20); 10 | } 11 | 12 | interface IRewardsRecipientWithPlatformToken { 13 | function notifyRewardAmount(uint256 reward) external; 14 | 15 | function getRewardToken() external view returns (IERC20); 16 | 17 | function getPlatformToken() external view returns (IERC20); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/interfaces/IRootChainManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IRootChainManager { 5 | function depositFor( 6 | address userAddress, 7 | address rootToken, 8 | bytes memory data 9 | ) external; 10 | } 11 | 12 | interface IStateReceiver { 13 | function onStateReceive(uint256 id, bytes calldata data) external; 14 | } 15 | 16 | interface IChildToken { 17 | function deposit(address user, bytes calldata depositData) external; 18 | 19 | function withdraw(uint256 amount) external; 20 | } -------------------------------------------------------------------------------- /contracts/interfaces/ISavingsManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title ISavingsManager 6 | */ 7 | interface ISavingsManager { 8 | /** @dev Admin privs */ 9 | function distributeUnallocatedInterest(address _mAsset) external; 10 | 11 | /** @dev Liquidator */ 12 | function depositLiquidation(address _mAsset, uint256 _liquidation) external; 13 | 14 | /** @dev Liquidator */ 15 | function collectAndStreamInterest(address _mAsset) external; 16 | 17 | /** @dev Public privs */ 18 | function collectAndDistributeInterest(address _mAsset) external; 19 | 20 | /** @dev getter for public lastBatchCollected mapping */ 21 | function lastBatchCollected(address _mAsset) external view returns (uint256); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interfaces/IUnwrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title IUnwrapper 6 | */ 7 | interface IUnwrapper { 8 | // @dev Get bAssetOut status 9 | function getIsBassetOut( 10 | address _masset, 11 | bool _inputIsCredit, 12 | address _output 13 | ) external view returns (bool isBassetOut); 14 | 15 | /// @dev Estimate output 16 | function getUnwrapOutput( 17 | bool _isBassetOut, 18 | address _router, 19 | address _input, 20 | bool _inputIsCredit, 21 | address _output, 22 | uint256 _amount 23 | ) external view returns (uint256 output); 24 | 25 | /// @dev Unwrap and send 26 | function unwrapAndSend( 27 | bool _isBassetOut, 28 | address _router, 29 | address _input, 30 | address _output, 31 | uint256 _amount, 32 | uint256 _minAmountOut, 33 | address _beneficiary 34 | ) external returns (uint256 outputQuantity); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/interfaces/IVotes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IVotes { 5 | function getVotes(address account) external view returns (uint256); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IVotiumBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | 6 | interface IVotiumBribe { 7 | function depositBribe(address _token, uint256 _amount, bytes32 _proposal, uint256 _choiceIndex) external ; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/masset/liquidator/ILiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface ILiquidator { 5 | function createLiquidation( 6 | address _integration, 7 | address _sellToken, 8 | address _bAsset, 9 | bytes calldata _uniswapPath, 10 | bytes calldata _uniswapPathReversed, 11 | uint256 _trancheAmount, 12 | uint256 _minReturn, 13 | address _mAsset, 14 | bool _useAave 15 | ) external; 16 | 17 | function updateBasset( 18 | address _integration, 19 | address _bAsset, 20 | bytes calldata _uniswapPath, 21 | bytes calldata _uniswapPathReversed, 22 | uint256 _trancheAmount, 23 | uint256 _minReturn 24 | ) external; 25 | 26 | function deleteLiquidation(address _integration) external; 27 | 28 | function triggerLiquidation(address _integration) external; 29 | 30 | function claimStakedAave() external; 31 | 32 | function triggerLiquidationAave() external; 33 | } 34 | -------------------------------------------------------------------------------- /contracts/masset/peripheral/IDudPlatform.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IDudPlatform { 5 | function bAsset() external view returns (address); 6 | 7 | function deposit(address _bAsset, uint256 _amount) external; 8 | 9 | function integration() external view returns (address); 10 | 11 | function withdraw(address _bAsset, uint256 _amount) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/masset/versions/MV1Migrator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | pragma abicoder v2; 4 | 5 | import "../MassetStructs.sol"; 6 | 7 | import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; 8 | 9 | import { IBasketManager } from "../../z_mocks/masset/migrate2/IBasketManager.sol"; 10 | import { Basket, Basset } from "../../z_mocks/masset/migrate2/MassetStructsV1.sol"; 11 | 12 | library MV1Migrator { 13 | function upgrade( 14 | IBasketManager basketManager, 15 | BassetPersonal[] storage bAssetPersonal, 16 | BassetData[] storage bAssetData, 17 | mapping(address => uint8) storage bAssetIndexes 18 | ) external { 19 | Basket memory importedBasket = basketManager.getBasket(); 20 | 21 | uint256 len = importedBasket.bassets.length; 22 | uint256[] memory scaledVaultBalances = new uint256[](len); 23 | uint256 maxScaledVaultBalance; 24 | for (uint8 i = 0; i < len; i++) { 25 | Basset memory bAsset = importedBasket.bassets[i]; 26 | address bAssetAddress = bAsset.addr; 27 | bAssetIndexes[bAssetAddress] = i; 28 | 29 | address integratorAddress = basketManager.getBassetIntegrator(bAssetAddress); 30 | bAssetPersonal.push( 31 | BassetPersonal({ 32 | addr: bAssetAddress, 33 | integrator: integratorAddress, 34 | hasTxFee: false, 35 | status: BassetStatus.Normal 36 | }) 37 | ); 38 | 39 | uint128 ratio = SafeCast.toUint128(bAsset.ratio); 40 | uint128 vaultBalance = SafeCast.toUint128(bAsset.vaultBalance); 41 | bAssetData.push(BassetData({ ratio: ratio, vaultBalance: vaultBalance })); 42 | 43 | // caclulate scaled vault bAsset balance and total vault balance 44 | uint128 scaledVaultBalance = (vaultBalance * ratio) / 1e8; 45 | scaledVaultBalances[i] = scaledVaultBalance; 46 | maxScaledVaultBalance += scaledVaultBalance; 47 | } 48 | 49 | // Check each bAsset is under 25.01% weight 50 | uint256 maxWeight = 2501; 51 | if (len == 3) { 52 | maxWeight = 3334; 53 | } else if (len != 4) { 54 | revert("Invalid length"); 55 | } 56 | maxScaledVaultBalance = (maxScaledVaultBalance * 2501) / 10000; 57 | for (uint8 i = 0; i < len; i++) { 58 | require(scaledVaultBalances[i] < maxScaledVaultBalance, "imbalanced"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/peripheral/Aave/IAaveIncentivesController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity 0.8.6; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface IAaveIncentivesController { 6 | /** 7 | * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards 8 | * @param amount Amount of rewards to claim 9 | * @param to Address that will be receiving the rewards 10 | * @return Rewards claimed 11 | **/ 12 | function claimRewards( 13 | address[] calldata assets, 14 | uint256 amount, 15 | address to 16 | ) external returns (uint256); 17 | 18 | /** 19 | * @dev Returns the total of rewards of an user, already accrued + not yet accrued 20 | * @param user The address of the user 21 | * @return The rewards 22 | **/ 23 | function getRewardsBalance(address[] calldata assets, address user) 24 | external 25 | view 26 | returns (uint256); 27 | 28 | /** 29 | * @dev returns the unclaimed rewards of the user 30 | * @param user the address of the user 31 | * @return the unclaimed user rewards 32 | */ 33 | function getUserUnclaimedRewards(address user) external view returns (uint256); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/peripheral/Alchemix/IAlchemixStakingPools.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @dev Alchemix Staking Pool 6 | * Source: https://github.com/alchemix-finance/alchemix-protocol/blob/master/contracts/StakingPools.sol 7 | */ 8 | interface IAlchemixStakingPools { 9 | function claim(uint256 _poolId) external; 10 | 11 | function deposit(uint256 _poolId, uint256 _depositAmount) external; 12 | 13 | function exit(uint256 _poolId) external; 14 | 15 | function getStakeTotalDeposited(address _account, uint256 _poolId) 16 | external 17 | view 18 | returns (uint256); 19 | 20 | function getStakeTotalUnclaimed(address _account, uint256 _poolId) 21 | external 22 | view 23 | returns (uint256); 24 | 25 | function getPoolRewardRate(uint256 _poolId) external view returns (uint256); 26 | 27 | function getPoolRewardWeight(uint256 _poolId) external view returns (uint256); 28 | 29 | function getPoolToken(uint256 _poolId) external view returns (address); 30 | 31 | function reward() external view returns (address); 32 | 33 | function tokenPoolIds(address _token) external view returns (uint256); 34 | 35 | function withdraw(uint256 _poolId, uint256 _withdrawAmount) external; 36 | } 37 | -------------------------------------------------------------------------------- /contracts/peripheral/Balancer/IBalancerGauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IBalancerGauge is IERC20 { 7 | /** 8 | * @notice Deposit `_value` LP tokens. eg mBPT. 9 | * @param _value Number of tokens to deposit 10 | */ 11 | function deposit(uint256 _value) external; 12 | 13 | /** 14 | * @notice Withdraw `_value` LP tokens. eg mBPT. 15 | * @param _value Number of tokens to withdraw 16 | */ 17 | function withdraw(uint256 _value) external; 18 | 19 | /** 20 | * @notice Set the default reward receiver for the caller. 21 | * @dev When set to ZERO_ADDRESS, rewards are sent to the caller 22 | * @param _receiver Receiver address for any rewards claimed via `claim_rewards` 23 | */ 24 | function set_rewards_receiver(address _receiver) external; 25 | 26 | 27 | /** 28 | * @notice Claim available reward tokens for `_addr` 29 | * @param _addr Address to claim for 30 | */ 31 | function claim_rewards(address _addr) external; 32 | } -------------------------------------------------------------------------------- /contracts/peripheral/Balancer/IChildChainStreamer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IChildChainStreamer { 7 | /** 8 | * @notice Notifies reward tokens for `rewardToken` 9 | * @param rewardToken Reward token to notify 10 | */ 11 | function notify_reward_amount(address rewardToken) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/peripheral/Compound/ICERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @dev Compound and Cream C Token interface 6 | * Documentation: https://compound.finance/developers/ctokens 7 | */ 8 | interface ICERC20 { 9 | /** 10 | * @notice The mint function transfers an asset into the protocol, which begins accumulating 11 | * interest based on the current Supply Rate for the asset. The user receives a quantity of 12 | * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate. 13 | * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset. 14 | * @return 0 on success, otherwise an Error codes 15 | */ 16 | function mint(uint256 mintAmount) external returns (uint256); 17 | 18 | /** 19 | * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying 20 | * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of 21 | * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less 22 | * than the user's Account Liquidity and the market's available liquidity. 23 | * @param redeemAmount The amount of underlying to be redeemed. 24 | * @return 0 on success, otherwise an Error codes 25 | */ 26 | function redeemUnderlying(uint256 redeemAmount) external returns (uint256); 27 | 28 | /** 29 | * @notice The user's underlying balance, representing their assets in the protocol, is equal to 30 | * the user's cToken balance multiplied by the Exchange Rate. 31 | * @param owner The account to get the underlying balance of. 32 | * @return The amount of underlying currently owned by the account. 33 | */ 34 | function balanceOfUnderlying(address owner) external returns (uint256); 35 | 36 | /** 37 | * @notice Calculates the exchange rate from the underlying to the CToken 38 | * @dev This function does not accrue interest before calculating the exchange rate 39 | * @return Calculated exchange rate scaled by 1e18 40 | */ 41 | function exchangeRateStored() external view returns (uint256); 42 | 43 | /** 44 | * @notice Get the token balance of the `owner` 45 | * @param owner The address of the account to query 46 | * @return The number of tokens owned by `owner` 47 | */ 48 | function balanceOf(address owner) external view returns (uint256); 49 | } 50 | -------------------------------------------------------------------------------- /contracts/peripheral/RAI/IRedemptionPriceSnap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity 0.8.6; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface IRedemptionPriceSnap { 6 | function TEN_THOUSAND() external view returns (uint256); 7 | 8 | function addAuthorization(address account) external; 9 | 10 | function authorizedAccounts(address) external view returns (uint256); 11 | 12 | function modifyParameters(bytes32 parameter, address data) external; 13 | 14 | function oracleRelayer() external view returns (address); 15 | 16 | function removeAuthorization(address account) external; 17 | 18 | function snappedRedemptionPrice() external view returns (uint256); 19 | 20 | function updateAndGetSnappedPrice() external returns (uint256); 21 | 22 | function updateSnappedPrice() external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/peripheral/Uniswap/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IUniswapV2Router02 { 5 | function swapExactTokensForTokens( 6 | uint256 amountIn, 7 | uint256 amountOutMin, // calculated off chain 8 | address[] calldata path, // also worked out off chain 9 | address to, 10 | uint256 deadline 11 | ) external returns (uint256[] memory amounts); 12 | 13 | function swapExactETHForTokens( 14 | uint256 amountOutMin, 15 | address[] calldata path, 16 | address to, 17 | uint256 deadline 18 | ) external payable returns (uint256[] memory amounts); 19 | 20 | function getAmountsIn(uint256 amountOut, address[] calldata path) 21 | external 22 | view 23 | returns (uint256[] memory amounts); 24 | 25 | function getAmountsOut(uint256 amountIn, address[] calldata path) 26 | external 27 | view 28 | returns (uint256[] memory amounts); 29 | } 30 | -------------------------------------------------------------------------------- /contracts/peripheral/dydx/DyDx.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity ^0.8.6; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface Structs { 6 | struct Val { 7 | uint256 value; 8 | } 9 | 10 | enum ActionType { 11 | Deposit, // supply tokens 12 | Withdraw, // borrow tokens 13 | Transfer, // transfer balance between accounts 14 | Buy, // buy an amount of some token (externally) 15 | Sell, // sell an amount of some token (externally) 16 | Trade, // trade tokens against another account 17 | Liquidate, // liquidate an undercollateralized or expiring account 18 | Vaporize, // use excess tokens to zero-out a completely negative account 19 | Call // send arbitrary data to an address 20 | } 21 | 22 | enum AssetDenomination { 23 | Wei // the amount is denominated in wei 24 | } 25 | 26 | enum AssetReference { 27 | Delta // the amount is given as a delta from the current value 28 | } 29 | 30 | struct AssetAmount { 31 | bool sign; // true if positive 32 | AssetDenomination denomination; 33 | AssetReference ref; 34 | uint256 value; 35 | } 36 | 37 | struct ActionArgs { 38 | ActionType actionType; 39 | uint256 accountId; 40 | AssetAmount amount; 41 | uint256 primaryMarketId; 42 | uint256 secondaryMarketId; 43 | address otherAddress; 44 | uint256 otherAccountId; 45 | bytes data; 46 | } 47 | 48 | struct Info { 49 | address owner; // The address that owns the account 50 | uint256 number; // A nonce that allows a single address to control many accounts 51 | } 52 | 53 | struct Wei { 54 | bool sign; // true if positive 55 | uint256 value; 56 | } 57 | } 58 | 59 | interface DyDxPool is Structs { 60 | function getAccountWei(Info memory account, uint256 marketId) 61 | external 62 | view 63 | returns (Wei memory); 64 | 65 | function operate(Info[] memory, ActionArgs[] memory) external; 66 | } 67 | -------------------------------------------------------------------------------- /contracts/polygon/IClaimRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IClaimRewards { 5 | /** 6 | * @notice claims platform reward tokens from a platform integration. 7 | * For example, stkAAVE from Aave. 8 | */ 9 | function claimRewards() external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/polygon/IPLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IPLiquidator { 5 | function createLiquidation( 6 | address _integration, 7 | address _sellToken, 8 | address _bAsset, 9 | address[] calldata _uniswapPath, 10 | uint256 _minReturn 11 | ) external; 12 | 13 | function updateBasset( 14 | address _integration, 15 | address _bAsset, 16 | address[] calldata _uniswapPath, 17 | uint256 _minReturn 18 | ) external; 19 | 20 | function deleteLiquidation(address _integration) external; 21 | 22 | function triggerLiquidation(address _integration) external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/polygon/PAaveIntegration.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IAaveATokenV2, IAaveLendingPoolV2, ILendingPoolAddressesProviderV2 } from "../peripheral/Aave/IAave.sol"; 5 | import { AaveV2Integration } from "../masset/peripheral/AaveV2Integration.sol"; 6 | import { IAaveIncentivesController } from "../peripheral/Aave/IAaveIncentivesController.sol"; 7 | 8 | /** 9 | * @title PAaveIntegration 10 | * @author mStable 11 | * @notice A simple connection to deposit and withdraw bAssets from Aave on Polygon 12 | * @dev VERSION: 1.0 13 | * DATE: 2020-16-11 14 | */ 15 | contract PAaveIntegration is AaveV2Integration { 16 | event RewardsClaimed(address[] assets, uint256 amount); 17 | 18 | IAaveIncentivesController public immutable rewardController; 19 | 20 | /** 21 | * @param _nexus Address of the Nexus 22 | * @param _lp Address of LP 23 | * @param _platformAddress Generic platform address 24 | * @param _rewardToken Reward token, if any 25 | * @param _rewardController AaveIncentivesController 26 | */ 27 | constructor( 28 | address _nexus, 29 | address _lp, 30 | address _platformAddress, 31 | address _rewardToken, 32 | address _rewardController 33 | ) AaveV2Integration(_nexus, _lp, _platformAddress, _rewardToken) { 34 | require(_rewardController != address(0), "Invalid controller address"); 35 | 36 | rewardController = IAaveIncentivesController(_rewardController); 37 | } 38 | 39 | /** 40 | * @dev Claims outstanding rewards from market 41 | */ 42 | function claimRewards() external { 43 | uint256 len = bAssetsMapped.length; 44 | address[] memory pTokens = new address[](len); 45 | for (uint256 i = 0; i < len; i++) { 46 | pTokens[i] = bAssetToPToken[bAssetsMapped[i]]; 47 | } 48 | uint256 rewards = rewardController.claimRewards(pTokens, type(uint256).max, address(this)); 49 | 50 | emit RewardsClaimed(pTokens, rewards); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/rewards/InitializableRewardsDistributionRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ImmutableModule } from "../shared/ImmutableModule.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol"; 7 | 8 | /** 9 | * @title RewardsDistributionRecipient 10 | * @author Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/RewardsDistributionRecipient.sol) 11 | * Changes by: mStable 12 | * @notice RewardsDistributionRecipient gets notified of additional rewards by the rewardsDistributor 13 | * @dev Changes: Addition of Module and abstract `getRewardToken` func + cosmetic 14 | */ 15 | abstract contract InitializableRewardsDistributionRecipient is 16 | IRewardsDistributionRecipient, 17 | ImmutableModule 18 | { 19 | // This address has the ability to distribute the rewards 20 | address public rewardsDistributor; 21 | 22 | constructor(address _nexus) ImmutableModule(_nexus) {} 23 | 24 | /** @dev Recipient is a module, governed by mStable governance */ 25 | function _initialize(address _rewardsDistributor) internal virtual { 26 | rewardsDistributor = _rewardsDistributor; 27 | } 28 | 29 | /** 30 | * @dev Only the rewards distributor can notify about rewards 31 | */ 32 | modifier onlyRewardsDistributor() { 33 | require(msg.sender == rewardsDistributor, "Caller is not reward distributor"); 34 | _; 35 | } 36 | 37 | /** 38 | * @dev Change the rewardsDistributor - only called by mStable governor 39 | * @param _rewardsDistributor Address of the new distributor 40 | */ 41 | function setRewardsDistribution(address _rewardsDistributor) external onlyGovernor { 42 | rewardsDistributor = _rewardsDistributor; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/rewards/README.md: -------------------------------------------------------------------------------- 1 | ## Files 2 | 3 | Why so many? 4 | 5 | Actually only 3 are base contracts 6 | 7 | ### RewardsDistributor 8 | 9 | Allows reward allocators ("FundManagers") to distribute rewards. 10 | 11 | ### StakingRewards 12 | 13 | `StakingRewards` is `InitializableRewardsDistributionRecipient` 14 | --------------> is `StakingTokenWrapper` 15 | 16 | This preserves the code written, tested, audited and deployed by `Synthetix` (StakingRewards & StakingTokenWrapper). 17 | 18 | Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/StakingRewards.sol) 19 | Audit: https://github.com/sigp/public-audits/blob/master/synthetix/unipool/review.pdf` 20 | 21 | ### StakingRewardsWithPlatformToken 22 | 23 | `StakingRewardsWithPlatformToken` is `InitializableRewardsDistributionRecipient` 24 | -------------------------------> is `StakingTokenWrapper` 25 | 26 | `StakingRewardsWithPlatformToken` deploys `PlatformTokenVendor` during its constructor 27 | -------------------------------------------------------------------------------- /contracts/rewards/RewardsDistributionRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ImmutableModule } from "../shared/ImmutableModule.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol"; 7 | 8 | /** 9 | * @title RewardsDistributionRecipient 10 | * @author Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/RewardsDistributionRecipient.sol) 11 | * Changes by: mStable 12 | * @notice RewardsDistributionRecipient gets notified of additional rewards by the rewardsDistributor 13 | * @dev Changes: Addition of ImmutableModule and abstract `getRewardToken` func + cosmetic 14 | */ 15 | abstract contract RewardsDistributionRecipient is IRewardsDistributionRecipient, ImmutableModule { 16 | // @abstract 17 | function notifyRewardAmount(uint256 reward) external virtual override; 18 | 19 | function getRewardToken() external view virtual override returns (IERC20); 20 | 21 | // This address has the ability to distribute the rewards 22 | address public rewardsDistributor; 23 | 24 | /** @dev Recipient is a module, governed by mStable governance */ 25 | constructor(address _nexus, address _rewardsDistributor) ImmutableModule(_nexus) { 26 | rewardsDistributor = _rewardsDistributor; 27 | } 28 | 29 | /** 30 | * @dev Only the rewards distributor can notify about rewards 31 | */ 32 | modifier onlyRewardsDistributor() { 33 | require(msg.sender == rewardsDistributor, "Caller is not reward distributor"); 34 | _; 35 | } 36 | 37 | /** 38 | * @dev Change the rewardsDistributor - only called by mStable governor 39 | * @param _rewardsDistributor Address of the new distributor 40 | */ 41 | function setRewardsDistribution(address _rewardsDistributor) external onlyGovernor { 42 | rewardsDistributor = _rewardsDistributor; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/rewards/staking/PlatformTokenVendor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { MassetHelpers } from "../../shared/MassetHelpers.sol"; 6 | 7 | /** 8 | * @title PlatformTokenVendor 9 | * @author mStable 10 | * @notice Stores platform tokens for distributing to StakingReward participants 11 | * @dev Only deploy this during the constructor of a given StakingReward contract 12 | */ 13 | contract PlatformTokenVendor { 14 | IERC20 public immutable platformToken; 15 | address public immutable parentStakingContract; 16 | 17 | /** @dev Simple constructor that stores the parent address */ 18 | constructor(IERC20 _platformToken) { 19 | parentStakingContract = msg.sender; 20 | platformToken = _platformToken; 21 | MassetHelpers.safeInfiniteApprove(address(_platformToken), msg.sender); 22 | } 23 | 24 | /** 25 | * @dev Re-approves the StakingReward contract to spend the platform token. 26 | * Just incase for some reason approval has been reset. 27 | */ 28 | function reApproveOwner() external { 29 | MassetHelpers.safeInfiniteApprove(address(platformToken), parentStakingContract); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/rewards/staking/PlatformTokenVendorFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { PlatformTokenVendor } from "./PlatformTokenVendor.sol"; 6 | 7 | /** 8 | * @title PlatformTokenVendorFactory 9 | * @author mStable 10 | * @notice Library that deploys a PlatformTokenVendor contract which holds rewards tokens 11 | * @dev Used to reduce the byte size of the contracts that need to deploy a PlatformTokenVendor contract 12 | */ 13 | library PlatformTokenVendorFactory { 14 | /// @dev for some reason Typechain will not generate the types if the library only has the create function 15 | function dummy() public pure returns (bool) { 16 | return true; 17 | } 18 | 19 | /** 20 | * @notice Deploys a new PlatformTokenVendor contract 21 | * @param _rewardsToken reward or platform rewards token. eg MTA or WMATIC 22 | * @return address of the deployed PlatformTokenVendor contract 23 | */ 24 | function create(IERC20 _rewardsToken) public returns (address) { 25 | PlatformTokenVendor newPlatformTokenVendor = new PlatformTokenVendor(_rewardsToken); 26 | return address(newPlatformTokenVendor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/savings/Poker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IBoostedVaultWithLockup } from "../interfaces/IBoostedVaultWithLockup.sol"; 5 | 6 | struct PokeVaultAccounts { 7 | // Address of the Boosted Vault 8 | address boostVault; 9 | // List of accounts to be poked 10 | address[] accounts; 11 | } 12 | 13 | /** 14 | * @title Poker 15 | * @author mStable 16 | * @notice Pokes accounts on boosted vaults so their vault balances can be recalculated. 17 | * @dev VERSION: 1.0 18 | * DATE: 2021-04-17 19 | */ 20 | contract Poker { 21 | /** 22 | * @dev For each boosted vault, poke all the over boosted accounts. 23 | * @param _vaultAccounts An array of PokeVaultAccounts structs 24 | */ 25 | function poke(PokeVaultAccounts[] memory _vaultAccounts) external { 26 | uint256 vaultCount = _vaultAccounts.length; 27 | for (uint256 i = 0; i < vaultCount; i++) { 28 | PokeVaultAccounts memory vaultAccounts = _vaultAccounts[i]; 29 | address boostVaultAddress = vaultAccounts.boostVault; 30 | require(boostVaultAddress != address(0), "blank vault address"); 31 | IBoostedVaultWithLockup boostVault = IBoostedVaultWithLockup(boostVaultAddress); 32 | 33 | uint256 accountsLength = vaultAccounts.accounts.length; 34 | for (uint256 j = 0; j < accountsLength; j++) { 35 | address accountAddress = vaultAccounts.accounts[j]; 36 | require(accountAddress != address(0), "blank address"); 37 | boostVault.pokeBoost(accountAddress); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/savings/peripheral/IConnector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IConnector { 5 | /** 6 | * @notice Deposits the mAsset into the connector 7 | * @param _amount Units of mAsset to receive and deposit 8 | */ 9 | function deposit(uint256 _amount) external; 10 | 11 | /** 12 | * @notice Withdraws a specific amount of mAsset from the connector 13 | * @param _amount Units of mAsset to withdraw 14 | */ 15 | function withdraw(uint256 _amount) external; 16 | 17 | /** 18 | * @notice Withdraws all mAsset from the connector 19 | */ 20 | function withdrawAll() external; 21 | 22 | /** 23 | * @notice Returns the available balance in the connector. In connections 24 | * where there is likely to be an initial dip in value due to conservative 25 | * exchange rates (e.g. with Curves `get_virtual_price`), it should return 26 | * max(deposited, balance) to avoid temporary negative yield. Any negative yield 27 | * should be corrected during a withdrawal or over time. 28 | * @return Balance of mAsset in the connector 29 | */ 30 | function checkBalance() external view returns (uint256); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/shared/@openzeppelin-2.5/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /* 5 | * @dev Provides information about the current execution context, including the 6 | * sender of the transaction and its data. While these are generally available 7 | * via msg.sender and msg.data, they should not be accessed in such a direct 8 | * manner, since when dealing with GSN meta-transactions the account sending and 9 | * paying for execution may not be the actual sender (as far as an application 10 | * is concerned). 11 | * 12 | * This contract is only required for intermediate, library-like contracts. 13 | */ 14 | abstract contract Context { 15 | // Empty internal constructor, to prevent people from mistakenly deploying 16 | // an instance of this contract, which should be used via inheritance. 17 | // constructor () internal { } 18 | // solhint-disable-previous-line no-empty-blocks 19 | 20 | function _msgSender() internal view returns (address payable) { 21 | return payable(msg.sender); 22 | } 23 | 24 | function _msgData() internal view returns (bytes memory) { 25 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 26 | return msg.data; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/shared/@openzeppelin-2.5/Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | contract Initializable { 5 | /** 6 | * @dev Indicates that the contract has been initialized. 7 | */ 8 | bool private initialized; 9 | 10 | /** 11 | * @dev Indicates that the contract is in the process of being initialized. 12 | */ 13 | bool private initializing; 14 | 15 | /** 16 | * @dev Modifier to use in the initializer function of a contract. 17 | */ 18 | modifier initializer() { 19 | require( 20 | initializing || isConstructor() || !initialized, 21 | "Contract instance has already been initialized" 22 | ); 23 | 24 | bool isTopLevelCall = !initializing; 25 | if (isTopLevelCall) { 26 | initializing = true; 27 | initialized = true; 28 | } 29 | 30 | _; 31 | 32 | if (isTopLevelCall) { 33 | initializing = false; 34 | } 35 | } 36 | 37 | /// @dev Returns true if and only if the function is running in the constructor 38 | function isConstructor() private view returns (bool) { 39 | // extcodesize checks the size of the code stored in an address, and 40 | // address returns the current address. Since the code is still not 41 | // deployed when running a constructor, any checks on its code size will 42 | // yield zero, making it an effective way to detect if a contract is 43 | // under construction or not. 44 | address self = address(this); 45 | uint256 cs; 46 | assembly { 47 | cs := extcodesize(self) 48 | } 49 | return cs == 0; 50 | } 51 | 52 | // Reserved storage space to allow for layout changes in the future. 53 | uint256[50] private ______gap; 54 | } 55 | -------------------------------------------------------------------------------- /contracts/shared/@openzeppelin/InstantProxyAdmin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 6 | 7 | contract InstantProxyAdmin is ProxyAdmin {} 8 | -------------------------------------------------------------------------------- /contracts/shared/GovernedMinterRole.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | import { ImmutableModule } from "../shared/ImmutableModule.sol"; 4 | import { INexus } from "../interfaces/INexus.sol"; 5 | 6 | import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; 8 | 9 | /** 10 | * @title GovernedMinterRole 11 | * @author OpenZeppelin (forked from @openzeppelin/contracts/access/roles/MinterRole.sol) 12 | * Update 2021/08/19 MinterRole.sol has been removed, AccessControl.sol is used instead. 13 | * @dev Forked from OpenZeppelin 'MinterRole' with changes: 14 | * - `addMinter` modified from `onlyMinter` to `onlyGovernor` 15 | * - `removeMinter` function added, callable by `onlyGovernor` 16 | */ 17 | abstract contract GovernedMinterRole is ImmutableModule, AccessControl { 18 | event MinterAdded(address indexed account); 19 | event MinterRemoved(address indexed account); 20 | 21 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 22 | 23 | constructor(address _nexus) ImmutableModule(_nexus) { 24 | _setupRole(DEFAULT_ADMIN_ROLE, INexus(_nexus).governor()); 25 | } 26 | 27 | modifier onlyMinter() { 28 | require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role"); 29 | _; 30 | } 31 | 32 | function isMinter(address account) public view returns (bool) { 33 | return hasRole(MINTER_ROLE, account); 34 | } 35 | 36 | function addMinter(address account) public onlyGovernor { 37 | _addMinter(account); 38 | } 39 | 40 | function removeMinter(address account) public onlyGovernor { 41 | _removeMinter(account); 42 | } 43 | 44 | function renounceMinter() public onlyMinter { 45 | _renounceMinter(msg.sender); 46 | } 47 | 48 | function _addMinter(address account) internal { 49 | grantRole(MINTER_ROLE, account); 50 | emit MinterAdded(account); 51 | } 52 | 53 | function _removeMinter(address account) internal { 54 | revokeRole(MINTER_ROLE, account); 55 | emit MinterRemoved(account); 56 | } 57 | 58 | function _renounceMinter(address account) internal { 59 | renounceRole(MINTER_ROLE, account); 60 | emit MinterRemoved(account); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/shared/IBasicToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface IBasicToken { 5 | function decimals() external view returns (uint8); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/shared/IERC20WithCheckpointing.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | // From https://github.com/aragonone/voting-connectors 5 | abstract contract IERC20WithCheckpointing { 6 | function balanceOf(address _owner) public view virtual returns (uint256); 7 | 8 | function balanceOfAt(address _owner, uint256 _blockNumber) 9 | public 10 | view 11 | virtual 12 | returns (uint256); 13 | 14 | function totalSupply() public view virtual returns (uint256); 15 | 16 | function totalSupplyAt(uint256 _blockNumber) public view virtual returns (uint256); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/shared/InitializableERC20Detailed.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | /** 7 | * @dev Optional functions from the ERC20 standard. 8 | * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol 9 | */ 10 | abstract contract InitializableERC20Detailed is IERC20 { 11 | string private _name; 12 | string private _symbol; 13 | uint8 private _decimals; 14 | 15 | /** 16 | * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of 17 | * these values are immutable: they can only be set once during 18 | * construction. 19 | * @notice To avoid variable shadowing appended `Arg` after arguments name. 20 | */ 21 | function _initialize( 22 | string memory nameArg, 23 | string memory symbolArg, 24 | uint8 decimalsArg 25 | ) internal { 26 | _name = nameArg; 27 | _symbol = symbolArg; 28 | _decimals = decimalsArg; 29 | } 30 | 31 | /** 32 | * @dev Returns the name of the token. 33 | */ 34 | function name() public view returns (string memory) { 35 | return _name; 36 | } 37 | 38 | /** 39 | * @dev Returns the symbol of the token, usually a shorter version of the 40 | * name. 41 | */ 42 | function symbol() public view returns (string memory) { 43 | return _symbol; 44 | } 45 | 46 | /** 47 | * @dev Returns the number of decimals used to get its user representation. 48 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 49 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 50 | * 51 | * Tokens usually opt for a value of 18, imitating the relationship between 52 | * Ether and Wei. 53 | * 54 | * NOTE: This information is only used for _display_ purposes: it in 55 | * no way affects any of the arithmetic of the contract, including 56 | * {IERC20-balanceOf} and {IERC20-transfer}. 57 | */ 58 | function decimals() public view returns (uint8) { 59 | return _decimals; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/shared/InitializableReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @dev Contract module that helps prevent reentrant calls to a function. 6 | * 7 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 8 | * available, which can be applied to functions to make sure there are no nested 9 | * (reentrant) calls to them. 10 | * 11 | * Note that because there is a single `nonReentrant` guard, functions marked as 12 | * `nonReentrant` may not call one another. This can be worked around by making 13 | * those functions `private`, and then adding `external` `nonReentrant` entry 14 | * points to them. 15 | * 16 | * TIP: If you would like to learn more about reentrancy and alternative ways 17 | * to protect against it, check out our blog post 18 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 19 | * 20 | * _Since v2.5.0:_ this module is now much more gas efficient, given net gas 21 | * metering changes introduced in the Istanbul hardfork. 22 | */ 23 | contract InitializableReentrancyGuard { 24 | bool private _notEntered; 25 | 26 | function _initializeReentrancyGuard() internal { 27 | // Storing an initial non-zero value makes deployment a bit more 28 | // expensive, but in exchange the refund on every call to nonReentrant 29 | // will be lower in amount. Since refunds are capped to a percetange of 30 | // the total transaction's gas, it is best to keep them low in cases 31 | // like this one, to increase the likelihood of the full refund coming 32 | // into effect. 33 | _notEntered = true; 34 | } 35 | 36 | /** 37 | * @dev Prevents a contract from calling itself, directly or indirectly. 38 | * Calling a `nonReentrant` function from another `nonReentrant` 39 | * function is not supported. It is possible to prevent this from happening 40 | * by making the `nonReentrant` function external, and make it call a 41 | * `private` function that does the actual work. 42 | */ 43 | modifier nonReentrant() { 44 | // On the first call to nonReentrant, _notEntered will be true 45 | require(_notEntered, "ReentrancyGuard: reentrant call"); 46 | 47 | // Any calls to nonReentrant after this point will fail 48 | _notEntered = false; 49 | 50 | _; 51 | 52 | // By storing the original value once again, a refund is triggered (see 53 | // https://eips.ethereum.org/EIPS/eip-2200) 54 | _notEntered = true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/shared/InitializableToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ERC205, IERC20 } from "./@openzeppelin-2.5/ERC205.sol"; 5 | import { InitializableERC20Detailed } from "./InitializableERC20Detailed.sol"; 6 | 7 | /** 8 | * @title InitializableToken 9 | * @author mStable 10 | * @dev Basic ERC20Detailed Token functionality for Masset 11 | */ 12 | abstract contract InitializableToken is ERC205, InitializableERC20Detailed { 13 | /** 14 | * @dev Initialization function for implementing contract 15 | * @notice To avoid variable shadowing appended `Arg` after arguments name. 16 | */ 17 | function _initialize(string memory _nameArg, string memory _symbolArg) internal virtual { 18 | InitializableERC20Detailed._initialize(_nameArg, _symbolArg, 18); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/shared/MassetHelpers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | /** 8 | * @title MassetHelpers 9 | * @author mStable 10 | * @notice Helper functions to facilitate minting and redemption from off chain 11 | * @dev VERSION: 1.0 12 | * DATE: 2020-03-28 13 | */ 14 | library MassetHelpers { 15 | using SafeERC20 for IERC20; 16 | 17 | function transferReturnBalance( 18 | address _sender, 19 | address _recipient, 20 | address _bAsset, 21 | uint256 _qty 22 | ) internal returns (uint256 receivedQty, uint256 recipientBalance) { 23 | uint256 balBefore = IERC20(_bAsset).balanceOf(_recipient); 24 | IERC20(_bAsset).safeTransferFrom(_sender, _recipient, _qty); 25 | recipientBalance = IERC20(_bAsset).balanceOf(_recipient); 26 | receivedQty = recipientBalance - balBefore; 27 | } 28 | 29 | function safeInfiniteApprove(address _asset, address _spender) internal { 30 | IERC20(_asset).safeApprove(_spender, 0); 31 | IERC20(_asset).safeApprove(_spender, 2**256 - 1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/shared/MetaToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { GovernedMinterRole } from "./GovernedMinterRole.sol"; 5 | import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 7 | 8 | /** 9 | * @title MintableToken 10 | * @author mStable 11 | */ 12 | contract MintableToken is ERC20, GovernedMinterRole, ERC20Burnable { 13 | constructor(address _nexus, address _initialRecipient) 14 | ERC20("Meta", "MTA") 15 | GovernedMinterRole(_nexus) 16 | { 17 | // 100m initial supply 18 | _mint(_initialRecipient, 100000000 * (10**18)); 19 | } 20 | 21 | /** 22 | * @dev See {ERC20-_mint}. 23 | * 24 | * Requirements: 25 | * 26 | * - the caller must have the {MinterRole}. 27 | */ 28 | function mint(address account, uint256 amount) public onlyMinter returns (bool) { 29 | _mint(account, amount); 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/shared/ModuleKeys.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title ModuleKeys 6 | * @author mStable 7 | * @notice Provides system wide access to the byte32 represntations of system modules 8 | * This allows each system module to be able to reference and update one another in a 9 | * friendly way 10 | * @dev keccak256() values are hardcoded to avoid re-evaluation of the constants at runtime. 11 | */ 12 | contract ModuleKeys { 13 | // Governance 14 | // =========== 15 | // keccak256("Governance"); 16 | bytes32 internal constant KEY_GOVERNANCE = 17 | 0x9409903de1e6fd852dfc61c9dacb48196c48535b60e25abf92acc92dd689078d; 18 | //keccak256("Staking"); 19 | bytes32 internal constant KEY_STAKING = 20 | 0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034; 21 | //keccak256("ProxyAdmin"); 22 | bytes32 internal constant KEY_PROXY_ADMIN = 23 | 0x96ed0203eb7e975a4cbcaa23951943fa35c5d8288117d50c12b3d48b0fab48d1; 24 | 25 | // mStable 26 | // ======= 27 | // keccak256("OracleHub"); 28 | bytes32 internal constant KEY_ORACLE_HUB = 29 | 0x8ae3a082c61a7379e2280f3356a5131507d9829d222d853bfa7c9fe1200dd040; 30 | // keccak256("Manager"); 31 | bytes32 internal constant KEY_MANAGER = 32 | 0x6d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6f; 33 | //keccak256("Recollateraliser"); 34 | bytes32 internal constant KEY_RECOLLATERALISER = 35 | 0x39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f; 36 | //keccak256("MetaToken"); 37 | bytes32 internal constant KEY_META_TOKEN = 38 | 0xea7469b14936af748ee93c53b2fe510b9928edbdccac3963321efca7eb1a57a2; 39 | // keccak256("SavingsManager"); 40 | bytes32 internal constant KEY_SAVINGS_MANAGER = 41 | 0x12fe936c77a1e196473c4314f3bed8eeac1d757b319abb85bdda70df35511bf1; 42 | // keccak256("Liquidator"); 43 | bytes32 internal constant KEY_LIQUIDATOR = 44 | 0x1e9cb14d7560734a61fa5ff9273953e971ff3cd9283c03d8346e3264617933d4; 45 | // keccak256("InterestValidator"); 46 | bytes32 internal constant KEY_INTEREST_VALIDATOR = 47 | 0xc10a28f028c7f7282a03c90608e38a4a646e136e614e4b07d119280c5f7f839f; 48 | // keccak256("Keeper"); 49 | bytes32 internal constant KEY_KEEPER = 50 | 0x4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de; 51 | } 52 | -------------------------------------------------------------------------------- /contracts/shared/ModuleKeysStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title InitializableModuleKeysStorage 6 | * @author mStable 7 | * @notice Used to preserve storage slots for proxy contracts that 8 | * used InitializableModuleKeys but are now using ImmutableModule 9 | * which uses immutable variables rather than contract storage. 10 | * @dev VERSION: 1.0 11 | * DATE: 2021-05-27 12 | */ 13 | contract ModuleKeysStorage { 14 | // Deprecated stotage variables, but kept around to mirror storage layout 15 | bytes32 private DEPRECATED_KEY_GOVERNANCE; 16 | bytes32 private DEPRECATED_KEY_STAKING; 17 | bytes32 private DEPRECATED_KEY_PROXY_ADMIN; 18 | bytes32 private DEPRECATED_KEY_ORACLE_HUB; 19 | bytes32 private DEPRECATED_KEY_MANAGER; 20 | bytes32 private DEPRECATED_KEY_RECOLLATERALISER; 21 | bytes32 private DEPRECATED_KEY_META_TOKEN; 22 | bytes32 private DEPRECATED_KEY_SAVINGS_MANAGER; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/shared/PausableModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ImmutableModule } from "./ImmutableModule.sol"; 5 | 6 | /** 7 | * @title PausableModule 8 | * @author mStable 9 | * @dev Forked from @openzeppelin/contracts/lifecycle/pausable 10 | * Changes: `onlyGovernor` can pause 11 | */ 12 | abstract contract PausableModule is ImmutableModule { 13 | /** 14 | * @dev Emitted when the pause is triggered by Governor 15 | */ 16 | event Paused(address account); 17 | 18 | /** 19 | * @dev Emitted when the pause is lifted by Governor 20 | */ 21 | event Unpaused(address account); 22 | 23 | bool internal _paused = false; 24 | 25 | /** 26 | * @dev Modifier to make a function callable only when the contract is not paused. 27 | */ 28 | modifier whenNotPaused() { 29 | require(!_paused, "Pausable: paused"); 30 | _; 31 | } 32 | 33 | /** 34 | * @dev Modifier to make a function callable only when the contract is paused. 35 | */ 36 | modifier whenPaused() { 37 | require(_paused, "Pausable: not paused"); 38 | _; 39 | } 40 | 41 | /** 42 | * @dev Initializes the contract in unpaused state. 43 | * Hooks into the Module to give the Governor ability to pause 44 | * @param _nexus Nexus contract address 45 | */ 46 | constructor(address _nexus) ImmutableModule(_nexus) { 47 | _paused = false; 48 | } 49 | 50 | /** 51 | * @dev Returns true if the contract is paused, and false otherwise. 52 | * @return Returns `true` when paused, otherwise `false` 53 | */ 54 | function paused() external view returns (bool) { 55 | return _paused; 56 | } 57 | 58 | /** 59 | * @dev Called by the Governor to pause, triggers stopped state. 60 | */ 61 | function pause() external onlyGovernor whenNotPaused { 62 | _paused = true; 63 | emit Paused(msg.sender); 64 | } 65 | 66 | /** 67 | * @dev Called by Governor to unpause, returns to normal state. 68 | */ 69 | function unpause() external onlyGovernor whenPaused { 70 | _paused = false; 71 | emit Unpaused(msg.sender); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/shared/Root.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | library Root { 5 | /** 6 | * @dev Returns the square root of a given number 7 | * @param x Input 8 | * @return y Square root of Input 9 | */ 10 | function sqrt(uint256 x) internal pure returns (uint256 y) { 11 | if (x == 0) return 0; 12 | else { 13 | uint256 xx = x; 14 | uint256 r = 1; 15 | if (xx >= 0x100000000000000000000000000000000) { 16 | xx >>= 128; 17 | r <<= 64; 18 | } 19 | if (xx >= 0x10000000000000000) { 20 | xx >>= 64; 21 | r <<= 32; 22 | } 23 | if (xx >= 0x100000000) { 24 | xx >>= 32; 25 | r <<= 16; 26 | } 27 | if (xx >= 0x10000) { 28 | xx >>= 16; 29 | r <<= 8; 30 | } 31 | if (xx >= 0x100) { 32 | xx >>= 8; 33 | r <<= 4; 34 | } 35 | if (xx >= 0x10) { 36 | xx >>= 4; 37 | r <<= 2; 38 | } 39 | if (xx >= 0x8) { 40 | r <<= 1; 41 | } 42 | r = (r + x / r) >> 1; 43 | r = (r + x / r) >> 1; 44 | r = (r + x / r) >> 1; 45 | r = (r + x / r) >> 1; 46 | r = (r + x / r) >> 1; 47 | r = (r + x / r) >> 1; 48 | r = (r + x / r) >> 1; // Seven iterations should be enough 49 | uint256 r1 = x / r; 50 | return uint256(r < r1 ? r : r1); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/shared/SlotFiller51.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @title Fills 51 storage slots. 6 | * @notice To be used in inheritance when upgrading contracts to preserve previous storage slots. 7 | * @author mStable 8 | * @dev VERSION: 1.0 9 | * DATE: 2022-04-13 10 | */ 11 | contract SlotFiller51 { 12 | uint256[51] private __gap; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/z_mocks/buy-and-make/CRP.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | pragma abicoder v2; 4 | 5 | abstract contract ConfigurableRightsPool { 6 | function createPool(uint256 initialSupply) external virtual; 7 | 8 | function whitelistLiquidityProvider(address provider) external virtual; 9 | 10 | function setController(address newOwner) external virtual; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/z_mocks/buy-and-make/CRPFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | pragma abicoder v2; 4 | 5 | abstract contract CRPFactory { 6 | struct Rights { 7 | bool canPauseSwapping; 8 | bool canChangeSwapFee; 9 | bool canChangeWeights; 10 | bool canAddRemoveTokens; 11 | bool canWhitelistLPs; 12 | bool canChangeCap; 13 | } 14 | 15 | struct PoolParams { 16 | string poolTokenSymbol; 17 | string poolTokenName; 18 | address[] constituentTokens; 19 | uint256[] tokenBalances; 20 | uint256[] tokenWeights; 21 | uint256 swapFee; 22 | } 23 | 24 | function newCrp( 25 | address factoryAddress, 26 | PoolParams calldata poolParams, 27 | Rights calldata rights 28 | ) external virtual returns (address); 29 | } 30 | -------------------------------------------------------------------------------- /contracts/z_mocks/buy-and-make/MockBPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | import { IBPool } from "../../buy-and-make/IBPool.sol"; 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract MockBPool is ERC20, IBPool { 8 | // output = input * ratio / 1e18 9 | uint256 inputToOutputRatio; 10 | mapping(address => bool) private _tokenIsValid; 11 | 12 | constructor( 13 | uint256 _inputToOutputRatio, 14 | address[] memory _tokens, 15 | string memory _name, 16 | string memory _symbol 17 | ) ERC20(_name, _symbol) { 18 | inputToOutputRatio = _inputToOutputRatio; 19 | uint256 len = _tokens.length; 20 | for (uint256 i = 0; i < len; i++) { 21 | _tokenIsValid[_tokens[i]] = true; 22 | } 23 | } 24 | 25 | function addOutputToken(address _token, uint256 _amt) external { 26 | IERC20(_token).transferFrom(msg.sender, address(this), _amt); 27 | } 28 | 29 | function joinswapExternAmountIn( 30 | address tokenIn, 31 | uint256 tokenAmountIn, 32 | uint256 minPoolAmountOut 33 | ) external override returns (uint256 poolAmountOut) { 34 | require(_tokenIsValid[tokenIn], "Invalid token"); 35 | IERC20(tokenIn).transferFrom(msg.sender, address(this), tokenAmountIn); 36 | poolAmountOut = (tokenAmountIn * inputToOutputRatio) / 1e18; 37 | require(poolAmountOut > minPoolAmountOut, "Invalid output amount"); 38 | _mint(msg.sender, poolAmountOut); 39 | } 40 | 41 | function swapExactAmountIn( 42 | address tokenIn, 43 | uint256 tokenAmountIn, 44 | address tokenOut, 45 | uint256 minAmountOut, 46 | uint256 /*maxPrice*/ 47 | ) 48 | external 49 | override 50 | returns ( 51 | uint256 tokenAmountOut, 52 | uint256 /*spotPriceAfter*/ 53 | ) 54 | { 55 | require(_tokenIsValid[tokenIn], "Invalid token"); 56 | require(_tokenIsValid[tokenOut], "Invalid token"); 57 | IERC20(tokenIn).transferFrom(msg.sender, address(this), tokenAmountIn); 58 | 59 | tokenAmountOut = (tokenAmountIn * inputToOutputRatio) / 1e18; 60 | require(tokenAmountOut > minAmountOut, "Invalid output amount"); 61 | IERC20(tokenOut).transfer(msg.sender, tokenAmountOut); 62 | return (tokenAmountOut, 0); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/z_mocks/buy-and-make/RecipientV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | interface RevenueRecipientV1 { 5 | function migrateBPT(address _recipient) external; 6 | } 7 | -------------------------------------------------------------------------------- /contracts/z_mocks/emissions/MockChildChainStreamer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { IChildChainStreamer } from "../../peripheral/Balancer/IChildChainStreamer.sol"; 6 | 7 | contract MockChildChainStreamer is IChildChainStreamer { 8 | function notify_reward_amount(address token) external override { 9 | // stream it to the gauge 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/z_mocks/emissions/MockDisperse.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { IDisperse } from "../../interfaces/IDisperse.sol"; 6 | 7 | contract MockDisperse is IDisperse { 8 | function disperseTokenSimple( 9 | IERC20 token, 10 | address[] calldata recipients, 11 | uint256[] calldata values 12 | ) external override { 13 | for (uint256 i = 0; i < recipients.length; i++) { 14 | // solhint-disable-next-line reason-string 15 | require(token.transferFrom(msg.sender, recipients[i], values[i])); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/z_mocks/emissions/MockVotiumBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import { IVotiumBribe } from "../../interfaces/IVotiumBribe.sol"; 7 | 8 | contract MockVotiumBribe is IVotiumBribe { 9 | using SafeERC20 for IERC20; 10 | 11 | address public feeAddress = 0xe39b8617D571CEe5e75e1EC6B2bb40DdC8CF6Fa3; // Votium multisig 12 | event Bribed(address _token, uint256 _amount, bytes32 indexed _proposal, uint256 _choiceIndex); 13 | 14 | function depositBribe( 15 | address _token, 16 | uint256 _amount, 17 | bytes32 _proposal, 18 | uint256 _choiceIndex 19 | ) external override { 20 | uint256 fee = 0; 21 | uint256 bribeTotal = _amount - fee; 22 | // Sends the fee to votium multisig 23 | IERC20(_token).safeTransferFrom(msg.sender, feeAddress, fee); 24 | // if distributor contract is not set, store in this contract until ready 25 | IERC20(_token).safeTransferFrom(msg.sender, address(this), bribeTotal); 26 | 27 | emit Bribed(_token, bribeTotal, _proposal, _choiceIndex); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/z_mocks/feeder/ExposedFeederLogic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../../masset/MassetStructs.sol"; 5 | import { FeederLogic } from "../../feeders/FeederLogic.sol"; 6 | 7 | contract ExposedFeederLogic { 8 | function computeMint( 9 | BassetData[] memory _bAssets, 10 | uint8 _i, 11 | uint256 _rawInput, 12 | FeederConfig memory _config 13 | ) public pure returns (uint256 mintAmount) { 14 | return FeederLogic.computeMint(_bAssets, _i, _rawInput, _config); 15 | } 16 | 17 | function computeMintMulti( 18 | BassetData[] memory _bAssets, 19 | uint8[] memory _indices, 20 | uint256[] memory _rawInputs, 21 | FeederConfig memory _config 22 | ) public pure returns (uint256 mintAmount) { 23 | return FeederLogic.computeMintMulti(_bAssets, _indices, _rawInputs, _config); 24 | } 25 | 26 | function computeSwap( 27 | BassetData[] memory _bAssets, 28 | uint8 _i, 29 | uint8 _o, 30 | uint256 _rawInput, 31 | uint256 _feeRate, 32 | FeederConfig memory _config 33 | ) public pure returns (uint256 bAssetOutputQuantity, uint256 scaledSwapFee) { 34 | return FeederLogic.computeSwap(_bAssets, _i, _o, _rawInput, _feeRate, _config); 35 | } 36 | 37 | function computeRedeem( 38 | BassetData[] memory _bAssets, 39 | uint8 _o, 40 | uint256 _netMassetQuantity, 41 | FeederConfig memory _config 42 | ) public pure returns (uint256 rawOutputUnits) { 43 | return FeederLogic.computeRedeem(_bAssets, _o, _netMassetQuantity, _config); 44 | } 45 | 46 | function computeRedeemExact( 47 | BassetData[] memory _bAssets, 48 | uint8[] memory _indices, 49 | uint256[] memory _rawOutputs, 50 | FeederConfig memory _config 51 | ) public pure returns (uint256 totalmAssets) { 52 | return FeederLogic.computeRedeemExact(_bAssets, _indices, _rawOutputs, _config); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/z_mocks/feeder/ExposedFeederPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../../masset/MassetStructs.sol"; 5 | import { FeederPool } from "../../feeders/FeederPool.sol"; 6 | 7 | contract ExposedFeederPool is FeederPool { 8 | constructor(address _nexus, address _mAsset) 9 | FeederPool(_nexus, _mAsset) {} 10 | } 11 | -------------------------------------------------------------------------------- /contracts/z_mocks/feeder/ExposedNonPeggedFeederPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../../masset/MassetStructs.sol"; 5 | import { NonPeggedFeederPool } from "../../feeders/NonPeggedFeederPool.sol"; 6 | 7 | contract ExposedNonPeggedFeederPool is NonPeggedFeederPool { 8 | constructor( 9 | address _nexus, 10 | address _mAsset, 11 | address _fAssetRedemptionPrice 12 | ) NonPeggedFeederPool(_nexus, _mAsset, _fAssetRedemptionPrice) {} 13 | } 14 | -------------------------------------------------------------------------------- /contracts/z_mocks/feeder/RedemptionPriceSnapMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | contract RedemptionPriceSnapMock { 5 | uint256 internal internalSnappedRedemptionPrice; 6 | 7 | constructor() { 8 | // Set redemption price to $1 (ray) so existing common tests which expect pegged coin can run. 9 | internalSnappedRedemptionPrice = 1000000000000000000000000000; 10 | } 11 | 12 | function setRedemptionPriceSnap(uint256 newPrice) external { 13 | internalSnappedRedemptionPrice = newPrice; 14 | } 15 | 16 | function snappedRedemptionPrice() public view returns (uint256) { 17 | return internalSnappedRedemptionPrice; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/z_mocks/governance/MockBPT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 6 | 7 | contract MockBPT is ERC20, ERC20Burnable { 8 | constructor(string memory name, string memory symbol) ERC20(name, symbol) { 9 | // 100m initial supply 10 | _mint(msg.sender, 10000 * (10**18)); 11 | } 12 | 13 | function onExitPool(address sender, uint256 amt) external { 14 | _burn(sender, amt); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/z_mocks/governance/MockBPTGauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IBalancerGauge } from "../../peripheral/Balancer/IBalancerGauge.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | 8 | contract MockBPTGauge is IBalancerGauge, ERC20 { 9 | 10 | IERC20 public immutable stakedToken; 11 | mapping (address => address) public rewards_receiver; 12 | 13 | constructor( 14 | address _stakedToken 15 | ) ERC20("BPT Gauge", "BPT-Gauge") { 16 | stakedToken = IERC20(_stakedToken); 17 | } 18 | 19 | function deposit(uint256 amount) external override { 20 | stakedToken.transferFrom(msg.sender, address(this), amount); 21 | 22 | _mint(msg.sender, amount); 23 | } 24 | 25 | function withdraw( 26 | uint256 amount 27 | ) external override { 28 | 29 | // Burn stkAave 30 | _burn(msg.sender, amount); 31 | 32 | // Transfer AAVE 33 | stakedToken.transfer(msg.sender, amount); 34 | } 35 | 36 | function set_rewards_receiver(address _receiver) external override { 37 | rewards_receiver[msg.sender] = _receiver; 38 | } 39 | 40 | function claim_rewards(address _addr) external override { 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/z_mocks/governance/MockStakedTokenWithPrice.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | pragma abicoder v2; 4 | 5 | import { StakedToken } from "../../governance/staking/StakedToken.sol"; 6 | import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 7 | 8 | /** 9 | * @title StakedTokenBPT 10 | * @dev Derives from StakedToken, and simply adds the ability to withdraw any unclaimed $BAL tokens 11 | * that are at this address 12 | **/ 13 | contract MockStakedTokenWithPrice is StakedToken, Initializable { 14 | /// @notice Most recent PriceCoefficient 15 | uint256 public priceCoefficient; 16 | 17 | event PriceCoefficientUpdated(uint16 newPriceCoeff); 18 | 19 | constructor( 20 | address _nexus, 21 | address _rewardsToken, 22 | address _questManager, 23 | address _stakedToken, 24 | uint256 _cooldownSeconds 25 | ) 26 | StakedToken( 27 | _nexus, 28 | _rewardsToken, 29 | _questManager, 30 | _stakedToken, 31 | _cooldownSeconds, 32 | true 33 | ) 34 | {} 35 | 36 | function initialize( 37 | bytes32 _nameArg, 38 | bytes32 _symbolArg, 39 | address _rewardsDistributorArg 40 | ) external initializer { 41 | __StakedToken_init(_nameArg, _symbolArg, _rewardsDistributorArg); 42 | priceCoefficient = 10000; 43 | } 44 | 45 | /** 46 | * @dev Sets the recipient for any potential $BAL earnings 47 | */ 48 | function setPriceCoefficient(uint16 _newCoeff) external { 49 | priceCoefficient = _newCoeff; 50 | 51 | emit PriceCoefficientUpdated(_newCoeff); 52 | } 53 | 54 | /** 55 | * @dev Get the current priceCoeff 56 | */ 57 | function _getPriceCoeff() internal view override returns (uint256) { 58 | return priceCoefficient; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/z_mocks/governance/stakedTokenWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import { StakedToken } from "../../governance/staking/StakedToken.sol"; 7 | 8 | /** 9 | * Used to test contract interactions with the StakedToken 10 | */ 11 | contract StakedTokenWrapper { 12 | using SafeERC20 for IERC20; 13 | 14 | IERC20 public rewardsToken; 15 | StakedToken public stakedToken; 16 | 17 | constructor(address _rewardsToken, address _stakedToken) { 18 | stakedToken = StakedToken(_stakedToken); 19 | rewardsToken = IERC20(_rewardsToken); 20 | rewardsToken.safeApprove(_stakedToken, 2**256 - 1); 21 | } 22 | 23 | function stake(uint256 _amount) external { 24 | stakedToken.stake(_amount); 25 | } 26 | 27 | function stake(uint256 _amount, address _delegatee) external { 28 | stakedToken.stake(_amount, _delegatee); 29 | } 30 | 31 | function withdraw( 32 | uint256 _amount, 33 | address _recipient, 34 | bool _amountIncludesFee, 35 | bool _exitCooldown 36 | ) external { 37 | stakedToken.withdraw(_amount, _recipient, _amountIncludesFee, _exitCooldown); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/ExposedMasset.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { Masset, InvariantConfig } from "../../masset/Masset.sol"; 5 | import { MassetLogic } from "../../masset/MassetLogic.sol"; 6 | 7 | contract ExposedMasset is Masset { 8 | constructor(address _nexus, uint256 _recolFee) Masset(_nexus, _recolFee) {} 9 | 10 | uint256 private amountToMint = 0; 11 | 12 | function getK() external view returns (uint256 k) { 13 | (, k) = MassetLogic.computePrice(data.bAssetData, _getConfig()); 14 | } 15 | 16 | function getA() public view returns (uint256) { 17 | return super._getA(); 18 | } 19 | 20 | function simulateRedeemMasset( 21 | uint256 _amt, 22 | uint256[] calldata _minOut, 23 | uint256 _recolFee 24 | ) external { 25 | // Get config before burning. Burn > CacheSize 26 | InvariantConfig memory config = _getConfig(); 27 | config.recolFee = _recolFee; 28 | MassetLogic.redeemProportionately(data, config, _amt, _minOut, msg.sender); 29 | } 30 | 31 | // Inject amount of tokens to mint 32 | function setAmountForCollectInterest(uint256 _amount) public { 33 | amountToMint = _amount; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/ExposedMassetLogic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "../../masset/MassetStructs.sol"; 5 | import { MassetLogic } from "../../masset/MassetLogic.sol"; 6 | 7 | contract ExposedMassetLogic { 8 | function computeMint( 9 | BassetData[] memory _bAssets, 10 | uint8 _i, 11 | uint256 _rawInput, 12 | InvariantConfig memory _config 13 | ) public pure returns (uint256 mintAmount) { 14 | return MassetLogic.computeMint(_bAssets, _i, _rawInput, _config); 15 | } 16 | 17 | function computeMintMulti( 18 | BassetData[] memory _bAssets, 19 | uint8[] memory _indices, 20 | uint256[] memory _rawInputs, 21 | InvariantConfig memory _config 22 | ) public pure returns (uint256 mintAmount) { 23 | return MassetLogic.computeMintMulti(_bAssets, _indices, _rawInputs, _config); 24 | } 25 | 26 | function computeSwap( 27 | BassetData[] memory _bAssets, 28 | uint8 _i, 29 | uint8 _o, 30 | uint256 _rawInput, 31 | uint256 _feeRate, 32 | InvariantConfig memory _config 33 | ) public pure returns (uint256 bAssetOutputQuantity, uint256 scaledSwapFee) { 34 | return MassetLogic.computeSwap(_bAssets, _i, _o, _rawInput, _feeRate, _config); 35 | } 36 | 37 | function computeRedeem( 38 | BassetData[] memory _bAssets, 39 | uint8 _o, 40 | uint256 _netMassetQuantity, 41 | InvariantConfig memory _config, 42 | uint256 _feeRate 43 | ) public pure returns (uint256 rawOutputUnits, uint256 scaledFee) { 44 | return MassetLogic.computeRedeem(_bAssets, _o, _netMassetQuantity, _config, _feeRate); 45 | } 46 | 47 | function computeRedeemExact( 48 | BassetData[] memory _bAssets, 49 | uint8[] memory _indices, 50 | uint256[] memory _rawOutputs, 51 | InvariantConfig memory _config, 52 | uint256 _feeRate 53 | ) public pure returns (uint256 grossMasset, uint256 fee) { 54 | return MassetLogic.computeRedeemExact(_bAssets, _indices, _rawOutputs, _config, _feeRate); 55 | } 56 | 57 | function getK(BassetData[] memory _bAssets, InvariantConfig memory _config) 58 | external 59 | pure 60 | returns (uint256 k) 61 | { 62 | (, k) = MassetLogic.computePrice(_bAssets, _config); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/MockPlatformIntegrationWithToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { MassetHelpers } from "../../shared/MassetHelpers.sol"; 5 | import { MockPlatformIntegration } from "./MockPlatformIntegration.sol"; 6 | 7 | // Overrides approveRewardToken 8 | contract MockPlatformIntegrationWithToken is MockPlatformIntegration { 9 | event RewardTokenApproved(address token, address spender); 10 | 11 | address rewardToken; 12 | 13 | constructor( 14 | address _nexus, 15 | address _platformAddress, 16 | address[] memory _bAssets, 17 | address[] memory _pTokens 18 | ) MockPlatformIntegration(_nexus, _platformAddress, _bAssets, _pTokens) {} 19 | 20 | // @override 21 | function approveRewardToken() external { 22 | address liquidator = nexus.getModule(keccak256("Liquidator")); 23 | require(liquidator != address(0), "Liquidator address cannot be zero"); 24 | 25 | MassetHelpers.safeInfiniteApprove(rewardToken, liquidator); 26 | 27 | emit RewardTokenApproved(rewardToken, liquidator); 28 | } 29 | 30 | function setRewardToken(address _token) external { 31 | rewardToken = _token; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/MockRewardToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { MassetHelpers } from "../../shared/MassetHelpers.sol"; 5 | import { ImmutableModule } from "../../shared/ImmutableModule.sol"; 6 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | 8 | // Overrides approveRewardToken 9 | contract MockRewardToken is ImmutableModule { 10 | event RewardTokenApproved(address token, address spender); 11 | 12 | address rewardToken; 13 | uint256 rewardAmount = 1000; 14 | 15 | constructor(address _nexus) ImmutableModule(_nexus) {} 16 | 17 | // @override 18 | function approveRewardToken() external { 19 | address liquidator = nexus.getModule(keccak256("Liquidator")); 20 | require(liquidator != address(0), "Liquidator address cannot be zero"); 21 | 22 | MassetHelpers.safeInfiniteApprove(rewardToken, liquidator); 23 | 24 | emit RewardTokenApproved(rewardToken, liquidator); 25 | } 26 | 27 | function setRewardToken(address _token) external { 28 | rewardToken = _token; 29 | } 30 | 31 | function setRewardAmount(uint256 _rewardAmount) external { 32 | rewardAmount = _rewardAmount; 33 | } 34 | 35 | /// @dev this assumes some reward tokens have been transferred to this contract 36 | function claimRewards() external { 37 | IERC20(rewardToken).transfer(msg.sender, rewardAmount); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/ValidatorWithTVLCap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | contract ValidatorWithTVLCap { 5 | // Data used for determining max TVL during guarded launch 6 | uint256 public startTime; 7 | uint256 public startingCap; 8 | uint256 public capFactor; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/migrate2/IBasketManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { Basket } from "./MassetStructsV1.sol"; 5 | 6 | /** 7 | * @notice Is the Basket Manager V2.0 interface used in the upgrade of mUSD from V2.0 to V3.0. 8 | * @author mStable 9 | * @dev VERSION: 2.0 10 | * DATE: 2021-02-23 11 | */ 12 | interface IBasketManager { 13 | function getBassetIntegrator(address _bAsset) external view returns (address integrator); 14 | 15 | function getBasket() external view returns (Basket memory b); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/migrate2/InitializableModuleV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @notice Is only used in the upgrade of mUSD from V2.0 to V3.0. 6 | This contract preserves the V2.0 storage positions in 7 | the new V3.0 Masset contract. 8 | * @author mStable 9 | * @dev VERSION: 3.0 10 | * DATE: 2021-02-23 11 | */ 12 | contract InitializableModuleKeysV1 { 13 | // Governance // Phases 14 | bytes32 private KEY_GOVERNANCE_DEPRICATED; // 2.x 15 | bytes32 private KEY_STAKING_DEPRICATED; // 1.2 16 | bytes32 private KEY_PROXY_ADMIN_DEPRICATED; // 1.0 17 | 18 | // mStable 19 | bytes32 private KEY_ORACLE_HUB_DEPRICATED; // 1.2 20 | bytes32 private KEY_MANAGER_DEPRICATED; // 1.2 21 | bytes32 private KEY_RECOLLATERALISER_DEPRICATED; // 2.x 22 | bytes32 private KEY_META_TOKEN_DEPRICATED; // 1.1 23 | bytes32 private KEY_SAVINGS_MANAGER_DEPRICATED; // 1.0 24 | } 25 | 26 | contract InitializableModuleV1 is InitializableModuleKeysV1 { 27 | address private nexus_depricated; 28 | } 29 | -------------------------------------------------------------------------------- /contracts/z_mocks/masset/migrate2/MassetStructsV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | /** 5 | * @notice Is the Masset V2.0 structs used in the upgrade of mUSD from V2.0 to V3.0. 6 | * @author mStable 7 | * @dev VERSION: 2.0 8 | * DATE: 2021-02-23 9 | */ 10 | 11 | /** @dev Stores high level basket info */ 12 | struct Basket { 13 | Basset[] bassets; 14 | uint8 maxBassets; 15 | bool undergoingRecol; 16 | bool failed; 17 | uint256 collateralisationRatio; 18 | } 19 | 20 | /** @dev Stores bAsset info. The struct takes 5 storage slots per Basset */ 21 | struct Basset { 22 | address addr; 23 | BassetStatus status; 24 | bool isTransferFeeCharged; 25 | uint256 ratio; 26 | uint256 maxWeight; 27 | uint256 vaultBalance; 28 | } 29 | 30 | /** @dev Status of the Basset - has it broken its peg? */ 31 | enum BassetStatus { 32 | Default, 33 | Normal, 34 | BrokenBelowPeg, 35 | BrokenAbovePeg, 36 | Blacklisted, 37 | Liquidating, 38 | Liquidated, 39 | Failed 40 | } 41 | 42 | /** @dev Internal details on Basset */ 43 | struct BassetDetails { 44 | Basset bAsset; 45 | address integrator; 46 | uint8 index; 47 | } 48 | -------------------------------------------------------------------------------- /contracts/z_mocks/nexus/MockNexus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ModuleKeys } from "../../shared/ModuleKeys.sol"; 5 | 6 | contract MockNexus is ModuleKeys { 7 | address public governor; 8 | bool private _initialized; 9 | 10 | mapping(bytes32 => address) public modules; 11 | 12 | constructor( 13 | address _governorAddr, 14 | address _savingsManager, 15 | address _interestValidator 16 | ) { 17 | governor = _governorAddr; 18 | modules[KEY_SAVINGS_MANAGER] = _savingsManager; 19 | modules[KEY_INTEREST_VALIDATOR] = _interestValidator; 20 | _initialized = true; 21 | } 22 | 23 | function initialized() external view returns (bool) { 24 | return _initialized; 25 | } 26 | 27 | function setModule(bytes32 _key, address _module) external { 28 | modules[_key] = _module; 29 | } 30 | 31 | function getModule(bytes32 _key) external view returns (address) { 32 | return modules[_key]; 33 | } 34 | 35 | function setSavingsManager(address _savingsManager) external { 36 | modules[KEY_SAVINGS_MANAGER] = _savingsManager; 37 | } 38 | 39 | function setInterestValidator(address _interestValidator) external { 40 | modules[KEY_INTEREST_VALIDATOR] = _interestValidator; 41 | } 42 | 43 | function setLiquidator(address _liquidator) external { 44 | modules[KEY_LIQUIDATOR] = _liquidator; 45 | } 46 | 47 | function setRecollateraliser(address _recollateraliser) external { 48 | modules[KEY_RECOLLATERALISER] = _recollateraliser; 49 | } 50 | 51 | function setKeeper(address _keeper) external { 52 | modules[KEY_KEEPER] = _keeper; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/z_mocks/rewards/MockRewardsDistributionRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { IRewardsRecipientWithPlatformToken } from "../../interfaces/IRewardsDistributionRecipient.sol"; 6 | import { IRewardsDistributionRecipient } from "../../interfaces/IRewardsDistributionRecipient.sol"; 7 | 8 | contract MockRewardsDistributionRecipient is 9 | IRewardsDistributionRecipient, 10 | IRewardsRecipientWithPlatformToken 11 | { 12 | IERC20 public rewardToken; 13 | IERC20 public platformToken; 14 | 15 | constructor(IERC20 _rewardToken, IERC20 _platformToken) { 16 | rewardToken = _rewardToken; 17 | platformToken = _platformToken; 18 | } 19 | 20 | function notifyRewardAmount(uint256 reward) 21 | external 22 | override(IRewardsDistributionRecipient, IRewardsRecipientWithPlatformToken) 23 | { 24 | // do nothing 25 | } 26 | 27 | function getRewardToken() 28 | external 29 | view 30 | override(IRewardsDistributionRecipient, IRewardsRecipientWithPlatformToken) 31 | returns (IERC20) 32 | { 33 | return rewardToken; 34 | } 35 | 36 | function getPlatformToken() external view override returns (IERC20) { 37 | return platformToken; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/z_mocks/rewards/MockRootChainManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { IRootChainManager } from "../../interfaces/IRootChainManager.sol"; 6 | 7 | contract MockRootChainManager is IRootChainManager { 8 | address public constant ETHER_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 9 | 10 | event DepositFor(address indexed userAddress, address indexed rootToken, bytes data); 11 | 12 | function depositFor( 13 | address userAddress, 14 | address rootToken, 15 | bytes memory data 16 | ) external override { 17 | require(rootToken != ETHER_ADDRESS, "RootChainManager: INVALID_ROOT_TOKEN"); 18 | require(userAddress != address(0), "RootChainManager: INVALID_USER"); 19 | 20 | uint256 amount = abi.decode(data, (uint256)); 21 | require(amount > 0, "RootChainManager: INVALID_AMOUNT"); 22 | IERC20(rootToken).transferFrom(msg.sender, address(this), amount); 23 | 24 | emit DepositFor(userAddress, rootToken, data); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/MockBoostedSavingsVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IBoostDirector } from "../../interfaces/IBoostDirector.sol"; 5 | 6 | contract MockBoostedVault { 7 | IBoostDirector public immutable boostDirector; 8 | 9 | event Poked(address indexed user); 10 | event TestGetBalance(uint256 balance); 11 | 12 | constructor(address _boostDirector) { 13 | boostDirector = IBoostDirector(_boostDirector); 14 | } 15 | 16 | function pokeBoost(address _user) external { 17 | emit Poked(_user); 18 | } 19 | 20 | function testGetBalance(address _user) external returns (uint256 balance) { 21 | balance = boostDirector.getBalance(_user); 22 | 23 | emit TestGetBalance(balance); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/MockRevenueRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IRevenueRecipient } from "../../interfaces/IRevenueRecipient.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | contract MockRevenueRecipient is IRevenueRecipient { 8 | function notifyRedistributionAmount(address _mAsset, uint256 _amount) external override { 9 | IERC20(_mAsset).transferFrom(msg.sender, address(this), _amount); 10 | } 11 | 12 | function depositToPool(address[] calldata _mAssets, uint256[] calldata _percentages) 13 | external 14 | override 15 | {} 16 | } 17 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/MockSavingsManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IMasset } from "../../interfaces/IMasset.sol"; 5 | import { ISavingsContractV1 } from "../../interfaces/ISavingsContract.sol"; 6 | import { IRevenueRecipient } from "../../interfaces/IRevenueRecipient.sol"; 7 | import { IERC20 } from "../shared/MockERC20.sol"; 8 | 9 | contract MockSavingsManager { 10 | address public immutable save; 11 | IRevenueRecipient public recipient; 12 | uint256 public rate = 1e18; 13 | 14 | constructor(address _save) { 15 | save = _save; 16 | } 17 | 18 | function collectAndDistributeInterest(address _mAsset) public { 19 | require(save != address(0), "Must have a valid savings contract"); 20 | 21 | // 1. Collect the new interest from the mAsset 22 | IMasset mAsset = IMasset(_mAsset); 23 | (uint256 interestCollected, ) = mAsset.collectInterest(); 24 | 25 | // 3. Validate that interest is collected correctly and does not exceed max APY 26 | if (interestCollected > 0) { 27 | IERC20(_mAsset).approve(save, interestCollected); 28 | 29 | ISavingsContractV1(save).depositInterest((interestCollected * rate) / 1e18); 30 | } 31 | } 32 | 33 | function setRecipient(address _recipient, uint256 _rate) public { 34 | recipient = IRevenueRecipient(_recipient); 35 | rate = _rate; 36 | } 37 | 38 | function distributeUnallocatedInterest(address _mAsset) public { 39 | require(save != address(0), "Must have a valid savings contract"); 40 | 41 | uint256 bal = IERC20(_mAsset).balanceOf(address(this)); 42 | IERC20(_mAsset).approve(save, bal); 43 | 44 | recipient.notifyRedistributionAmount(_mAsset, bal); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/MockStakingContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | 3 | import { IGovernanceHook } from "../../governance/staking/interfaces/IGovernanceHook.sol"; 4 | 5 | pragma solidity 0.8.6; 6 | 7 | contract MockStakingContract { 8 | mapping(address => uint256) private _balances; 9 | mapping(address => uint256) private _votes; 10 | uint256 public totalSupply; 11 | 12 | IGovernanceHook govHook; 13 | 14 | function setBalanceOf(address account, uint256 balance) public { 15 | _balances[account] = balance; 16 | } 17 | 18 | function setTotalSupply(uint256 _totalSupply) public { 19 | totalSupply = _totalSupply; 20 | } 21 | 22 | function setVotes(address account, uint256 newVotes) public { 23 | uint256 oldVotes = _votes[account]; 24 | _votes[account] = newVotes; 25 | 26 | if (address(govHook) != address(0)) { 27 | if (oldVotes <= newVotes) { 28 | govHook.moveVotingPowerHook(address(0), account, newVotes - oldVotes); 29 | } else if (oldVotes > newVotes) { 30 | govHook.moveVotingPowerHook(account, address(0), oldVotes - newVotes); 31 | } 32 | } 33 | } 34 | 35 | function transferVotes( 36 | address from, 37 | address to, 38 | uint256 votes 39 | ) public { 40 | _votes[from] -= votes; 41 | _votes[to] += votes; 42 | 43 | if (address(govHook) != address(0)) { 44 | govHook.moveVotingPowerHook(from, to, votes); 45 | } 46 | } 47 | 48 | function setGovernanceHook(address _govHook) public { 49 | govHook = IGovernanceHook(_govHook); 50 | } 51 | 52 | function balanceOf(address account) public view returns (uint256) { 53 | return _balances[account]; 54 | } 55 | 56 | function getVotes(address account) external view returns (uint256) { 57 | return _votes[account]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/connectors/MockConnector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import { IConnector } from "../../../savings/peripheral/IConnector.sol"; 6 | 7 | contract MockConnector is IConnector { 8 | address save; 9 | address mUSD; 10 | uint256 deposited; 11 | 12 | constructor(address _save, address _mUSD) { 13 | save = _save; 14 | mUSD = _mUSD; 15 | } 16 | 17 | modifier onlySave() { 18 | require(save == msg.sender, "Only SAVE can call this"); 19 | _; 20 | } 21 | 22 | function deposit(uint256 _amount) external override onlySave { 23 | IERC20(mUSD).transferFrom(save, address(this), _amount); 24 | deposited += _amount; 25 | } 26 | 27 | function withdraw(uint256 _amount) external override onlySave { 28 | IERC20(mUSD).transfer(save, _amount); 29 | deposited -= _amount; 30 | } 31 | 32 | function withdrawAll() external override onlySave { 33 | IERC20(mUSD).transfer(save, deposited); 34 | deposited = 0; 35 | } 36 | 37 | function checkBalance() external view override returns (uint256) { 38 | return deposited; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/connectors/MockErroneousConnector1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import { IConnector } from "../../../savings/peripheral/IConnector.sol"; 6 | 7 | // 1. Doesn't withdraw full amount during withdrawal 8 | contract MockErroneousConnector1 is IConnector { 9 | address save; 10 | address mUSD; 11 | 12 | uint256 lastValue; 13 | uint256 lastAccrual; 14 | uint256 constant perSecond = 31709791983; 15 | 16 | constructor(address _save, address _mUSD) { 17 | save = _save; 18 | mUSD = _mUSD; 19 | } 20 | 21 | modifier onlySave() { 22 | require(save == msg.sender, "Only SAVE can call this"); 23 | _; 24 | } 25 | 26 | modifier _accrueValue() { 27 | uint256 currentTime = block.timestamp; 28 | if (lastAccrual != 0) { 29 | uint256 timeDelta = currentTime - lastAccrual; 30 | uint256 interest = timeDelta * perSecond; 31 | uint256 newValue = (lastValue * interest) / 1e18; 32 | lastValue += newValue; 33 | } 34 | lastAccrual = currentTime; 35 | _; 36 | } 37 | 38 | function poke() external _accrueValue {} 39 | 40 | function deposit(uint256 _amount) external override _accrueValue onlySave { 41 | IERC20(mUSD).transferFrom(save, address(this), _amount); 42 | lastValue += _amount; 43 | } 44 | 45 | function withdraw(uint256 _amount) external override _accrueValue onlySave { 46 | lastValue -= _amount; 47 | } 48 | 49 | function withdrawAll() external override _accrueValue onlySave { 50 | IERC20(mUSD).transfer(save, lastValue); 51 | lastValue -= lastValue; 52 | } 53 | 54 | function checkBalance() external view override returns (uint256) { 55 | return lastValue; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/connectors/MockErroneousConnector2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import { IConnector } from "../../../savings/peripheral/IConnector.sol"; 6 | 7 | // 2. Returns invalid balance on checkbalance 8 | // 3. Returns negative balance immediately after checkbalance 9 | contract MockErroneousConnector2 is IConnector { 10 | address save; 11 | address mUSD; 12 | 13 | uint256 lastValue; 14 | uint256 lastAccrual; 15 | uint256 constant perSecond = 31709791983; 16 | 17 | constructor(address _save, address _mUSD) { 18 | save = _save; 19 | mUSD = _mUSD; 20 | } 21 | 22 | modifier onlySave() { 23 | require(save == msg.sender, "Only SAVE can call this"); 24 | _; 25 | } 26 | 27 | function poke() external { 28 | lastValue -= 100; 29 | } 30 | 31 | function deposit(uint256 _amount) external override onlySave { 32 | IERC20(mUSD).transferFrom(save, address(this), _amount); 33 | lastValue += _amount; 34 | } 35 | 36 | function withdraw(uint256 _amount) external override onlySave { 37 | IERC20(mUSD).transfer(save, _amount); 38 | lastValue -= _amount; 39 | lastValue -= 1; 40 | } 41 | 42 | function withdrawAll() external override onlySave { 43 | IERC20(mUSD).transfer(save, lastValue); 44 | lastValue -= lastValue; 45 | } 46 | 47 | function checkBalance() external view override returns (uint256) { 48 | return lastValue; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/z_mocks/savings/connectors/MockLendingConnector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import { IConnector } from "../../../savings/peripheral/IConnector.sol"; 6 | 7 | contract MockLendingConnector is IConnector { 8 | address save; 9 | address mUSD; 10 | 11 | uint256 lastValue; 12 | uint256 lastAccrual; 13 | uint256 constant perSecond = 31709791983; 14 | 15 | constructor(address _save, address _mUSD) { 16 | save = _save; 17 | mUSD = _mUSD; 18 | } 19 | 20 | modifier onlySave() { 21 | require(save == msg.sender, "Only SAVE can call this"); 22 | _; 23 | } 24 | 25 | modifier _accrueValue() { 26 | uint256 currentTime = block.timestamp; 27 | if (lastAccrual != 0) { 28 | uint256 timeDelta = currentTime - lastAccrual; 29 | uint256 interest = timeDelta * perSecond; 30 | uint256 newValue = (lastValue * interest) / 1e18; 31 | lastValue += newValue; 32 | } 33 | lastAccrual = currentTime; 34 | _; 35 | } 36 | 37 | function poke() external _accrueValue {} 38 | 39 | function deposit(uint256 _amount) external override _accrueValue onlySave { 40 | IERC20(mUSD).transferFrom(save, address(this), _amount); 41 | lastValue += _amount; 42 | } 43 | 44 | function withdraw(uint256 _amount) external override _accrueValue onlySave { 45 | IERC20(mUSD).transfer(save, _amount); 46 | lastValue -= _amount; 47 | } 48 | 49 | function withdrawAll() external override _accrueValue onlySave { 50 | IERC20(mUSD).transfer(save, lastValue); 51 | lastValue -= lastValue; 52 | } 53 | 54 | function checkBalance() external view override returns (uint256) { 55 | return lastValue; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/DeadToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | contract DeadToken { 5 | uint8 private _decimals = 18; 6 | 7 | function decimals() public view returns (uint8) { 8 | return _decimals; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/IMStableVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.6; 3 | 4 | interface IMStableVoterProxy { 5 | function createLock(uint256 _endTime) external; 6 | 7 | function harvestMta() external; 8 | 9 | function extendLock(uint256 _unlockTime) external; 10 | 11 | function exitLock() external returns (uint256 mtaBalance); 12 | 13 | function changeLockAddress(address _newLock) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockAaveIncentivesController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import { IAaveIncentivesController } from "../../peripheral/Aave/IAaveIncentivesController.sol"; 6 | 7 | contract MockAaveIncentivesController is IAaveIncentivesController { 8 | address public immutable rewardsToken; 9 | 10 | constructor(address _rewardsToken) { 11 | rewardsToken = _rewardsToken; 12 | } 13 | 14 | function claimRewards( 15 | address[] calldata, /* assets*/ 16 | uint256, /* amount */ 17 | address /* to */ 18 | ) external override returns (uint256) { 19 | IERC20(rewardsToken).transfer(msg.sender, 1e20); 20 | return 1e20; 21 | } 22 | 23 | function getRewardsBalance( 24 | address[] calldata, /*assets*/ 25 | address /*user*/ 26 | ) external pure override returns (uint256) { 27 | return 1e20; 28 | } 29 | 30 | function getUserUnclaimedRewards( 31 | address /*user*/ 32 | ) external pure override returns (uint256) { 33 | return 1e20; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract MockERC20 is ERC20 { 7 | uint8 dec; 8 | 9 | constructor( 10 | string memory _name, 11 | string memory _symbol, 12 | uint8 _decimals, 13 | address _initialRecipient, 14 | uint256 _initialMint 15 | ) ERC20(_name, _symbol) { 16 | dec = _decimals; 17 | _mint(_initialRecipient, _initialMint * (10**uint256(_decimals))); 18 | } 19 | 20 | function decimals() public view override returns (uint8) { 21 | return dec; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockGovernable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { Governable } from "../../governance/Governable.sol"; 5 | 6 | contract MockGovernable is Governable {} 7 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockInitializableToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ERC205 } from "../../shared/@openzeppelin-2.5/ERC205.sol"; 5 | import { InitializableERC20Detailed } from "../../shared/InitializableERC20Detailed.sol"; 6 | 7 | /** 8 | * @title InitializableToken 9 | * @author mStable 10 | * @dev Basic ERC20Detailed Token functionality for Masset 11 | */ 12 | contract MockInitializableToken is ERC205, InitializableERC20Detailed { 13 | /** 14 | * @dev Initialization function for implementing contract 15 | * @notice To avoid variable shadowing appended `Arg` after arguments name. 16 | */ 17 | function initialize( 18 | string calldata _nameArg, 19 | string calldata _symbolArg, 20 | uint8 _decimals, 21 | address _initialRecipient, 22 | uint256 _initialMint 23 | ) external { 24 | InitializableERC20Detailed._initialize(_nameArg, _symbolArg, _decimals); 25 | 26 | _mint(_initialRecipient, _initialMint * (10**uint256(_decimals))); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockInitializableTokenWithFee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { ERC20WithFee } from "./MockERC20WithFee.sol"; 5 | 6 | /** 7 | * @title InitializableToken 8 | * @author mStable 9 | * @dev Basic ERC20Detailed Token functionality for Masset 10 | */ 11 | contract MockInitializableTokenWithFee is ERC20WithFee { 12 | /** 13 | * @dev Initialization function for implementing contract 14 | * @notice To avoid variable shadowing appended `Arg` after arguments name. 15 | */ 16 | function initialize( 17 | string calldata _nameArg, 18 | string calldata _symbolArg, 19 | uint8 _decimals, 20 | address _initialRecipient, 21 | uint256 _initialMint 22 | ) external { 23 | ERC20WithFee._initialize(_nameArg, _symbolArg, _decimals); 24 | feeRate = 1e15; 25 | _mint(_initialRecipient, _initialMint * 10**uint256(_decimals)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockRoot.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { Root } from "../../shared/Root.sol"; 5 | 6 | contract MockRoot { 7 | function sqrt(uint256 r) public pure returns (uint256) { 8 | return Root.sqrt(r); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockStakedAave.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IStakedAave } from "../../peripheral/Aave/IAave.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | 8 | /** Interface for Staking AAVE Token 9 | * Documentation: https://docs.aave.com/developers/protocol-governance/staking-aave 10 | */ 11 | contract MockStakedAave is IStakedAave, ERC20 { 12 | uint256 public override COOLDOWN_SECONDS = 864000; 13 | uint256 public override UNSTAKE_WINDOW = 172800; 14 | // mapping of stakers to cooldown start time in epoch seconds 15 | mapping(address => uint256) public override stakersCooldowns; 16 | 17 | IERC20 public immutable aave; 18 | 19 | constructor( 20 | address _aave, 21 | address _initialRecipient, 22 | uint256 _initialMint 23 | ) ERC20("Staked Aave", "stkAAVE") { 24 | aave = IERC20(_aave); 25 | _mint(_initialRecipient, _initialMint * 1e18); 26 | } 27 | 28 | function stake(address to, uint256 amount) external override { 29 | // transfer in Aave 30 | aave.transferFrom(msg.sender, address(this), amount); 31 | 32 | stakersCooldowns[msg.sender] = block.timestamp; 33 | 34 | // Mint stkAave 35 | _mint(to, amount); 36 | } 37 | 38 | function redeem( 39 | address to, 40 | uint256 /*amount*/ 41 | ) external override { 42 | require( 43 | block.timestamp > stakersCooldowns[msg.sender] + COOLDOWN_SECONDS, 44 | "INSUFFICIENT_COOLDOWN" 45 | ); 46 | require( 47 | block.timestamp < stakersCooldowns[msg.sender] + COOLDOWN_SECONDS + UNSTAKE_WINDOW, 48 | "UNSTAKE_WINDOW_FINISHED" 49 | ); 50 | 51 | // Get the amount of stkAAVE the redeemer has 52 | uint256 redeemAmount = balanceOf(msg.sender); 53 | 54 | // Burn stkAave 55 | _burn(msg.sender, redeemAmount); 56 | 57 | // Transfer AAVE 58 | aave.transfer(to, redeemAmount); 59 | } 60 | 61 | function cooldown() external override { 62 | stakersCooldowns[msg.sender] = block.timestamp; 63 | } 64 | 65 | function claimRewards(address to, uint256 amount) external override { 66 | // This is just a mock for testing so mint whatever 67 | _mint(to, amount); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockTrigger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | import { ILiquidator } from "../../masset/liquidator/ILiquidator.sol"; 4 | 5 | contract MockTrigger { 6 | function trigger(ILiquidator _liq, address _integration) external { 7 | _liq.triggerLiquidation(_integration); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/MockUniswap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { IUniswapV2Router02 } from "../../peripheral/Uniswap/IUniswapV2Router02.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | // Simulates the selling of COMP 8 | // Assumptions: 9 | // COMP = $106 10 | // out token has 18 decimals 11 | contract MockUniswap is IUniswapV2Router02 { 12 | // how many tokens to give out for 1 in 13 | uint256 ratio = 106; 14 | 15 | function setRatio(uint256 _outRatio) external { 16 | ratio = _outRatio; 17 | } 18 | 19 | // takes input from sender, produces output 20 | function swapExactTokensForTokens( 21 | uint256 amountIn, 22 | uint256 amountOutMin, 23 | address[] calldata path, 24 | address, /*to*/ 25 | uint256 /*deadline*/ 26 | ) external override returns (uint256[] memory amounts) { 27 | uint256 len = path.length; 28 | 29 | amounts = new uint256[](len); 30 | amounts[0] = amountIn; 31 | IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn); 32 | 33 | uint256 output = amountIn * ratio; 34 | require(output >= amountOutMin, "UNI: Output amount not enough"); 35 | 36 | amounts[len - 1] = output; 37 | IERC20(path[len - 1]).transfer(msg.sender, output); 38 | } 39 | 40 | function getAmountsIn(uint256 amountOut, address[] calldata path) 41 | external 42 | view 43 | override 44 | returns (uint256[] memory amounts) 45 | { 46 | uint256 amountIn = amountOut / ratio; 47 | uint256 len = path.length; 48 | amounts = new uint256[](len); 49 | amounts[0] = amountIn; 50 | amounts[len - 1] = amountOut; 51 | } 52 | 53 | function swapExactETHForTokens( 54 | uint256, /*amountOutMin*/ 55 | address[] calldata, /*path*/ 56 | address, /*to*/ 57 | uint256 /*deadline*/ 58 | ) external payable override returns (uint256[] memory amounts) { 59 | return new uint256[](0); 60 | } 61 | 62 | function getAmountsOut( 63 | uint256, /*amountIn*/ 64 | address[] calldata /*path*/ 65 | ) external pure override returns (uint256[] memory amounts) { 66 | return new uint256[](0); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/z_mocks/shared/PublicStableMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.8.6; 3 | 4 | import { StableMath } from "../../shared/StableMath.sol"; 5 | 6 | contract PublicStableMath { 7 | using StableMath for uint256; 8 | 9 | function getFullScale() external pure returns (uint256) { 10 | return StableMath.getFullScale(); 11 | } 12 | 13 | function getRatioScale() external pure returns (uint256) { 14 | return StableMath.getRatioScale(); 15 | } 16 | 17 | function scaleInteger(uint256 x) external pure returns (uint256) { 18 | return x.scaleInteger(); 19 | } 20 | 21 | function mulTruncateScale( 22 | uint256 x, 23 | uint256 y, 24 | uint256 scale 25 | ) external pure returns (uint256) { 26 | return x.mulTruncateScale(y, scale); 27 | } 28 | 29 | function mulTruncate(uint256 x, uint256 y) external pure returns (uint256) { 30 | return x.mulTruncate(y); 31 | } 32 | 33 | function mulTruncateCeil(uint256 x, uint256 y) external pure returns (uint256) { 34 | return x.mulTruncateCeil(y); 35 | } 36 | 37 | function divPrecisely(uint256 x, uint256 y) external pure returns (uint256) { 38 | return x.divPrecisely(y); 39 | } 40 | 41 | function mulRatioTruncate(uint256 x, uint256 ratio) external pure returns (uint256) { 42 | return x.mulRatioTruncate(ratio); 43 | } 44 | 45 | function mulRatioTruncateCeil(uint256 x, uint256 ratio) external pure returns (uint256) { 46 | return x.mulRatioTruncateCeil(ratio); 47 | } 48 | 49 | function divRatioPrecisely(uint256 x, uint256 ratio) external pure returns (uint256) { 50 | return x.divRatioPrecisely(ratio); 51 | } 52 | 53 | function min(uint256 x, uint256 y) external pure returns (uint256) { 54 | return x.min(y); 55 | } 56 | 57 | function max(uint256 x, uint256 y) external pure returns (uint256) { 58 | return x.max(y); 59 | } 60 | 61 | function clamp(uint256 x, uint256 upperBound) external pure returns (uint256) { 62 | return x.clamp(upperBound); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/emissions/EmissionsController.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/EmissionsController.png -------------------------------------------------------------------------------- /docs/emissions/EmissionsControllerPolygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/EmissionsControllerPolygon.png -------------------------------------------------------------------------------- /docs/emissions/EmissionsSchedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/EmissionsSchedule.png -------------------------------------------------------------------------------- /docs/emissions/buyBackForStakers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/buyBackForStakers.png -------------------------------------------------------------------------------- /docs/emissions/polygonBridge_balancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/polygonBridge_balancer.png -------------------------------------------------------------------------------- /docs/emissions/polygonBridge_frax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/polygonBridge_frax.png -------------------------------------------------------------------------------- /docs/emissions/polygonBridge_vimUSD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/polygonBridge_vimUSD.png -------------------------------------------------------------------------------- /docs/emissions/polygonContractDependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/polygonContractDependencies.png -------------------------------------------------------------------------------- /docs/emissions/weeklyEmissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstable/mStable-contracts/51da0272104d207abcbecb5dd545fec2e6abbfe9/docs/emissions/weeklyEmissions.png -------------------------------------------------------------------------------- /hardhat-fork-polygon.config.ts: -------------------------------------------------------------------------------- 1 | import { hardhatConfig } from "./hardhat.config" 2 | 3 | export default { 4 | ...hardhatConfig, 5 | networks: { 6 | ...hardhatConfig.networks, 7 | hardhat: { 8 | allowUnlimitedContractSize: false, 9 | blockGasLimit: 20000000, // 20 million 10 | forking: { 11 | url: process.env.NODE_URL || "https://matic-mainnet-archive-rpc.bwarelabs.com", 12 | }, 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /hardhat-fork.config.ts: -------------------------------------------------------------------------------- 1 | import { hardhatConfig } from "./hardhat.config" 2 | 3 | export default { 4 | ...hardhatConfig, 5 | networks: { 6 | ...hardhatConfig.networks, 7 | hardhat: { 8 | allowUnlimitedContractSize: false, 9 | forking: { 10 | url: process.env.NODE_URL || "", 11 | }, 12 | }, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | // Export functions from the package/module 2 | export * from "./test-utils" 3 | export * from "./tasks/utils" 4 | 5 | // Export types 6 | export * from "./types" 7 | -------------------------------------------------------------------------------- /security/run-flattener.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # To allow `ctrl + c` to exit from the script 4 | trap "exit" INT 5 | 6 | # Run sol-merger on all contracts. The command needs all contracts 7 | ../node_modules/sol-merger/dist/bin/sol-merger.js "../contracts/**/*.sol" ../_flat 8 | 9 | MOCK='Mock' 10 | 11 | # Loop each file present in `flat` folder 12 | for filename in ../_flat/*.sol; do 13 | 14 | name=${filename##*/} 15 | 16 | # Remove any file which contains "Mock" in filename 17 | if [[ "$name" == *"$MOCK"* ]]; then 18 | rm $filename 19 | fi 20 | done 21 | -------------------------------------------------------------------------------- /security/run-securify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################# REQUIREMENTS ################################# 4 | # Flattened contract files must be present under `flat` folder. 5 | # Securify2 must be installed as Docker image 6 | # https://app.gitbook.com/@mstable/s/mstable-protocol/security-tools-1/security-tools 7 | ################################################################################ 8 | 9 | # To allow `ctrl + c` to exit from the script 10 | trap "exit" INT 11 | 12 | # Run flattener 13 | ./run-flattener.sh 14 | 15 | # Create the folder if it not exist 16 | mkdir -p ./securify/securify-report 17 | 18 | # Loop each file present in `flat` folder and run slither on it 19 | # Slither report of each file will be created under `slither-report` folder 20 | for filename in ../_flat/*.sol; do 21 | 22 | name=${filename##*/} 23 | docker run -it -v $PWD/../_flat:/share securify /share/$name 2>&1 | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g' | tee ./securify/securify-report/$name.log 24 | done -------------------------------------------------------------------------------- /security/run-slither.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################# REQUIREMENTS ################################# 4 | # Flattened contract files must be present under `flat` folder. 5 | # Slither must be installed 6 | ################################################################################ 7 | 8 | # To allow `ctrl + c` to exit from the script 9 | trap "exit" INT 10 | 11 | # Run flattener 12 | ./run-flattener.sh 13 | 14 | # Create the folder if it not exist 15 | mkdir -p ./slither/slither-report 16 | 17 | # Loop each file present in `flat` folder and run slither on it 18 | # Slither report of each file will be created under `slither-report` folder 19 | for filename in ../_flat/*.sol; do 20 | 21 | name=${filename##*/} 22 | slither $filename --print human-summary 2>&1 | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g' | tee ./slither/slither-report/$name.log 23 | slither $filename 2>&1 | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g' | tee ./slither/slither-report/$name-default.log 24 | done 25 | 26 | # Run the default slither on all contracts 27 | slither . 2>&1 | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g' | tee ./slither-report/slither.log -------------------------------------------------------------------------------- /tasks-fork-polygon.config.ts: -------------------------------------------------------------------------------- 1 | import config from "./hardhat-fork-polygon.config" 2 | 3 | import "./tasks/deployEmissionsController" 4 | import "./tasks/deployIntegration" 5 | import "./tasks/deployFeeders" 6 | import "./tasks/deployPolygon" 7 | import "./tasks/deploySavingsContract4626" 8 | import "./tasks/deployUnwrapper" 9 | import "./tasks/bridge" 10 | import "./tasks/emissions" 11 | import "./tasks/feeder" 12 | import "./tasks/masset" 13 | import "./tasks/mUSD" 14 | import "./tasks/token" 15 | import "./tasks/ops" 16 | import "./tasks/rewards" 17 | import "./tasks/save" 18 | import "./tasks/SaveWrapper" 19 | 20 | export default config 21 | -------------------------------------------------------------------------------- /tasks-fork.config.ts: -------------------------------------------------------------------------------- 1 | import config from "./hardhat-fork.config" 2 | 3 | import "./tasks/deployEmissionsController" 4 | import "./tasks/deployIntegration" 5 | import "./tasks/deployRewards" 6 | import "./tasks/deployMbtc" 7 | import "./tasks/deployFeeders" 8 | import "./tasks/deployMV3" 9 | import "./tasks/deployPolygon" 10 | import "./tasks/deployRevenueForwarder" 11 | import "./tasks/deployBriber" 12 | import "./tasks/deploySavingsManager" 13 | import "./tasks/deploySavingsContract4626" 14 | import "./tasks/deployUnwrapper" 15 | import "./tasks/bridge" 16 | import "./tasks/dials" 17 | import "./tasks/emissions" 18 | import "./tasks/feeder" 19 | import "./tasks/masset" 20 | import "./tasks/mBTC" 21 | import "./tasks/mUSD" 22 | import "./tasks/ops" 23 | import "./tasks/poker" 24 | import "./tasks/rewards" 25 | import "./tasks/save" 26 | import "./tasks/SaveWrapper" 27 | import "./tasks/stakingV1" 28 | import "./tasks/stakingV2" 29 | import "./tasks/token" 30 | import "./tasks/vault" 31 | import "./tasks/ens" 32 | import "./tasks/ironBankMigration" 33 | 34 | export default config 35 | -------------------------------------------------------------------------------- /tasks.config.ts: -------------------------------------------------------------------------------- 1 | import config from "./hardhat.config" 2 | 3 | import "./tasks/deployEmissionsController" 4 | import "./tasks/deployIntegration" 5 | import "./tasks/deployRewards" 6 | import "./tasks/deployMbtc" 7 | import "./tasks/deployFeeders" 8 | import "./tasks/deployMV3" 9 | import "./tasks/deployPolygon" 10 | import "./tasks/deployRevenueForwarder" 11 | import "./tasks/deployBriber" 12 | import "./tasks/deploySavingsManager" 13 | import "./tasks/deploySavingsContract4626" 14 | import "./tasks/deployUnwrapper" 15 | import "./tasks/bridge" 16 | import "./tasks/dials" 17 | import "./tasks/emissions" 18 | import "./tasks/feeder" 19 | import "./tasks/masset" 20 | import "./tasks/mBTC" 21 | import "./tasks/mUSD" 22 | import "./tasks/ops" 23 | import "./tasks/poker" 24 | import "./tasks/prepareAccountFork" 25 | import "./tasks/rewards" 26 | import "./tasks/save" 27 | import "./tasks/SaveWrapper" 28 | import "./tasks/stakingV1" 29 | import "./tasks/stakingV2" 30 | import "./tasks/token" 31 | import "./tasks/vault" 32 | import "./tasks/ens" 33 | import "./tasks/ironBankMigration" 34 | 35 | export default config 36 | -------------------------------------------------------------------------------- /tasks/deployMV3.ts: -------------------------------------------------------------------------------- 1 | import "ts-node/register" 2 | import "tsconfig-paths/register" 3 | 4 | import { task } from "hardhat/config" 5 | import { DEAD_ADDRESS } from "@utils/constants" 6 | import { simpleToExactAmount } from "@utils/math" 7 | 8 | const defaultConfig = { 9 | a: 120, 10 | limits: { 11 | min: simpleToExactAmount(5, 16), 12 | max: simpleToExactAmount(65, 16), 13 | }, 14 | } 15 | 16 | task("deployMV3", "Deploys the mUSD V3 implementation").setAction(async (_, hre) => { 17 | const { ethers, network } = hre 18 | 19 | const nexus = network.name === "mainnet" ? "0xafce80b19a8ce13dec0739a1aab7a028d6845eb3" : DEAD_ADDRESS 20 | 21 | const Logic = await ethers.getContractFactory("MassetLogic") 22 | const logicLib = await Logic.deploy() 23 | await logicLib.deployTransaction.wait() 24 | const Manager = await ethers.getContractFactory("MassetManager") 25 | const managerLib = await Manager.deploy() 26 | await managerLib.deployTransaction.wait() 27 | const Migrator = await ethers.getContractFactory("Migrator") 28 | const migratorLib = await Migrator.deploy() 29 | await migratorLib.deployTransaction.wait() 30 | 31 | const linkedAddress = { 32 | libraries: { 33 | MassetLogic: logicLib.address, 34 | MassetManager: managerLib.address, 35 | }, 36 | } 37 | const massetFactory = await ethers.getContractFactory("MV3", linkedAddress) 38 | const size = massetFactory.bytecode.length / 2 / 1000 39 | if (size > 24.576) { 40 | console.error(`Masset size is ${size} kb: ${size - 24.576} kb too big`) 41 | } else { 42 | console.log(`Masset = ${size} kb`) 43 | } 44 | const impl = await massetFactory.deploy(nexus) 45 | const receiptImpl = await impl.deployTransaction.wait() 46 | console.log(`Deployed to ${impl.address}. gas used ${receiptImpl.gasUsed}`) 47 | 48 | const Validator = await ethers.getContractFactory("InvariantValidator") 49 | const validator = await Validator.deploy() 50 | await validator.deployTransaction.wait() 51 | const data = await impl.interface.encodeFunctionData("upgrade", [validator.address, defaultConfig]) 52 | console.log(`Upgrade data:\n\n${data}\n\n`) 53 | }) 54 | 55 | module.exports = {} 56 | -------------------------------------------------------------------------------- /tasks/deployRevenueForwarder.ts: -------------------------------------------------------------------------------- 1 | import "ts-node/register" 2 | import "tsconfig-paths/register" 3 | import { task, types } from "hardhat/config" 4 | import { RevenueForwarder__factory } from "types/generated" 5 | import { deployContract } from "./utils/deploy-utils" 6 | import { getSigner } from "./utils/signerFactory" 7 | import { getChain, resolveAddress } from "./utils/networkAddressFactory" 8 | 9 | task("deploy-RevenueForwarder") 10 | .addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", types.string) 11 | .setAction(async (taskArgs, hre) => { 12 | const signer = await getSigner(hre, taskArgs.speed) 13 | const chain = getChain(hre) 14 | 15 | const nexus = resolveAddress("Nexus", chain) 16 | const musd = resolveAddress("mUSD", chain, "address") 17 | const keeper = "0xdccb7a6567603af223c090be4b9c83eced210f18" 18 | const forwarder = "0xd0f0F590585384AF7AB420bE1CFB3A3F8a82D775" 19 | 20 | await deployContract(new RevenueForwarder__factory(signer), "RevenueForwarder", [nexus, musd, keeper, forwarder]) 21 | }) 22 | 23 | module.exports = {} 24 | -------------------------------------------------------------------------------- /tasks/deploySavingsManager.ts: -------------------------------------------------------------------------------- 1 | import "ts-node/register" 2 | import "tsconfig-paths/register" 3 | import { task, types } from "hardhat/config" 4 | import { SavingsManager__factory } from "types/generated" 5 | import { simpleToExactAmount } from "@utils/math" 6 | import { ONE_WEEK } from "@utils/constants" 7 | import { deployContract } from "./utils/deploy-utils" 8 | import { getSigner } from "./utils/signerFactory" 9 | import { verifyEtherscan } from "./utils/etherscan" 10 | import { getChain, resolveAddress } from "./utils/networkAddressFactory" 11 | 12 | task("deploy-SavingsManager") 13 | .addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", types.string) 14 | .setAction(async (taskArgs, hre) => { 15 | const signer = await getSigner(hre, taskArgs.speed) 16 | const chain = getChain(hre) 17 | 18 | const nexus = resolveAddress("Nexus", chain) 19 | const revenueRecipient = resolveAddress("RevenueRecipient", chain) 20 | 21 | const musd = resolveAddress("mUSD", chain, "address") 22 | const musdSave = resolveAddress("mUSD", chain, "savings") 23 | const mbtc = resolveAddress("mBTC", chain, "address") 24 | const mbtcSave = resolveAddress("mBTC", chain, "savings") 25 | 26 | const savingsManager = await deployContract(new SavingsManager__factory(signer), "SavingsManager", [ 27 | nexus, 28 | [musd, mbtc], 29 | [musdSave, mbtcSave], 30 | [revenueRecipient, revenueRecipient], 31 | simpleToExactAmount(9, 17), 32 | ONE_WEEK, 33 | ]) 34 | 35 | await verifyEtherscan(hre, { 36 | address: savingsManager.address, 37 | contract: "contracts/savings/SavingsManager.sol:SavingsManager", 38 | }) 39 | }) 40 | 41 | module.exports = {} 42 | -------------------------------------------------------------------------------- /tasks/prepareAccountFork.ts: -------------------------------------------------------------------------------- 1 | import { subtask, task, types } from "hardhat/config" 2 | 3 | import { impersonate } from "@utils/fork" 4 | import { IERC20, IERC20__factory } from "types" 5 | import { simpleToExactAmount } from "@utils/math" 6 | import { getChain, resolveAddress } from "./utils/networkAddressFactory" 7 | 8 | const mtaWhaleAddress = "0x3dd46846eed8D147841AE162C8425c08BD8E1b41" 9 | 10 | let mtaToken: IERC20 11 | 12 | subtask("prepareAccount", "Prepares an Accounts for a local hardhat node for testing.") 13 | .addParam("address", "Address to prepare", undefined, types.string) 14 | .setAction(async (taskArgs, hre) => { 15 | const signer = await hre.ethers.getSigners()[0] 16 | const account = taskArgs.address 17 | const chain = getChain(hre) 18 | 19 | mtaToken = IERC20__factory.connect(resolveAddress("MTA", chain), signer) 20 | 21 | const mtaWhale = await impersonate(mtaWhaleAddress) 22 | 23 | // Send MTA to address from the mtaWhale account 24 | await mtaToken.connect(mtaWhale).transfer(account, simpleToExactAmount(1000)) 25 | }) 26 | 27 | task("prepareAccount").setAction(async (_, __, runSuper) => { 28 | await runSuper() 29 | }) 30 | -------------------------------------------------------------------------------- /tasks/save.ts: -------------------------------------------------------------------------------- 1 | import { subtask, task, types } from "hardhat/config" 2 | import { SavingsContract__factory } from "types/generated" 3 | import { simpleToExactAmount } from "@utils/math" 4 | import { getSignerAccount } from "./utils/signerFactory" 5 | import { logTxDetails } from "./utils/deploy-utils" 6 | import { getChain, resolveAddress } from "./utils/networkAddressFactory" 7 | 8 | subtask("save-deposit", "Deposit to savings contract") 9 | .addParam("masset", "Symbol of the mAsset. eg mUSD or mBTC", undefined, types.string) 10 | .addParam("amount", "Amount to be staked", undefined, types.float) 11 | .addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", types.string) 12 | .setAction(async (taskArgs, hre) => { 13 | const chain = getChain(hre) 14 | const signerAccount = await getSignerAccount(hre, taskArgs.speed) 15 | 16 | const saveAddress = resolveAddress(taskArgs.masset, chain, "savings") 17 | const save = SavingsContract__factory.connect(saveAddress, signerAccount.signer) 18 | 19 | const amount = simpleToExactAmount(taskArgs.amount) 20 | 21 | const tx = await save["depositSavings(uint256)"](amount) 22 | await logTxDetails(tx, `deposit ${taskArgs.amount} ${taskArgs.masset} in Save`) 23 | }) 24 | task("save-deposit").setAction(async (_, __, runSuper) => { 25 | await runSuper() 26 | }) 27 | 28 | subtask("save-redeem", "Redeems a number of Save credits from a savings contract") 29 | .addParam("masset", "Symbol of the mAsset. eg mUSD or mBTC", undefined, types.string) 30 | .addParam("amount", "Amount of Save credits to be redeemed", undefined, types.float) 31 | .addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", types.string) 32 | .setAction(async (taskArgs, hre) => { 33 | const chain = getChain(hre) 34 | const signerAccount = await getSignerAccount(hre, taskArgs.speed) 35 | 36 | const saveAddress = resolveAddress(taskArgs.masset, chain, "savings") 37 | const save = SavingsContract__factory.connect(saveAddress, signerAccount.signer) 38 | 39 | const amount = simpleToExactAmount(taskArgs.amount) 40 | 41 | const tx = await save["redeem(uint256)"](amount) 42 | await logTxDetails(tx, `redeem ${taskArgs.amount} ${taskArgs.masset} in Save`) 43 | }) 44 | task("save-redeem").setAction(async (_, __, runSuper) => { 45 | await runSuper() 46 | }) 47 | 48 | module.exports = {} 49 | -------------------------------------------------------------------------------- /tasks/utils/deploy-utils.ts: -------------------------------------------------------------------------------- 1 | import { formatUnits } from "@ethersproject/units" 2 | import debug from "debug" 3 | import { Contract, ContractFactory, ContractReceipt, ContractTransaction, Overrides } from "ethers" 4 | 5 | export const deployContract = async ( 6 | contractFactory: ContractFactory, 7 | contractName = "Contract", 8 | constructorArgs: Array = [], 9 | overrides: Overrides = {}, 10 | ): Promise => { 11 | const contract = (await contractFactory.deploy(...constructorArgs, overrides)) as T 12 | console.log( 13 | `Deploying ${contractName} contract with hash ${contract.deployTransaction.hash} from ${ 14 | contract.deployTransaction.from 15 | } with gas price ${contract.deployTransaction.gasPrice.toNumber() / 1e9} Gwei`, 16 | ) 17 | const receipt = await contract.deployTransaction.wait() 18 | const txCost = receipt.gasUsed.mul(contract.deployTransaction.gasPrice) 19 | const abiEncodedConstructorArgs = contract.interface.encodeDeploy(constructorArgs) 20 | console.log( 21 | `Deployed ${contractName} to ${contract.address} in block ${receipt.blockNumber}, using ${ 22 | receipt.gasUsed 23 | } gas costing ${formatUnits(txCost)} ETH`, 24 | ) 25 | console.log(`ABI encoded args: ${abiEncodedConstructorArgs.slice(2)}`) 26 | return contract 27 | } 28 | 29 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 30 | export const logger = (...args: string[]) => debug(`mstable:${args.join(":")}`) 31 | 32 | export const logTxDetails = async (tx: ContractTransaction, method: string): Promise => { 33 | console.log(`Sent ${method} transaction with hash ${tx.hash} from ${tx.from} with gas price ${tx.gasPrice?.toNumber() / 1e9} Gwei`) 34 | const receipt = await tx.wait() 35 | 36 | // Calculate tx cost in Wei 37 | const txCost = receipt.gasUsed.mul(tx.gasPrice ?? 0) 38 | console.log(`Processed ${method} tx in block ${receipt.blockNumber}, using ${receipt.gasUsed} gas costing ${formatUnits(txCost)} ETH`) 39 | 40 | return receipt 41 | } 42 | -------------------------------------------------------------------------------- /tasks/utils/etherscan.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from "hardhat/types" 2 | 3 | export const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) 4 | 5 | interface VerifyEtherscan { 6 | address: string 7 | contract?: string 8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types 9 | constructorArguments?: any[] 10 | libraries?: { 11 | [libraryName: string]: string 12 | } 13 | } 14 | 15 | export const verifyEtherscan = async (hre: HardhatRuntimeEnvironment, contract: VerifyEtherscan): Promise => { 16 | if (hre.network.name !== "hardhat" && hre.network.name !== "local") { 17 | // wait for the Etherscan backend to pick up the deployed contract 18 | await sleep(10000) 19 | 20 | console.log(`About to verify ${contract.address} on Etherscan`) 21 | await hre.run("verify:verify", contract) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tasks/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./signerFactory" 2 | export * from "./deploy-utils" 3 | export * from "./quantity-formatters" 4 | export * from "./tokens" 5 | -------------------------------------------------------------------------------- /tasks/utils/params.ts: -------------------------------------------------------------------------------- 1 | import { CLIArgumentType } from "hardhat/src/types/index" 2 | import { isValidAddress } from "ethereumjs-util" 3 | import { HardhatError } from "hardhat/internal/core/errors" 4 | import { ERRORS } from "hardhat/internal/core/errors-list" 5 | 6 | /** 7 | * Hardhat task CLI argument types 8 | */ 9 | export const params = { 10 | address: { 11 | name: "address", 12 | parse: (argName, strValue) => strValue, 13 | validate: (argName: string, value: unknown): void => { 14 | const isValid = typeof value === "string" && isValidAddress(value) 15 | 16 | if (!isValid) { 17 | throw new HardhatError(ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE, { 18 | value, 19 | name: argName, 20 | type: "address", 21 | }) 22 | } 23 | }, 24 | } as CLIArgumentType, 25 | addressArray: { 26 | name: "address[]", 27 | parse: (argName, strValue) => strValue.split(","), 28 | validate: (argName: string, value: unknown): void => { 29 | const isValid = Array.isArray(value) && value.every(isValidAddress) 30 | 31 | if (!isValid) { 32 | throw new HardhatError(ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE, { 33 | value, 34 | name: argName, 35 | type: "address[]", 36 | }) 37 | } 38 | }, 39 | } as CLIArgumentType, 40 | } 41 | -------------------------------------------------------------------------------- /tasks/utils/quantity-formatters.ts: -------------------------------------------------------------------------------- 1 | import { BN } from "@utils/math" 2 | import { formatUnits } from "ethers/lib/utils" 3 | 4 | export type QuantityFormatter = (amount: BN, decimals?: number, pad?: number, displayDecimals?: number) => string 5 | 6 | export const usdFormatter: QuantityFormatter = (amount: BN, decimals = 18, pad = 14, displayDecimals = 2): string => { 7 | const string2decimals = parseFloat(formatUnits(amount, decimals)).toFixed(displayDecimals) 8 | // Add thousands separator 9 | return string2decimals.replace(/\B(?=(\d{3})+(?!\d))/g, ",").padStart(pad) 10 | } 11 | 12 | export const btcFormatter: QuantityFormatter = (amount: BN, decimals = 18, pad = 7, displayDecimals = 3): string => { 13 | const string2decimals = parseFloat(formatUnits(amount, decimals)).toFixed(displayDecimals) 14 | // Add thousands separator 15 | return string2decimals.replace(/\B(?=(\d{3})+(?!\d))/g, ",").padStart(pad) 16 | } 17 | -------------------------------------------------------------------------------- /tasks/utils/quest-utils.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { BigNumberish, Signer } from "ethers" 3 | import { arrayify, solidityKeccak256 } from "ethers/lib/utils" 4 | 5 | const questBookUrl = "https://europe-west1-mstable-questbook.cloudfunctions.net/questbook" 6 | 7 | export const signUserQuests = async (user: string, questIds: BigNumberish[], questSigner: Signer): Promise => { 8 | const messageHash = solidityKeccak256(["address", "uint256[]"], [user, questIds]) 9 | const signature = await questSigner.signMessage(arrayify(messageHash)) 10 | return signature 11 | } 12 | 13 | export const signQuestUsers = async (questId: BigNumberish, users: string[], questSigner: Signer): Promise => { 14 | const messageHash = solidityKeccak256(["uint256", "address[]"], [questId, users]) 15 | const signature = await questSigner.signMessage(arrayify(messageHash)) 16 | return signature 17 | } 18 | 19 | export const getQueuedUsersForQuest = async (questId: number): Promise => { 20 | // get users who have completed quests from the queue 21 | const response = await axios.post(questBookUrl, { 22 | query: `query { queue { userId ethereumId } }`, 23 | }) 24 | const { queue } = response?.data?.data 25 | if (!queue) { 26 | console.log(response?.data) 27 | throw Error(`Failed to get quests from queue`) 28 | } 29 | // filter users to just the requested quest identifier 30 | const usersInQueue = queue.filter((quest) => quest.ethereumId === questId) 31 | const usersForQuest = usersInQueue.map((quest) => quest.userId) 32 | 33 | return usersForQuest 34 | } 35 | 36 | export const hasUserCompletedQuest = async (user: string, questName: string): Promise => { 37 | const response = await axios.post(questBookUrl, { 38 | query: `query { user(userId: "${user}") { quests { 39 | id 40 | complete 41 | progress 42 | } } }`, 43 | }) 44 | const quests = response?.data?.data?.user?.quests 45 | if (!quests) { 46 | console.log(response?.data) 47 | throw Error(`Failed to get quests for user ${user}`) 48 | } 49 | 50 | // Filter user's quest for the named quest. eg theGreatMigration 51 | const quest = quests.find((q) => q.id === `${questName}.${user}`) 52 | 53 | return quest?.complete 54 | } 55 | -------------------------------------------------------------------------------- /tenderly.yaml: -------------------------------------------------------------------------------- 1 | account_id: 7b4c7a8a-d93e-4ada-825c-104794b6abc6 2 | exports: 3 | staked-token: 4 | chain_config: 5 | berlin_block: 0 6 | byzantium_block: 0 7 | constantinople_block: 0 8 | eip150_block: 0 9 | eip150_hash: "0x0000000000000000000000000000000000000000000000000000000000000000" 10 | eip155_block: 0 11 | eip158_block: 0 12 | homestead_block: 0 13 | istanbul_block: 0 14 | petersburg_block: 0 15 | forked_network: "" 16 | project_slug: alsco77/mstable 17 | protocol: "" 18 | rpc_address: 127.0.0.1:8545 19 | project_slug: mstable 20 | provider: Hardhat 21 | -------------------------------------------------------------------------------- /test-utils/constants.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | import { utils, BigNumber as BN } from "ethers" 3 | 4 | /** 5 | * @notice This file contains constants relevant across the mStable test suite 6 | * Wherever possible, it should conform to fixed on chain vars 7 | */ 8 | 9 | export const ratioScale = BN.from(10).pow(8) 10 | export const fullScale: BN = BN.from(10).pow(18) 11 | 12 | export const DEFAULT_DECIMALS = 18 13 | 14 | export const DEAD_ADDRESS = "0x0000000000000000000000000000000000000001" 15 | export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" 16 | export const ZERO_KEY = "0x0000000000000000000000000000000000000000000000000000000000000000" 17 | 18 | export const MAX_UINT256 = BN.from(2).pow(256).sub(1) 19 | export const MAX_INT128 = BN.from(2).pow(127).sub(1) 20 | export const MIN_INT128 = BN.from(2).pow(127).mul(-1) 21 | 22 | export const ZERO = BN.from(0) 23 | export const ONE_MIN = BN.from(60) 24 | export const TEN_MINS = BN.from(60 * 10) 25 | export const ONE_HOUR = BN.from(60 * 60) 26 | export const ONE_DAY = BN.from(60 * 60 * 24) 27 | export const FIVE_DAYS = BN.from(60 * 60 * 24 * 5) 28 | export const TEN_DAYS = BN.from(60 * 60 * 24 * 10) 29 | export const ONE_WEEK = BN.from(60 * 60 * 24 * 7) 30 | export const ONE_YEAR = BN.from(60 * 60 * 24 * 365) 31 | 32 | export const KEY_SAVINGS_MANAGER = utils.keccak256(utils.toUtf8Bytes("SavingsManager")) 33 | export const KEY_PROXY_ADMIN = utils.keccak256(utils.toUtf8Bytes("ProxyAdmin")) 34 | export const KEY_LIQUIDATOR = utils.keccak256(utils.toUtf8Bytes("Liquidator")) 35 | -------------------------------------------------------------------------------- /test-utils/deploy.ts: -------------------------------------------------------------------------------- 1 | import { BytesLike } from "@ethersproject/bytes" 2 | import { ONE_WEEK } from "@utils/constants" 3 | import { increaseTime } from "@utils/time" 4 | import { Contract, ContractFactory, Signer } from "ethers" 5 | import { DelayedProxyAdmin } from "types/generated" 6 | 7 | export const upgradeContract = async ( 8 | contractFactory: ContractFactory, 9 | implementation: Contract, 10 | proxyAddress: string, 11 | signer: Signer, 12 | delayedProxyAdmin: DelayedProxyAdmin, 13 | upgradeData: BytesLike = [], 14 | ): Promise => { 15 | await delayedProxyAdmin.proposeUpgrade(proxyAddress, implementation.address, upgradeData) 16 | 17 | const proposeUpgradeData = delayedProxyAdmin.interface.encodeFunctionData("proposeUpgrade", [ 18 | proxyAddress, 19 | implementation.address, 20 | upgradeData, 21 | ]) 22 | 23 | console.log(`\ndelayedProxyAdmin.proposeUpgrade to ${delayedProxyAdmin.address}, data:\n${proposeUpgradeData}`) 24 | 25 | await increaseTime(ONE_WEEK.add(60)) 26 | 27 | // check request is correct 28 | const request = await delayedProxyAdmin.requests(proxyAddress) 29 | if (request.implementation !== implementation.address) throw new Error("Upgrade request incorrect") 30 | 31 | // accept upgrade 32 | await delayedProxyAdmin.acceptUpgradeRequest(proxyAddress) 33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 34 | const proxyUpgraded = (contractFactory as any).connect(proxyAddress, signer) 35 | 36 | return proxyUpgraded 37 | } 38 | -------------------------------------------------------------------------------- /test-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./assertions" 2 | export * from "./constants" 3 | export * from "./fork" 4 | export * from "./math" 5 | export * from "./time" 6 | export * from "./machines/index" 7 | -------------------------------------------------------------------------------- /test-utils/machines/index.ts: -------------------------------------------------------------------------------- 1 | export { MassetMachine, MassetDetails } from "./mAssetMachine" 2 | export { StandardAccounts } from "./standardAccounts" 3 | export { FeederMachine, FeederDetails } from "./feederMachine" 4 | -------------------------------------------------------------------------------- /test-utils/machines/standardAccounts.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from "ethers" 2 | import { Account } from "types" 3 | 4 | /** 5 | * @dev Standard accounts 6 | */ 7 | export class StandardAccounts { 8 | /** 9 | * @dev Default accounts as per system Migrations 10 | */ 11 | public all: Account[] 12 | 13 | public default: Account 14 | 15 | public governor: Account 16 | 17 | public other: Account 18 | 19 | public dummy1: Account 20 | 21 | public dummy2: Account 22 | 23 | public dummy3: Account 24 | 25 | public dummy4: Account 26 | 27 | public dummy5: Account 28 | 29 | public dummy6: Account 30 | 31 | public dummy7: Account 32 | 33 | public fundManager: Account 34 | 35 | public fundManager2: Account 36 | 37 | public questMaster: Account 38 | 39 | public questSigner: Account 40 | 41 | public mockSavingsManager: Account 42 | 43 | public mockInterestValidator: Account 44 | 45 | public mockRecollateraliser: Account 46 | 47 | public mockMasset: Account 48 | 49 | public mockRewardsDistributor: Account 50 | 51 | public keeper: Account 52 | 53 | public async initAccounts(signers: Signer[]): Promise { 54 | this.all = await Promise.all( 55 | signers.map(async (s) => ({ 56 | signer: s, 57 | address: await s.getAddress(), 58 | })), 59 | ) 60 | ;[ 61 | this.default, 62 | this.governor, 63 | this.other, 64 | this.dummy1, 65 | this.dummy2, 66 | this.dummy3, 67 | this.dummy4, 68 | this.dummy5, 69 | this.dummy6, 70 | this.dummy7, 71 | this.fundManager, 72 | this.fundManager2, 73 | this.questMaster, 74 | this.questSigner, 75 | this.mockSavingsManager, 76 | this.mockInterestValidator, 77 | this.mockRecollateraliser, 78 | this.mockMasset, 79 | this.mockRewardsDistributor, 80 | this.keeper, 81 | ] = this.all 82 | return this 83 | } 84 | } 85 | 86 | export default StandardAccounts 87 | -------------------------------------------------------------------------------- /test-utils/regex.ts: -------------------------------------------------------------------------------- 1 | export const bytes = /^0x([A-Fa-f0-9]{1,})$/ 2 | 3 | export const bytesFixed = (x: number): RegExp => new RegExp(`^0x([A-Fa-f0-9]{${x * 2}})$`) 4 | 5 | export const bytes32 = bytesFixed(32) 6 | export const ethereumAddress = bytesFixed(20) 7 | export const transactionHash = bytes32 8 | 9 | export const privateKey = /^[A-Fa-f0-9]{1,64}$/ 10 | -------------------------------------------------------------------------------- /test-utils/time.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | import { Block } from "@ethersproject/abstract-provider" 3 | import { BigNumberish } from "@ethersproject/bignumber" 4 | import { BN } from "./math" 5 | import { ONE_WEEK } from "./constants" 6 | 7 | export const advanceBlock = async (): Promise => ethers.provider.send("evm_mine", []) 8 | 9 | export const increaseTime = async (length: BN | number): Promise => { 10 | await ethers.provider.send("evm_increaseTime", [BN.from(length).toNumber()]) 11 | await advanceBlock() 12 | } 13 | export const latestBlock = async (): Promise => ethers.provider.getBlock(await ethers.provider.getBlockNumber()) 14 | 15 | export const getTimestamp = async (): Promise => BN.from((await latestBlock()).timestamp) 16 | 17 | export const increaseTimeTo = async (target: BN | number): Promise => { 18 | const now = await getTimestamp() 19 | const later = BN.from(target) 20 | if (later.lt(now)) throw Error(`Cannot increase current time (${now.toNumber()}) to a moment in the past (${later.toNumber()})`) 21 | const diff = later.sub(now) 22 | await increaseTime(diff) 23 | await advanceBlock() 24 | } 25 | 26 | export const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) 27 | 28 | export const startWeek = (epochSeconds: BigNumberish): BN => BN.from(epochSeconds).div(ONE_WEEK).mul(ONE_WEEK) 29 | export const startCurrentWeek = async (): Promise => startWeek(await getTimestamp()) 30 | 31 | export const weekEpoch = (epochSeconds: BigNumberish): BN => BN.from(epochSeconds).div(ONE_WEEK) 32 | export const currentWeekEpoch = async (): Promise => weekEpoch(await getTimestamp()) 33 | -------------------------------------------------------------------------------- /test-utils/validator-data/cross/index.ts: -------------------------------------------------------------------------------- 1 | // import fullIntegrationData from "./full/crossIntegrationData.json" 2 | 3 | import integrationData from "./crossIntegrationData.json" 4 | 5 | export default { integrationData } 6 | -------------------------------------------------------------------------------- /test-utils/validator-data/feeder/index.ts: -------------------------------------------------------------------------------- 1 | import integrationData from "./fPoolIntegrationData.json" 2 | import mintData from "./fPoolMintData.json" 3 | import mintMultiData from "./fPoolMintMultiData.json" 4 | import swapData from "./fPoolSwapData.json" 5 | import redeemData from "./fPoolRedeemData.json" 6 | import redeemProportionalData from "./fPoolRedeemProportionalData.json" 7 | import redeemExactData from "./fPoolRedeemMultiData.json" 8 | 9 | export default { integrationData, mintData, mintMultiData, swapData, redeemData, redeemProportionalData, redeemExactData } 10 | -------------------------------------------------------------------------------- /test-utils/validator-data/index.ts: -------------------------------------------------------------------------------- 1 | import mAssetData from "./masset" 2 | import feederData from "./feeder" 3 | import crossData from "./cross" 4 | 5 | export { mAssetData, feederData, crossData } 6 | -------------------------------------------------------------------------------- /test-utils/validator-data/masset/index.ts: -------------------------------------------------------------------------------- 1 | import integrationData from "./integrationData.json" 2 | import mintData from "./mintTestData.json" 3 | import mintMultiData from "./mintMultiTestData.json" 4 | import swapData from "./swapTestData.json" 5 | import redeemData from "./redeemTestData.json" 6 | import redeemMassetData from "./redeemMassetTestData.json" 7 | import redeemExactData from "./redeemExactTestData.json" 8 | 9 | export default { integrationData, mintData, mintMultiData, swapData, redeemData, redeemMassetData, redeemExactData } 10 | -------------------------------------------------------------------------------- /test/governance/Governable.behaviour.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai" 2 | import { Governable } from "types/generated/Governable" 3 | import { ZERO_ADDRESS } from "@utils/constants" 4 | import { Account } from "types" 5 | 6 | export interface IGovernableBehaviourContext { 7 | governable: Governable 8 | owner: Account 9 | other: Account 10 | } 11 | 12 | export function shouldBehaveLikeGovernable(ctx: IGovernableBehaviourContext): void { 13 | describe("as a Governable", () => { 14 | it("should have a Governor", async () => { 15 | expect(await ctx.governable.governor()).to.equal(ctx.owner.address) 16 | }) 17 | 18 | it("changes governor after transfer", async () => { 19 | expect(await ctx.governable.connect(ctx.other.signer).isGovernor()).to.be.equal(false) 20 | const tx = ctx.governable.connect(ctx.owner.signer).changeGovernor(ctx.other.address) 21 | await expect(tx).to.emit(ctx.governable, "GovernorChanged") 22 | expect(await ctx.governable.governor()).to.equal(ctx.other.address) 23 | expect(await ctx.governable.connect(ctx.other.signer).isGovernor()).to.be.equal(true) 24 | }) 25 | 26 | it("should prevent non-governor from changing governor", async () => { 27 | await expect(ctx.governable.connect(ctx.other.signer).changeGovernor(ctx.other.address)).to.be.revertedWith( 28 | "GOV: caller is not the Governor", 29 | ) 30 | }) 31 | 32 | // NOTE - For some reason this does not pass with the exact string even though it is emitted (false negative) 33 | it("should guard ownership against stuck state", async () => { 34 | await expect(ctx.governable.connect(ctx.owner.signer).changeGovernor(ZERO_ADDRESS)).to.be.revertedWith("VM Exception") 35 | }) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /test/governance/claimable-governor.spec.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | import { expect } from "chai" 3 | 4 | import { MassetMachine } from "@utils/machines" 5 | import { ClaimableGovernor__factory } from "types/generated" 6 | import { shouldBehaveLikeClaimable, IClaimableGovernableBehaviourContext } from "./ClaimableGovernor.behaviour" 7 | 8 | describe("ClaimableGovernable", () => { 9 | const ctx: Partial = {} 10 | 11 | beforeEach("Create Contract", async () => { 12 | const accounts = await ethers.getSigners() 13 | const mAssetMachine = await new MassetMachine().initAccounts(accounts) 14 | ctx.default = mAssetMachine.sa.default 15 | ctx.governor = mAssetMachine.sa.governor 16 | ctx.other = mAssetMachine.sa.other 17 | ctx.claimable = await new ClaimableGovernor__factory(mAssetMachine.sa.governor.signer).deploy(mAssetMachine.sa.governor.address) 18 | }) 19 | 20 | shouldBehaveLikeClaimable(ctx as Required) 21 | 22 | describe("after initiating a transfer", () => { 23 | let newOwner 24 | 25 | beforeEach(async () => { 26 | const accounts = await ethers.getSigners() 27 | const mAssetMachine = await new MassetMachine().initAccounts(accounts) 28 | newOwner = mAssetMachine.sa.other 29 | await ctx.claimable.connect(mAssetMachine.sa.governor.signer).requestGovernorChange(newOwner.address) 30 | }) 31 | 32 | it("changes allow pending owner to claim ownership", async () => { 33 | await ctx.claimable.connect(newOwner.signer).claimGovernorChange() 34 | const owner = await ctx.claimable.governor() 35 | 36 | expect(owner === newOwner.address).to.equal(true) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/governance/delayed-governor.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai" 2 | import { ethers } from "hardhat" 3 | import { MassetMachine } from "@utils/machines" 4 | import { DelayedClaimableGovernor__factory } from "types/generated" 5 | import { shouldBehaveLikeDelayedClaimable, IGovernableBehaviourContext } from "./DelayedClaimableGovernor.behaviour" 6 | import { shouldBehaveLikeClaimable } from "./ClaimableGovernor.behaviour" 7 | 8 | describe("DelayedClaimableGovernor", () => { 9 | const ctx: Partial = {} 10 | const GOVERNANCE_DELAY = 60 * 60 * 24 * 7 // 1 week 11 | 12 | describe("Should behave like Claimable", () => { 13 | beforeEach("Create Contract", async () => { 14 | const accounts = await ethers.getSigners() 15 | const mAssetMachine = await new MassetMachine().initAccounts(accounts) 16 | ctx.default = mAssetMachine.sa.default 17 | ctx.governor = mAssetMachine.sa.governor 18 | ctx.other = mAssetMachine.sa.other 19 | ctx.claimable = await new DelayedClaimableGovernor__factory(ctx.governor.signer).deploy(ctx.governor.address, GOVERNANCE_DELAY) 20 | }) 21 | 22 | shouldBehaveLikeClaimable(ctx as Required) 23 | }) 24 | 25 | describe("Should behave like DelayedClaimable", () => { 26 | beforeEach("Initiate change Governor", async () => { 27 | const accounts = await ethers.getSigners() 28 | const mAssetMachine = await new MassetMachine().initAccounts(accounts) 29 | ctx.default = mAssetMachine.sa.default 30 | ctx.governor = mAssetMachine.sa.governor 31 | ctx.other = mAssetMachine.sa.other 32 | ctx.claimable = await new DelayedClaimableGovernor__factory(ctx.governor.signer).deploy(ctx.governor.address, GOVERNANCE_DELAY) 33 | 34 | await ctx.claimable.requestGovernorChange(ctx.other.address) 35 | }) 36 | 37 | shouldBehaveLikeDelayedClaimable(ctx as Required) 38 | 39 | it("should not allow zero delay", async () => { 40 | await expect(new DelayedClaimableGovernor__factory(ctx.governor.signer).deploy(ctx.governor.address, 0)).to.be.revertedWith( 41 | "Delay must be greater than zero", 42 | ) 43 | }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/governance/governable.spec.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | import { MassetMachine } from "@utils/machines" 3 | import { MockGovernable__factory } from "types/generated" 4 | import { shouldBehaveLikeGovernable, IGovernableBehaviourContext } from "./Governable.behaviour" 5 | 6 | describe("Governable", () => { 7 | const ctx: Partial = {} 8 | 9 | beforeEach("Create Contract", async () => { 10 | const accounts = await ethers.getSigners() 11 | const mAssetMachine = await new MassetMachine().initAccounts(accounts) 12 | ctx.governable = await new MockGovernable__factory(mAssetMachine.sa.governor.signer).deploy() 13 | ctx.owner = mAssetMachine.sa.governor 14 | ctx.other = mAssetMachine.sa.other 15 | }) 16 | 17 | shouldBehaveLikeGovernable(ctx as Required) 18 | }) 19 | -------------------------------------------------------------------------------- /test/masset/extra/erc20.spec.ts: -------------------------------------------------------------------------------- 1 | import { MassetMachine } from "@utils/machines" 2 | import { simpleToExactAmount } from "@utils/math" 3 | import { ethers } from "hardhat" 4 | import { ERC20 } from "types/generated/ERC20" 5 | import { IERC20BehaviourContext, shouldBehaveLikeERC20 } from "../../shared/ERC20.behaviour" 6 | 7 | describe("Masset - ERC20", () => { 8 | const ctx: Partial = {} 9 | 10 | const runSetup = async (seedBasket = false): Promise => { 11 | ctx.details = await ctx.mAssetMachine.deployMasset() 12 | if (seedBasket) { 13 | await ctx.mAssetMachine.seedWithWeightings(ctx.details, [25, 25, 25, 25]) 14 | } 15 | } 16 | before("Init contract", async () => { 17 | const accounts = await ethers.getSigners() 18 | ctx.mAssetMachine = await new MassetMachine().initAccounts(accounts) 19 | ctx.initialHolder = ctx.mAssetMachine.sa.default 20 | ctx.recipient = ctx.mAssetMachine.sa.dummy1 21 | ctx.anotherAccount = ctx.mAssetMachine.sa.dummy2 22 | }) 23 | beforeEach("reset contracts", async () => { 24 | await runSetup(true) 25 | ctx.token = ctx.details.mAsset as ERC20 26 | }) 27 | 28 | shouldBehaveLikeERC20(ctx as IERC20BehaviourContext, "ERC20", simpleToExactAmount(100, 18)) 29 | }) 30 | -------------------------------------------------------------------------------- /test/shared/Module.behaviour.ts: -------------------------------------------------------------------------------- 1 | import { StandardAccounts } from "@utils/machines" 2 | import { expect } from "chai" 3 | import { ZERO_ADDRESS } from "@utils/constants" 4 | import { INexus__factory, ImmutableModule } from "types/generated" 5 | 6 | export interface IModuleBehaviourContext { 7 | module: ImmutableModule 8 | sa: StandardAccounts 9 | } 10 | 11 | export function shouldBehaveLikeModule(ctx: IModuleBehaviourContext): void { 12 | it("should have Nexus", async () => { 13 | const nexusAddr = await ctx.module.nexus() 14 | expect(nexusAddr).to.not.equal(ZERO_ADDRESS) 15 | }) 16 | 17 | it("should have Governor address", async () => { 18 | const nexusAddr = await ctx.module.nexus() 19 | const nexus = await INexus__factory.connect(nexusAddr, ctx.sa.default.signer) 20 | 21 | const nexusGovernor = await nexus.governor() 22 | expect(nexusGovernor).to.equal(ctx.sa.governor.address) 23 | }) 24 | } 25 | 26 | export default shouldBehaveLikeModule 27 | -------------------------------------------------------------------------------- /test/shared/PausableModule.behaviour.ts: -------------------------------------------------------------------------------- 1 | import { StandardAccounts } from "@utils/machines" 2 | import { expect } from "chai" 3 | import { ZERO_ADDRESS } from "@utils/constants" 4 | import { INexus__factory, PausableModule } from "types/generated" 5 | 6 | export interface IPausableModuleBehaviourContext { 7 | module: PausableModule 8 | sa: StandardAccounts 9 | } 10 | 11 | export function shouldBehaveLikePausableModule(ctx: IPausableModuleBehaviourContext): void { 12 | it("should have Nexus", async () => { 13 | const nexusAddr = await ctx.module.nexus() 14 | expect(nexusAddr).to.not.equal(ZERO_ADDRESS) 15 | }) 16 | 17 | it("should have Governor address", async () => { 18 | const nexusAddr = await ctx.module.nexus() 19 | const nexus = await INexus__factory.connect(nexusAddr, ctx.sa.default.signer) 20 | 21 | const nexusGovernor = await nexus.governor() 22 | expect(nexusGovernor).to.equal(ctx.sa.governor.address) 23 | }) 24 | 25 | it("should not be paused", async () => { 26 | const paused = await ctx.module.paused() 27 | expect(paused).to.eq(false) 28 | }) 29 | it("should allow pausing and unpausing by governor", async () => { 30 | // Pause 31 | let tx = ctx.module.connect(ctx.sa.governor.signer).pause() 32 | await expect(tx).to.emit(ctx.module, "Paused").withArgs(ctx.sa.governor.address) 33 | // Fail if already paused 34 | await expect(ctx.module.connect(ctx.sa.governor.signer).pause()).to.be.revertedWith("Pausable: paused") 35 | 36 | // Unpause 37 | tx = ctx.module.connect(ctx.sa.governor.signer).unpause() 38 | await expect(tx).to.emit(ctx.module, "Unpaused").withArgs(ctx.sa.governor.address) 39 | 40 | // Fail to unpause twice 41 | await expect(ctx.module.connect(ctx.sa.governor.signer).unpause()).to.be.revertedWith("Pausable: not paused") 42 | }) 43 | it("should fail to pause if non-governor", async () => { 44 | await expect(ctx.module.connect(ctx.sa.other.signer).pause()).to.be.revertedWith("Only governor can execute") 45 | }) 46 | } 47 | 48 | export default shouldBehaveLikePausableModule 49 | -------------------------------------------------------------------------------- /test/shared/RewardsDistributionRecipient.behaviour.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai" 2 | import { ZERO_ADDRESS } from "@utils/constants" 3 | import { InitializableRewardsDistributionRecipient } from "types/generated" 4 | import { IModuleBehaviourContext, shouldBehaveLikeModule } from "./Module.behaviour" 5 | 6 | function behaveLikeAModule(ctx: IModuleBehaviourContext): void { 7 | return shouldBehaveLikeModule(ctx) 8 | } 9 | 10 | export interface IRewardsDistributionRecipientContext extends IModuleBehaviourContext { 11 | recipient: InitializableRewardsDistributionRecipient 12 | } 13 | 14 | export function shouldBehaveLikeDistributionRecipient(ctx: IRewardsDistributionRecipientContext): void { 15 | behaveLikeAModule(ctx as IModuleBehaviourContext) 16 | 17 | it("should have a distributor", async () => { 18 | const distributor = await ctx.recipient.rewardsDistributor() 19 | expect(distributor).not.eq(ZERO_ADDRESS) 20 | }) 21 | 22 | it("should allow governor to change the distributor", async () => { 23 | const newDistributor = ctx.sa.other 24 | await ctx.recipient.connect(ctx.sa.governor.signer).setRewardsDistribution(newDistributor.address) 25 | expect(await ctx.recipient.rewardsDistributor()).eq(newDistributor.address) 26 | }) 27 | 28 | it("should prevent change from non-governor", async () => { 29 | const newDistributor = ctx.sa.other 30 | const oldDistributor = await ctx.recipient.rewardsDistributor() 31 | await expect(ctx.recipient.connect(ctx.sa.default.signer).setRewardsDistribution(newDistributor.address)).to.be.revertedWith( 32 | "Only governor can execute", 33 | ) 34 | expect(await ctx.recipient.rewardsDistributor()).eq(oldDistributor) 35 | }) 36 | } 37 | 38 | export default shouldBehaveLikeDistributionRecipient 39 | -------------------------------------------------------------------------------- /test/shared/root.spec.ts: -------------------------------------------------------------------------------- 1 | import { BN } from "@utils/math" 2 | import { ethers } from "hardhat" 3 | import { MockRoot, MockRoot__factory } from "types/generated" 4 | import { expect } from "chai" 5 | 6 | describe("Root", () => { 7 | let root: MockRoot 8 | before(async () => { 9 | const accounts = await ethers.getSigners() 10 | root = await (await new MockRoot__factory(accounts[0])).deploy() 11 | }) 12 | 13 | describe("calculating the root", () => { 14 | it("returns floored root", async () => { 15 | let amt = BN.from(1000000000) 16 | let res = await root.sqrt(amt) 17 | expect(res).to.be.eq(BN.from(31622)) 18 | amt = BN.from(64) 19 | res = await root.sqrt(amt) 20 | expect(res).to.be.eq(BN.from(8)) 21 | amt = BN.from("160000000000000000") 22 | res = await root.sqrt(amt) 23 | expect(res).to.be.eq(BN.from(400000000)) 24 | }) 25 | it("returns root for seconds in year", async () => { 26 | const amt = BN.from("31540000") 27 | const res = await root.sqrt(amt) 28 | expect(res).to.be.eq(BN.from(5616)) 29 | }) 30 | it("returns root for seconds in 6 months", async () => { 31 | const amt = BN.from("15724800") 32 | const res = await root.sqrt(amt) 33 | expect(res).to.be.eq(BN.from(3965)) 34 | }) 35 | it("returns root for seconds in week", async () => { 36 | const amt = BN.from("604800") 37 | const res = await root.sqrt(amt) 38 | expect(res).to.be.eq(BN.from(777)) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "target": "ES2019", 5 | "lib": ["dom", "ES2019"], 6 | "module": "CommonJS", 7 | "declaration": true, 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "noImplicitAny": false, 11 | "allowJs": false, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "baseUrl": ".", 15 | "rootDir": ".", 16 | "paths": { 17 | "@utils/*": ["test-utils/*"] 18 | }, 19 | "typeRoots": ["node_modules/@types"] 20 | }, 21 | "include": ["./types/**/*.ts", "./tasks/**/*.ts", "./test/**/*.ts", "./test-fork/**/*.ts", "./test-utils/**/*.ts", "./*.js"], 22 | "files": [ 23 | "./index.ts", 24 | "./hardhat.config.ts", 25 | "./hardhat-fork.config.ts", 26 | "./hardhat-fork-polygon.config.ts", 27 | "./tasks.config.ts", 28 | "./tasks-fork.config.ts", 29 | "./tasks-fork-polygon.config.ts" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /types/common.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from "ethers" 2 | 3 | export type EthAddress = string 4 | export type Bytes32 = string 5 | 6 | export interface Account { 7 | signer: Signer 8 | address: string 9 | } 10 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./common" 2 | export * from "./machines" 3 | export * from "./generated" 4 | export * from "./stakedToken" 5 | -------------------------------------------------------------------------------- /types/machines.ts: -------------------------------------------------------------------------------- 1 | import { BN } from "../test-utils/math" 2 | import { EthAddress } from "./common" 3 | import { Basset } from "../test-utils/mstable-objects" 4 | import { MockERC20 } from "./generated" 5 | 6 | export interface ATokenDetails { 7 | bAsset: EthAddress 8 | aToken: EthAddress 9 | } 10 | export interface CTokenDetails { 11 | bAsset: EthAddress 12 | cToken: EthAddress 13 | } 14 | 15 | export enum IntegrationPlatform { 16 | none, 17 | aave, 18 | compound, 19 | } 20 | 21 | export interface BassetIntegrationDetails { 22 | bAssets: Array 23 | bAssetTxFees: boolean[] 24 | platforms?: Array 25 | aavePlatformAddress?: EthAddress 26 | aTokens?: Array 27 | cTokens?: Array 28 | } 29 | 30 | export interface BassetDetails extends Basset { 31 | address: EthAddress 32 | mAssetUnits: BN 33 | actualBalance: BN 34 | rawBalance?: BN 35 | platformBalance?: BN 36 | } 37 | 38 | export interface BasketComposition { 39 | bAssets: Array 40 | totalSupply: BN 41 | surplus: BN 42 | sumOfBassets: BN 43 | failed: boolean 44 | undergoingRecol: boolean 45 | colRatio?: BN 46 | } 47 | 48 | export interface ActionDetails { 49 | hasLendingMarket: boolean 50 | expectInteraction: boolean 51 | amount?: BN 52 | rawBalance?: BN 53 | } 54 | -------------------------------------------------------------------------------- /types/stakedToken.ts: -------------------------------------------------------------------------------- 1 | import { BN } from "@utils/math" 2 | 3 | export interface UserBalance { 4 | raw: BN 5 | weightedTimestamp: number 6 | questMultiplier: number 7 | timeMultiplier: number 8 | cooldownTimestamp: number 9 | cooldownUnits: BN 10 | } 11 | 12 | export interface QuestBalance { 13 | lastAction: number 14 | permMultiplier: number 15 | seasonMultiplier: number 16 | } 17 | 18 | export interface UserStakingData { 19 | scaledBalance: BN 20 | votes: BN 21 | pastStakerVotes?: BN 22 | earnedRewards: BN 23 | numCheckpoints?: number 24 | rewardTokenBalance?: BN 25 | rawBalance: UserBalance 26 | userPriceCoeff: BN 27 | questBalance?: QuestBalance 28 | balData?: BalConfig 29 | } 30 | export interface BalConfig { 31 | totalSupply: BN 32 | pastTotalSupply?: BN 33 | pendingBPTFees: BN 34 | priceCoefficient: BN 35 | lastPriceUpdateTime: BN 36 | mbptBalOfStakedToken?: BN 37 | mbptBalOfGauge?: BN 38 | stakerBal?: BN 39 | stakerVotes?: BN 40 | pastStakerVotes?: BN 41 | deployerStkbptBal?: BN 42 | whitelisted?: boolean[] 43 | delegatee?: string 44 | } 45 | 46 | export enum QuestType { 47 | PERMANENT, 48 | SEASONAL, 49 | } 50 | 51 | export enum QuestStatus { 52 | ACTIVE, 53 | EXPIRED, 54 | } 55 | --------------------------------------------------------------------------------