├── .editorconfig ├── .env.example ├── .eslintrc.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitleaks.toml ├── .gitleaksignore ├── .gitmodules ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.json ├── .solhint.json ├── .solhintignore ├── LICENSE ├── README.md ├── docs └── images │ ├── claim-rewards-v2.svg │ ├── root-system-view-1.svg │ ├── root-user-view-1.svg │ ├── root-user-view-2.svg │ └── root-user-view-3.svg ├── foundry.toml ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── remappings.txt ├── slither.config.json ├── solver ├── README.md ├── commands │ ├── BalancerV2MetaStablePoolAdapter.ts │ ├── BaseAdapter.ts │ ├── BeethovenAdapter.ts │ ├── CurveV2FactoryCryptoAdapter.ts │ ├── MaverickAdapter.ts │ └── VelodromeAdapter.ts ├── helpers │ └── common.ts ├── test │ ├── Adapters.test.ts │ └── payloads │ │ └── adapters │ │ ├── balancerv2-add-liquidity.json │ │ ├── balancerv2-remove-liquidity.json │ │ ├── beethoven-add-liquidity.json │ │ ├── beethoven-remove-liquidity.json │ │ ├── curvev2-add-liquidity.json │ │ ├── curvev2-remove-liquidity.json │ │ ├── maverick-add-liquidity.json │ │ ├── maverick-remove-liquidity.json │ │ ├── velodrome-add-liquidity.json │ │ └── velodrome-remove-liquidity.json └── typings │ └── common.ts ├── src ├── SystemComponent.sol ├── SystemRegistry.sol ├── access │ └── Ownable2Step.sol ├── beacon │ └── FrxBeaconChainBacking.sol ├── destinations │ ├── DestinationRegistry.sol │ └── adapters │ │ ├── BalancerBeethovenAdapter.sol │ │ ├── CurveV2FactoryCryptoAdapter.sol │ │ ├── MaverickAdapter.sol │ │ ├── README.md │ │ ├── VelodromeAdapter.sol │ │ ├── rewards │ │ ├── AuraRewardsAdapter.sol │ │ ├── BeethovenRewardsAdapter.sol │ │ ├── CamelotRewardsAdapter.sol │ │ ├── ConvexArbitrumRewardsAdapter.sol │ │ ├── ConvexRewardsAdapter.sol │ │ ├── CurveRewardsAdapter.sol │ │ ├── MaverickRewardsAdapter.sol │ │ ├── RewardAdapter.sol │ │ └── VelodromeRewardsAdapter.sol │ │ └── staking │ │ ├── AuraAdapter.sol │ │ ├── ConvexAdapter.sol │ │ ├── MaverickStakingAdapter.sol │ │ └── VelodromeStakingAdapter.sol ├── interfaces │ ├── ISystemComponent.sol │ ├── ISystemRegistry.sol │ ├── beacon │ │ └── IBeaconChainBacking.sol │ ├── destinations │ │ ├── IClaimableRewardsAdapter.sol │ │ ├── IDestinationAdapter.sol │ │ ├── IDestinationRegistry.sol │ │ ├── IPoolAdapter.sol │ │ └── IStakingAdapter.sol │ ├── external │ │ ├── balancer │ │ │ ├── IAsset.sol │ │ │ ├── IBalancerComposableStablePool.sol │ │ │ ├── IBalancerMetaStablePool.sol │ │ │ ├── IBalancerPool.sol │ │ │ ├── IBasePool.sol │ │ │ ├── IProtocolFeesCollector.sol │ │ │ ├── IRateProvider.sol │ │ │ └── IVault.sol │ │ ├── beethoven │ │ │ ├── IChildChainGaugeRewardHelper.sol │ │ │ ├── IChildChainStreamer.sol │ │ │ └── IRewardsOnlyGauge.sol │ │ ├── camelot │ │ │ ├── ICamelotPair.sol │ │ │ ├── INFTHandler.sol │ │ │ ├── INFTPool.sol │ │ │ └── INitroPool.sol │ │ ├── chainlink │ │ │ └── IAggregatorV3Interface.sol │ │ ├── coinbase │ │ │ └── IStakedTokenV1.sol │ │ ├── convex │ │ │ ├── IBaseRewardPool.sol │ │ │ ├── IConvexBooster.sol │ │ │ ├── IConvexBoosterArbitrum.sol │ │ │ └── IConvexRewardPool.sol │ │ ├── curve │ │ │ ├── ICryptoSwapPool.sol │ │ │ ├── ICurveFactoryV2.sol │ │ │ ├── ICurveMetaPoolFactory.sol │ │ │ ├── ICurveMetaRegistry.sol │ │ │ ├── ICurveMetaStableFactory.sol │ │ │ ├── ICurveOwner.sol │ │ │ ├── ICurveRegistry.sol │ │ │ ├── ICurveRegistryV2.sol │ │ │ ├── ICurveStableSwapNG.sol │ │ │ ├── ICurveTokenV2.sol │ │ │ ├── ICurveV1StableSwap.sol │ │ │ ├── ICurveV2Swap.sol │ │ │ ├── ILiquidityGaugeV2.sol │ │ │ ├── IPool.sol │ │ │ └── IStakingRewards.sol │ │ ├── frax │ │ │ └── ISfrxEth.sol │ │ ├── lido │ │ │ ├── IstEth.sol │ │ │ └── IwstEth.sol │ │ ├── maverick │ │ │ ├── IPool.sol │ │ │ ├── IPoolPositionDynamicSlim.sol │ │ │ ├── IPoolPositionSlim.sol │ │ │ ├── IPosition.sol │ │ │ ├── IPositionMetadata.sol │ │ │ ├── IReward.sol │ │ │ └── IRouter.sol │ │ ├── rocket-pool │ │ │ ├── IRocketOvmPriceOracle.sol │ │ │ └── IRocketTokenRETHInterface.sol │ │ ├── swell │ │ │ └── IswETH.sol │ │ ├── uniswap │ │ │ ├── IPairUniV2.sol │ │ │ ├── IUniswapV2Pair.sol │ │ │ └── IUniswapV3SwapRouter.sol │ │ ├── velodrome │ │ │ ├── IBaseBribe.sol │ │ │ ├── IExternalBribe.sol │ │ │ ├── IGauge.sol │ │ │ ├── IInternalBribe.sol │ │ │ ├── IPair.sol │ │ │ ├── IRewardsDistributor.sol │ │ │ ├── IRouter.sol │ │ │ ├── IVoter.sol │ │ │ ├── IVotingEscrow.sol │ │ │ ├── IWrappedExternalBribe.sol │ │ │ └── IWrappedExternalBribeFactory.sol │ │ └── wsteth │ │ │ └── IWstEth.sol │ ├── liquidation │ │ ├── IAsyncSwapper.sol │ │ ├── IAsyncSwapperRegistry.sol │ │ └── ILiquidationRow.sol │ ├── oracles │ │ ├── IPriceOracle.sol │ │ └── IRootPriceOracle.sol │ ├── pricing │ │ └── IEthValueOracle.sol │ ├── rewarders │ │ ├── IBaseRewarder.sol │ │ ├── IExtraRewarder.sol │ │ ├── IMainRewarder.sol │ │ └── IStakeTracking.sol │ ├── rewards │ │ └── IClaimableRewards.sol │ ├── security │ │ ├── IAccessController.sol │ │ └── ISystemSecurity.sol │ ├── staking │ │ └── IGPToke.sol │ ├── stats │ │ ├── IDexLSTStats.sol │ │ ├── IIncentivesPricingStats.sol │ │ ├── ILSTStats.sol │ │ ├── IStatsCalculator.sol │ │ ├── IStatsCalculatorFactory.sol │ │ └── IStatsCalculatorRegistry.sol │ ├── strategy │ │ └── IStrategy.sol │ ├── swapper │ │ ├── ISwapRouter.sol │ │ └── ISyncSwapper.sol │ ├── utils │ │ ├── ICurveResolver.sol │ │ ├── IERC20PermitAllowed.sol │ │ ├── IMulticall.sol │ │ ├── ISelfPermit.sol │ │ └── IWETH9.sol │ └── vault │ │ ├── IBaseAssetVault.sol │ │ ├── IDestinationVault.sol │ │ ├── IDestinationVaultFactory.sol │ │ ├── IDestinationVaultRegistry.sol │ │ ├── ILMPVault.sol │ │ ├── ILMPVaultFactory.sol │ │ ├── ILMPVaultRegistry.sol │ │ ├── ILMPVaultRouter.sol │ │ └── ILMPVaultRouterBase.sol ├── libs │ ├── BalancerUtilities.sol │ ├── ConvexRewards.sol │ ├── LibAdapter.sol │ └── Roles.sol ├── liquidation │ ├── AsyncSwapperRegistry.sol │ ├── BaseAsyncSwapper.sol │ ├── LiquidationRow.sol │ └── README.md ├── oracles │ ├── README.md │ ├── RootPriceOracle.sol │ ├── image-1.png │ ├── image.png │ └── providers │ │ ├── BalancerLPComposableStableEthOracle.sol │ │ ├── BalancerLPMetaStableEthOracle.sol │ │ ├── ChainlinkOracle.sol │ │ ├── CurveV1StableEthOracle.sol │ │ ├── CurveV2CryptoEthOracle.sol │ │ ├── CustomSetOracle.sol │ │ ├── EthPeggedOracle.sol │ │ ├── MavEthOracle.sol │ │ ├── SfrxEthEthOracle.sol │ │ ├── SwEthEthOracle.sol │ │ ├── TellorOracle.sol │ │ ├── UniswapV2EthOracle.sol │ │ ├── WstETHEthOracle.sol │ │ └── base │ │ └── BaseOracleDenominations.sol ├── rewarders │ ├── AbstractRewarder.sol │ ├── ExtraRewarder.sol │ ├── MainRewarder.sol │ └── README.md ├── security │ ├── AccessController.sol │ ├── Pausable.sol │ ├── SecurityBase.sol │ └── SystemSecurity.sol ├── solver │ ├── CommandBuilder.sol │ ├── VM.sol │ ├── helpers │ │ ├── ArraysConverter.sol │ │ ├── BlockchainInfo.sol │ │ ├── Bytes32.sol │ │ ├── Integer.sol │ │ └── Tupler.sol │ └── test │ │ ├── SolverCaller.sol │ │ └── TestableVM.sol ├── staking │ └── GPToke.sol ├── stats │ ├── Stats.md │ ├── Stats.sol │ ├── StatsCalculatorFactory.sol │ ├── StatsCalculatorRegistry.sol │ ├── calculators │ │ ├── Calculators.md │ │ ├── CbethLSTCalculator.sol │ │ ├── CurveV1ConvexStatsCalculator.sol │ │ ├── CurveV1PoolNoRebasingStatsCalculator.sol │ │ ├── CurveV1PoolRebasingStatsCalculator.sol │ │ ├── IncentivePricingStats.sol │ │ ├── ProxyLSTCalculator.sol │ │ ├── RethLSTCalculator.sol │ │ ├── StethLSTCalculator.sol │ │ ├── SwethLSTCalculator.sol │ │ └── base │ │ │ ├── BalancerStablePoolCalculatorBase.sol │ │ │ ├── BaseStatsCalculator.sol │ │ │ ├── CurvePoolNoRebasingCalculatorBase.sol │ │ │ ├── CurvePoolRebasingCalculatorBase.sol │ │ │ └── LSTCalculatorBase.sol │ ├── images │ │ ├── stats_example.svg │ │ └── stats_high_level.svg │ └── utils │ │ └── CurveUtils.sol ├── strategy │ ├── LMPStrategy.sol │ └── StrategyFactory.sol ├── swapper │ ├── README.md │ ├── SwapRouter.sol │ └── adapters │ │ ├── BalancerV2Swap.sol │ │ ├── BaseAdapter.sol │ │ ├── CurveV1StableSwap.sol │ │ ├── CurveV2Swap.sol │ │ └── UniV3Swap.sol ├── utils │ ├── Arrays.sol │ ├── CurveResolverMainnet.sol │ ├── Errors.sol │ ├── Multicall.sol │ ├── NonReentrant.sol │ ├── PeripheryPayments.sol │ ├── SelfPermit.sol │ └── univ3 │ │ ├── BytesLib.sol │ │ └── Path.sol └── vault │ ├── BalancerAuraDestinationVault.sol │ ├── CurveConvexDestinationVault.sol │ ├── DestinationVault.sol │ ├── DestinationVaultFactory.sol │ ├── DestinationVaultRegistry.sol │ ├── LMPVault.sol │ ├── LMPVaultFactory.sol │ ├── LMPVaultRegistry.sol │ ├── LMPVaultRouter.sol │ ├── LMPVaultRouterBase.sol │ ├── MaverickDestinationVault.sol │ ├── README.md │ ├── VaultTypes.sol │ └── libs │ ├── LMPDebt.sol │ └── LMPDestinations.sol ├── test ├── BaseTest.t.sol ├── SystemRegistry.t.sol ├── access │ └── Ownable2Step.t.sol ├── base │ └── CamelotBase.sol ├── beacon │ └── FrxBeaconChainBacking.t.sol ├── deployments │ └── Deployment.t.sol ├── destinations │ ├── DestinationRegistry.t.sol │ └── adapters │ │ ├── BalancerAdapter.t.sol │ │ ├── BeethovenAdapter.t.sol │ │ ├── CurveV2FactoryCryptoAdapter.t.sol │ │ ├── MaverickAdapter.t.sol │ │ ├── VelodromeAdapter.t.sol │ │ ├── rewards │ │ ├── AuraRewardsAdapter.t.sol │ │ ├── BeethovenRewardsAdapter.t.sol │ │ ├── CamelotRewardsAdapter.t.sol │ │ ├── ConvexArbitrumRewardsAdapter.t.sol │ │ ├── ConvexRewardsAdapter.t.sol │ │ ├── CurveRewardsAdapter.t.sol │ │ ├── MaverickRewardsAdapter.t.sol │ │ └── VelodromeRewardsAdapter.t.sol │ │ └── staking │ │ ├── AuraAdapter.t.sol │ │ ├── ConvexAdapter.t.sol │ │ ├── MaverickStakingAdapter.t.sol │ │ └── VelodromeStakingAdapter.t.sol ├── libs │ ├── BalancerUtilities.t.sol │ ├── ConvexRewards.t.sol │ └── LibAdapter.t.sol ├── liquidators │ ├── AsyncSwapperRegistry.t.sol │ ├── LiquidationRow.t.sol │ ├── OneInchAdapter.t.sol │ └── ZeroExAdapter.t.sol ├── mocks │ ├── CrvUsdOracle.sol │ ├── MockERC20.sol │ ├── StakeTrackingMock.sol │ ├── TestDestinationVault.sol │ └── TestERC20.sol ├── oracles │ ├── RootOracleIntegrationTest.t.sol │ ├── RootPriceOracle.t.sol │ └── providers │ │ ├── BalancerLPComposableStableEthOracle.t.sol │ │ ├── BalancerLPMetaStableEthOracle.t.sol │ │ ├── ChainlinkOracle.t.sol │ │ ├── CurveV1StableEthOracle.t.sol │ │ ├── CurveV2CryptoEthOracle.t.sol │ │ ├── CustomSetOracle.t.sol │ │ ├── EthPeggedOracle.t.sol │ │ ├── MavEthOracle.t.sol │ │ ├── SfrxEthEthOracle.t.sol │ │ ├── SwEthEthOracle.t.sol │ │ ├── TellorOracle.t.sol │ │ ├── UniswapV2EthOracle.t.sol │ │ └── WstETHEthOracle.t.sol ├── rewarders │ ├── AbstractRewarder.t.sol │ └── RewardVault.t.sol ├── security │ ├── AccessControl.t.sol │ ├── Pausable.t.sol │ └── SystemSecurity.t.sol ├── staking │ └── Staking.t.sol ├── stats │ ├── Stats.t.sol │ ├── StatsCalculatorFactory.t.sol │ ├── StatsCalculatorRegister.t.sol │ ├── calculators │ │ ├── CbethLSTCalculator.t..sol │ │ ├── CurveV1ConvexStatsCalculator.t.sol │ │ ├── CurveV1PoolNoRebasingStatsCalculator.t.sol │ │ ├── CurveV1PoolRebasingStatsCalculator.t.sol │ │ ├── IncentivePricingStats.t.sol │ │ ├── ProxyLSTCalculator.t.sol │ │ ├── RethLSTCalculator.t..sol │ │ ├── StethLSTCalculator.t.sol │ │ ├── SwethLSTCalculator.t..sol │ │ └── base │ │ │ ├── BalancerStablePoolCalculatorBase.t.sol │ │ │ └── LSTCalculatorBase.t.sol │ └── utils │ │ └── CurveUtils.t.sol ├── swapper │ ├── SwapRouter.t.sol │ └── adapters │ │ ├── BalancerV2Swap.t.sol │ │ ├── CurveStableSwap.sol │ │ ├── CurveV1StableSwap.sol │ │ └── UniV3Swap.t.sol ├── utils │ ├── Addresses.sol │ ├── Arrays.t.sol │ ├── CurveResolverMainnet.t.sol │ ├── ReadPlan.sol │ └── common.sol └── vault │ ├── BalancerAuraDestinationVault.t.sol │ ├── BalancerAuraDestinationVaultComposable.t.sol │ ├── CurveConvexDestinationVault.t.sol │ ├── DestinationVault.t.sol │ ├── DestinationVaultFactory.t.sol │ ├── DestinationVaultRegistry.t.sol │ ├── LMPVault-Withdraw.t.sol │ ├── LMPVault.t.sol │ ├── LMPVaultBaseTest.t.sol │ ├── LMPVaultFactory.t.sol │ ├── LMPVaultRouter.t.sol │ └── MaverickDestinationVault.t.sol └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # All files 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.sol] 16 | indent_size = 4 17 | 18 | [*.tree] 19 | indent_size = 1 20 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/ 2 | ARBITRUM_MAINNET_RPC_URL= 3 | OPTIMISM_MAINNET_RPC_URL= 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:json/recommended", 5 | "plugin:markdown/recommended", 6 | "plugin:yml/prettier", 7 | "plugin:@typescript-eslint/recommended", 8 | "prettier" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "plugins": ["@typescript-eslint"], 12 | "root": true, 13 | "overrides": [ 14 | { 15 | "files": ["*.yaml", "**/*.yml"], 16 | "parser": "yaml-eslint-parser" 17 | } 18 | ], 19 | "ignorePatterns": ["lib", "node_modules", "!**/*.json", "!**/*.yml", "lib/"] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | local/ 2 | cache/ 3 | out/ 4 | .env 5 | .vscode 6 | 7 | .DS_Store 8 | node_modules/ 9 | 10 | lcov.info 11 | bin 12 | 13 | # hardhat 14 | artifacts/ 15 | cache_hardhat/ 16 | typechain-types/ 17 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | #See https://github.com/zricethezav/gitleaks#configuration 2 | 3 | title = "Foundry Template" 4 | 5 | [extend] 6 | useDefault = true 7 | 8 | [[rules]] 9 | id = "ethereum-rule-1" 10 | description = "Ethereum private keys" 11 | regex = '''(?:\'|"|\s|^)(?:0x)?[a-fA-F0-9]{64}(?:\'|"|\s|;|$)''' 12 | 13 | 14 | [allowlist] 15 | paths = [ 16 | '''lib/(.*?)''', 17 | '''out/(.*?)''', 18 | '''cache/(.*?)''', 19 | '''artifacts/(.*?)''', 20 | '''node_modules/(.*?)''', 21 | '''test/utils/Addresses.sol''', 22 | '''test/oracles/providers/TellorOracle.t.sol''', 23 | '''test/pricing/TellorValueProvider.t.sol''', 24 | '''test/utils/ReadPlan.sol''', 25 | '''src/solver/VM.sol''', 26 | '''solver/test/Adapters.test.ts''', 27 | '''solver/test/payloads/adapters/(.*?)''', 28 | '''solver/README.md''', 29 | ] 30 | -------------------------------------------------------------------------------- /.gitleaksignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tokemak/v2-core-audit-2023-07-14/62445b8ee3365611534c96aef189642b721693bf/.gitleaksignore -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | branch = v1 5 | 6 | [submodule "lib/openzeppelin-contracts"] 7 | path = lib/openzeppelin-contracts 8 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 9 | branch = v4.8.1 10 | 11 | [submodule "lib/openzeppelin-contracts-upgradeable"] 12 | path = lib/openzeppelin-contracts-upgradeable 13 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 14 | [submodule "lib/erc4626-tests"] 15 | path = lib/erc4626-tests 16 | url = https://github.com/a16z/erc4626-tests 17 | [submodule "lib/prb-math"] 18 | path = lib/prb-math 19 | url = https://github.com/PaulRBerg/prb-math 20 | branch = v2.5.0 21 | [submodule "lib/usingtellor"] 22 | path = lib/usingtellor 23 | url = https://github.com/tellor-io/usingtellor 24 | branch = v5.0.4 25 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | echo "\n====================\nContract format\n====================\n" 5 | forge fmt --check 6 | echo "Complete" 7 | 8 | echo "\n====================\nOther format\n====================\n" 9 | npx --no -- pretty-quick --staged --bail 10 | 11 | echo "\n====================\nLint\n====================\n" 12 | npm run lint 13 | 14 | 15 | echo "\n====================\nGitleaks\n====================\n" 16 | gitleaks protect --verbose --staged 17 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/cache 2 | **/lib 3 | **/out 4 | **/node_modules 5 | *.env 6 | *.sol 7 | **/bin -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "printWidth": 120, 4 | "singleQuote": false, 5 | "tabWidth": 4, 6 | "trailingComma": "all", 7 | "useTabs": true, 8 | "overrides": [ 9 | { 10 | "files": "*.sol", 11 | "options": { 12 | "printWidth": 120, 13 | "tabWidth": 4, 14 | "singleQuote": false, 15 | "useTabs": true 16 | } 17 | }, 18 | { 19 | "files": "*.md", 20 | "options": { 21 | "semi": false 22 | } 23 | }, 24 | { 25 | "files": "*.yml", 26 | "options": { 27 | "tabWidth": 2 28 | } 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "max-line-length": ["error", 120], 5 | "compiler-version": ["error", "^0.8.7"], 6 | "no-empty-blocks": "off", 7 | "func-visibility": ["warn", { "ignoreConstructors": true }], 8 | "private-vars-leading-underscore": "off", 9 | "not-rely-on-time": "off" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | **/lib 2 | **/node_modules 3 | lib/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tokemak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | name = 'contracts-template' 3 | src = 'src' 4 | out = 'out' 5 | libs = ['lib'] 6 | solc = "0.8.17" 7 | evm_version = "shanghai" 8 | fs_permissions = [{ access = "read-write", path = "./" }] 9 | 10 | [fmt] 11 | bracket_spacing = true 12 | int_types = "long" 13 | line_length = 120 14 | multiline_func_header = "params_first" 15 | number_underscore = "thousands" 16 | quote_style = "double" 17 | tab_width = 4 18 | wrap_comments = true 19 | single_line_statement_blocks = "preserve" # can force either way 20 | exclude = ['lib/'] 21 | 22 | [fuzz] 23 | runs = 100 24 | # max_test_rejects = 10000000 25 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 26 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | 3 | import { HardhatUserConfig } from "hardhat/config"; 4 | 5 | import "@nomiclabs/hardhat-ethers"; 6 | import "@nomicfoundation/hardhat-toolbox"; 7 | import "@nomicfoundation/hardhat-foundry"; 8 | 9 | dotenv.config(); 10 | 11 | const config: HardhatUserConfig = { 12 | solidity: "0.8.17", 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | forge-std/=lib/forge-std/src/ 2 | ds-test/=lib/forge-std/lib/ds-test/src/ 3 | src/=src/ 4 | test/=test/ 5 | openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/ 6 | openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ 7 | erc4626-tests/=lib/erc4626-tests/ 8 | prb-math/=lib/prb-math 9 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter_paths": "lib|script|test", 3 | "solc_remaps": ["ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "src/=src/"], 4 | "detectors_to_exclude": "solc-version|naming-convention|calls-loop" 5 | } 6 | -------------------------------------------------------------------------------- /solver/commands/BalancerV2MetaStablePoolAdapter.ts: -------------------------------------------------------------------------------- 1 | import { artifacts } from "hardhat"; 2 | import { BaseAdapter } from "./BaseAdapter"; 3 | 4 | const artifact = artifacts.readArtifactSync("BalancerV2MetaStablePoolAdapter"); 5 | 6 | export class BalancerV2MetaStablePoolAdapter extends BaseAdapter { 7 | /** 8 | * Initializes the BalancerV2MetaStablePoolAdapter with the pool address, ABI, and the structures for the extra parameters 9 | * 10 | * @param address - The address of the pool 11 | */ 12 | constructor(address: string) { 13 | super(address, artifact.abi); 14 | } 15 | 16 | /** 17 | * Provides the structure for the "add liquidity" extra parameters. 18 | */ 19 | get addLiquidityExtraParamsStruct(): string[] { 20 | return ["tuple(address poolAddress, address[] tokens)"]; 21 | } 22 | 23 | /** 24 | * Provides the structure for the "remove liquidity" extra parameters. 25 | */ 26 | get removeLiquidityExtraParamsStruct(): string[] { 27 | return ["tuple(address poolAddress, address[] tokens)"]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /solver/commands/BeethovenAdapter.ts: -------------------------------------------------------------------------------- 1 | import { BalancerV2MetaStablePoolAdapter } from "./BalancerV2MetaStablePoolAdapter"; 2 | 3 | export class BeethovenAdapter extends BalancerV2MetaStablePoolAdapter {} 4 | -------------------------------------------------------------------------------- /solver/commands/CurveV2FactoryCryptoAdapter.ts: -------------------------------------------------------------------------------- 1 | import { artifacts } from "hardhat"; 2 | import { BaseAdapter } from "./BaseAdapter"; 3 | 4 | const artifact = artifacts.readArtifactSync("BalancerV2MetaStablePoolAdapter"); 5 | 6 | export class CurveV2FactoryCryptoAdapter extends BaseAdapter { 7 | /** 8 | * Initializes the CurveV2FactoryCryptoAdapter with the pool address, ABI, and the structures for the extra parameters 9 | * 10 | * @param address - The address of the pool 11 | */ 12 | constructor(address: string) { 13 | super(address, artifact.abi); 14 | } 15 | 16 | /** 17 | * Provides the structure for the "add liquidity" extra parameters. 18 | */ 19 | get addLiquidityExtraParamsStruct(): string[] { 20 | return ["tuple(address poolAddress, address lpToken, bool useEth)"]; 21 | } 22 | 23 | /** 24 | * Provides the structure for the "remove liquidity" extra parameters. 25 | */ 26 | get removeLiquidityExtraParamsStruct(): string[] { 27 | return ["tuple(address poolAddress, address lpToken, bool useEth)"]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /solver/commands/MaverickAdapter.ts: -------------------------------------------------------------------------------- 1 | import { artifacts } from "hardhat"; 2 | import { BaseAdapter } from "./BaseAdapter"; 3 | 4 | const artifact = artifacts.readArtifactSync("IPoolAdapter"); 5 | 6 | export class MaverickAdapter extends BaseAdapter { 7 | /** 8 | * Initializes the MaverickAdapter with the pool address, ABI, and the structures for the extra parameters 9 | * 10 | * @param address - The address of the pool 11 | */ 12 | constructor(address: string) { 13 | super(address, artifact.abi); 14 | } 15 | 16 | /** 17 | * Provides the structure for the "add liquidity" extra parameters. 18 | */ 19 | get addLiquidityExtraParamsStruct(): string[] { 20 | return [ 21 | "tuple(address, uint256, uint256, tuple(uint8 kind, int32 pos, bool isDelta, uint128 deltaA, uint128 deltaB)[] maverickParams)", 22 | ]; 23 | } 24 | 25 | /** 26 | * Provides the structure for the "remove liquidity" extra parameters. 27 | */ 28 | get removeLiquidityExtraParamsStruct(): string[] { 29 | return ["tuple(address, uint256, uint256, tuple(uint128 binId, uint128 amount)[] maverickParams)"]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /solver/commands/VelodromeAdapter.ts: -------------------------------------------------------------------------------- 1 | import { artifacts } from "hardhat"; 2 | import { BaseAdapter } from "./BaseAdapter"; 3 | 4 | const artifact = artifacts.readArtifactSync("VelodromeAdapter"); 5 | 6 | export class VelodromeAdapter extends BaseAdapter { 7 | /** 8 | * Initializes the VelodromeAdapter with the pool address, ABI, and the structures for the extra parameters 9 | * 10 | * @param address - The address of the pool 11 | */ 12 | constructor(address: string) { 13 | super(address, artifact.abi); 14 | } 15 | 16 | /** 17 | * Provides the structure for the "add liquidity" extra parameters. 18 | */ 19 | get addLiquidityExtraParamsStruct(): string[] { 20 | return [ 21 | "tuple(address tokenA, address tokenB, bool stable, uint256 amountAMin, uint256 amountBMin, uint256 deadline)", 22 | ]; 23 | } 24 | 25 | /** 26 | * Provides the structure for the "remove liquidity" extra parameters. 27 | */ 28 | get removeLiquidityExtraParamsStruct(): string[] { 29 | return [ 30 | "tuple(address tokenA, address tokenB, bool stable, uint256 amountAMin, uint256 amountBMin, uint256 deadline)", 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solver/helpers/common.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | 3 | /** 4 | * Converts an object to an array, recursively converting BigNumber values to strings. 5 | * @param obj - The object to convert to an array. 6 | * @returns The converted array. 7 | */ 8 | export function objectToArray(obj: Record): unknown[] { 9 | return Object.values(obj).map((value) => { 10 | if (BigNumber.isBigNumber(value)) { 11 | return value.toString(); 12 | } else if (typeof value === "object" && value !== null) { 13 | return objectToArray(value as Record); 14 | } else { 15 | return value; 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/balancerv2-add-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x9d79722500800182ffffffffcf7ed3acca5a467e9e704c703e8d87f634fb0fc9"], 3 | "state": [ 4 | "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000006f05b59d3b20000", 5 | "0x0000000000000000000000000000000000000000000000000000000000000001", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000009c6d47ff73e0f5e51be5fd53236e3f595c5793f2000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000be9895146f7af43049ca1c1ae358b0541ea49704" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/balancerv2-remove-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x7769e50e00800182ffffffff5fc8d32690cc91d4c39d9d3abcbd16989f875707"], 3 | "state": [ 4 | "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000", 5 | "0x0000000000000000000000000000000000000000000000002c9373c87789adb4", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000009c6d47ff73e0f5e51be5fd53236e3f595c5793f2000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000be9895146f7af43049ca1c1ae358b0541ea49704" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/beethoven-add-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x9d79722500800182ffffffffa513e6e4b8f2a923d98304ec87f64353c4d5c853"], 3 | "state": [ 4 | "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000006f05b59d3b20000", 5 | "0x0000000000000000000000000000000000000000000000000000000000000001", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000007b50775383d3d6f0215a8f290f2c9e2eebbeceb2000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001f32b1c2345538c0c6f582fcb022739c4a194ebb0000000000000000000000004200000000000000000000000000000000000006" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/beethoven-remove-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x7769e50e00800182ffffffff8a791620dd6260079bf849dc5567adc3f2fdc318"], 3 | "state": [ 4 | "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000", 5 | "0x0000000000000000000000000000000000000000000000002bebac527d1030c3", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000007b50775383d3d6f0215a8f290f2c9e2eebbeceb2000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001f32b1c2345538c0c6f582fcb022739c4a194ebb0000000000000000000000004200000000000000000000000000000000000006" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/curvev2-add-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x9d79722500800182ffffffffb7f8bc63bbcad18155201308c8f3540b07f84f5e"], 3 | "state": [ 4 | "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000", 5 | "0x0000000000000000000000000000000000000000000000000000000000000001", 6 | "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000005fae7e604fc3e24fd43a72867cebac94c65b404a0000000000000000000000005b6c539b224014a09b3388e51caaa8e354c959c80000000000000000000000000000000000000000000000000000000000000000" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/curvev2-remove-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x7769e50e00800182ffffffffa51c1fc2f0d1a1b8494ed1fe312d7c3a78ed91c0"], 3 | "state": [ 4 | "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000", 5 | "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", 6 | "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000005fae7e604fc3e24fd43a72867cebac94c65b404a0000000000000000000000005b6c539b224014a09b3388e51caaa8e354c959c80000000000000000000000000000000000000000000000000000000000000000" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/maverick-add-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x9d79722500800182ffffffffe7f1725e7734ce288f8367e1bb143e90bb3f0512"], 3 | "state": [ 4 | "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000", 5 | "0x0000000000000000000000000000000000000000000000000000000000000001", 6 | "0x000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000200000000000000000000000002ebe19aa2e29c8acadb14be3e7de153b0141e2aa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000004563918244f400000000000000000000000000000000000000000000000000004563918244f40000" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/maverick-remove-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x7769e50e00800182ffffffff9fe46736679d2d9a65f0992f2272de9f3c7fa6e0"], 3 | "state": [ 4 | "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000", 5 | "0x0000000000000000000000000000000000000000000000004563918244f40000", 6 | "0x000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000002ebe19aa2e29c8acadb14be3e7de153b0141e2aa0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000004563918244f40000" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/velodrome-add-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x9d79722500800182ffffffff0dcd1bf9a1b36ce34237eeafef220932846bcd82"], 3 | "state": [ 4 | "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000014d1120d7b16000000000000000000000000000000000000000000000000000014d1120d7b160000", 5 | "0x0000000000000000000000000000000000000000000000000000000000000001", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004200000000000000000000000000000000000006000000000000000000000000e405de8f52ba7559f9df3c368500b6e6ae6cee4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000642f01d8" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/test/payloads/adapters/velodrome-remove-liquidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": ["0x7769e50e00800182ffffffff9a676e781a523b5d0c0e43731313a708cb607508"], 3 | "state": [ 4 | "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000", 5 | "0x0000000000000000000000000000000000000000000000000c5edb26e66a0659", 6 | "0x00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004200000000000000000000000000000000000006000000000000000000000000e405de8f52ba7559f9df3c368500b6e6ae6cee4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000642f01d8" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /solver/typings/common.ts: -------------------------------------------------------------------------------- 1 | import { Value } from "@weiroll/weiroll.js"; 2 | 3 | export type FuncArg = T | Value; 4 | -------------------------------------------------------------------------------- /src/SystemComponent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ISystemComponent } from "src/interfaces/ISystemComponent.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | import { Errors } from "src/utils/Errors.sol"; 8 | 9 | contract SystemComponent is ISystemComponent { 10 | ISystemRegistry internal immutable systemRegistry; 11 | 12 | constructor(ISystemRegistry _systemRegistry) { 13 | Errors.verifyNotZero(address(_systemRegistry), "_systemRegistry"); 14 | systemRegistry = _systemRegistry; 15 | } 16 | 17 | function getSystemRegistry() external view returns (address) { 18 | return address(systemRegistry); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/access/Ownable2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Ownable2Step as OZOwnable2Step } from "openzeppelin-contracts/access/Ownable2Step.sol"; 7 | 8 | abstract contract Ownable2Step is OZOwnable2Step { 9 | function renounceOwnership() public view override onlyOwner { 10 | revert("cannot renounce ownership"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/beacon/FrxBeaconChainBacking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20Metadata } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 6 | 7 | import { IBeaconChainBacking } from "src/interfaces/beacon/IBeaconChainBacking.sol"; 8 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 9 | import { SecurityBase } from "src/security/SecurityBase.sol"; 10 | import { SystemComponent } from "src/SystemComponent.sol"; 11 | import { Errors } from "src/utils/Errors.sol"; 12 | import { Roles } from "src/libs/Roles.sol"; 13 | 14 | contract FrxBeaconChainBacking is SystemComponent, SecurityBase, IBeaconChainBacking { 15 | address public immutable token; 16 | uint96 public immutable decimalPad; 17 | 18 | Ratio public currentRatio; 19 | 20 | struct Ratio { 21 | uint208 ratio; 22 | uint48 timestamp; 23 | } 24 | 25 | event RatioUpdated(uint208 ratio, uint208 totalAssets, uint208 totalLiabilities, uint48 timestamp); 26 | 27 | constructor( 28 | ISystemRegistry _systemRegistry, 29 | address _token 30 | ) SystemComponent(_systemRegistry) SecurityBase(address(_systemRegistry.accessController())) { 31 | Errors.verifyNotZero(_token, "_token"); 32 | // slither-disable-next-line missing-zero-check 33 | token = _token; 34 | decimalPad = uint96(10 ** IERC20Metadata(token).decimals()); 35 | } 36 | 37 | /// @inheritdoc IBeaconChainBacking 38 | function update( 39 | uint208 totalAssets, 40 | uint208 totalLiabilities, 41 | uint48 queriedTimestamp 42 | ) public hasRole(Roles.LSD_BACKING_UPDATER) { 43 | Errors.verifyNotZero(totalAssets, "totalAssets"); 44 | Errors.verifyNotZero(totalLiabilities, "totalLiabilities"); 45 | 46 | if (queriedTimestamp < currentRatio.timestamp) { 47 | revert Errors.InvalidParam("queriedTimestamp"); 48 | } 49 | uint208 ratio = totalAssets * decimalPad / totalLiabilities; 50 | currentRatio = Ratio(ratio, queriedTimestamp); 51 | 52 | emit RatioUpdated(ratio, totalAssets, totalLiabilities, queriedTimestamp); 53 | } 54 | 55 | /// @inheritdoc IBeaconChainBacking 56 | function current() external view returns (uint208 ratio, uint48 queriedTimestamp) { 57 | ratio = currentRatio.ratio; 58 | queriedTimestamp = currentRatio.timestamp; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/destinations/adapters/README.md: -------------------------------------------------------------------------------- 1 | # Tokemak Destination Adapters Docs 2 | 3 | ## Destination Adapters 4 | 5 | - Destination Adapters are basically the libraries used by Destination Vaults and potentially in other parts of system for managing liquidity deployment/withdrawal process and claiming rewards from staking. 6 | 7 | - Each Adapter targets specific exchange and aims to support one of the three basic operations: to provide liquidity, withdraw or manage the rewards (claiming). 8 | 9 | - With the simple interface and very low knowledge of the details of particular exchange it's possible to easily achieve those operations and target liquidity to a needed space. 10 | 11 | - They also responsible for validating the calculations of received asset deltas after the operation, but do not handle any system-wide numbers and limited to their scope, so they do not persist any state and all the operations are atomic (some exchanges require to hold NFTs representing the position in the protocol, but that do not affects Adapters executions). 12 | -------------------------------------------------------------------------------- /src/destinations/adapters/rewards/AuraRewardsAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ConvexRewards } from "src/destinations/adapters/rewards/ConvexRewardsAdapter.sol"; 6 | 7 | /** 8 | * @title AuraAdapter 9 | * @dev This contract implements an adapter for interacting with Aura's reward system. 10 | * We're using a Convex Adapter as Aura uses the Convex interfaces for LPs staking. 11 | */ 12 | //slither-disable-next-line missing-inheritance 13 | library AuraRewards { 14 | /// @notice Claim rewards for Aura staked LP tokens 15 | /// @param gauge the reward contract in Aura 16 | /// @param defaultToken the reward token always provided. AURA for Aura 17 | /// @param sendTo the destination of the rewarded tokens 18 | /// @return amounts the amount of each token that was claimed 19 | /// @return tokens the tokens that were claimed 20 | function claimRewards( 21 | address gauge, 22 | address defaultToken, 23 | address sendTo 24 | ) public returns (uint256[] memory amounts, address[] memory tokens) { 25 | (amounts, tokens) = ConvexRewards.claimRewards(gauge, defaultToken, sendTo); 26 | } 27 | 28 | /// @notice Claim rewards for Aura staked LP tokens 29 | /// @param gauge the reward contract in Aura 30 | /// @param defaultToken the reward token always provided. AURA for Aura 31 | /// @return amounts the amount of each token that was claimed 32 | /// @return tokens the tokens that were claimed 33 | function claimRewards( 34 | address gauge, 35 | address defaultToken 36 | ) public returns (uint256[] memory amounts, address[] memory tokens) { 37 | (amounts, tokens) = ConvexRewards.claimRewards(gauge, defaultToken, address(this)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/destinations/adapters/rewards/ConvexArbitrumRewardsAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | import { ReentrancyGuard } from "openzeppelin-contracts/security/ReentrancyGuard.sol"; 7 | 8 | import { IConvexRewardPool, RewardType } from "src/interfaces/external/convex/IConvexRewardPool.sol"; 9 | import { IClaimableRewardsAdapter } from "src/interfaces/destinations/IClaimableRewardsAdapter.sol"; 10 | 11 | contract ConvexArbitrumRewardsAdapter is IClaimableRewardsAdapter, ReentrancyGuard { 12 | /** 13 | * @param gauge The gauge to claim rewards from 14 | */ 15 | function claimRewards(address gauge) public nonReentrant returns (uint256[] memory, IERC20[] memory) { 16 | if (gauge == address(0)) revert TokenAddressZero(); 17 | 18 | address account = address(this); 19 | 20 | IConvexRewardPool rewardPool = IConvexRewardPool(gauge); 21 | uint256 rewardsLength = rewardPool.rewardLength(); 22 | 23 | uint256[] memory balancesBefore = new uint256[](rewardsLength); 24 | uint256[] memory amountsClaimed = new uint256[](rewardsLength); 25 | IERC20[] memory rewardTokens = new IERC20[](rewardsLength); 26 | 27 | // get balances before 28 | for (uint256 i = 0; i < rewardsLength; ++i) { 29 | RewardType memory rewardType = rewardPool.rewards(i); 30 | IERC20 token = IERC20(rewardType.reward_token); 31 | rewardTokens[i] = token; 32 | balancesBefore[i] = token.balanceOf(account); 33 | } 34 | // TODO: Check if it mints CVX by default 35 | 36 | // claim rewards 37 | rewardPool.getReward(account); 38 | 39 | // get balances after and calculate amounts claimed 40 | for (uint256 i = 0; i < rewardsLength; ++i) { 41 | uint256 balance = rewardTokens[i].balanceOf(account); 42 | amountsClaimed[i] = balance - balancesBefore[i]; 43 | } 44 | 45 | emit RewardsClaimed(rewardTokens, amountsClaimed); 46 | 47 | return (amountsClaimed, rewardTokens); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/destinations/adapters/rewards/RewardAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | /// @title Common functionality for reward adapter libraries 6 | library RewardAdapter { 7 | error ClaimRewardsFailed(); 8 | 9 | event RewardsClaimed(address[] tokens, uint256[] amounts); 10 | 11 | /// @notice Emit RewardsClaimed(address[],uint256[]) event common to all reward claim libraries 12 | /// @param tokens reward token addresses claimed 13 | /// @param amounts amounts of each token claimed 14 | function emitRewardsClaimed(address[] memory tokens, uint256[] memory amounts) internal { 15 | emit RewardsClaimed(tokens, amounts); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/destinations/adapters/staking/AuraAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { ConvexStaking } from "./ConvexAdapter.sol"; 7 | import { IConvexBooster } from "src/interfaces/external/convex/IConvexBooster.sol"; 8 | 9 | /** 10 | * @title AuraAdapter 11 | * @dev This contract implements an adapter for interacting with Aura's reward system. 12 | * We're using a Convex Adapter as Aura uses the Convex interfaces for LPs staking. 13 | */ 14 | library AuraStaking { 15 | /** 16 | * @notice Deposits and stakes Curve LP tokens to Convex 17 | * @dev Calls to external contract 18 | * @param booster Convex Booster address 19 | * @param lpToken Curve LP token to deposit 20 | * @param staking Convex reward contract associated with the Curve LP token 21 | * @param poolId Convex poolId for the associated Curve LP token 22 | * @param amount Quantity of Curve LP token to deposit and stake 23 | */ 24 | function depositAndStake( 25 | IConvexBooster booster, 26 | address lpToken, 27 | address staking, 28 | uint256 poolId, 29 | uint256 amount 30 | ) public { 31 | ConvexStaking.depositAndStake(booster, lpToken, staking, poolId, amount); 32 | } 33 | 34 | /** 35 | * @notice Withdraws a Curve LP token from Convex 36 | * @dev Does not claim available rewards 37 | * @dev Calls to external contract 38 | * @param lpToken Curve LP token to withdraw 39 | * @param staking Convex reward contract associated with the Curve LP token 40 | * @param amount Quantity of Curve LP token to withdraw 41 | */ 42 | function withdrawStake(address lpToken, address staking, uint256 amount) public { 43 | ConvexStaking.withdrawStake(lpToken, staking, amount); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/interfaces/ISystemComponent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | /// @notice Stores a reference to the registry for this system 6 | interface ISystemComponent { 7 | /// @notice The system instance this contract is tied to 8 | function getSystemRegistry() external view returns (address registry); 9 | } 10 | -------------------------------------------------------------------------------- /src/interfaces/beacon/IBeaconChainBacking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | /** 7 | * Most LSD tokens report the amount of funds they have staked on the Beacon Chain back to Mainnet periodically. 8 | * FrxETH does not. This value is needed for various calculations and so we will run a backend service to keep this 9 | * value up to date. This contract should track this value and the last time it was set. 10 | */ 11 | interface IBeaconChainBacking { 12 | /** 13 | * @notice Saves the most recent ratio and it's timestamp into the contract storage 14 | * @dev Calculates `ratio` with `totalAssets * decimalPad / totalLiabilities` formula 15 | * @param totalAssets Amount of protocol's funds 16 | * @param totalLiabilities Amount of protocol's liability funds 17 | * @param queriedTimestamp Time-point when the value is actual 18 | */ 19 | function update(uint208 totalAssets, uint208 totalLiabilities, uint48 queriedTimestamp) external; 20 | 21 | /** 22 | * @notice Returns the most recent ratio and it's timestamp 23 | * @return ratio Amount of funds staked on the Beacon Chain 24 | * @return queriedTimestamp Last time the `ratio` was set 25 | */ 26 | function current() external view returns (uint208 ratio, uint48 queriedTimestamp); 27 | } 28 | -------------------------------------------------------------------------------- /src/interfaces/destinations/IClaimableRewardsAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 7 | import { IDestinationAdapter } from "./IDestinationAdapter.sol"; 8 | 9 | /** 10 | * @dev This interface is intended to be used with contracts that are meant to be delegate called. 11 | * Contracts that inherit from this interface should not have any state variables. 12 | */ 13 | interface IClaimableRewardsAdapter is IDestinationAdapter { 14 | error ClaimRewardsFailed(); 15 | error TokenAddressZero(); 16 | 17 | /** 18 | * @dev Emitted when rewards are claimed. 19 | * @param rewardTokens The tokens received as rewards. 20 | * @param amountsClaimed The amounts of each token claimed. 21 | */ 22 | event RewardsClaimed(IERC20[] rewardTokens, uint256[] amountsClaimed); 23 | 24 | /** 25 | * @notice Claim rewards for a given token and account 26 | * @param gauge The address of the token to claim rewards for 27 | * @return amountsClaimed The amounts of rewards claimed 28 | * @return tokens The tokens that rewards were claimed for 29 | */ 30 | function claimRewards(address gauge) external returns (uint256[] memory, IERC20[] memory); 31 | } 32 | -------------------------------------------------------------------------------- /src/interfaces/destinations/IDestinationAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | /** 5 | * @title IDestinationAdapter 6 | * @dev This is a super-interface to unify different types of adapters to be registered in Destination Registry. 7 | * Specific interface type is defined by extending from this interface. 8 | */ 9 | interface IDestinationAdapter { 10 | error MustBeMoreThanZero(); 11 | error ArraysLengthMismatch(); 12 | error BalanceMustIncrease(); 13 | error MinLpAmountNotReached(); 14 | error LpTokenAmountMismatch(); 15 | error NoNonZeroAmountProvided(); 16 | error InvalidBalanceChange(); 17 | error InvalidAddress(address); 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/destinations/IPoolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IDestinationAdapter } from "./IDestinationAdapter.sol"; 5 | 6 | /** 7 | * @title IPoolAdapter 8 | * @dev This is an interface to mark adapters as ones that are dedicated for liquidity deployment/withdrawal 9 | * to/from pools of a different protocols and to be registered in Destination Registry. 10 | * It shares unified functions to implement with basic params and `extraParams` 11 | * which can contain protocol or pool-specific params. 12 | */ 13 | interface IPoolAdapter is IDestinationAdapter { 14 | /** 15 | * @notice Deploy liquidity to the associated destination 16 | * @dev Calls into external contract 17 | * @param amounts Amounts of corresponding tokens to deploy 18 | * @param minLpMintAmount Min amount of LP tokens to mint in the deployment 19 | * @param extraParams Encoded params that are specific to the given destination 20 | */ 21 | function addLiquidity(uint256[] calldata amounts, uint256 minLpMintAmount, bytes calldata extraParams) external; 22 | 23 | /** 24 | * @notice Withdraw liquidity from the associated destination 25 | * @dev Calls into external contract 26 | * @param amounts Amounts of corresponding tokens to withdraw 27 | * @param maxLpBurnAmount Max amount of LP tokens to burn in the withdrawal 28 | * @param extraParams Encoded params that are specific to the given destination 29 | * @return actualAmounts Amounts of tokens that were actually withdrawn 30 | */ 31 | function removeLiquidity( 32 | uint256[] calldata amounts, 33 | uint256 maxLpBurnAmount, 34 | bytes calldata extraParams 35 | ) external returns (uint256[] memory actualAmounts); 36 | } 37 | -------------------------------------------------------------------------------- /src/interfaces/destinations/IStakingAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IDestinationAdapter } from "./IDestinationAdapter.sol"; 6 | 7 | /** 8 | * @title IStakingAdapter 9 | * @dev This is an interface to mark adapters as ones that are dedicated for LPs staking and to be registered in 10 | * Destination Registry. It doesn't contain any functions as staking interfaces usually are protocol-specific. 11 | */ 12 | interface IStakingAdapter is IDestinationAdapter { } 13 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IAsset.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | pragma solidity 0.8.17; 4 | 5 | /** 6 | * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero 7 | * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like 8 | * types. 9 | * 10 | * This concept is unrelated to a Pool's Asset Managers. 11 | */ 12 | interface IAsset { 13 | // solhint-disable-previous-line no-empty-blocks 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IBalancerComposableStablePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IBalancerComposableStablePool { 7 | function getActualSupply() external view returns (uint256); 8 | 9 | function getBptIndex() external view returns (uint256); 10 | 11 | function getPoolId() external view returns (bytes32); 12 | 13 | function getRate() external view returns (uint256); 14 | 15 | function getTokenRate(IERC20 token) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IBalancerMetaStablePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IRateProvider } from "src/interfaces/external/balancer/IRateProvider.sol"; 5 | 6 | interface IBalancerMetaStablePool { 7 | function getPoolId() external view returns (bytes32); 8 | 9 | function getRate() external view returns (uint256); 10 | 11 | function getLastInvariant() external view returns (uint256); 12 | 13 | function getLatest(uint8 x) external view returns (uint256); 14 | 15 | function totalSupply() external view returns (uint256); 16 | 17 | function getSwapFeePercentage() external view returns (uint256); 18 | 19 | function getRateProviders() external view returns (IRateProvider[] memory); 20 | } 21 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IBalancerPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20Metadata, IERC20 } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 5 | 6 | interface IBalancerPool is IERC20Metadata { 7 | /// @notice returns total supply of Balancer pool 8 | function totalSupply() external view returns (uint256); 9 | 10 | /** 11 | * @notice gets Balancer poolId 12 | * @return bytes32 poolId 13 | */ 14 | function getPoolId() external view returns (bytes32); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IBasePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | pragma solidity 0.8.17; 4 | 5 | interface IBasePool { 6 | /// @notice Returns the pool ID 7 | function getPoolId() external view returns (bytes32); 8 | } 9 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IProtocolFeesCollector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IProtocolFeesCollector { 5 | function getSwapFeePercentage() external view returns (uint256); 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/external/balancer/IRateProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | // This program is free software: you can redistribute it and/or modify 3 | // it under the terms of the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or 5 | // (at your option) any later version. 6 | 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU General Public License for more details. 11 | 12 | // You should have received a copy of the GNU General Public License 13 | // along with this program. If not, see . 14 | pragma solidity 0.8.17; 15 | 16 | interface IRateProvider { 17 | function getRate() external view returns (uint256); 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/external/beethoven/IChildChainGaugeRewardHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IRewardsOnlyGauge } from "./IRewardsOnlyGauge.sol"; 5 | 6 | // solhint-disable max-line-length 7 | /** 8 | * @dev based on the following abi 9 | * https://github.com/beethovenxfi/beets-frontend/blob/064f86d5d326f6fc4db13a320b923565a4fcfbfb/lib/abi/ChildChainGaugeRewardHelper.json 10 | */ 11 | interface IChildChainGaugeRewardHelper { 12 | /// @notice Claim rewards from a gauge 13 | function claimRewards(IRewardsOnlyGauge gauge, address user) external; 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/external/beethoven/IChildChainStreamer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | // solhint-disable func-name-mixedcase 7 | // slither-disable-start naming-convention 8 | 9 | /** 10 | * @title Child-Chain Streamer 11 | * @author Curve.Fi 12 | * @notice Evenly streams one or more reward tokens to a single recipient 13 | */ 14 | 15 | interface IChildChainStreamer { 16 | /// @notice The count of reward tokens 17 | function reward_count() external view returns (uint256); 18 | 19 | /// @notice The address of the reward token at a given index 20 | function reward_tokens(uint256 index) external view returns (IERC20); 21 | } 22 | // slither-disable-end naming-convention 23 | -------------------------------------------------------------------------------- /src/interfaces/external/beethoven/IRewardsOnlyGauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IChildChainStreamer } from "./IChildChainStreamer.sol"; 5 | 6 | // solhint-disable func-name-mixedcase 7 | // slither-disable-start naming-convention 8 | interface IRewardsOnlyGauge { 9 | /// @notice The address of the reward token 10 | function reward_contract() external view returns (IChildChainStreamer); 11 | 12 | function claimable_reward(address _addr, address _token) external view returns (uint256); 13 | } 14 | // slither-disable-end naming-convention 15 | -------------------------------------------------------------------------------- /src/interfaces/external/camelot/ICamelotPair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface ICamelotPair { 5 | /// @notice Gets reserves of Camelot pool. 6 | // slither-disable-start similar-names 7 | function getReserves() 8 | external 9 | view 10 | returns (uint112 _reserve0, uint112 _reserve1, uint16 _token0FeePercent, uint16 _token1FeePercent); 11 | // slither-disable-end similar-names 12 | } 13 | -------------------------------------------------------------------------------- /src/interfaces/external/camelot/INFTHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface INFTHandler { 5 | function onNFTHarvest( 6 | address operator, 7 | address to, 8 | uint256 tokenId, 9 | uint256 grailAmount, 10 | uint256 xGrailAmount 11 | ) external returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /src/interfaces/external/camelot/INFTPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC721Enumerable } from "openzeppelin-contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 5 | 6 | interface INFTPool is IERC721Enumerable { 7 | function exists(uint256 tokenId) external view returns (bool); 8 | function hasDeposits() external view returns (bool); 9 | function getPoolInfo() 10 | external 11 | view 12 | returns ( 13 | address lpToken, 14 | address grailToken, 15 | address sbtToken, 16 | uint256 lastRewardTime, 17 | uint256 accRewardsPerShare, 18 | uint256 lpSupply, 19 | uint256 lpSupplyWithMultiplier, 20 | uint256 allocPoint 21 | ); 22 | function getStakingPosition(uint256 tokenId) 23 | external 24 | view 25 | returns ( 26 | uint256 amount, 27 | uint256 amountWithMultiplier, 28 | uint256 startLockTime, 29 | uint256 lockDuration, 30 | uint256 lockMultiplier, 31 | uint256 rewardDebt, 32 | uint256 boostPoints, 33 | uint256 totalMultiplier 34 | ); 35 | 36 | function boost(uint256 userAddress, uint256 amount) external; 37 | function unboost(uint256 userAddress, uint256 amount) external; 38 | 39 | /** 40 | * @dev Harvest from a staking position 41 | * 42 | * Can only be called by spNFT's owner or approved address 43 | */ 44 | function harvestPosition(uint256 tokenId) external; 45 | 46 | /** 47 | * @dev Harvest from a staking position to "to" address 48 | * 49 | * Can only be called by spNFT's owner or approved address 50 | * spNFT's owner must be a contract 51 | */ 52 | function harvestPositionTo(uint256 tokenId, address to) external; 53 | } 54 | -------------------------------------------------------------------------------- /src/interfaces/external/camelot/INitroPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC721Enumerable } from "openzeppelin-contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 5 | 6 | interface INitroPool is IERC721Enumerable { 7 | /// @notice Returns the address of the rewards token 1 8 | function rewardsToken1() external view returns (address); 9 | 10 | /// @notice Returns the address of the rewards token 2 11 | function rewardsToken2() external view returns (address); 12 | 13 | /// @notice Harvest pending NitroPool rewards 14 | function harvest() external; 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/chainlink/IAggregatorV3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IAggregatorV3Interface { 5 | /// @notice Returns decimal precision of price feed. 6 | function decimals() external view returns (uint8); 7 | 8 | /** 9 | * @return roundId The round Id. 10 | * @return answer The price. 11 | * @return startedAt Timestamp when the round started. 12 | * @return updatedAt Timestamp when the round ended. 13 | * @return answeredInRound Round Id of round when answer was computed. 14 | */ 15 | function latestRoundData() 16 | external 17 | view 18 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); 19 | } 20 | -------------------------------------------------------------------------------- /src/interfaces/external/coinbase/IStakedTokenV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { Stats } from "src/stats/Stats.sol"; 5 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 6 | 7 | interface IStakedTokenV1 { 8 | function exchangeRate() external view returns (uint256 _exchangeRate); 9 | } 10 | -------------------------------------------------------------------------------- /src/interfaces/external/convex/IBaseRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IBaseRewardPool { 7 | /// @notice The address of the reward token 8 | function rewardToken() external view returns (IERC20); 9 | 10 | /// @notice The length of the extra rewards array 11 | function extraRewardsLength() external view returns (uint256); 12 | 13 | /// @notice The pool PID 14 | function pid() external view returns (uint256); 15 | 16 | /// @notice The address of the extra rewards token at a given index 17 | function extraRewards(uint256 i) external view returns (address); 18 | 19 | /// @notice Called by a staker to get their allocated rewards 20 | function getReward() external returns (bool); 21 | 22 | /// @notice Gives a staker their rewards, with the option of claiming extra rewards 23 | function getReward(address _account, bool _claimExtras) external returns (bool); 24 | 25 | /// @notice Get total rewards supply 26 | function totalSupply() external returns (uint256); 27 | 28 | /// @notice Get balance of an address 29 | function balanceOf(address _account) external view returns (uint256); 30 | 31 | /// @notice Withdraw directly to curve LP token 32 | function withdrawAndUnwrap(uint256 _amount, bool _claim) external returns (bool); 33 | } 34 | -------------------------------------------------------------------------------- /src/interfaces/external/convex/IConvexBooster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | /// @notice main Convex contract(booster.sol) basic interface 5 | interface IConvexBooster { 6 | /// @notice deposit into convex, receive a tokenized deposit. parameter to stake immediately 7 | function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool); 8 | 9 | /// @notice get poolInfo for a poolId 10 | function poolInfo(uint256 _pid) 11 | external 12 | view 13 | returns (address lptoken, address token, address gauge, address crvRewards, address stash, bool shutdown); 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/external/convex/IConvexBoosterArbitrum.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | /// @notice main Convex contract(booster.sol) basic interface 5 | interface IConvexBoosterArbitrum { 6 | /// @notice deposit into convex, receive a tokenized deposit 7 | function deposit(uint256 _pid, uint256 _amount) external returns (bool); 8 | } 9 | -------------------------------------------------------------------------------- /src/interfaces/external/convex/IConvexRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable var-name-mixedcase 5 | struct RewardType { 6 | address reward_token; 7 | uint128 reward_integral; 8 | uint128 reward_remaining; 9 | } 10 | 11 | interface IConvexRewardPool { 12 | /// @notice the address of the gauge 13 | function curveGauge() external view returns (address); 14 | 15 | /// @notice update and claim rewards from all locations 16 | function rewardLength() external view returns (uint256); 17 | 18 | /// @notice get the reward token address 19 | function rewards(uint256 i) external view returns (RewardType memory); 20 | 21 | /// @notice claim reward for given account (unguarded) 22 | function getReward(address _account) external; 23 | 24 | /// @notice convex pool id 25 | function convexPoolId() external view returns (uint256); 26 | } 27 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICryptoSwapPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IPool } from "./IPool.sol"; 5 | 6 | /* solhint-disable func-name-mixedcase, var-name-mixedcase */ 7 | interface ICryptoSwapPool is IPool { 8 | function token() external returns (address); 9 | 10 | // slither-disable-start naming-convention 11 | function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount) external payable returns (uint256); 12 | 13 | function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount) external payable returns (uint256); 14 | 15 | function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount) external payable returns (uint256); 16 | 17 | function remove_liquidity(uint256 amount, uint256[2] memory min_amounts) external; 18 | 19 | function remove_liquidity(uint256 amount, uint256[3] memory min_amounts) external; 20 | 21 | function remove_liquidity(uint256 amount, uint256[4] memory min_amounts) external; 22 | 23 | function remove_liquidity_one_coin(uint256 token_amount, uint256 i, uint256 min_amount) external; 24 | // slither-disable-end naming-convention 25 | 26 | function get_virtual_price() external returns (uint256); 27 | 28 | function claim_admin_fees() external; 29 | } 30 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveFactoryV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase, var-name-mixedcase 5 | 6 | /** 7 | * @notice Curve factory for V2 contracts. 8 | */ 9 | interface ICurveFactoryV2 { 10 | /// @notice Gets coin addresses for pool deployed by factory. 11 | function get_coins(address pool) external view returns (address[2] memory); 12 | 13 | /// @notice Gets balances of coins for pool deployed by factory. 14 | function get_balances(address pool) external view returns (uint256[2] memory); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveMetaPoolFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | /** 6 | * @notice This interface is used for interacting with the older version of the Curve factory at address 7 | * `0x0959158b6040D32d04c301A72CBFD6b39E21c9AE`. This factory can only be used to deploy metapools. 8 | */ 9 | interface ICurveMetaPoolFactory { 10 | /// @notice Gets coin addresses for pool deployed by factory. 11 | function get_coins(address pool) external view returns (address[2] memory); 12 | 13 | /// @notice Gets balances of coins for pool deployed by factory. 14 | function get_balances(address pool) external view returns (uint256[2] memory); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveMetaRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase, var-name-mixedcase 5 | // slither-disable-start naming-convention 6 | interface ICurveMetaRegistry { 7 | /// @notice Get the coins within a pool 8 | /// @dev For metapools, these are the wrapped coin addresses 9 | /// @param _pool Pool address 10 | /// @return List of coin addresses 11 | function get_coins(address _pool) external view returns (address[8] memory); 12 | 13 | /// @notice Get the coins within a pool 14 | /// @dev For metapools, these are the wrapped coin addresses 15 | /// @param _pool Pool address 16 | /// @param _handler_id id of registry handler 17 | /// @return List of coin addresses 18 | function get_coins(address _pool, uint256 _handler_id) external view returns (address[8] memory); 19 | 20 | /// @notice Get the number of coins in a pool 21 | /// @dev For metapools, it is tokens + wrapping/lending token (no underlying) 22 | /// @param _pool Pool address 23 | /// @return Number of coins 24 | function get_n_coins(address _pool) external view returns (uint256); 25 | 26 | /// @notice Get the number of coins in a pool 27 | /// @dev For metapools, it is tokens + wrapping/lending token (no underlying) 28 | /// @param _pool Pool address 29 | /// @param _handler_id Id of the registry to check 30 | /// @return Number of coins 31 | function get_n_coins(address _pool, uint256 _handler_id) external view returns (uint256); 32 | 33 | /// @notice Get the address of the LP token of a pool 34 | /// @param _pool Pool address 35 | /// @return Address of the LP token 36 | function get_lp_token(address _pool) external view returns (address); 37 | 38 | /// @notice Get the address of the LP token of a pool 39 | /// @param _pool Pool address 40 | /// @param _handler_id id of registry handler 41 | /// @return Address of the LP token 42 | function get_lp_token(address _pool, uint256 _handler_id) external view returns (address); 43 | } 44 | // slither-disable-end naming-convention 45 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveMetaStableFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | 6 | /** 7 | * @notice Interface used for newer version of Curve factory at address `0xB9fC157394Af804a3578134A6585C0dc9cc990d4`. 8 | * This factory used to deploy both stableswap and meta pools. 9 | */ 10 | interface ICurveMetaStableFactory { 11 | /// @notice Gets coin addresses for pool deployed by factory. 12 | function get_coins(address pool) external view returns (address[4] memory); 13 | 14 | /// @notice Gets balances of coins for pool deployed by factory. 15 | function get_balances(address pool) external view returns (uint256[4] memory); 16 | 17 | /// @notice Returns number of coins in pool. 18 | function get_n_coins(address pool) external view returns (uint256); 19 | } 20 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveOwner.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase, var-name-mixedcase 5 | // slither-disable-start naming-convention 6 | interface ICurveOwner { 7 | function withdraw_admin_fees(address _pool) external; 8 | } 9 | // slither-disable-end naming-convention 10 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | 6 | interface ICurveRegistry { 7 | /** 8 | * @notice Gets lp token address for Curve pool. 9 | * @param pool Address of pool to get lp token for. 10 | */ 11 | function get_lp_token(address pool) external view returns (address); 12 | 13 | /** 14 | * @notice Gets number of coins in pool. 15 | * @dev First value is coins in metaPool, second is including underlying pool. 16 | * @dev Each array value will be the same when pool is not meta or lending pool. 17 | * @param pool Address of pool to get coins for. 18 | */ 19 | function get_n_coins(address pool) external view returns (uint256[2] memory); 20 | 21 | /** 22 | * @notice Gets array of all coins in a pool. 23 | * @dev Will return address(0) for array slots that are not filled. 24 | * @param pool Address of pool to get coins for. 25 | */ 26 | function get_coins(address pool) external view returns (address[8] memory); 27 | 28 | /** 29 | * @notice Returns balances for pool tokens. 30 | * @dev Will returns zero for unused array slots. 31 | * @param pool Address of pool to get balances for. 32 | */ 33 | function get_balances(address pool) external view returns (uint256[8] memory); 34 | 35 | /** 36 | * @notice Gets pool address given lp token address. 37 | * @param lpToken Address of lp token being used to get pool address. 38 | */ 39 | function get_pool_from_lp_token(address lpToken) external view returns (address); 40 | } 41 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveRegistryV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | 6 | interface ICurveRegistryV2 { 7 | /** 8 | * @notice Gets address of lp token for pool. 9 | * @param pool Address of pool to get lp token for. 10 | */ 11 | function get_lp_token(address pool) external view returns (address); 12 | 13 | /** 14 | * Gets number of coins in a Curve pool. 15 | * @param pool Address of pool to get number of coins for. 16 | */ 17 | function get_n_coins(address pool) external view returns (uint256); 18 | 19 | /** 20 | * @notice Gets array of addresses of coins in pool. 21 | * @dev Will have address(0) in unused array slots. 22 | * @param pool Address of pool to get coins for. 23 | */ 24 | function get_coins(address pool) external view returns (address[8] memory); 25 | 26 | /** 27 | * @notice Returns balances for requested pool. 28 | * @dev Unused array slots will contain 0 value. 29 | * @param pool Address of pool to get balances for. 30 | */ 31 | function get_balances(address pool) external view returns (uint256[8] memory); 32 | 33 | /** 34 | * @notice Returns address of pool associated with lp token. 35 | * @param pool Address of pool. 36 | */ 37 | function get_pool_from_lp_token(address pool) external view returns (address); 38 | } 39 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveStableSwapNG.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | 6 | /// @notice Interface for next generation Curve pool oracle functionality. 7 | interface ICurveStableSwapNG { 8 | /// @notice Returns current price in pool. 9 | function price_oracle() external view returns (uint256); 10 | } 11 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveTokenV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase, var-name-mixedcase 5 | /// @notice Works with Curve lp tokens version 2 and above, v1 does not expose a public `minter()` method. 6 | interface ICurveTokenV2 { 7 | /// @notice Returns address of minter, which is the pool 8 | function minter() external view returns (address); 9 | } 10 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveV1StableSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase, var-name-mixedcase 5 | // slither-disable-start naming-convention 6 | interface ICurveV1StableSwap { 7 | function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount) external payable; 8 | 9 | function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount) external payable; 10 | 11 | function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount) external payable; 12 | 13 | function remove_liquidity(uint256 amount, uint256[2] memory min_amounts) external; 14 | 15 | function remove_liquidity(uint256 amount, uint256[3] memory min_amounts) external; 16 | 17 | function remove_liquidity(uint256 amount, uint256[4] memory min_amounts) external; 18 | 19 | function remove_liquidity_one_coin(uint256 token_amount, uint256 i, uint256 min_amount) external; 20 | 21 | function calc_withdraw_one_coin(uint256 token_amount, uint256 i) external returns (uint256); 22 | 23 | function coins(uint256 i) external view returns (address); 24 | 25 | function balanceOf(address account) external returns (uint256); 26 | 27 | function exchange( 28 | int128 sellTokenIndex, 29 | int128 buyTokenIndex, 30 | uint256 sellAmount, 31 | uint256 minBuyAmount 32 | ) external payable returns (uint256); 33 | 34 | function get_virtual_price() external view returns (uint256); 35 | 36 | function withdraw_admin_fees() external; 37 | 38 | function owner() external view returns (address); 39 | } 40 | // slither-disable-end naming-convention 41 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ICurveV2Swap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface ICurveV2Swap { 5 | function coins(uint256 i) external view returns (address); 6 | 7 | function exchange( 8 | uint256 sellTokenIndex, 9 | uint256 buyTokenIndex, 10 | uint256 sellAmount, 11 | uint256 minBuyAmount 12 | ) external payable returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/ILiquidityGaugeV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | // slither-disable-start naming-convention 6 | interface ILiquidityGaugeV2 { 7 | /// @notice the address of the reward contract 8 | function reward_contract() external view returns (address); 9 | 10 | /// @notice get the reward token address 11 | function reward_tokens(uint256 i) external view returns (address); 12 | 13 | /// @notice claim available reward tokens for `_account` 14 | function claim_rewards(address _account) external; 15 | 16 | /// @notice deposit `_value` LP tokens to the gauge 17 | function deposit(uint256 _value, address _addr) external; 18 | } 19 | // slither-disable-end naming-convention 20 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/IPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | //slither-disable-next-line name-reused 5 | interface IPool { 6 | function coins(uint256 i) external view returns (address); 7 | 8 | function balances(uint256 i) external view returns (uint256); 9 | 10 | // These method used for cases when Pool is a LP token at the same time 11 | function balanceOf(address account) external returns (uint256); 12 | 13 | // These method used for cases when Pool is a LP token at the same time 14 | function totalSupply() external returns (uint256); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/curve/IStakingRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IStakingRewards { 7 | function rewardsToken() external view returns (IERC20); 8 | } 9 | -------------------------------------------------------------------------------- /src/interfaces/external/frax/ISfrxEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface ISfrxEth { 5 | /// @notice Returns price of sfrxEth in frxEth. 6 | function pricePerShare() external view returns (uint256); 7 | 8 | /// @notice Represents frxETH 9 | function asset() external view returns (address); 10 | } 11 | -------------------------------------------------------------------------------- /src/interfaces/external/lido/IstEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IstEth { 5 | function submit(address _referral) external payable returns (uint256); 6 | 7 | function decimals() external view returns (uint8); 8 | 9 | /** 10 | * @notice Gets the amount of underlyer ETH by share 11 | * @dev returns answer in 18 decimals of precision. 12 | */ 13 | function getPooledEthByShares(uint256) external view returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/external/lido/IwstEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IwstEth { 5 | /// @notice Gets the amount of wstEth tokens per stEth token. 6 | /// @return amount of wstETH for a 1 stETH 7 | function tokensPerStEth() external view returns (uint256); 8 | 9 | /// @notice Get amount of stETH for a one wstETH 10 | /// @return amount of stETH for 1 wstETH 11 | function stEthPerToken() external view returns (uint256); 12 | 13 | /// @notice Returns address of stEth contract. 14 | function stETH() external view returns (address); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/maverick/IPoolPositionDynamicSlim.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | /// @notice Maverick boosted position. 7 | interface IPoolPositionDynamicSlim is IERC20 { 8 | /// @notice Gets token reserves in boosted position. 9 | function getReserves() external returns (uint256 reserveA, uint256 reserveB); 10 | 11 | /// @notice Pool that boosted position is for. 12 | function pool() external returns (address maverickPool); 13 | 14 | /// @notice Returns an array of all binIds that boosted position is active for. 15 | function allBinIds() external returns (uint128[] memory); 16 | } 17 | -------------------------------------------------------------------------------- /src/interfaces/external/maverick/IPosition.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC721Enumerable } from "openzeppelin-contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 5 | import { IPositionMetadata } from "./IPositionMetadata.sol"; 6 | 7 | interface IPosition is IERC721Enumerable { 8 | event SetMetadata(IPositionMetadata metadata); 9 | 10 | /// @notice mint new position NFT 11 | function mint(address to) external returns (uint256 tokenId); 12 | 13 | function tokenOfOwnerByIndexExists(address owner, uint256 index) external view returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/external/maverick/IPositionMetadata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IPositionMetadata { 5 | function tokenURI(uint256 tokenId) external view returns (string memory); 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/external/rocket-pool/IRocketOvmPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | /// @title Gets price of rEth from L1 Ethereum 5 | interface IRocketOvmPriceOracle { 6 | /** 7 | * @notice Returns rate of rEth to Eth 8 | * @dev Always returns price with Eth denomination, 18 decimals of precision 9 | */ 10 | function rate() external view returns (uint256); 11 | } 12 | -------------------------------------------------------------------------------- /src/interfaces/external/rocket-pool/IRocketTokenRETHInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IRocketTokenRETHInterface is IERC20 { 7 | function getEthValue(uint256 _rethAmount) external view returns (uint256); 8 | function getRethValue(uint256 _ethAmount) external view returns (uint256); 9 | function getExchangeRate() external view returns (uint256); 10 | function getTotalCollateral() external view returns (uint256); 11 | function getCollateralRate() external view returns (uint256); 12 | function depositExcess() external payable; 13 | function depositExcessCollateral() external; 14 | function mint(uint256 _ethAmount, address _to) external; 15 | function burn(uint256 _rethAmount) external; 16 | } 17 | -------------------------------------------------------------------------------- /src/interfaces/external/uniswap/IPairUniV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20Metadata } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 5 | 6 | /// @title Used for AMMs that use UniV2 based pair contract as pool. 7 | interface IPairUniV2 is IERC20Metadata { 8 | /// @notice Returns address of token0. 9 | function token0() external view returns (address); 10 | 11 | /// @notice Returns the address of token1. 12 | function token1() external view returns (address); 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IBaseBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IBaseBribe { 5 | function rewards(uint256 index) external view returns (address); 6 | function rewardsListLength() external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IExternalBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IExternalBribe { 5 | function rewards(uint256 index) external view returns (address); 6 | function rewardsListLength() external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IGauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IGauge is IERC20 { 7 | function deposit(uint256 amount, uint256 tokenId) external; 8 | function withdraw(uint256 amount) external; 9 | function withdrawToken(uint256 amount, uint256 tokenId) external; 10 | function stake() external view returns (address); 11 | function rewards(uint256 index) external view returns (address); 12 | function rewardsListLength() external view returns (uint256); 13 | function getReward(address account, address[] memory tokens) external; 14 | function claimFees() external returns (uint256 claimed0, uint256 claimed1); 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IInternalBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IInternalBribe { 5 | function rewards(uint256 index) external view returns (address); 6 | function rewardsListLength() external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IPair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IPair is IERC20 { 7 | function metadata() 8 | external 9 | view 10 | returns (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1); 11 | function claimFees() external returns (uint256, uint256); 12 | function tokens() external returns (address, address); 13 | function transfer(address dst, uint256 amount) external returns (bool); 14 | function transferFrom(address src, address dst, uint256 amount) external returns (bool); 15 | function permit( 16 | address owner, 17 | address spender, 18 | uint256 value, 19 | uint256 deadline, 20 | uint8 v, 21 | bytes32 r, 22 | bytes32 s 23 | ) external; 24 | function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; 25 | function burn(address to) external returns (uint256 amount0, uint256 amount1); 26 | function mint(address to) external returns (uint256 liquidity); 27 | function getReserves() external view returns (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast); 28 | function getAmountOut(uint256, address) external view returns (uint256); 29 | } 30 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IRewardsDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | // slither-disable-start naming-convention 6 | interface IRewardsDistributor { 7 | function claim(uint256 _tokenId) external returns (uint256); 8 | function claim_many(uint256[] memory _tokenIds) external returns (bool); 9 | } 10 | // slither-disable-end naming-convention 11 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | //slither-disable-start similar-names 5 | interface IRouter { 6 | function addLiquidity( 7 | address tokenA, 8 | address tokenB, 9 | bool stable, 10 | uint256 amountADesired, 11 | uint256 amountBDesired, 12 | uint256 amountAMin, 13 | uint256 amountBMin, 14 | address to, 15 | uint256 deadline 16 | ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); 17 | 18 | function removeLiquidity( 19 | address tokenA, 20 | address tokenB, 21 | bool stable, 22 | uint256 liquidity, 23 | uint256 amountAMin, 24 | uint256 amountBMin, 25 | address to, 26 | uint256 deadline 27 | ) external returns (uint256 amountA, uint256 amountB); 28 | 29 | function pairFor(address tokenA, address tokenB, bool stable) external view returns (address pair); 30 | } 31 | //slither-disable-end similar-names 32 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IVoter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable func-name-mixedcase 5 | interface IVoter { 6 | function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId) external; 7 | 8 | function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external; 9 | 10 | // mapping(address => address) public gauges; // pool => gauge 11 | function gauges(address pool) external view returns (address); 12 | 13 | // mapping(address => address) public poolForGauge; // gauge => pool 14 | function poolForGauge(address gauge) external view returns (address); 15 | 16 | // mapping(address => address) public internal_bribes; // gauge => internal bribe (only fees) 17 | // solhint-disable-next-line func-name-mixedcase 18 | function internal_bribes(address gauge) external view returns (address); 19 | 20 | // mapping(address => address) public external_bribes; // gauge => external bribe (real bribes) 21 | function external_bribes(address gauge) external view returns (address); 22 | } 23 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IVotingEscrow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { IERC721Enumerable } from "openzeppelin-contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 5 | 6 | // solhint-disable func-name-mixedcase 7 | interface IVotingEscrow is IERC721Enumerable { 8 | struct Point { 9 | int128 bias; 10 | int128 slope; // # -dweight / dt 11 | uint256 ts; 12 | uint256 blk; // block 13 | } 14 | 15 | function token() external view returns (address); 16 | function team() external returns (address); 17 | function epoch() external view returns (uint256); 18 | function point_history(uint256 loc) external view returns (Point memory); 19 | function user_point_history(uint256 tokenId, uint256 loc) external view returns (Point memory); 20 | function user_point_epoch(uint256 tokenId) external view returns (uint256); 21 | 22 | function ownerOf(uint256) external view returns (address); 23 | function isApprovedOrOwner(address, uint256) external view returns (bool); 24 | function transferFrom(address, address, uint256) external; 25 | 26 | function voting(uint256 tokenId) external; 27 | function abstain(uint256 tokenId) external; 28 | function attach(uint256 tokenId) external; 29 | function detach(uint256 tokenId) external; 30 | 31 | function checkpoint() external; 32 | function deposit_for(uint256 tokenId, uint256 value) external; 33 | function create_lock_for(uint256, uint256, address) external returns (uint256); 34 | 35 | function balanceOfNFT(uint256) external view returns (uint256); 36 | function totalSupply() external view returns (uint256); 37 | 38 | function attachments(uint256) external view returns (uint256); 39 | } 40 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IWrappedExternalBribe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IWrappedExternalBribe { 5 | function rewards(uint256 index) external view returns (address); 6 | function rewardsListLength() external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/external/velodrome/IWrappedExternalBribeFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IWrappedExternalBribeFactory { 5 | function oldBribeToNew(address existingBribe) external view returns (address); 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/external/wsteth/IWstEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | interface IWstEth { 5 | /** 6 | * @notice Gets the amount of wstEth tokens per stEth token. 7 | * @dev returns answer in 18 decimals of precision. 8 | */ 9 | function tokensPerStEth() external view returns (uint256); 10 | 11 | /// @notice Returns address of stEth contract. 12 | function stETH() external view returns (address); 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/liquidation/IAsyncSwapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | struct SwapParams { 6 | /// @dev The address of the token to be sold. 7 | address sellTokenAddress; 8 | /// @dev The amount of tokens to be sold. 9 | uint256 sellAmount; 10 | /// @dev The address of the token to be bought. 11 | address buyTokenAddress; 12 | /// @dev The expected minimum amount of tokens to be bought. 13 | uint256 buyAmount; 14 | /// @dev Data payload to be used for complex swap operations. 15 | bytes data; 16 | /// @dev Extra data payload reserved for future development. This field allows for additional information 17 | /// or functionality to be added without changing the struct and interface. 18 | bytes extraData; 19 | } 20 | 21 | interface IAsyncSwapper { 22 | error TokenAddressZero(); 23 | error SwapFailed(); 24 | error InsufficientBuyAmountReceived(uint256 buyTokenAmountReceived, uint256 buyAmount); 25 | error InsufficientSellAmount(); 26 | error InsufficientBuyAmount(); 27 | error InsufficientBalance(uint256 balanceNeeded, uint256 balanceAvailable); 28 | 29 | event Swapped( 30 | address indexed sellTokenAddress, 31 | address indexed buyTokenAddress, 32 | uint256 sellAmount, 33 | uint256 buyAmount, 34 | uint256 buyTokenAmountReceived 35 | ); 36 | 37 | /** 38 | * @notice Swaps sellToken for buyToken 39 | * @param swapParams Encoded swap data 40 | * @return buyTokenAmountReceived The amount of buyToken received from the swap 41 | */ 42 | function swap(SwapParams memory swapParams) external returns (uint256 buyTokenAmountReceived); 43 | } 44 | -------------------------------------------------------------------------------- /src/interfaces/liquidation/IAsyncSwapperRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | interface IAsyncSwapperRegistry { 6 | event SwapperAdded(address indexed item); 7 | event SwapperRemoved(address indexed item); 8 | 9 | /// @notice Registers an item 10 | /// @param item Item address to be added 11 | function register(address item) external; 12 | 13 | /// @notice Removes item registration 14 | /// @param item Item address to be removed 15 | function unregister(address item) external; 16 | 17 | /// @notice Returns a list of all registered items 18 | function list() external view returns (address[] memory); 19 | 20 | /// @notice Checks if an address is a valid item 21 | /// @param item Item address to be checked 22 | function isRegistered(address item) external view returns (bool); 23 | 24 | /// @notice Checks if an address is a valid swapper and reverts if not 25 | /// @param item Swapper address to be checked 26 | function verifyIsRegistered(address item) external view; 27 | } 28 | -------------------------------------------------------------------------------- /src/interfaces/oracles/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | /// @notice An oracle that can provide prices for single or multiple classes of tokens 7 | interface IPriceOracle { 8 | /// @notice Returns a fair price for the provided token in ETH 9 | /// @dev May require additional registration with the provider before being used for a token 10 | /// @param token Token to get the price of 11 | /// @return price The price of the token in ETH 12 | function getPriceInEth(address token) external returns (uint256 price); 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/oracles/IRootPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | /// @notice Retrieve a price for any token used in the system 7 | interface IRootPriceOracle { 8 | /// @notice Returns a fair price for the provided token in ETH 9 | /// @param token token to get the price of 10 | /// @return price the price of the token in ETH 11 | function getPriceInEth(address token) external returns (uint256 price); 12 | } 13 | -------------------------------------------------------------------------------- /src/interfaces/rewarders/IExtraRewarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IBaseRewarder } from "./IBaseRewarder.sol"; 6 | 7 | interface IExtraRewarder is IBaseRewarder { 8 | /** 9 | * @notice Withdraws the specified amount of tokens from the vault for the specified account. 10 | * @param account The address of the account to withdraw tokens for. 11 | * @param amount The amount of tokens to withdraw. 12 | */ 13 | function withdraw(address account, uint256 amount) external; 14 | 15 | /** 16 | * @notice Claims and transfers all rewards for the specified account from this contract. 17 | * @param account The address of the account to claim rewards for. 18 | */ 19 | function getReward(address account) external; 20 | } 21 | -------------------------------------------------------------------------------- /src/interfaces/rewarders/IMainRewarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IBaseRewarder } from "./IBaseRewarder.sol"; 6 | import { IExtraRewarder } from "src/interfaces/rewarders/IExtraRewarder.sol"; 7 | 8 | interface IMainRewarder is IBaseRewarder { 9 | /** 10 | * @notice Adds an ExtraRewarder contract address to the extraRewards array. 11 | * @param reward The address of the ExtraRewarder contract. 12 | */ 13 | function addExtraReward(address reward) external; 14 | 15 | /** 16 | * @notice Withdraws the specified amount of tokens from the vault for the specified account, and transfers all 17 | * rewards for the account from this contract and any linked extra reward contracts. 18 | * @param account The address of the account to withdraw tokens and claim rewards for. 19 | * @param amount The amount of tokens to withdraw. 20 | * @param claim If true, claims all rewards for the account from this contract and any linked extra reward 21 | * contracts. 22 | */ 23 | function withdraw(address account, uint256 amount, bool claim) external; 24 | 25 | /** 26 | * @notice Clears the extraRewards array. 27 | */ 28 | function clearExtraRewards() external; 29 | 30 | /** 31 | * @notice Claims and transfers all rewards for the specified account from this contract and any linked extra reward 32 | * contracts. 33 | * @dev If claimExtras is true, also claims all rewards from linked extra reward contracts. 34 | * @param account The address of the account to claim rewards for. 35 | * @param claimExtras If true, claims rewards from linked extra reward contracts. 36 | */ 37 | function getReward(address account, bool claimExtras) external; 38 | 39 | /** 40 | * @notice Number of extra rewards currently registered 41 | */ 42 | function extraRewardsLength() external view returns (uint256); 43 | 44 | /** 45 | * @notice Get the rewarder at the specified index 46 | */ 47 | function getExtraRewarder(uint256 index) external view returns (IExtraRewarder); 48 | } 49 | -------------------------------------------------------------------------------- /src/interfaces/rewarders/IStakeTracking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity 0.8.17; 3 | 4 | interface IStakeTracking { 5 | function totalSupply() external view returns (uint256); 6 | function balanceOf(address account) external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/rewards/IClaimableRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface IClaimableRewards { 8 | error ClaimRewardsFailed(); 9 | error TokenAddressZero(); 10 | 11 | event RewardsClaimed(IERC20[], uint256[]); 12 | } 13 | -------------------------------------------------------------------------------- /src/interfaces/security/IAccessController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IAccessControlEnumerable } from "openzeppelin-contracts/access/IAccessControlEnumerable.sol"; 6 | 7 | interface IAccessController is IAccessControlEnumerable { 8 | error AccessDenied(); 9 | 10 | function setupRole(bytes32 role, address account) external; 11 | 12 | function verifyOwner(address account) external view; 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/security/ISystemSecurity.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | interface ISystemSecurity { 6 | /// @notice Get the number of NAV/share operations currently in progress 7 | /// @return Number of operations 8 | function navOpsInProgress() external view returns (uint256); 9 | 10 | /// @notice Called at the start of any NAV/share changing operation 11 | function enterNavOperation() external; 12 | 13 | /// @notice Called at the end of any NAV/share changing operation 14 | function exitNavOperation() external; 15 | 16 | /// @notice Whether or not the system as a whole is paused 17 | function isSystemPaused() external view returns (bool); 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/stats/IDexLSTStats.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Stats } from "src/stats/Stats.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | import { ILSTStats } from "src/interfaces/stats/ILSTStats.sol"; 8 | 9 | /// @title Return stats DEXs with LSTs 10 | interface IDexLSTStats { 11 | event DexSnapshotTaken(uint256 snapshotTimestamp, uint256 priorFeeApr, uint256 newFeeApr, uint256 unfilteredFeeApr); 12 | 13 | struct DexLSTStatsData { 14 | uint256 lastSnapshotTimestamp; 15 | uint256 feeApr; 16 | uint256[] reservesInEth; 17 | ILSTStats.LSTStatsData[] lstStatsData; 18 | } 19 | 20 | /// @notice Get the current stats for the DEX with underlying LST tokens 21 | /// @dev Returned data is a combination of current data and filtered snapshots 22 | /// @return dexLSTStatsData current data on the DEX 23 | function current() external returns (DexLSTStatsData memory dexLSTStatsData); 24 | } 25 | -------------------------------------------------------------------------------- /src/interfaces/stats/ILSTStats.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Stats } from "src/stats/Stats.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | 8 | /// @title Return stats on base LSTs 9 | interface ILSTStats { 10 | struct LSTStatsData { 11 | uint256 lastSnapshotTimestamp; 12 | uint256 baseApr; 13 | int256 premium; // positive number is a premium, negative is a discount 14 | uint256[] slashingCosts; 15 | uint256[] slashingTimestamps; 16 | } 17 | 18 | /// @notice Get the current stats for the LST 19 | /// @dev Returned data is a combination of current data and filtered snapshots 20 | /// @return lstStatsData current data on the LST 21 | function current() external returns (LSTStatsData memory lstStatsData); 22 | 23 | /// @notice Get the EthPerToken (or Share) for the LST 24 | /// @return ethPerShare the backing eth for the LST 25 | function calculateEthPerToken() external view returns (uint256 ethPerShare); 26 | 27 | /// @notice Get if the underlying LST token is rebasing 28 | /// @return rebasing is true if the lst is a rebasing token 29 | function isRebasing() external view returns (bool rebasing); 30 | } 31 | -------------------------------------------------------------------------------- /src/interfaces/stats/IStatsCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | /// @title Capture information about a pool or destination 6 | interface IStatsCalculator { 7 | /// @notice thrown when no snapshot is taken 8 | error NoSnapshotTaken(); 9 | 10 | /// @notice The id for this instance of a calculator 11 | function getAprId() external view returns (bytes32); 12 | 13 | /// @notice The id of the underlying asset/pool/destination this calculator represents 14 | /// @dev This may be a generated address 15 | function getAddressId() external view returns (address); 16 | 17 | /// @notice Setup the calculator after it has been copied 18 | /// @dev Should only be executed one time 19 | /// @param dependentAprIds apr ids that cover the dependencies of this calculator 20 | /// @param initData setup data specific to this type of calculator 21 | function initialize(bytes32[] calldata dependentAprIds, bytes calldata initData) external; 22 | 23 | /// @notice Capture stat data about this setup 24 | function snapshot() external; 25 | 26 | /// @notice Indicates if a snapshot should be taken 27 | /// @return takeSnapshot if true then a snapshot should be taken. If false, calling snapshot will do nothing 28 | function shouldSnapshot() external view returns (bool takeSnapshot); 29 | } 30 | -------------------------------------------------------------------------------- /src/interfaces/stats/IStatsCalculatorFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ISystemComponent } from "src/interfaces/ISystemComponent.sol"; 6 | 7 | /// @title Create and register stat calculators 8 | interface IStatsCalculatorFactory is ISystemComponent { 9 | /// @notice Create an instance of a calculator pointed to a pool or destination 10 | /// @param aprTemplateId id of the template registered with the factory 11 | /// @param dependentAprIds apr ids that cover the dependencies of this calculator 12 | /// @param initData setup data specific to the type of calculator 13 | /// @return calculatorAddress the id that was generated based on the init data 14 | function create( 15 | bytes32 aprTemplateId, 16 | bytes32[] calldata dependentAprIds, 17 | bytes calldata initData 18 | ) external returns (address calculatorAddress); 19 | } 20 | -------------------------------------------------------------------------------- /src/interfaces/stats/IStatsCalculatorRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IStatsCalculator } from "src/interfaces/stats/IStatsCalculator.sol"; 6 | 7 | /// @notice Track stat calculators for this instance of the system 8 | interface IStatsCalculatorRegistry { 9 | /// @notice Get a registered calculator 10 | /// @dev Should revert if missing 11 | /// @param aprId key of the calculator to get 12 | /// @return calculator instance of the calculator 13 | function getCalculator(bytes32 aprId) external view returns (IStatsCalculator calculator); 14 | 15 | /// @notice Register a new stats calculator 16 | /// @param calculator address of the calculator 17 | function register(address calculator) external; 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/swapper/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ISyncSwapper } from "./ISyncSwapper.sol"; 6 | 7 | interface ISwapRouter { 8 | struct SwapData { 9 | address token; 10 | address pool; 11 | ISyncSwapper swapper; 12 | bytes data; 13 | } 14 | 15 | error MaxSlippageExceeded(); 16 | error SwapRouteLookupFailed(); 17 | error SwapFailed(); 18 | 19 | event SwapRouteSet(address indexed token, SwapData[] routes); 20 | event SwapForQuoteSuccessful( 21 | address indexed assetToken, 22 | uint256 sellAmount, 23 | address indexed quoteToken, 24 | uint256 minBuyAmount, 25 | uint256 buyAmount 26 | ); 27 | 28 | /** 29 | * @notice Sets a new swap route for a given asset token. 30 | * @param assetToken The asset token for which the swap route is being set. 31 | * @param _swapRoute The new swap route as an array of SwapData. The last element represents the quoteToken. 32 | * @dev Each 'hop' in the swap route is validated using the respective swapper's validate function. The validate 33 | * function ensures that the encoded data contains the correct 'fromAddress' and 'toAddress' (swapData.token), and 34 | * verifies that these tokens are in the pool. 35 | */ 36 | function setSwapRoute(address assetToken, SwapData[] calldata _swapRoute) external; 37 | 38 | /** 39 | * @notice Swaps the asset token for the quote token. 40 | * @dev We're adopting an "exact in, variable out" model for all our swaps. This ensures that the entire sellAmount 41 | * is used, eliminating the need for additional balance checks and refunds. This model is expected to be followed by 42 | * all swapper implementations to maintain consistency and to optimize for gas efficiency. 43 | * @param assetToken The address of the asset token to swap. 44 | * @param sellAmount The exact amount of the asset token to swap. 45 | * @param quoteToken The address of the quote token. 46 | * @param minBuyAmount The minimum amount of the quote token expected to be received from the swap. 47 | * @return The amount received from the swap. 48 | */ 49 | function swapForQuote( 50 | address assetToken, 51 | uint256 sellAmount, 52 | address quoteToken, 53 | uint256 minBuyAmount 54 | ) external returns (uint256); 55 | } 56 | -------------------------------------------------------------------------------- /src/interfaces/swapper/ISyncSwapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ISwapRouter } from "src/interfaces/swapper/ISwapRouter.sol"; 6 | 7 | interface ISyncSwapper { 8 | error DataMismatch(string element); 9 | error InvalidIndex(); 10 | 11 | /** 12 | * @notice Swaps sellToken for buyToken 13 | * @param pool The address of the pool for the swapper 14 | * @param sellTokenAddress The address of the token to sell 15 | * @param sellAmount The amount of sellToken to sell 16 | * @param buyTokenAddress The address of the token to buy 17 | * @param minBuyAmount The minimum amount of buyToken expected 18 | * @param data Additional data used differently by the different swappers 19 | * @return actualBuyAmount The actual amount received from the swap 20 | */ 21 | function swap( 22 | address pool, 23 | address sellTokenAddress, 24 | uint256 sellAmount, 25 | address buyTokenAddress, 26 | uint256 minBuyAmount, 27 | bytes memory data 28 | ) external returns (uint256 actualBuyAmount); 29 | 30 | /** 31 | * @notice Validates that the swapData contains the correct information, ensuring that the encoded data contains the 32 | * correct 'fromAddress' and 'toAddress' (swapData.token), and verifies that these tokens are in the pool 33 | * @dev This function should revert with a DataMismatch error if the swapData is invalid 34 | * @param fromAddress The address from which the swap originates 35 | * @param swapData The data associated with the swap that needs to be validated 36 | */ 37 | function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view; 38 | } 39 | -------------------------------------------------------------------------------- /src/interfaces/utils/ICurveResolver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | interface ICurveResolver { 7 | /// @notice Resolve details of a Curve pool regardless of type or version 8 | /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools. 9 | /// If you need a dynamic array of tokens use Arrays.convertFixedCurveTokenArrayToDynamic(tokens,numTokens) 10 | /// @param poolAddress pool address to lookup 11 | /// @return tokens tokens that make up the pool 12 | /// @return numTokens the number of tokens. tokens are not unwrapped. 13 | /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap 14 | function resolve(address poolAddress) 15 | external 16 | view 17 | returns (address[8] memory tokens, uint256 numTokens, bool isStableSwap); 18 | 19 | /// @notice Resolve details of a Curve pool regardless of type or version 20 | /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools. 21 | /// If you need a dynamic array of tokens use Arrays.convertFixedCurveTokenArrayToDynamic(tokens,numTokens) 22 | /// @param poolAddress pool address to lookup 23 | /// @return tokens tokens that make up the pool 24 | /// @return numTokens the number of tokens. tokens are not unwrapped 25 | /// @return lpToken lp token of the pool 26 | /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap 27 | function resolveWithLpToken(address poolAddress) 28 | external 29 | view 30 | returns (address[8] memory tokens, uint256 numTokens, address lpToken, bool isStableSwap); 31 | } 32 | -------------------------------------------------------------------------------- /src/interfaces/utils/IERC20PermitAllowed.sol: -------------------------------------------------------------------------------- 1 | // forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/external/IERC20PermitAllowed.sol 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | pragma solidity >=0.8.7; 4 | 5 | /// @title Interface for permit 6 | /// @notice Interface used by DAI/CHAI for permit 7 | interface IERC20PermitAllowed { 8 | /// @notice Approve the spender to spend some tokens via the holder signature 9 | /// @dev This is the permit interface used by DAI and CHAI 10 | /// @param holder The address of the token holder, the token owner 11 | /// @param spender The address of the token spender 12 | /// @param nonce The holder's nonce, increases at each call to permit 13 | /// @param expiry The timestamp at which the permit is no longer valid 14 | /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0 15 | /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` 16 | /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` 17 | /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` 18 | function permit( 19 | address holder, 20 | address spender, 21 | uint256 nonce, 22 | uint256 expiry, 23 | bool allowed, 24 | uint8 v, 25 | bytes32 r, 26 | bytes32 s 27 | ) external; 28 | } 29 | -------------------------------------------------------------------------------- /src/interfaces/utils/IMulticall.sol: -------------------------------------------------------------------------------- 1 | // forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IMulticall.sol 2 | 3 | // SPDX-License-Identifier: GPL-2.0-or-later 4 | pragma solidity >=0.8.7; 5 | 6 | /// @title Multicall interface 7 | /// @notice Enables calling multiple methods in a single call to the contract 8 | interface IMulticall { 9 | /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed 10 | /// @dev The `msg.value` should not be trusted for any method callable from multicall. 11 | /// @param data The encoded function data for each of the calls to make to this contract 12 | /// @return results The results from each of the calls passed in via data 13 | function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); 14 | } 15 | -------------------------------------------------------------------------------- /src/interfaces/utils/IWETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.7; 3 | 4 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IWETH9 is IERC20 { 7 | function symbol() external view returns (string memory); 8 | 9 | function deposit() external payable; 10 | function withdraw(uint256 amount) external; 11 | } 12 | -------------------------------------------------------------------------------- /src/interfaces/vault/IBaseAssetVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20Metadata as IERC20 } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 6 | 7 | interface IBaseAssetVault { 8 | /// @notice Asset that this Vault primarily manages 9 | /// @dev Vault decimals should be the same as the baseAsset 10 | function baseAsset() external view returns (address); 11 | } 12 | -------------------------------------------------------------------------------- /src/interfaces/vault/IDestinationVaultFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { ISystemComponent } from "src/interfaces/ISystemComponent.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | 8 | /// @notice Creates and registers Destination Vaults for the system 9 | interface IDestinationVaultFactory is ISystemComponent { 10 | /// @notice Creates a vault of the specified type 11 | /// @dev vaultType will be bytes32 encoded and checked that a template is registered 12 | /// @param vaultType human readable key of the vault template 13 | /// @param baseAsset Base asset of the system. WETH/USDC/etc 14 | /// @param underlyer Underlying asset the vault will wrap 15 | /// @param additionalTrackedTokens Any tokens in addition to base and underlyer that should be tracked 16 | /// @param salt Contracts are created via CREATE2 with this value 17 | /// @param params params to be passed to vaults initialize function 18 | /// @return vault address of the newly created destination vault 19 | function create( 20 | string memory vaultType, 21 | address baseAsset, 22 | address underlyer, 23 | address[] memory additionalTrackedTokens, 24 | bytes32 salt, 25 | bytes memory params 26 | ) external returns (address vault); 27 | } 28 | -------------------------------------------------------------------------------- /src/interfaces/vault/IDestinationVaultRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | /// @notice Tracks valid Destination Vaults for the system 6 | interface IDestinationVaultRegistry { 7 | /// @notice Determines if a given address is a valid Destination Vault in the system 8 | /// @param destinationVault address to check 9 | /// @return True if vault is registered 10 | function isRegistered(address destinationVault) external view returns (bool); 11 | 12 | /// @notice Registers a new Destination Vault 13 | /// @dev Should be locked down to only a factory 14 | /// @param newDestinationVault Address of the new vault 15 | function register(address newDestinationVault) external; 16 | 17 | /// @notice Checks if an address is a valid Destination Vault and reverts if not 18 | /// @param destinationVault Destination Vault address to checked 19 | function verifyIsRegistered(address destinationVault) external view; 20 | } 21 | -------------------------------------------------------------------------------- /src/interfaces/vault/ILMPVaultFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | interface ILMPVaultFactory { 6 | /////////////////////////////////////////////////////////////////// 7 | // Vault Creation 8 | /////////////////////////////////////////////////////////////////// 9 | 10 | /** 11 | * @notice Spin up a new LMPVault 12 | * @param supplyLimit Total supply limit for the new vault 13 | * @param walletLimit Wallet limit for the new vault 14 | * @param symbolSuffix Symbol suffix of the new token 15 | * @param descPrefix Description prefix of the new token 16 | * @param salt Vault creation salt 17 | * @param extraParams Any extra data needed for the vault 18 | */ 19 | function createVault( 20 | uint256 supplyLimit, 21 | uint256 walletLimit, 22 | string memory symbolSuffix, 23 | string memory descPrefix, 24 | bytes32 salt, 25 | bytes calldata extraParams 26 | ) external returns (address newVaultAddress); 27 | } 28 | -------------------------------------------------------------------------------- /src/interfaces/vault/ILMPVaultRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | /// @title Keep track of Vaults created through the Vault Factory 6 | interface ILMPVaultRegistry { 7 | /////////////////////////////////////////////////////////////////// 8 | // Errors 9 | /////////////////////////////////////////////////////////////////// 10 | 11 | error VaultNotFound(address vaultAddress); 12 | error VaultAlreadyExists(address vaultAddress); 13 | 14 | /////////////////////////////////////////////////////////////////// 15 | // Events 16 | /////////////////////////////////////////////////////////////////// 17 | event VaultAdded(address indexed asset, address indexed vault); 18 | event VaultRemoved(address indexed asset, address indexed vault); 19 | 20 | /////////////////////////////////////////////////////////////////// 21 | // Functions 22 | /////////////////////////////////////////////////////////////////// 23 | 24 | /// @notice Checks if an address is a valid vault 25 | /// @param vaultAddress Vault address to be added 26 | function isVault(address vaultAddress) external view returns (bool); 27 | 28 | /// @notice Registers a vault 29 | /// @param vaultAddress Vault address to be added 30 | function addVault(address vaultAddress) external; 31 | 32 | /// @notice Removes vault registration 33 | /// @param vaultAddress Vault address to be removed 34 | function removeVault(address vaultAddress) external; 35 | 36 | /// @notice Returns a list of all registered vaults 37 | function listVaults() external view returns (address[] memory); 38 | 39 | /// @notice Returns a list of all registered vaults for a given asset 40 | /// @param asset Asset address 41 | function listVaultsForAsset(address asset) external view returns (address[] memory); 42 | 43 | /// @notice Returns a list of all registered vaults for a given type 44 | /// @param _vaultType Vault type 45 | function listVaultsForType(bytes32 _vaultType) external view returns (address[] memory); 46 | } 47 | -------------------------------------------------------------------------------- /src/libs/BalancerUtilities.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { IVault } from "src/interfaces/external/balancer/IVault.sol"; 7 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 8 | 9 | library BalancerUtilities { 10 | error BalancerVaultReentrancy(); 11 | 12 | // 400 is Balancers Vault REENTRANCY error code 13 | bytes32 internal constant REENTRANCY_ERROR_HASH = keccak256(abi.encodeWithSignature("Error(string)", "BAL#400")); 14 | 15 | /** 16 | * @notice Verifies reentrancy to the Balancer Vault 17 | * @dev Reverts if gets BAL#400 error 18 | */ 19 | function checkReentrancy(address balancerVault) external view { 20 | // solhint-disable max-line-length 21 | // https://github.com/balancer/balancer-v2-monorepo/blob/90f77293fef4b8782feae68643c745c754bac45c/pkg/pool-utils/contracts/lib/VaultReentrancyLib.sol 22 | (, bytes memory returnData) = balancerVault.staticcall( 23 | abi.encodeWithSelector(IVault.manageUserBalance.selector, new IVault.UserBalanceOp[](0)) 24 | ); 25 | if (keccak256(returnData) == REENTRANCY_ERROR_HASH) { 26 | revert BalancerVaultReentrancy(); 27 | } 28 | } 29 | 30 | /** 31 | * @notice Checks if a given address is Balancer Composable pool 32 | * @dev Using the presence of a getBptIndex() fn as an indicator of pool type 33 | */ 34 | function isComposablePool(address pool) public view returns (bool) { 35 | // slither-disable-start low-level-calls 36 | // solhint-disable-next-line no-unused-vars 37 | (bool success, bytes memory data) = pool.staticcall(abi.encodeWithSignature("getBptIndex()")); 38 | if (success) { 39 | return data.length > 0; 40 | } 41 | // slither-disable-end low-level-calls 42 | return success; 43 | } 44 | 45 | /** 46 | * @dev This helper function is a fast and cheap way to convert between IERC20[] and IAsset[] types 47 | */ 48 | function _convertERC20sToAddresses(IERC20[] memory tokens) internal pure returns (address[] memory assets) { 49 | //slither-disable-start assembly 50 | //solhint-disable-next-line no-inline-assembly 51 | assembly { 52 | assets := tokens 53 | } 54 | //slither-disable-end assembly 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/libs/ConvexRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 7 | 8 | // reference: https://docs.convexfinance.com/convexfinanceintegration/cvx-minting 9 | library ConvexRewards { 10 | uint256 internal constant CVX_MAX_SUPPLY = 100_000_000 * 1e18; // 100 mil max supply 11 | uint256 internal constant CLIFF_COUNT = 1000; 12 | uint256 internal constant CLIFF_SIZE = CVX_MAX_SUPPLY / CLIFF_COUNT; // 100_000 per clif 13 | 14 | /** 15 | * @notice Calculates the amount of CVX that is minted given the amount of CRV earned 16 | * @param cvxToken address for CVX token 17 | * @param crvEarned the amount of CRV reward that was earned 18 | */ 19 | function getCVXMintAmount(address cvxToken, uint256 crvEarned) internal view returns (uint256) { 20 | uint256 cvxSupply = IERC20(cvxToken).totalSupply(); 21 | 22 | // if no cvx has been minted, pre-mine the same amount as the provided crv 23 | if (cvxSupply == 0) { 24 | return crvEarned; 25 | } 26 | 27 | // determine the current cliff 28 | uint256 currentCliff = cvxSupply / CLIFF_SIZE; 29 | 30 | // if the current cliff is below the max, then CVX will be minted 31 | if (currentCliff < CLIFF_COUNT) { 32 | uint256 remainingCliffs = CLIFF_COUNT - currentCliff; 33 | uint256 cvxEarned = crvEarned * remainingCliffs / CLIFF_COUNT; 34 | 35 | // ensure that the max supply has not been exceeded 36 | uint256 amountUntilMax = CVX_MAX_SUPPLY - cvxSupply; 37 | if (cvxEarned > amountUntilMax) { 38 | // if maxSupply would be exceeded then return the remaining supply 39 | return amountUntilMax; 40 | } 41 | 42 | return cvxEarned; 43 | } 44 | 45 | return 0; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/libs/LibAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | import { SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | library LibAdapter { 9 | using SafeERC20 for IERC20; 10 | 11 | address public constant CURVE_REGISTRY_ETH_ADDRESS_POINTER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 12 | 13 | error MinLpAmountNotReached(); 14 | error LpTokenAmountMismatch(); 15 | error NoNonZeroAmountProvided(); 16 | error InvalidBalanceChange(); 17 | 18 | // Utils 19 | function _approve(IERC20 token, address spender, uint256 amount) internal { 20 | uint256 currentAllowance = token.allowance(address(this), spender); 21 | if (currentAllowance > 0) { 22 | token.safeDecreaseAllowance(spender, currentAllowance); 23 | } 24 | token.safeIncreaseAllowance(spender, amount); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/libs/Roles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | library Roles { 7 | // -------------------------------------------------------------------- 8 | // Central roles list used by all contracts that call AccessController 9 | // -------------------------------------------------------------------- 10 | 11 | bytes32 public constant REBALANCER_ROLE = keccak256("REBALANCER_ROLE"); 12 | bytes32 public constant CREATE_POOL_ROLE = keccak256("CREATE_POOL_ROLE"); 13 | bytes32 public constant CREATE_DESTINATION_VAULT_ROLE = keccak256("CREATE_DESTINATION_VAULT_ROLE"); 14 | bytes32 public constant REGISTRY_UPDATER = keccak256("REGISTRY_UPDATER"); 15 | 16 | bytes32 public constant TOKEN_RECOVERY_ROLE = keccak256("TOKEN_RECOVERY_ROLE"); 17 | bytes32 public constant DESTINATION_VAULTS_UPDATER = keccak256("DESTINATION_VAULTS_UPDATER"); 18 | bytes32 public constant SET_WITHDRAWAL_QUEUE_ROLE = keccak256("SET_WITHDRAWAL_QUEUE_ROLE"); 19 | 20 | bytes32 public constant DESTINATION_VAULT_OPERATOR_ROLE = keccak256("DESTINATION_VAULT_OPERATOR_ROLE"); 21 | 22 | bytes32 public constant DV_REWARD_MANAGER_ROLE = keccak256("DV_REWARD_MANAGER_ROLE"); 23 | 24 | bytes32 public constant LIQUIDATOR_ROLE = keccak256("LIQUIDATOR_ROLE"); 25 | 26 | bytes32 public constant CREATE_STATS_CALC_ROLE = keccak256("CREATE_STATS_CALC_ROLE"); 27 | bytes32 public constant STATS_CALC_TEMPLATE_MGMT_ROLE = keccak256("STATS_CALC_TEMPLATE_MGMT_ROLE"); 28 | bytes32 public constant STATS_SNAPSHOT_ROLE = keccak256("STATS_SNAPSHOT_ROLE"); 29 | bytes32 public constant STATS_INCENTIVE_TOKEN_UPDATER = keccak256("STATS_INCENTIVE_TOKEN_UPDATER"); 30 | 31 | bytes32 public constant SOLVER_ROLE = keccak256("SOLVER_ROLE"); 32 | 33 | bytes32 public constant LMP_FEE_SETTER_ROLE = keccak256("LMP_FEE_SETTER_ROLE"); 34 | 35 | bytes32 public constant EMERGENCY_PAUSER = keccak256("EMERGENCY_PAUSER"); 36 | 37 | bytes32 public constant ORACLE_MANAGER_ROLE = keccak256("ORACLE_MANAGER_ROLE"); 38 | 39 | bytes32 public constant LSD_BACKING_UPDATER = keccak256("LSD_BACKING_UPDATER"); 40 | } 41 | -------------------------------------------------------------------------------- /src/liquidation/AsyncSwapperRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { EnumerableSet } from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; 6 | 7 | import { IAsyncSwapperRegistry } from "src/interfaces/liquidation/IAsyncSwapperRegistry.sol"; 8 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 9 | import { SecurityBase } from "src/security/SecurityBase.sol"; 10 | import { Roles } from "src/libs/Roles.sol"; 11 | import { Errors } from "src/utils/Errors.sol"; 12 | import { SystemComponent } from "src/SystemComponent.sol"; 13 | 14 | contract AsyncSwapperRegistry is SystemComponent, IAsyncSwapperRegistry, SecurityBase { 15 | using EnumerableSet for EnumerableSet.AddressSet; 16 | 17 | EnumerableSet.AddressSet private _swappers; 18 | 19 | constructor(ISystemRegistry _systemRegistry) 20 | SystemComponent(_systemRegistry) 21 | SecurityBase(address(_systemRegistry.accessController())) 22 | { } 23 | 24 | function register(address swapperAddress) external override hasRole(Roles.REGISTRY_UPDATER) { 25 | Errors.verifyNotZero(swapperAddress, "swapperAddress"); 26 | 27 | if (!_swappers.add(swapperAddress)) revert Errors.ItemExists(); 28 | 29 | emit SwapperAdded(swapperAddress); 30 | } 31 | 32 | function unregister(address swapperAddress) external override hasRole(Roles.REGISTRY_UPDATER) { 33 | Errors.verifyNotZero(swapperAddress, "swapperAddress"); 34 | 35 | if (!_swappers.remove(swapperAddress)) revert Errors.ItemNotFound(); 36 | 37 | emit SwapperRemoved(swapperAddress); 38 | } 39 | 40 | function isRegistered(address swapperAddress) external view override returns (bool) { 41 | return _swappers.contains(swapperAddress); 42 | } 43 | 44 | function verifyIsRegistered(address swapperAddress) external view override { 45 | if (!_swappers.contains(swapperAddress)) revert Errors.NotRegistered(); 46 | } 47 | 48 | function list() external view override returns (address[] memory) { 49 | return _swappers.values(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/oracles/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tokemak/v2-core-audit-2023-07-14/62445b8ee3365611534c96aef189642b721693bf/src/oracles/image-1.png -------------------------------------------------------------------------------- /src/oracles/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tokemak/v2-core-audit-2023-07-14/62445b8ee3365611534c96aef189642b721693bf/src/oracles/image.png -------------------------------------------------------------------------------- /src/oracles/providers/EthPeggedOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | import { IPriceOracle } from "src/interfaces/oracles/IPriceOracle.sol"; 9 | import { SystemComponent } from "src/SystemComponent.sol"; 10 | 11 | /// @title Price oracle for tokens we want to configure 1:1 to ETH. WETH for example 12 | /// @dev getPriceEth is not a view fn to support reentrancy checks. Dont actually change state. 13 | contract EthPeggedOracle is SystemComponent, IPriceOracle { 14 | constructor(ISystemRegistry _systemRegistry) SystemComponent(_systemRegistry) { } 15 | 16 | /// @inheritdoc IPriceOracle 17 | function getPriceInEth(address) external pure returns (uint256 price) { 18 | price = 1e18; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/oracles/providers/SfrxEthEthOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { ISfrxEth } from "src/interfaces/external/frax/ISfrxEth.sol"; 8 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 9 | import { IPriceOracle } from "src/interfaces/oracles/IPriceOracle.sol"; 10 | import { IERC20Metadata } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 11 | import { SystemComponent } from "src/SystemComponent.sol"; 12 | 13 | /// @title Price oracle specifically for sfrxETH 14 | /// @dev getPriceEth is not a view fn to support reentrancy checks. Dont actually change state. 15 | contract SfrxEthEthOracle is SystemComponent, IPriceOracle { 16 | ISfrxEth public immutable sfrxETH; 17 | IERC20Metadata public immutable frxETH; 18 | uint256 public immutable frxETHPrecision; 19 | 20 | error InvalidToken(address token); 21 | error InvalidDecimals(address token, uint8 decimals); 22 | 23 | constructor(ISystemRegistry _systemRegistry, address _sfrxETH) SystemComponent(_systemRegistry) { 24 | // System registry must be properly initialized first 25 | Errors.verifyNotZero(address(_systemRegistry), "_systemRegistry"); 26 | Errors.verifyNotZero(address(_systemRegistry.rootPriceOracle()), "rootPriceOracle"); 27 | 28 | Errors.verifyNotZero(address(_sfrxETH), "_sfrxETH"); 29 | 30 | sfrxETH = ISfrxEth(_sfrxETH); 31 | 32 | address assetAddress = sfrxETH.asset(); 33 | Errors.verifyNotZero(assetAddress, "assetAddress"); 34 | 35 | frxETH = IERC20Metadata(assetAddress); 36 | uint8 frxETHDecimals = frxETH.decimals(); 37 | 38 | if (frxETHDecimals == 0) { 39 | revert InvalidDecimals(address(frxETH), frxETHDecimals); 40 | } 41 | frxETHPrecision = 10 ** frxETHDecimals; 42 | } 43 | 44 | /// @inheritdoc IPriceOracle 45 | function getPriceInEth(address token) external returns (uint256 price) { 46 | // This oracle is only setup to handle a single token but could possibly be 47 | // configured incorrectly at the root level and receive others to price. 48 | if (token != address(sfrxETH)) { 49 | revert Errors.InvalidToken(token); 50 | } 51 | 52 | uint256 frxETHPrice = systemRegistry.rootPriceOracle().getPriceInEth(address(frxETH)); 53 | 54 | // Our prices are always in 1e18 so just use frxETH precision to get back to 1e18; 55 | price = (sfrxETH.pricePerShare() * frxETHPrice) / frxETHPrecision; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/oracles/providers/SwEthEthOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { IswETH } from "src/interfaces/external/swell/IswETH.sol"; 8 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 9 | import { IPriceOracle } from "src/interfaces/oracles/IPriceOracle.sol"; 10 | import { SystemComponent } from "src/SystemComponent.sol"; 11 | 12 | /** 13 | * @notice Price oracle specifically for swEth (Swell Eth). 14 | * @dev getPriceEth is not a view fn to support reentrancy checks. Does not actually change state. 15 | */ 16 | contract SwEthEthOracle is SystemComponent, IPriceOracle { 17 | IswETH public immutable swEth; 18 | 19 | constructor(ISystemRegistry _systemRegistry, IswETH _swEth) SystemComponent(_systemRegistry) { 20 | Errors.verifyNotZero(address(_swEth), "_swEth"); 21 | 22 | swEth = _swEth; 23 | } 24 | 25 | /// @inheritdoc IPriceOracle 26 | function getPriceInEth(address token) external view returns (uint256 price) { 27 | // Prevents incorrect config at root level. 28 | if (token != address(swEth)) revert Errors.InvalidToken(token); 29 | 30 | // Returns in 1e18 precision. 31 | price = swEth.swETHToETHRate(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/oracles/providers/WstETHEthOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { IstEth } from "src/interfaces/external/lido/IstEth.sol"; 8 | import { IwstEth } from "src/interfaces/external/lido/IwstEth.sol"; 9 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 10 | import { IPriceOracle } from "src/interfaces/oracles/IPriceOracle.sol"; 11 | import { SystemComponent } from "src/SystemComponent.sol"; 12 | 13 | /// @title Price oracle specifically for wstETH 14 | /// @dev getPriceEth is not a view fn to support reentrancy checks. Dont actually change state. 15 | contract WstETHEthOracle is SystemComponent, IPriceOracle { 16 | IwstEth public immutable wstETH; 17 | IstEth public immutable stETH; 18 | uint256 public immutable stETHPrecision; 19 | 20 | error InvalidDecimals(address token, uint8 decimals); 21 | 22 | constructor(ISystemRegistry _systemRegistry, address _wstETH) SystemComponent(_systemRegistry) { 23 | // System registry must be properly initialized first 24 | Errors.verifyNotZero(address(_systemRegistry.rootPriceOracle()), "rootPriceOracle"); 25 | 26 | Errors.verifyNotZero(address(_wstETH), "_wstETH"); 27 | 28 | wstETH = IwstEth(_wstETH); 29 | 30 | address stETHAddress = wstETH.stETH(); 31 | Errors.verifyNotZero(stETHAddress, "stETHAddress"); 32 | 33 | stETH = IstEth(stETHAddress); 34 | uint8 stETHDecimals = stETH.decimals(); 35 | 36 | if (stETHDecimals == 0) { 37 | revert InvalidDecimals(stETHAddress, stETHDecimals); 38 | } 39 | stETHPrecision = 10 ** stETHDecimals; 40 | } 41 | 42 | /// @inheritdoc IPriceOracle 43 | function getPriceInEth(address token) external returns (uint256 price) { 44 | // This oracle is only setup to handle a single token but could possibly be 45 | // configured incorrectly at the root level and receive others to price. 46 | if (token != address(wstETH)) { 47 | revert Errors.InvalidToken(token); 48 | } 49 | 50 | uint256 stETHPrice = systemRegistry.rootPriceOracle().getPriceInEth(address(stETH)); 51 | 52 | // Our prices are always in 1e18 so just use steths precision to get back to 1e18; 53 | price = (wstETH.stEthPerToken() * stETHPrice) / stETHPrecision; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/rewarders/ExtraRewarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | import { SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; 7 | import { ReentrancyGuard } from "openzeppelin-contracts/security/ReentrancyGuard.sol"; 8 | 9 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 10 | 11 | import { IStakeTracking } from "src/interfaces/rewarders/IStakeTracking.sol"; 12 | import { IExtraRewarder } from "src/interfaces/rewarders/IExtraRewarder.sol"; 13 | import { AbstractRewarder } from "./AbstractRewarder.sol"; 14 | 15 | import { Errors } from "src/utils/Errors.sol"; 16 | 17 | contract ExtraRewarder is AbstractRewarder, IExtraRewarder, ReentrancyGuard { 18 | address public immutable mainReward; 19 | 20 | error MainRewardOnly(); 21 | 22 | constructor( 23 | ISystemRegistry _systemRegistry, 24 | address _stakeTracker, 25 | address _rewardToken, 26 | address _mainReward, 27 | uint256 _newRewardRatio, 28 | uint256 _durationInBlock 29 | ) AbstractRewarder(_systemRegistry, _stakeTracker, _rewardToken, _newRewardRatio, _durationInBlock) { 30 | Errors.verifyNotZero(_mainReward, "_mainReward"); 31 | 32 | // slither-disable-next-line missing-zero-check 33 | mainReward = _mainReward; 34 | } 35 | 36 | modifier mainRewardOnly() { 37 | if (msg.sender != mainReward) { 38 | revert MainRewardOnly(); 39 | } 40 | _; 41 | } 42 | 43 | function stake(address account, uint256 amount) external mainRewardOnly { 44 | _updateReward(account); 45 | _stake(account, amount); 46 | } 47 | 48 | function withdraw(address account, uint256 amount) external mainRewardOnly { 49 | _updateReward(account); 50 | _withdraw(account, amount); 51 | } 52 | 53 | function getReward(address account) public nonReentrant { 54 | _updateReward(account); 55 | _getReward(account); 56 | } 57 | 58 | function getReward() external { 59 | getReward(msg.sender); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/rewarders/README.md: -------------------------------------------------------------------------------- 1 | # Rewards Contracts Architecture 2 | 3 | The rewards architecture consists of three main contracts 4 | 5 | - MainRewarder, 6 | - ExtraRewarder, 7 | - StakeTracking. 8 | 9 | The purpose of these contracts is to distribute rewards to users who stake their tokens. 10 | 11 | ## Summary 12 | 13 | The StakeTracking contract keeps track of staked token balances, the MainRewarder contract distributes the main reward tokens and manages the distribution of additional reward tokens through the ExtraRewarder contracts, and the ExtraRewarder contracts distribute additional reward tokens. 14 | 15 | ## StakeTracking 16 | 17 | The StakeTracking contract is responsible for keeping track of the total supply and balance of tokens staked by users in Vaults. It should be called by the Vaults at any liquidity moves. 18 | 19 | It acts as a proxy between Vaults and the MainRewarder contract, monitoring the total supply and user balances of deposited tokens. This intermediary role enables accurate tracking of staking activities on multiple Vaults. 20 | 21 | ## MainRewarder 22 | 23 | The MainRewarder contract is responsible for distributing the main reward tokens to stakers. 24 | 25 | The operator can queue new rewards to be distributed to stakers using the queueNewRewards function. The rewards are added to a reward queue, which is then distributed to stakers based on their staked balances. 26 | 27 | The addExtraReward function adds the address of the ExtraRewarder contract to a list of ExtraRewarder contracts that can distribute additional rewards to stakers. When a user calls the getReward function, the MainRewarder contract distributes rewards from the main reward queue and all extra reward queues. The amount of rewards distributed from each queue is proportional to the user's staked balance. 28 | 29 | The MainRewarder contract also includes a stake and withdraw functions that allow the StakeTracking contract to keep track of liquidity moves like stake or withdraw. 30 | When the StakeTracking contract calls the stake or withdraw function, the MainRewarder contract also calls the stake or withdraw function of any added ExtraRewarder contracts. This ensures that the user's staked balance is tracked in all associated contracts. 31 | 32 | ## ExtraRewarder 33 | 34 | The ExtraRewarder contract is a simpler version of the MainRewarder contract and is responsible for distributing additional reward tokens to stakers. 35 | -------------------------------------------------------------------------------- /src/security/AccessController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Roles } from "src/libs/Roles.sol"; 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | import { IAccessController } from "src/interfaces/security/IAccessController.sol"; 9 | import { AccessControlEnumerable } from "openzeppelin-contracts/access/AccessControlEnumerable.sol"; 10 | import { SystemComponent } from "src/SystemComponent.sol"; 11 | 12 | contract AccessController is SystemComponent, AccessControlEnumerable, IAccessController { 13 | // ------------------------------------------------------------ 14 | // Pre-initialize roles list for deployer 15 | // ------------------------------------------------------------ 16 | constructor(address _systemRegistry) SystemComponent(ISystemRegistry(_systemRegistry)) { 17 | Errors.verifyNotZero(_systemRegistry, "systemRegistry"); 18 | 19 | _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); 20 | _setupRole(Roles.REBALANCER_ROLE, msg.sender); 21 | _setupRole(Roles.CREATE_POOL_ROLE, msg.sender); 22 | } 23 | 24 | // ------------------------------------------------------------ 25 | // Role management methods 26 | // ------------------------------------------------------------ 27 | function setupRole(bytes32 role, address account) external { 28 | if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) { 29 | revert AccessDenied(); 30 | } 31 | 32 | // only do if role is not registered already 33 | if (!hasRole(role, account)) { 34 | _setupRole(role, account); 35 | } 36 | } 37 | 38 | function verifyOwner(address account) public view { 39 | if (!hasRole(DEFAULT_ADMIN_ROLE, account)) { 40 | revert AccessDenied(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/security/SecurityBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IAccessController } from "src/interfaces/security/IAccessController.sol"; 6 | import { Context } from "openzeppelin-contracts/utils/Context.sol"; 7 | import { Errors } from "src/utils/Errors.sol"; 8 | 9 | contract SecurityBase { 10 | IAccessController public immutable accessController; 11 | 12 | error UndefinedAddress(); 13 | 14 | constructor(address _accessController) { 15 | if (_accessController == address(0)) revert UndefinedAddress(); 16 | 17 | accessController = IAccessController(_accessController); 18 | } 19 | 20 | modifier onlyOwner() { 21 | accessController.verifyOwner(msg.sender); 22 | _; 23 | } 24 | 25 | modifier hasRole(bytes32 role) { 26 | if (!accessController.hasRole(role, msg.sender)) revert Errors.AccessDenied(); 27 | _; 28 | } 29 | 30 | /////////////////////////////////////////////////////////////////// 31 | // 32 | // Forward all the regular methods to central security module 33 | // 34 | /////////////////////////////////////////////////////////////////// 35 | 36 | function _hasRole(bytes32 role, address account) internal view returns (bool) { 37 | return accessController.hasRole(role, account); 38 | } 39 | 40 | // NOTE: left commented forward methods in here for potential future use 41 | // function _getRoleAdmin(bytes32 role) internal view returns (bytes32) { 42 | // return accessController.getRoleAdmin(role); 43 | // } 44 | // 45 | // function _grantRole(bytes32 role, address account) internal { 46 | // accessController.grantRole(role, account); 47 | // } 48 | // 49 | // function _revokeRole(bytes32 role, address account) internal { 50 | // accessController.revokeRole(role, account); 51 | // } 52 | // 53 | // function _renounceRole(bytes32 role, address account) internal { 54 | // accessController.renounceRole(role, account); 55 | // } 56 | } 57 | -------------------------------------------------------------------------------- /src/solver/helpers/ArraysConverter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | contract ArraysConverter { 5 | /** 6 | * @notice Converts two uint256 values into a uint256 array 7 | * @param a The first uint256 value 8 | * @param b The second uint256 value 9 | * @return values The array containing the two uint256 inputs 10 | */ 11 | function toUint256Array(uint256 a, uint256 b) public pure returns (uint256[] memory) { 12 | uint256[] memory values = new uint256[](2); 13 | values[0] = a; 14 | values[1] = b; 15 | return values; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/solver/helpers/BlockchainInfo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | /** 5 | * @dev A contract to retrieve basic blockchain information 6 | */ 7 | contract BlockchainInfo { 8 | /** 9 | * @notice Get the current block number 10 | * @return The current block number 11 | */ 12 | function getCurrentBlockNumber() public view returns (uint256) { 13 | return block.number; 14 | } 15 | 16 | /** 17 | * @notice Get the current block timestamp 18 | * @return The current block timestamp in seconds since Unix epoch 19 | */ 20 | function getCurrentBlockTimestamp() public view returns (uint256) { 21 | // solhint-disable-next-line not-rely-on-time 22 | return block.timestamp; 23 | } 24 | 25 | /** 26 | * @notice Get the current block gas limit 27 | * @return The current block gas limit 28 | */ 29 | function getCurrentBlockGasLimit() public view returns (uint256) { 30 | return block.gaslimit; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/solver/helpers/Bytes32.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | contract Bytes32 { 5 | /** 6 | * @notice Converts a bytes32 value to a uint256 value. 7 | * @param val The bytes32 value to be converted. 8 | * @return The converted uint256 value. 9 | */ 10 | function toUint256(bytes32 val) public pure returns (uint256) { 11 | return uint256(val); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/solver/helpers/Integer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | contract Integer { 5 | /** 6 | * @notice NotLessThan error is emitted when a >= b. 7 | * @param a The first unsigned integer. 8 | * @param b The second unsigned integer. 9 | */ 10 | error NotLessThan(uint256 a, uint256 b); 11 | 12 | /** 13 | * @notice Checks if a given unsigned integer 'a' is greater than another unsigned integer 'b'. 14 | * @param a The first unsigned integer. 15 | * @param b The second unsigned integer. 16 | */ 17 | function isGte(uint256 a, uint256 b) public pure { 18 | if (a < b) { 19 | revert NotLessThan(a, b); 20 | } 21 | } 22 | 23 | /** 24 | * @notice Adds two unsigned integers and returns the result. 25 | * @param a The first unsigned integer. 26 | * @param b The second unsigned integer. 27 | * @return The sum of a and b. 28 | */ 29 | function add(uint256 a, uint256 b) public pure returns (uint256) { 30 | return a + b; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/solver/helpers/Tupler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | //slither-disable-start assembly 5 | contract Tupler { 6 | /** 7 | * @notice Extracts an element from a byte tuple at a given index. 8 | * @param tuple The input byte tuple from which to extract the element. 9 | * @param index The index of the element to be extracted (0-based). 10 | * @return The extracted bytes32 element. 11 | */ 12 | function extractElement(bytes memory tuple, uint256 index) public pure returns (bytes32) { 13 | // solhint-disable-next-line no-inline-assembly 14 | assembly { 15 | return(add(tuple, mul(add(index, 1), 32)), 32) 16 | } 17 | } 18 | } 19 | //slither-disable-end assembly 20 | -------------------------------------------------------------------------------- /src/solver/test/SolverCaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | // solhint-disable avoid-low-level-calls 5 | contract SolverCaller { 6 | function execute(address target, bytes32[] memory data32, bytes[] memory data) public { 7 | (bool success, bytes memory result) = 8 | target.delegatecall(abi.encodeWithSignature("execute(bytes32[],bytes[])", data32, data)); 9 | 10 | if (!success) { 11 | if (result.length == 0) revert("No reason found"); 12 | // solhint-disable-next-line no-inline-assembly 13 | assembly { 14 | revert(add(32, result), mload(result)) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/solver/test/TestableVM.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { VM } from "../VM.sol"; 5 | 6 | contract TestableVM is VM { 7 | function execute(bytes32[] calldata commands, bytes[] memory state) public payable returns (bytes[] memory) { 8 | return _execute(commands, state); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/stats/Stats.md: -------------------------------------------------------------------------------- 1 | # Stats 2 | 3 | The `Stats` contracts and libraries provide aggregated and consistent statistics for 4 | the destinations for Tokemak's LMPs. This allows all LMPs to use the same 5 | data when making rebalancing decisions. 6 | 7 | ## Architecture 8 | 9 | The diagram below provides the high-level architecture for the stats system 10 | 11 | ![Stats High Level](./images/stats_high_level.svg) 12 | 13 | - Templates are created for each unique type of entity that needs to be tracked. For example, LSTs (stETH, rETH), DEXs (Curve, Balancer, Maverick), Staking (Convex, Aura) 14 | - The templates are registered with the `StatsCalculatorFactory` 15 | - Concrete contracts for specific entities (e.g., Curve V1 stETH/ETH pool) are created by the `StatsCalculatorFactory` 16 | - Each concrete calculator has a unique `AprId` that is registered at the `StatsCalculatorRegistry` by the factory 17 | - The registry and AprId is used by the LMP and other calculators to find relevant stats contracts (more detail on this below) 18 | 19 | ## ![Stats Calculators](./calculators/Calculators.md) 20 | 21 | The purpose of the calculators is to store, augment, and clean data relevant for the LMPs. Each calculator must be snapshot by a keeper to stay up to date. 22 | 23 | The following section provides details on how calculators are used and maintained after being created by the factory. 24 | A `Convex Curve stETH/ETH` pool is used as an example, but the general logic applies to all calculators. 25 | 26 | ![Stats Example](./images/stats_example.svg) 27 | 28 | In this example, there are three stats calculators. Each adds additional stats relevant for the LMP to make rebalancing decisions. 29 | 30 | 1. stETH LST Stats Calculator: provides stats on the base LST, such as base yield, slashing events, price-to-book value 31 | 2. Curve V1 stETH/ETH Stats Calculator: provides stats on the DEX, primarily trading fees 32 | 3. Convex Curve V1 stETH/ETH Stats Calculator: provides additional incentive yields 33 | 34 | LMP Vaults read the highest-level stats calculator, in this case the Convex/Curve calculator, that provides a complete rollup of the information from the three calculators. 35 | Each calculator has a `current` method that the LMP calls. 36 | 37 | A keeper network is used to periodically snapshot new data. Each calculator type defines the frequency and conditions under which a snapshot should be taken. 38 | Importantly, each calculator only stores the required information to provide its stats. If it needs to provide stats from another calculator, 39 | those are read at the time of the request to ensure that data is consistent. 40 | -------------------------------------------------------------------------------- /src/stats/calculators/Calculators.md: -------------------------------------------------------------------------------- 1 | # Calculators 2 | 3 | The 'Calculators' contract provide three sets of descriptive statistics - those that describe the LST level performance, those that describe the Dex level performance and incentive token pricing. All base contracts exist in the 'base' folder. The 'BaseStatsCalculator' captures common behavior across all the calculators, performs security checks and general rollup behavior. 4 | The three sets are described below. 5 | 6 | 1. **LST Statistics** - The base contract is 'LSTCalculatorBase'. This includes the general implementation for the LST statistics calculation including time for snapshot for stats such as base APR and slashing events. It also describes the filtering process (inclusive of filter initialization) used to post-process the measurements made during each snapshot event. 7 | The protocol specific implementation of the LST statistics calculator exists outside of the base folder. Those are 'RethLSTCalculator', 'StethLSTCalculator', 'SwethLSTCalculator' & 'CbethLSTCalculator'. 8 | 2. **DEX Statistics** - The base contract for DEX level statistics exist in the base folder . Just like the LST contract, these implement the common filter logic used for fee APR and related processing. They are 'CurvePoolNoRebasingCalculator', 'CurvePoolRebasingCalculator', 'BalancerStablePoolCalculatorBase'. These describe the common implementation inherited by the specific DEX statistics contracts - 'CurveV1PoolNoRebasingStatsCalculator', 'CurveV1PoolRebasingStatsCalculator'. 9 | 3. **Incentive Pricing** - The contract 'IncentivePricingStats' models the post-processing needed to provide a robust estimate of incentive pricing to the LMP. This is to be used to determine the incentive APR in units of ETH. The logic implements two filters - slow & fast. The final estimate of incentive price is the combination of the two filters. The snapshots for price is taken at randomized intervals triggered by a external cronjob. Thus, the method 'snapshot' in this contract is permissioned implemented with a modifier 'OnlyStatsSnapshot'. This modifier requires that the caller must have a registered role for triggering snapshots. 10 | -------------------------------------------------------------------------------- /src/stats/calculators/CbethLSTCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 6 | import { IStakedTokenV1 } from "src/interfaces/external/coinbase/IStakedTokenV1.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | 9 | contract CbethLSTCalculator is LSTCalculatorBase { 10 | constructor(ISystemRegistry _systemRegistry) LSTCalculatorBase(_systemRegistry) { } 11 | 12 | function calculateEthPerToken() public view override returns (uint256) { 13 | return IStakedTokenV1(lstTokenAddress).exchangeRate(); 14 | } 15 | 16 | function isRebasing() public pure override returns (bool) { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/stats/calculators/CurveV1PoolNoRebasingStatsCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Stats } from "src/stats/Stats.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | import { ICurveRegistry } from "src/interfaces/external/curve/ICurveRegistry.sol"; 8 | import { CurvePoolNoRebasingCalculatorBase } from "src/stats/calculators/base/CurvePoolNoRebasingCalculatorBase.sol"; 9 | import { IStatsCalculator } from "src/interfaces/stats/IStatsCalculator.sol"; 10 | import { IDexLSTStats } from "src/interfaces/stats/IDexLSTStats.sol"; 11 | import { ICurveV1StableSwap } from "src/interfaces/external/curve/ICurveV1StableSwap.sol"; 12 | import { ICurveOwner } from "src/interfaces/external/curve/ICurveOwner.sol"; 13 | 14 | /// @title Curve V1 Pool No Rebasing 15 | /// @notice Calculate stats for a Curve V1 StableSwap pool 16 | /// @dev Do not use this contract for pools with ETH due to a reentrancy issue 17 | contract CurveV1PoolNoRebasingStatsCalculator is CurvePoolNoRebasingCalculatorBase { 18 | constructor(ISystemRegistry _systemRegistry) CurvePoolNoRebasingCalculatorBase(_systemRegistry) { } 19 | 20 | function getVirtualPrice() internal view override returns (uint256 virtualPrice) { 21 | // NOTE: this contract is not intended to be used with ETH pools due to reentrancy issues 22 | return ICurveV1StableSwap(poolAddress).get_virtual_price(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/stats/calculators/CurveV1PoolRebasingStatsCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Stats } from "src/stats/Stats.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | import { ICurveRegistry } from "src/interfaces/external/curve/ICurveRegistry.sol"; 8 | import { CurvePoolRebasingCalculatorBase } from "src/stats/calculators/base/CurvePoolRebasingCalculatorBase.sol"; 9 | import { IStatsCalculator } from "src/interfaces/stats/IStatsCalculator.sol"; 10 | import { IDexLSTStats } from "src/interfaces/stats/IDexLSTStats.sol"; 11 | import { ICurveV1StableSwap } from "src/interfaces/external/curve/ICurveV1StableSwap.sol"; 12 | import { ICurveOwner } from "src/interfaces/external/curve/ICurveOwner.sol"; 13 | 14 | /// @title Curve V1 Pool With Rebasing Tokens 15 | /// @notice Calculate stats for a Curve V1 StableSwap pool 16 | contract CurveV1PoolRebasingStatsCalculator is CurvePoolRebasingCalculatorBase { 17 | constructor(ISystemRegistry _systemRegistry) CurvePoolRebasingCalculatorBase(_systemRegistry) { } 18 | 19 | function getVirtualPrice() internal override returns (uint256 virtualPrice) { 20 | ICurveV1StableSwap pool = ICurveV1StableSwap(poolAddress); 21 | ICurveOwner(pool.owner()).withdraw_admin_fees(address(pool)); 22 | 23 | return pool.get_virtual_price(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/stats/calculators/RethLSTCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 6 | import { IRocketTokenRETHInterface } from "src/interfaces/external/rocket-pool/IRocketTokenRETHInterface.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | 9 | contract RethLSTCalculator is LSTCalculatorBase { 10 | constructor(ISystemRegistry _systemRegistry) LSTCalculatorBase(_systemRegistry) { } 11 | 12 | function calculateEthPerToken() public view override returns (uint256) { 13 | return IRocketTokenRETHInterface(lstTokenAddress).getExchangeRate(); 14 | } 15 | 16 | function isRebasing() public pure override returns (bool) { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/stats/calculators/StethLSTCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 6 | import { IstEth } from "src/interfaces/external/lido/IstEth.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | 9 | contract StethLSTCalculator is LSTCalculatorBase { 10 | constructor(ISystemRegistry _systemRegistry) LSTCalculatorBase(_systemRegistry) { } 11 | 12 | function calculateEthPerToken() public view override returns (uint256) { 13 | return IstEth(lstTokenAddress).getPooledEthByShares(1 ether); 14 | } 15 | 16 | function isRebasing() public pure override returns (bool) { 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/stats/calculators/SwethLSTCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 6 | import { IswETH } from "src/interfaces/external/swell/IswETH.sol"; 7 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 8 | 9 | contract SwethLSTCalculator is LSTCalculatorBase { 10 | constructor(ISystemRegistry _systemRegistry) LSTCalculatorBase(_systemRegistry) { } 11 | 12 | function calculateEthPerToken() public view override returns (uint256) { 13 | return IswETH(lstTokenAddress).swETHToETHRate(); 14 | } 15 | 16 | function isRebasing() public pure override returns (bool) { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/stats/calculators/base/BaseStatsCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Stats } from "src/stats/Stats.sol"; 6 | import { Roles } from "src/libs/Roles.sol"; 7 | import { Errors } from "src/utils/Errors.sol"; 8 | import { SecurityBase } from "src/security/SecurityBase.sol"; 9 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 10 | import { IStatsCalculator } from "src/interfaces/stats/IStatsCalculator.sol"; 11 | import { ICurveRegistry } from "src/interfaces/external/curve/ICurveRegistry.sol"; 12 | import { IStatsCalculatorRegistry } from "src/interfaces/stats/IStatsCalculatorRegistry.sol"; 13 | 14 | /// @title Base Stats Calculator 15 | /// @notice Captures common behavior across all calculators 16 | /// @dev Performs security checks and general roll-up behavior 17 | abstract contract BaseStatsCalculator is IStatsCalculator, SecurityBase { 18 | ISystemRegistry public immutable systemRegistry; 19 | 20 | modifier onlyStatsSnapshot() { 21 | if (!_hasRole(Roles.STATS_SNAPSHOT_ROLE, msg.sender)) { 22 | revert Errors.MissingRole(Roles.STATS_SNAPSHOT_ROLE, msg.sender); 23 | } 24 | _; 25 | } 26 | 27 | constructor(ISystemRegistry _systemRegistry) SecurityBase(address(_systemRegistry.accessController())) { 28 | systemRegistry = _systemRegistry; 29 | } 30 | 31 | /// @inheritdoc IStatsCalculator 32 | function snapshot() external override onlyStatsSnapshot { 33 | if (!shouldSnapshot()) { 34 | revert NoSnapshotTaken(); 35 | } 36 | _snapshot(); 37 | } 38 | 39 | /// @notice Capture stat data about this setup 40 | /// @dev This is protected by the STATS_SNAPSHOT_ROLE 41 | function _snapshot() internal virtual; 42 | 43 | /// @inheritdoc IStatsCalculator 44 | function shouldSnapshot() public view virtual returns (bool takeSnapshot); 45 | } 46 | -------------------------------------------------------------------------------- /src/stats/utils/CurveUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20Metadata } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; 6 | import { Stats } from "src/stats/Stats.sol"; 7 | 8 | library CurveUtils { 9 | function getDecimals(address token) internal view returns (uint256) { 10 | if (token == Stats.CURVE_ETH) { 11 | return 18; 12 | } else { 13 | return IERC20Metadata(token).decimals(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/strategy/LMPStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IDestinationVault } from "src/interfaces/vault/IDestinationVault.sol"; 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { IStrategy } from "src/interfaces/strategy/IStrategy.sol"; 8 | 9 | library LMPStrategy { 10 | /// @notice verify that a rebalance (swap between destinations) meets all the strategy constraints 11 | /// @dev Signature identical to IStrategy.verifyRebalance 12 | function verifyRebalance(IStrategy.RebalanceParams memory) 13 | internal 14 | pure 15 | returns (bool success, string memory message) 16 | { 17 | // short-circuit for now 18 | // TODO: proper checks 19 | return (true, ""); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/strategy/StrategyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | /* solhint-disable no-unused-vars */ 7 | /* solhint-disable state-mutability */ 8 | 9 | contract StrategyFactory { 10 | // solhint-disable-next-line no-unused-vars 11 | function createStrategy(address[] memory) public pure returns (address) { 12 | // NOTE: shortcircuited just as a place holder for now 13 | return address(1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/swapper/README.md: -------------------------------------------------------------------------------- 1 | # On-Chain Swappers 2 | 3 | The SwapRouter defined here is used for swapping assets back to the base asset during a users withdrawal flow. This registry of routes will need to be maintained to ensure optimal results for the user. Each of these swappers take and enforce a "min amount" check, but because of how they are used will always receive 0 for that value. An off-chain users withdrawal flow should always start at the LMPVaultRouter which forces an account for slippage. Any on-chain interaction with the vaults should enforce their own slippage checks as they see fit. 4 | -------------------------------------------------------------------------------- /src/swapper/adapters/BaseAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Errors } from "src/utils/Errors.sol"; 6 | import { ISwapRouter } from "src/interfaces/swapper/ISwapRouter.sol"; 7 | import { ISyncSwapper } from "src/interfaces/swapper/ISyncSwapper.sol"; 8 | 9 | /// @dev Reminder from ISyncSwapper: we're adopting an "exact in, variable out" model for all our swaps. This ensures 10 | /// that the entire sellAmount is used, eliminating the need for additional balance checks and refunds. This model is 11 | /// expected to be followed by all swapper implementations to maintain consistency and to optimize for gas efficiency. 12 | abstract contract BaseAdapter is ISyncSwapper { 13 | ISwapRouter public immutable router; 14 | 15 | constructor(address _router) { 16 | Errors.verifyNotZero(_router, "router"); 17 | router = ISwapRouter(_router); 18 | } 19 | 20 | /// @dev Reverts if the delegate caller is not the router. 21 | modifier onlyRouter() { 22 | if (address(this) != address(router)) revert Errors.AccessDenied(); 23 | _; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/swapper/adapters/CurveV2Swap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IERC20, SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | import { ICurveV2Swap } from "src/interfaces/external/curve/ICurveV2Swap.sol"; 8 | import { ISwapRouter } from "src/interfaces/swapper/ISwapRouter.sol"; 9 | import { BaseAdapter, ISyncSwapper } from "src/swapper/adapters/BaseAdapter.sol"; 10 | 11 | contract CurveV2Swap is BaseAdapter { 12 | using SafeERC20 for IERC20; 13 | 14 | constructor(address _router) BaseAdapter(_router) { } 15 | 16 | /// @inheritdoc ISyncSwapper 17 | function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view override { 18 | (uint256 sellIndex, uint256 buyIndex) = abi.decode(swapData.data, (uint256, uint256)); 19 | 20 | ICurveV2Swap pool = ICurveV2Swap(swapData.pool); 21 | 22 | address sellAddress = pool.coins(sellIndex); 23 | address buyAddress = pool.coins(buyIndex); 24 | 25 | // verify that the fromAddress and toAddress are in the pool 26 | if (fromAddress != sellAddress) revert DataMismatch("fromAddress"); 27 | if (swapData.token != buyAddress) revert DataMismatch("toAddress"); 28 | } 29 | 30 | /// @inheritdoc ISyncSwapper 31 | function swap( 32 | address poolAddress, 33 | address sellTokenAddress, 34 | uint256 sellAmount, 35 | address, 36 | uint256 minBuyAmount, 37 | bytes memory data 38 | ) external override onlyRouter returns (uint256) { 39 | (uint256 sellIndex, uint256 buyIndex) = abi.decode(data, (uint256, uint256)); 40 | ICurveV2Swap pool = ICurveV2Swap(poolAddress); 41 | 42 | IERC20(sellTokenAddress).safeApprove(poolAddress, sellAmount); 43 | 44 | return pool.exchange(sellIndex, buyIndex, sellAmount, minBuyAmount); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/Arrays.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | library Arrays { 7 | /// @notice Convert Curve token and array data into a dynamic array 8 | /// @param tokens fixed 8 item array from Curve 9 | /// @param numTokens number of reported actual tokens from Curve 10 | /// @return dynTokens array of token data with numToken number of items 11 | function convertFixedCurveTokenArrayToDynamic( 12 | address[8] memory tokens, 13 | uint256 numTokens 14 | ) external pure returns (address[] memory dynTokens) { 15 | dynTokens = new address[](numTokens); 16 | for (uint256 i = 0; i < numTokens;) { 17 | dynTokens[i] = tokens[i]; 18 | 19 | unchecked { 20 | ++i; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/CurveResolverMainnet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | import { ICurveResolver } from "src/interfaces/utils/ICurveResolver.sol"; 8 | import { ICurveMetaRegistry } from "src/interfaces/external/curve/ICurveMetaRegistry.sol"; 9 | 10 | contract CurveResolverMainnet is ICurveResolver { 11 | ICurveMetaRegistry public immutable curveMetaRegistry; 12 | 13 | constructor(ICurveMetaRegistry _curveMetaRegistry) { 14 | Errors.verifyNotZero(address(_curveMetaRegistry), "_curveMetaRegistry"); 15 | 16 | curveMetaRegistry = _curveMetaRegistry; 17 | } 18 | 19 | /// @inheritdoc ICurveResolver 20 | function resolve(address poolAddress) 21 | public 22 | view 23 | returns (address[8] memory tokens, uint256 numTokens, bool isStableSwap) 24 | { 25 | Errors.verifyNotZero(poolAddress, "poolAddress"); 26 | 27 | tokens = curveMetaRegistry.get_coins(poolAddress); 28 | numTokens = curveMetaRegistry.get_n_coins(poolAddress); 29 | 30 | // Using the presence of a gamma() fn as an indicator of pool type 31 | // Zero check for the poolAddress is above 32 | // slither-disable-start low-level-calls,missing-zero-check,unchecked-lowlevel 33 | // solhint-disable-next-line avoid-low-level-calls 34 | (bool success,) = poolAddress.staticcall(abi.encodeWithSignature("gamma()")); 35 | // slither-disable-end low-level-calls,missing-zero-check,unchecked-lowlevel 36 | 37 | isStableSwap = !success; 38 | } 39 | 40 | /// @inheritdoc ICurveResolver 41 | function resolveWithLpToken(address poolAddress) 42 | external 43 | view 44 | returns (address[8] memory tokens, uint256 numTokens, address lpToken, bool isStableSwap) 45 | { 46 | (tokens, numTokens, isStableSwap) = resolve(poolAddress); 47 | 48 | lpToken = curveMetaRegistry.get_lp_token(poolAddress); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/Multicall.sol: -------------------------------------------------------------------------------- 1 | // forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | pragma solidity 0.8.17; 4 | pragma abicoder v2; 5 | 6 | import { IMulticall } from "src/interfaces/utils/IMulticall.sol"; 7 | 8 | /// @title Multicall 9 | /// @notice Enables calling multiple methods in a single call to the contract 10 | abstract contract Multicall is IMulticall { 11 | /// @inheritdoc IMulticall 12 | function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) { 13 | results = new bytes[](data.length); 14 | 15 | /* solhint-disable avoid-low-level-calls, reason-string, no-inline-assembly */ 16 | for (uint256 i = 0; i < data.length; i++) { 17 | // slither-disable-next-line delegatecall-loop,low-level-calls 18 | (bool success, bytes memory result) = address(this).delegatecall(data[i]); 19 | 20 | if (!success) { 21 | // Next 5 lines from https://ethereum.stackexchange.com/a/83577 22 | if (result.length < 68) revert(); 23 | // slither-disable-next-line assembly 24 | assembly { 25 | result := add(result, 0x04) 26 | } 27 | revert(abi.decode(result, (string))); 28 | } 29 | 30 | results[i] = result; 31 | } 32 | /* solhint-enable avoid-low-level-calls, reason-string, no-inline-assembly */ 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/NonReentrant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | import { Errors } from "src/utils/Errors.sol"; 7 | 8 | /// @title Copy of OZ's ReentrancyGuard with a read only variant added 9 | abstract contract NonReentrant { 10 | uint256 private constant _NOT_ENTERED = 1; 11 | uint256 private constant _ENTERED = 2; 12 | 13 | uint256 private _status; 14 | 15 | constructor() { 16 | _status = _NOT_ENTERED; 17 | } 18 | 19 | modifier nonReentrantReadOnly() { 20 | require(_status != _ENTERED, "NonReentrant"); 21 | _; 22 | } 23 | 24 | modifier nonReentrant() { 25 | _nonReentrantBefore(); 26 | _; 27 | _nonReentrantAfter(); 28 | } 29 | 30 | function _nonReentrantBefore() private { 31 | // On the first call to nonReentrant, _status will be _NOT_ENTERED 32 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 33 | 34 | // Any calls to nonReentrant after this point will fail 35 | _status = _ENTERED; 36 | } 37 | 38 | function _nonReentrantAfter() private { 39 | // By storing the original value once again, a refund is triggered (see 40 | // https://eips.ethereum.org/EIPS/eip-2200) 41 | _status = _NOT_ENTERED; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/PeripheryPayments.sol: -------------------------------------------------------------------------------- 1 | // forked from https://github.com/fei-protocol/ERC4626/blob/main/src/external/PeripheryPayments.sol 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | pragma solidity >=0.8.7 < 0.9.0; 4 | 5 | import { IWETH9 } from "src/interfaces/utils/IWETH9.sol"; 6 | import { IERC20, SafeERC20, Address } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | /** 9 | * @title Periphery Payments 10 | * @notice Immutable state used by periphery contracts 11 | * Largely Forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol 12 | * Changes: 13 | * no interface 14 | * no inheritdoc 15 | * add immutable WETH9 in constructor instead of PeripheryImmutableState 16 | * receive from any address 17 | * Solmate interfaces and transfer lib 18 | * casting 19 | * add approve, wrapWETH9 and pullToken 20 | */ 21 | abstract contract PeripheryPayments { 22 | using SafeERC20 for IERC20; 23 | 24 | IWETH9 public immutable weth9; 25 | 26 | error InsufficientWETH9(); 27 | error InsufficientToken(); 28 | 29 | constructor(IWETH9 _weth9) { 30 | weth9 = _weth9; 31 | } 32 | 33 | receive() external payable { } 34 | 35 | function approve(IERC20 token, address to, uint256 amount) public payable { 36 | token.safeApprove(to, amount); 37 | } 38 | 39 | function unwrapWETH9(uint256 amountMinimum, address recipient) public payable { 40 | uint256 balanceWETH9 = weth9.balanceOf(address(this)); 41 | 42 | if (balanceWETH9 < amountMinimum) revert InsufficientWETH9(); 43 | 44 | if (balanceWETH9 > 0) { 45 | weth9.withdraw(balanceWETH9); 46 | Address.sendValue(payable(recipient), balanceWETH9); 47 | } 48 | } 49 | 50 | function wrapWETH9() public payable { 51 | if (address(this).balance > 0) weth9.deposit{ value: address(this).balance }(); // wrap everything 52 | } 53 | 54 | function pullToken(IERC20 token, uint256 amount, address recipient) public payable { 55 | token.safeTransferFrom(msg.sender, recipient, amount); 56 | } 57 | 58 | function sweepToken(IERC20 token, uint256 amountMinimum, address recipient) public payable { 59 | uint256 balanceToken = token.balanceOf(address(this)); 60 | if (balanceToken < amountMinimum) revert InsufficientToken(); 61 | 62 | if (balanceToken > 0) { 63 | token.safeTransfer(recipient, balanceToken); 64 | } 65 | } 66 | 67 | function refundETH() external payable { 68 | if (address(this).balance > 0) Address.sendValue(payable(msg.sender), address(this).balance); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/utils/SelfPermit.sol: -------------------------------------------------------------------------------- 1 | // forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/SelfPermit.sol 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | pragma solidity >=0.8.7; 4 | 5 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 6 | import { IERC20Permit } from "openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; 7 | 8 | import { ISelfPermit } from "src/interfaces/utils/ISelfPermit.sol"; 9 | import { IERC20PermitAllowed } from "src/interfaces/utils/IERC20PermitAllowed.sol"; 10 | 11 | /// @title Self Permit 12 | /// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route 13 | /// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a 14 | // contract and call a function that requires an approval in a single transaction. 15 | abstract contract SelfPermit is ISelfPermit { 16 | /// @inheritdoc ISelfPermit 17 | function selfPermit( 18 | address token, 19 | uint256 value, 20 | uint256 deadline, 21 | uint8 v, 22 | bytes32 r, 23 | bytes32 s 24 | ) public payable override { 25 | IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s); 26 | } 27 | 28 | /// @inheritdoc ISelfPermit 29 | function selfPermitIfNecessary( 30 | address token, 31 | uint256 value, 32 | uint256 deadline, 33 | uint8 v, 34 | bytes32 r, 35 | bytes32 s 36 | ) external payable override { 37 | if (IERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s); 38 | } 39 | 40 | /// @inheritdoc ISelfPermit 41 | function selfPermitAllowed( 42 | address token, 43 | uint256 nonce, 44 | uint256 expiry, 45 | uint8 v, 46 | bytes32 r, 47 | bytes32 s 48 | ) public payable override { 49 | IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s); 50 | } 51 | 52 | /// @inheritdoc ISelfPermit 53 | function selfPermitAllowedIfNecessary( 54 | address token, 55 | uint256 nonce, 56 | uint256 expiry, 57 | uint8 v, 58 | bytes32 r, 59 | bytes32 s 60 | ) external payable override { 61 | if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max) { 62 | selfPermitAllowed(token, nonce, expiry, v, r, s); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/utils/univ3/Path.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | // This file is a direct duplicate of https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/Path.sol. 5 | // However, due to version constraints, it has been copied into the project instead of being installed as a module. 6 | // Unused functions have been removed. 7 | 8 | pragma solidity 0.8.17; 9 | 10 | import { BytesLib } from "src/utils/univ3/BytesLib.sol"; 11 | 12 | /// @title Functions for manipulating path data for multihop swaps 13 | library Path { 14 | using BytesLib for bytes; 15 | 16 | /// @dev The length of the bytes encoded address 17 | uint256 private constant ADDR_SIZE = 20; 18 | /// @dev The length of the bytes encoded fee 19 | uint256 private constant FEE_SIZE = 3; 20 | 21 | /// @dev The offset of a single token address and pool fee 22 | uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; 23 | /// @dev The offset of an encoded pool key 24 | uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; 25 | /// @dev The minimum length of an encoding that contains 2 or more pools 26 | uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET; 27 | 28 | /// @notice Returns true iff the path contains two or more pools 29 | /// @param path The encoded swap path 30 | /// @return True if path contains two or more pools, otherwise false 31 | function hasMultiplePools(bytes memory path) internal pure returns (bool) { 32 | return path.length >= MULTIPLE_POOLS_MIN_LENGTH; 33 | } 34 | 35 | /// @notice Decodes the first pool in path 36 | /// @param path The bytes encoded swap path 37 | /// @return tokenA The first token of the given pool 38 | /// @return tokenB The second token of the given pool 39 | /// @return fee The fee level of the pool 40 | function decodeFirstPool(bytes memory path) internal pure returns (address tokenA, address tokenB, uint24 fee) { 41 | tokenA = path.toAddress(0); 42 | fee = path.toUint24(ADDR_SIZE); 43 | tokenB = path.toAddress(NEXT_OFFSET); 44 | } 45 | 46 | /// @notice Skips a token + fee element from the buffer and returns the remainder 47 | /// @param path The swap path 48 | /// @return The remaining token + fee elements in the path 49 | function skipToken(bytes memory path) internal pure returns (bytes memory) { 50 | return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/vault/VaultTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | library VaultTypes { 6 | bytes32 public constant LST = keccak256("LST"); 7 | bytes32 public constant STABLE = keccak256("STABLE"); 8 | } 9 | -------------------------------------------------------------------------------- /test/access/Ownable2Step.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | // solhint-disable func-name-mixedcase 6 | // solhint-disable max-states-count 7 | 8 | import { Test, StdCheats, StdUtils } from "forge-std/Test.sol"; 9 | import { TestERC20 } from "test/mocks/TestERC20.sol"; 10 | import { LibAdapter } from "src/libs/LibAdapter.sol"; 11 | import { Ownable2Step } from "src/access/Ownable2Step.sol"; 12 | 13 | contract Ownable2StepTest is Test { 14 | OwnableOps private ops; 15 | 16 | function setUp() public { 17 | ops = new OwnableOps(); 18 | } 19 | 20 | function test_renounceOwner_IsNotPermitted() public { 21 | vm.expectRevert(); 22 | ops.renounceOwnership(); 23 | 24 | ops.run(); 25 | assertTrue(ops.ran()); 26 | } 27 | 28 | function test_onlyOwner_ProtectsFunctions() public { 29 | assertTrue(!ops.ran()); 30 | 31 | vm.startPrank(address(6)); 32 | vm.expectRevert(); 33 | ops.run(); 34 | vm.stopPrank(); 35 | 36 | assertTrue(!ops.ran()); 37 | ops.run(); 38 | assertTrue(ops.ran()); 39 | } 40 | } 41 | 42 | contract OwnableOps is Ownable2Step { 43 | bool public ran; 44 | 45 | function run() external onlyOwner { 46 | ran = true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/base/CamelotBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | 7 | import { INFTPool } from "src/interfaces/external/camelot/INFTPool.sol"; 8 | 9 | /// @notice This contract is used to test Camelot contracts. 10 | abstract contract CamelotBase is Test { 11 | function getNFTs(address whale, address nftPoolAddress) public view returns (uint256[] memory) { 12 | INFTPool nftPool = INFTPool(nftPoolAddress); 13 | uint256 length = nftPool.balanceOf(whale); 14 | uint256[] memory tokenIds = new uint256[](length); 15 | for (uint256 i = 0; i < length; i++) { 16 | uint256 tokenId = nftPool.tokenOfOwnerByIndex(whale, i); 17 | tokenIds[i] = tokenId; 18 | } 19 | return tokenIds; 20 | } 21 | 22 | function transferNFTsTo(address nftPoolAddress, address from, address to) public { 23 | INFTPool nftPool = INFTPool(nftPoolAddress); 24 | uint256[] memory tokenIds = getNFTs(from, nftPoolAddress); 25 | uint256 length = tokenIds.length; 26 | for (uint256 i = 0; i < length; i++) { 27 | nftPool.transferFrom(from, to, tokenIds[i]); 28 | } 29 | } 30 | 31 | function increase1Week() public { 32 | // Move 7 days later 33 | vm.roll(block.number + 7200 * 7); 34 | // solhint-disable-next-line not-rely-on-time 35 | vm.warp(block.timestamp + 7 days); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/deployments/Deployment.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | import { Test, StdCheats } from "forge-std/Test.sol"; 6 | 7 | import { SystemRegistry } from "src/SystemRegistry.sol"; 8 | import { AccessController } from "src/security/AccessController.sol"; 9 | import { DestinationVaultRegistry } from "src/vault/DestinationVaultRegistry.sol"; 10 | import { ILMPVaultRegistry } from "src/interfaces/vault/ILMPVaultRegistry.sol"; 11 | import { IDestinationVaultRegistry } from "src/interfaces/vault/IDestinationVaultRegistry.sol"; 12 | import { IAccessController } from "src/interfaces/security/IAccessController.sol"; 13 | import { TOKE_MAINNET, WETH_MAINNET } from "test/utils/Addresses.sol"; 14 | 15 | contract DeploymentTest is Test { 16 | address public owner; 17 | SystemRegistry private _systemRegistry; 18 | AccessController private _accessController; 19 | DestinationVaultRegistry private _destinationVaultRegistry; 20 | 21 | function setUp() public { 22 | _systemRegistry = new SystemRegistry(TOKE_MAINNET, WETH_MAINNET); 23 | _accessController = new AccessController(address(_systemRegistry)); 24 | 25 | _systemRegistry.setAccessController(address(_accessController)); 26 | 27 | _destinationVaultRegistry = new DestinationVaultRegistry(_systemRegistry); 28 | 29 | _systemRegistry.setDestinationVaultRegistry(address(_destinationVaultRegistry)); 30 | } 31 | 32 | function testInitialDeployment() public { } 33 | } 34 | -------------------------------------------------------------------------------- /test/libs/BalancerUtilities.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | // solhint-disable func-name-mixedcase 6 | 7 | import { Test, StdCheats, StdUtils } from "forge-std/Test.sol"; 8 | import { BalancerUtilities } from "src/libs/BalancerUtilities.sol"; 9 | import { WSETH_RETH_SFRXETH_BAL_POOL, WSETH_WETH_BAL_POOL } from "test/utils/Addresses.sol"; 10 | 11 | contract BalancerUtilitiesTest is Test { 12 | function setUp() public { 13 | uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL")); 14 | vm.selectFork(mainnetFork); 15 | } 16 | 17 | function test_isComposablePool_ReturnsTrueOnValidComposable() public { 18 | assertTrue(BalancerUtilities.isComposablePool(WSETH_RETH_SFRXETH_BAL_POOL)); 19 | } 20 | 21 | function test_isComposablePool_ReturnsFalseOnMetastable() public { 22 | assertFalse(BalancerUtilities.isComposablePool(WSETH_WETH_BAL_POOL)); 23 | } 24 | 25 | function test_isComposablePool_ReturnsFalseOnEOA() public { 26 | assertFalse(BalancerUtilities.isComposablePool(vm.addr(5))); 27 | } 28 | 29 | function test_isComposablePool_ReturnsFalseOnInvalidContract() public { 30 | assertFalse(BalancerUtilities.isComposablePool(address(new Noop()))); 31 | } 32 | } 33 | 34 | contract Noop { } 35 | -------------------------------------------------------------------------------- /test/libs/ConvexRewards.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 7 | import { ConvexRewards } from "src/libs/ConvexRewards.sol"; 8 | import { CVX_MAINNET } from "test/utils/Addresses.sol"; 9 | 10 | contract ConvexRewardTest is Test { 11 | using ConvexRewards for address; 12 | 13 | function testCVXRewardHistoric() public { 14 | // targeting this transaction: 15 | // https://etherscan.io/tx/0x46314e9730ef79c43135de478daf04b08acec36095045376f46ec7920daef0bb 16 | vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 17_580_732); 17 | uint256 crvEarned = 43_632_695_514_897_054_325; 18 | uint256 expectedCVX = 698_123_128_238_352_869; 19 | 20 | uint256 cvxAmount = ConvexRewards.getCVXMintAmount(CVX_MAINNET, crvEarned); 21 | 22 | assertEq(cvxAmount, expectedCVX); 23 | } 24 | 25 | function testCVXRewardIfTotalSupplyIsZero() public { 26 | checkCVXMintAmount(0, 987, 987); 27 | } 28 | 29 | function testCVXRewardIfInCliffs() public { 30 | uint256 totalSupply = 101_000 * 1e18; // each cliff is 100_000 tokens, so we're in the 1st cliff (zero indexed) 31 | uint256 crvEarned = 898_000; 32 | uint256 expectedCVX = crvEarned * 999 / 1000; 33 | checkCVXMintAmount(totalSupply, crvEarned, expectedCVX); 34 | } 35 | 36 | function testCVXRewardIfInLastCliff() public { 37 | uint256 totalSupply = 99_999_000 * 1e18; // leaves 1000 tokens 38 | uint256 crvEarned = 1001 * 1000 * 1e18; // try to exceed the max supply 39 | uint256 expectedCVX = 1000 * 1e18; // expect to only get the remaining 1000 tokens, not 1001 40 | 41 | address cvx = vm.addr(totalSupply + crvEarned); 42 | vm.mockCall(cvx, abi.encodeWithSelector(IERC20.totalSupply.selector), abi.encode(totalSupply)); 43 | 44 | uint256 cvxAmount = cvx.getCVXMintAmount(crvEarned); 45 | 46 | assertEq(cvxAmount, expectedCVX); 47 | } 48 | 49 | function checkCVXMintAmount(uint256 totalSupply, uint256 crvEarned, uint256 expectedCVXAmount) internal { 50 | address cvx = vm.addr(totalSupply + crvEarned); 51 | vm.mockCall(cvx, abi.encodeWithSelector(IERC20.totalSupply.selector), abi.encode(totalSupply)); 52 | 53 | uint256 cvxAmount = cvx.getCVXMintAmount(crvEarned); 54 | 55 | assertEq(cvxAmount, expectedCVXAmount); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/libs/LibAdapter.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | import { Test, StdCheats, StdUtils } from "forge-std/Test.sol"; 6 | import { TestERC20 } from "test/mocks/TestERC20.sol"; 7 | import { LibAdapter } from "src/libs/LibAdapter.sol"; 8 | 9 | contract LibAdapterTests is Test { 10 | TestERC20 private token; 11 | 12 | function setUp() public { 13 | token = new TestERC20("token", "token"); 14 | } 15 | 16 | function testApproveZeroToNot() public { 17 | address user1 = vm.addr(1); 18 | LibAdapter._approve(token, user1, 10); 19 | 20 | uint256 queried = token.allowance(address(this), user1); 21 | 22 | assertEq(queried, 10); 23 | } 24 | 25 | function testAllowanceGreaterThanZeroLessThanAmount() public { 26 | address user1 = vm.addr(1); 27 | LibAdapter._approve(token, user1, 10); 28 | LibAdapter._approve(token, user1, 30); 29 | 30 | uint256 queried = token.allowance(address(this), user1); 31 | 32 | assertEq(queried, 30); 33 | } 34 | 35 | function testAllowanceGreaterThanZeroGreaterThanAmount() public { 36 | address user1 = vm.addr(1); 37 | LibAdapter._approve(token, user1, 100); 38 | LibAdapter._approve(token, user1, 30); 39 | 40 | uint256 queried = token.allowance(address(this), user1); 41 | 42 | assertEq(queried, 30); 43 | } 44 | 45 | function testAllowanceGreaterThanZeroToZero() public { 46 | address user1 = vm.addr(1); 47 | LibAdapter._approve(token, user1, 100); 48 | LibAdapter._approve(token, user1, 30); 49 | LibAdapter._approve(token, user1, 0); 50 | 51 | uint256 queried = token.allowance(address(this), user1); 52 | 53 | assertEq(queried, 0); 54 | } 55 | 56 | function testAllowanceIncreasing() public { 57 | address user1 = vm.addr(1); 58 | LibAdapter._approve(token, user1, 100); 59 | LibAdapter._approve(token, user1, 200); 60 | 61 | uint256 queried = token.allowance(address(this), user1); 62 | 63 | assertEq(queried, 200); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/mocks/MockERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | import { ERC20 } from "openzeppelin-contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract MockERC20 is ERC20 { 8 | constructor() ERC20("Mock ERC20", "MOCK") { 9 | this; 10 | } 11 | 12 | function mint(address to, uint256 amount) public { 13 | _mint(to, amount); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/mocks/StakeTrackingMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { IStakeTracking } from "src/interfaces/rewarders/IStakeTracking.sol"; 6 | 7 | contract StakeTrackingMock is IStakeTracking { 8 | function totalSupply() external pure returns (uint256) { 9 | return 100_000_000_000_000_000; 10 | } 11 | 12 | function balanceOf(address) external pure returns (uint256) { 13 | return 100_000_000_000_000_000; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/mocks/TestERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | import { ERC20 } from "openzeppelin-contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract TestERC20 is ERC20 { 8 | uint8 private _decimals; 9 | 10 | constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { 11 | _decimals = 18; 12 | } 13 | 14 | function decimals() public view override returns (uint8) { 15 | return _decimals; 16 | } 17 | 18 | function setDecimals(uint8 newDecimals) external { 19 | _decimals = newDecimals; 20 | } 21 | 22 | function mint(address account, uint256 amount) external { 23 | _mint(account, amount); 24 | } 25 | 26 | function burn(address account, uint256 amount) external { 27 | _burn(account, amount); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/oracles/providers/EthPeggedOracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test, StdCheats, StdUtils } from "forge-std/Test.sol"; 6 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 7 | import { EthPeggedOracle } from "src/oracles/providers/EthPeggedOracle.sol"; 8 | 9 | contract EthPeggedOracleTests is Test { 10 | ISystemRegistry private _systemRegistry; 11 | EthPeggedOracle private _oracle; 12 | 13 | function setUp() public { 14 | _systemRegistry = ISystemRegistry(vm.addr(324)); 15 | _oracle = new EthPeggedOracle(_systemRegistry); 16 | } 17 | 18 | function testBasicPrice(address token) public { 19 | uint256 price = _oracle.getPriceInEth(token); 20 | 21 | assertEq(price, 1e18); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/oracles/providers/SwEthEthOracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | // solhint-disable func-name-mixedcase 7 | 8 | import { Test } from "forge-std/Test.sol"; 9 | import { SWETH_MAINNET } from "test/utils/Addresses.sol"; 10 | 11 | import { SwEthEthOracle } from "src/oracles/providers/SwEthEthOracle.sol"; 12 | import { Errors } from "src/utils/Errors.sol"; 13 | 14 | import { IswETH } from "src/interfaces/external/swell/IswETH.sol"; 15 | import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol"; 16 | 17 | contract SwEthEthOracleTest is Test { 18 | SwEthEthOracle public swEthOracle; 19 | 20 | function setUp() external { 21 | vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 17_400_000); 22 | swEthOracle = new SwEthEthOracle(ISystemRegistry(address(1)), IswETH(SWETH_MAINNET)); 23 | } 24 | 25 | // Test constructor 26 | function test_RevertsConstructor() external { 27 | vm.expectRevert(abi.encodeWithSelector(Errors.ZeroAddress.selector, "_systemRegistry")); 28 | new SwEthEthOracle(ISystemRegistry(address(0)), IswETH(SWETH_MAINNET)); 29 | 30 | vm.expectRevert(abi.encodeWithSelector(Errors.ZeroAddress.selector, "_swEth")); 31 | new SwEthEthOracle(ISystemRegistry(address(1)), IswETH(address(0))); 32 | } 33 | 34 | function test_StateVariablesSetConstructor() external { 35 | assertEq(address(swEthOracle.getSystemRegistry()), address(1)); 36 | assertEq(address(swEthOracle.swEth()), SWETH_MAINNET); 37 | } 38 | 39 | // Test pricing functionality 40 | function test_RevertNotSwEth() external { 41 | vm.expectRevert(abi.encodeWithSelector(Errors.InvalidToken.selector, address(2))); 42 | swEthOracle.getPriceInEth(address(2)); 43 | } 44 | 45 | function test_GetPriceInEth() external { 46 | uint256 price = swEthOracle.getPriceInEth(SWETH_MAINNET); 47 | assertGt(price, 0); 48 | } 49 | 50 | // Test get system registry 51 | function test_GetSystemRegistry() external { 52 | assertEq(address(swEthOracle.getSystemRegistry()), address(1)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/security/AccessControl.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | 4 | pragma solidity 0.8.17; 5 | 6 | // solhint-disable func-name-mixedcase 7 | // solhint-disable var-name-mixedcase 8 | 9 | import { BaseTest } from "test/BaseTest.t.sol"; 10 | 11 | contract AccessControlTest is BaseTest { 12 | function setUp() public virtual override(BaseTest) { 13 | BaseTest.setUp(); 14 | } 15 | 16 | function testRoles() public { 17 | bytes32 ROLE = keccak256("ROLE"); 18 | assertFalse(accessController.hasRole(ROLE, address(this))); 19 | accessController.setupRole(ROLE, address(this)); 20 | assertTrue(accessController.hasRole(ROLE, address(this))); 21 | accessController.revokeRole(ROLE, address(this)); 22 | assertFalse(accessController.hasRole(ROLE, address(this))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/stats/calculators/CbethLSTCalculator.t..sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { CbethLSTCalculator } from "src/stats/calculators/CbethLSTCalculator.sol"; 7 | import { SystemRegistry } from "src/SystemRegistry.sol"; 8 | import { AccessController } from "src/security/AccessController.sol"; 9 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 10 | import { CBETH_MAINNET, TOKE_MAINNET, WETH_MAINNET } from "test/utils/Addresses.sol"; 11 | import { Roles } from "src/libs/Roles.sol"; 12 | 13 | contract CbethLSTCalculatorTest is Test { 14 | function testStethEthPerToken() public { 15 | checkEthPerToken(17_272_708, 1_037_000_314_216_931_031); 16 | checkEthPerToken(17_279_454, 1_037_145_502_666_710_522); 17 | checkEthPerToken(17_286_461, 1_037_260_648_657_762_304); 18 | checkEthPerToken(17_293_521, 1_037_389_203_733_762_949); 19 | checkEthPerToken(17_393_019, 1_039_032_065_195_261_641); 20 | } 21 | 22 | function checkEthPerToken(uint256 targetBlock, uint256 expected) private { 23 | uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), targetBlock); 24 | vm.selectFork(mainnetFork); 25 | 26 | SystemRegistry systemRegistry = new SystemRegistry(TOKE_MAINNET, WETH_MAINNET); 27 | AccessController accessController = new AccessController(address(systemRegistry)); 28 | systemRegistry.setAccessController(address(accessController)); 29 | accessController.grantRole(Roles.STATS_SNAPSHOT_ROLE, address(this)); 30 | 31 | CbethLSTCalculator calculator = new CbethLSTCalculator(systemRegistry); 32 | bytes32[] memory dependantAprs = new bytes32[](0); 33 | LSTCalculatorBase.InitData memory initData = LSTCalculatorBase.InitData({ lstTokenAddress: CBETH_MAINNET }); 34 | calculator.initialize(dependantAprs, abi.encode(initData)); 35 | 36 | assertEq(calculator.calculateEthPerToken(), expected); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/stats/calculators/RethLSTCalculator.t..sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { StethLSTCalculator } from "src/stats/calculators/StethLSTCalculator.sol"; 7 | import { RethLSTCalculator } from "src/stats/calculators/RethLSTCalculator.sol"; 8 | import { SystemRegistry } from "src/SystemRegistry.sol"; 9 | import { AccessController } from "src/security/AccessController.sol"; 10 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 11 | import { RETH_MAINNET, TOKE_MAINNET, WETH_MAINNET } from "test/utils/Addresses.sol"; 12 | import { Roles } from "src/libs/Roles.sol"; 13 | 14 | contract RethLSTCalculatorTest is Test { 15 | function testStethEthPerToken() public { 16 | checkEthPerToken(17_272_708, 1_070_685_171_168_549_185); 17 | checkEthPerToken(17_279_454, 1_070_789_567_827_940_207); 18 | checkEthPerToken(17_286_461, 1_070_999_366_517_302_681); 19 | checkEthPerToken(17_293_521, 1_071_137_533_974_942_357); 20 | checkEthPerToken(17_393_019, 1_072_763_940_592_978_363); 21 | } 22 | 23 | function checkEthPerToken(uint256 targetBlock, uint256 expected) private { 24 | uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), targetBlock); 25 | vm.selectFork(mainnetFork); 26 | 27 | SystemRegistry systemRegistry = new SystemRegistry(TOKE_MAINNET, WETH_MAINNET); 28 | AccessController accessController = new AccessController(address(systemRegistry)); 29 | systemRegistry.setAccessController(address(accessController)); 30 | accessController.grantRole(Roles.STATS_SNAPSHOT_ROLE, address(this)); 31 | 32 | RethLSTCalculator calculator = new RethLSTCalculator(systemRegistry); 33 | bytes32[] memory dependantAprs = new bytes32[](0); 34 | LSTCalculatorBase.InitData memory initData = LSTCalculatorBase.InitData({ lstTokenAddress: RETH_MAINNET }); 35 | calculator.initialize(dependantAprs, abi.encode(initData)); 36 | 37 | assertEq(calculator.calculateEthPerToken(), expected); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/stats/calculators/StethLSTCalculator.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { StethLSTCalculator } from "src/stats/calculators/StethLSTCalculator.sol"; 7 | import { SystemRegistry } from "src/SystemRegistry.sol"; 8 | import { AccessController } from "src/security/AccessController.sol"; 9 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 10 | import { TOKE_MAINNET, WETH_MAINNET, STETH_MAINNET } from "test/utils/Addresses.sol"; 11 | import { Roles } from "src/libs/Roles.sol"; 12 | 13 | contract StethLSTCalculatorTest is Test { 14 | function testStethEthPerToken() public { 15 | checkEthPerToken(17_272_708, 1_124_349_506_893_718_109); 16 | checkEthPerToken(17_279_454, 1_124_504_367_992_424_664); 17 | checkEthPerToken(17_286_461, 1_124_666_417_311_180_217); 18 | checkEthPerToken(17_293_521, 1_124_835_614_410_438_130); 19 | checkEthPerToken(17_393_019, 1_126_897_087_511_522_171); 20 | } 21 | 22 | function checkEthPerToken(uint256 targetBlock, uint256 expected) private { 23 | uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), targetBlock); 24 | vm.selectFork(mainnetFork); 25 | 26 | SystemRegistry systemRegistry = new SystemRegistry(TOKE_MAINNET, WETH_MAINNET); 27 | AccessController accessController = new AccessController(address(systemRegistry)); 28 | systemRegistry.setAccessController(address(accessController)); 29 | accessController.grantRole(Roles.STATS_SNAPSHOT_ROLE, address(this)); 30 | 31 | StethLSTCalculator calculator = new StethLSTCalculator(systemRegistry); 32 | bytes32[] memory dependantAprs = new bytes32[](0); 33 | LSTCalculatorBase.InitData memory initData = LSTCalculatorBase.InitData({ lstTokenAddress: STETH_MAINNET }); 34 | calculator.initialize(dependantAprs, abi.encode(initData)); 35 | 36 | assertEq(calculator.calculateEthPerToken(), expected); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/stats/calculators/SwethLSTCalculator.t..sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { SwethLSTCalculator } from "src/stats/calculators/SwethLSTCalculator.sol"; 7 | import { SystemRegistry } from "src/SystemRegistry.sol"; 8 | import { AccessController } from "src/security/AccessController.sol"; 9 | import { LSTCalculatorBase } from "src/stats/calculators/base/LSTCalculatorBase.sol"; 10 | import { SWETH_MAINNET, TOKE_MAINNET, WETH_MAINNET } from "test/utils/Addresses.sol"; 11 | import { Roles } from "src/libs/Roles.sol"; 12 | 13 | contract SwethLSTCalculatorTest is Test { 14 | function testStethEthPerToken() public { 15 | checkEthPerToken(17_272_708, 1_026_937_210_012_217_451); 16 | checkEthPerToken(17_279_454, 1_026_937_210_012_217_451); 17 | checkEthPerToken(17_286_461, 1_026_937_210_012_217_451); 18 | checkEthPerToken(17_293_521, 1_027_139_938_284_152_259); 19 | checkEthPerToken(17_393_019, 1_028_031_999_300_723_065); 20 | } 21 | 22 | function checkEthPerToken(uint256 targetBlock, uint256 expected) private { 23 | uint256 mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), targetBlock); 24 | vm.selectFork(mainnetFork); 25 | 26 | SystemRegistry systemRegistry = new SystemRegistry(TOKE_MAINNET, WETH_MAINNET); 27 | AccessController accessController = new AccessController(address(systemRegistry)); 28 | systemRegistry.setAccessController(address(accessController)); 29 | accessController.grantRole(Roles.STATS_SNAPSHOT_ROLE, address(this)); 30 | 31 | SwethLSTCalculator calculator = new SwethLSTCalculator(systemRegistry); 32 | bytes32[] memory dependantAprs = new bytes32[](0); 33 | LSTCalculatorBase.InitData memory initData = LSTCalculatorBase.InitData({ lstTokenAddress: SWETH_MAINNET }); 34 | calculator.initialize(dependantAprs, abi.encode(initData)); 35 | 36 | assertEq(calculator.calculateEthPerToken(), expected); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/stats/utils/CurveUtils.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | import { Test } from "forge-std/Test.sol"; 6 | import { CurveUtils } from "src/stats/utils/CurveUtils.sol"; 7 | import { Stats } from "src/stats/Stats.sol"; 8 | import { RETH_MAINNET, USDC_MAINNET } from "test/utils/Addresses.sol"; 9 | 10 | contract CurveUtilsTest is Test { 11 | function setUp() public { 12 | vm.createSelectFork(vm.envString("MAINNET_RPC_URL")); 13 | } 14 | 15 | function testGetDecimals() public { 16 | uint256 curveEthDecimals = CurveUtils.getDecimals(Stats.CURVE_ETH); 17 | assertEq(curveEthDecimals, 18); 18 | 19 | uint256 rEthDecimals = CurveUtils.getDecimals(RETH_MAINNET); 20 | assertEq(rEthDecimals, 18); 21 | 22 | uint256 usdcDecimals = CurveUtils.getDecimals(USDC_MAINNET); 23 | assertEq(usdcDecimals, 6); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/utils/Arrays.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.7; 4 | 5 | import { Test, StdCheats, StdUtils } from "forge-std/Test.sol"; 6 | import { CurveResolverMainnet } from "src/utils/CurveResolverMainnet.sol"; 7 | import { ICurveMetaRegistry } from "src/interfaces/external/curve/ICurveMetaRegistry.sol"; 8 | import { Arrays } from "src/utils/Arrays.sol"; 9 | 10 | contract ArraysUtilTests is Test { 11 | address[8] private eight; 12 | 13 | function setUp() public { 14 | eight[0] = vm.addr(1); 15 | eight[1] = vm.addr(2); 16 | eight[2] = vm.addr(3); 17 | eight[3] = vm.addr(4); 18 | eight[4] = vm.addr(5); 19 | } 20 | 21 | function testConvertFixedCurveTokenArrayToDynamic() public { 22 | address[] memory converted = Arrays.convertFixedCurveTokenArrayToDynamic(eight, 5); 23 | 24 | assertEq(converted.length, 5, "length"); 25 | assertEq(eight[0], converted[0]); 26 | assertEq(eight[1], converted[1]); 27 | assertEq(eight[2], converted[2]); 28 | assertEq(eight[3], converted[3]); 29 | assertEq(eight[4], converted[4]); 30 | assertEq(eight[5], address(0)); 31 | assertEq(eight[6], address(0)); 32 | assertEq(eight[7], address(0)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/utils/ReadPlan.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.17; 3 | 4 | import { Vm } from "forge-std/Vm.sol"; 5 | 6 | library ReadPlan { 7 | //slither-disable-next-line too-many-digits 8 | bytes32 public constant MASK = 0xffffffffffffffffffffffff0000000000000000000000000000000000000000; 9 | 10 | function getPayload( 11 | Vm vm, 12 | string memory fileName, 13 | address adapter 14 | ) public returns (bytes32[] memory, bytes[] memory) { 15 | string memory root = "solver/test/payloads/adapters/"; 16 | string memory path = string.concat(root, fileName); 17 | string memory data = vm.readFile(path); 18 | 19 | bytes32[] memory commands = vm.parseJsonBytes32Array(data, ".commands"); 20 | bytes[] memory elements = vm.parseJsonBytesArray(data, ".state"); 21 | 22 | commands = setCommandsAddress(commands, adapter); 23 | 24 | return (commands, elements); 25 | } 26 | 27 | function setCommandsAddress(bytes32[] memory commands, address newAddress) public pure returns (bytes32[] memory) { 28 | bytes32 addressBytes = bytes32(uint256(uint160(newAddress))); 29 | 30 | uint256 length = commands.length; 31 | bytes32[] memory newCommands = new bytes32[](length); 32 | for (uint256 i = 0; i < length; ++i) { 33 | bytes32 command = commands[i]; 34 | 35 | // Mask out the address part in the original bytes32 36 | bytes32 maskedData = command & MASK; 37 | // Combine the masked data with the new address 38 | newCommands[i] = maskedData | addressBytes; 39 | } 40 | return newCommands; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/utils/common.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity >=0.8.17 <0.9.0; 4 | 5 | // solhint-disable-next-line no-console 6 | import { console2 as console } from "forge-std/console2.sol"; 7 | 8 | library Utils { 9 | function getContractSize(address _addr) public view returns (uint256) { 10 | uint256 size; 11 | // solhint-disable-next-line no-inline-assembly 12 | assembly { 13 | size := extcodesize(_addr) 14 | } 15 | return size; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/vault/LMPVault.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (c) 2023 Tokemak Foundation. All rights reserved. 3 | pragma solidity 0.8.17; 4 | 5 | // NOTE: should be put back in once the fuzzing constraints can be implemented 6 | 7 | import { ERC4626Test } from "erc4626-tests/ERC4626.test.sol"; 8 | 9 | import { ERC20Mock } from "openzeppelin-contracts/mocks/ERC20Mock.sol"; 10 | import { ERC4626Mock, IERC20Metadata } from "openzeppelin-contracts/mocks/ERC4626Mock.sol"; 11 | import { BaseTest } from "test/BaseTest.t.sol"; 12 | import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; 13 | import { IMainRewarder, MainRewarder } from "src/rewarders/MainRewarder.sol"; 14 | 15 | import { ILMPVault, LMPVault } from "src/vault/LMPVault.sol"; 16 | 17 | import { Roles } from "src/libs/Roles.sol"; 18 | 19 | contract LMPVaultTest is ERC4626Test, BaseTest { 20 | function setUp() public override(BaseTest, ERC4626Test) { 21 | // everything's mocked, so disable forking 22 | super._setUp(false); 23 | 24 | _underlying_ = address(baseAsset); 25 | 26 | // create vault 27 | LMPVault vault = 28 | LMPVault(lmpVaultFactory.createVault(type(uint256).max, type(uint256).max, "x", "y", keccak256("v8"), "")); 29 | 30 | _vault_ = address(vault); 31 | _delta_ = 0; 32 | _vaultMayBeEmpty = true; 33 | _unlimitedAmount = false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "newLine": "LF", 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitAny": true, 13 | "noImplicitThis": true, 14 | "strictBindCallApply": true, 15 | "strictNullChecks": true, 16 | "strictPropertyInitialization": true, 17 | "stripInternal": true, 18 | "typeRoots": ["node_modules/@types", "solver/typings"] 19 | }, 20 | "include": ["src", "solver"], 21 | "exclude": ["node_modules"] 22 | } 23 | --------------------------------------------------------------------------------