├── deployments ├── base │ └── .chainId ├── bob │ └── .chainId ├── fraxtal │ └── .chainId ├── mode │ └── .chainId └── optimism │ └── .chainId ├── tasks ├── oracle │ ├── index.ts │ └── admin.ts ├── chain-specific │ ├── mode │ │ └── index.ts │ ├── index.ts │ ├── fraxtal │ │ ├── index.ts │ │ └── pool.ts │ ├── base │ │ ├── index.ts │ │ ├── disableFlywheel.ts │ │ └── removeFlywheel.ts │ └── bob.ts ├── auth │ ├── index.ts │ └── user.ts ├── irm │ ├── index.ts │ └── admin.ts ├── plugin │ ├── index.ts │ └── deploy.ts ├── pool │ ├── index.ts │ ├── upgrade │ │ ├── index.ts │ │ └── non-owner.ts │ └── admin │ │ ├── index.ts │ │ └── deprecate.ts ├── flywheel │ ├── index.ts │ ├── replace.ts │ └── retire.ts ├── market │ ├── risk │ │ └── index.ts │ ├── index.ts │ └── set-plugin.ts ├── vaults │ ├── index.ts │ └── registry.ts ├── admin │ ├── index.ts │ ├── configure-ap-strategies.ts │ └── edit-deployers.ts └── index.ts ├── docker ├── hh-test.sh ├── forge-test.sh ├── start-node.sh ├── start-bsc-node.sh └── hh-bsc-test.sh ├── .prettierignore ├── .vscode ├── extensions.json └── settings.json ├── funding.json ├── contracts ├── external │ ├── olympus │ │ ├── sOlympus.sol │ │ └── OlympusStaking.sol │ ├── uniswap │ │ ├── IUniswapV1Factory.sol │ │ ├── IUniswapV2Callee.sol │ │ ├── IUniswapV1Exchange.sol │ │ ├── quoter │ │ │ ├── libraries │ │ │ │ ├── FixedPoint96.sol │ │ │ │ ├── LiquidityMath.sol │ │ │ │ ├── UnsafeMath.sol │ │ │ │ ├── SafeCast.sol │ │ │ │ └── LowGasSafeMath.sol │ │ │ └── interfaces │ │ │ │ └── IQuoter.sol │ │ ├── IUniswapV2Factory.sol │ │ ├── IUniswapV3FlashCallback.sol │ │ ├── IUniswapV2Router02.sol │ │ └── IUniswapV3PoolImmutables.sol │ ├── api3 │ │ └── IProxy.sol │ ├── chainlink │ │ ├── AggregatorV2V3Interface.sol │ │ ├── AggregatorInterface.sol │ │ ├── AggregatorV3Interface.sol │ │ └── Denominations.sol │ ├── jarvis │ │ ├── ISynthereumLiquidityPoolGeneral.sol │ │ ├── ISynthereumFinder.sol │ │ └── ISynthereumDeployment.sol │ ├── curve │ │ ├── ICurveLiquidityGaugeV2.sol │ │ ├── ICurveRegistry.sol │ │ ├── ICurveV2Pool.sol │ │ ├── ICurveStableSwap.sol │ │ └── ICurvePool.sol │ ├── mstable │ │ ├── ISavingsContractV2.sol │ │ └── MassetStructs.sol │ ├── alpha │ │ ├── ISafeBoxETH.sol │ │ ├── ISafeBox.sol │ │ └── Bank.sol │ ├── balancer │ │ ├── IRateProvider.sol │ │ ├── IBalancerLinearPool.sol │ │ ├── IBalancerStablePool.sol │ │ ├── BConst.sol │ │ └── IBalancerPool.sol │ ├── inverse │ │ └── Stabilizer.sol │ ├── yearn │ │ ├── IVault.sol │ │ └── IVaultV2.sol │ ├── redstone │ │ └── IRedstoneOracle.sol │ ├── sushi │ │ └── SushiBar.sol │ ├── compound │ │ ├── IPriceOracle.sol │ │ ├── IUnitroller.sol │ │ ├── ICErc20.sol │ │ ├── LICENSE │ │ └── IRewardsDistributor.sol │ ├── pstake │ │ └── IStakePool.sol │ ├── stader │ │ └── IStakeManager.sol │ ├── bomb │ │ └── IXBomb.sol │ ├── gamma │ │ ├── IUniProxy.sol │ │ └── IHypervisor.sol │ ├── harvest │ │ └── IFarmVault.sol │ ├── algebra │ │ └── IAlgebraSwapCallback.sol │ ├── gelato │ │ └── GUniPool.sol │ └── lido │ │ └── IWstETH.sol ├── ionic │ ├── IFlashLoanReceiver.sol │ ├── SafeOwnable.sol │ ├── strategies │ │ ├── flywheel │ │ │ ├── IIonicFlywheel.sol │ │ │ ├── rewards │ │ │ │ ├── WithdrawableFlywheelStaticRewards.sol │ │ │ │ ├── ReplacingFlywheelStaticRewards.sol │ │ │ │ ├── IonicFlywheelDynamicRewards.sol │ │ │ │ ├── BaseFlywheelRewards.sol │ │ │ │ ├── IonicFlywheelDynamicRewardsPlugin.sol │ │ │ │ └── ReplacingFlywheelDynamicRewards.sol │ │ │ ├── IonicFlywheelBorrowBooster.sol │ │ │ ├── IonicFlywheelBorrow.sol │ │ │ ├── IonicFlywheel.sol │ │ │ ├── LooplessFlywheelBooster.sol │ │ │ ├── IFlywheelBooster.sol │ │ │ └── IIonicFlywheelBorrowBooster.sol │ │ └── MockERC4626.sol │ └── levered │ │ ├── LeveredPositionStorage.sol │ │ └── LeveredPositionFactoryStorage.sol ├── test │ ├── abstracts │ │ └── ITestConfigStorage.sol │ ├── oracles │ │ ├── default │ │ │ ├── BNBxPriceOracleTest.t.sol │ │ │ ├── WombatLpTokenPriceOracleTest.t.sol │ │ │ ├── StkBNBPriceOracleTest.t.sol │ │ │ ├── WSTEthPriceOracleTest.sol │ │ │ ├── GelatoGUniPriceOracleTest.t.sol │ │ │ ├── SimplePriceOracleTest.sol │ │ │ ├── UniswapTwapPriceOracleV2Resolver.t.sol │ │ │ ├── AnkrCertificateTokenPriceOracleTest.t.sol │ │ │ └── SaddleLpPriceOracleTest.t.sol │ │ └── RedstoneAdapterOracleTest.t.sol │ ├── liquidators │ │ ├── AaveTokenLiquidatorTest.t.sol │ │ ├── UniswapV2LiquidatorFunderTest.sol │ │ └── XBombLiquidatorTest.t.sol │ ├── helpers │ │ └── BalancerReentrancyAttacker.sol │ ├── SafeOwnableUpgradeableTest.t.sol │ ├── irm │ │ └── AdjustableJumpRateModelTest.t.sol │ └── PoolDirectoryTest.sol ├── utils │ ├── TOUCHToken.sol │ ├── TRIBEToken.sol │ ├── IW_NATIVE.sol │ ├── IMulticall.sol │ └── Multicall.sol ├── adrastia │ └── PrudentiaLib.sol ├── compound │ ├── PriceOracle.sol │ ├── IFeeDistributor.sol │ ├── InterestRateModel.sol │ ├── CErc20RewardsDelegate.sol │ ├── CErc20PluginRewardsDelegate.sol │ └── CErc20Delegate.sol ├── liquidators │ ├── IFundsConversionStrategy.sol │ ├── KimUniV2Liquidator.sol │ ├── UniswapV2Liquidator.sol │ ├── registry │ │ ├── LiquidatorsRegistry.sol │ │ └── LiquidatorsRegistryStorage.sol │ ├── IRedemptionStrategy.sol │ ├── UniswapV2LiquidatorFunder.sol │ ├── WSTEthLiquidator.sol │ ├── SaddleLpTokenLiquidator.sol │ ├── BalancerSwapLiquidator.sol │ ├── SushiBarLiquidator.sol │ ├── YearnYVaultV2Liquidator.sol │ ├── YearnYVaultV1Liquidator.sol │ ├── UniswapV3LiquidatorFunder.sol │ ├── AlphaHomoraV2SafeBoxLiquidator.sol │ └── CErc20Liquidator.sol └── oracles │ ├── 1337 │ └── MockRevertPriceOracle.sol │ ├── BasePriceOracle.sol │ └── default │ ├── FixedNativePriceOracle.sol │ ├── StkBNBPriceOracle.sol │ ├── RecursivePriceOracle.sol │ ├── VelodromePriceOracle.sol │ ├── BNBxPriceOracle.sol │ ├── UniswapV3PriceOracle.sol │ └── AlgebraPriceOracle.sol ├── rpc-cache-keyfile ├── scripts ├── move-to-monorepo.sh └── prune.js ├── wagmi.config.ts ├── tsconfig.json ├── deploy ├── 15-configure-levered-position-pairs.ts ├── 16-upgrade-all-pools.ts ├── 09-chain-deploy.ts ├── 08-deploy-irms.ts ├── 07-address-provider.ts ├── 13-configure-address-provider.ts ├── 04-deploy-pool-directory.ts ├── 05-deploy-flywheel.ts └── 11-deploy-liquidators.ts ├── .gitignore ├── remappings.txt ├── .prettierrc ├── chainDeploy ├── helpers │ ├── index.ts │ ├── getCgPrice.ts │ ├── utils.ts │ └── oracles │ │ ├── erc4626.ts │ │ ├── redstoneFallbacks.ts │ │ ├── redstoneWeETHFallbacks.ts │ │ ├── redstone.ts │ │ ├── redstoneWeETH.ts │ │ └── redstoneWrsETH.ts └── index.ts ├── foundry.toml ├── .github ├── ISSUE_TEMPLATE │ ├── custom-oracle.md │ ├── custom-liquidation-strategy.md │ ├── erc-4626-strategy.md │ ├── default-issue-.md │ ├── bug-report.md │ └── feature-request.md ├── issue_template.md └── pull_request_template.md └── .gitmodules /deployments/base/.chainId: -------------------------------------------------------------------------------- 1 | 8453 -------------------------------------------------------------------------------- /deployments/bob/.chainId: -------------------------------------------------------------------------------- 1 | 60808 -------------------------------------------------------------------------------- /deployments/fraxtal/.chainId: -------------------------------------------------------------------------------- 1 | 252 -------------------------------------------------------------------------------- /deployments/mode/.chainId: -------------------------------------------------------------------------------- 1 | 34443 -------------------------------------------------------------------------------- /deployments/optimism/.chainId: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /tasks/oracle/index.ts: -------------------------------------------------------------------------------- 1 | import "./admin"; 2 | -------------------------------------------------------------------------------- /tasks/chain-specific/mode/index.ts: -------------------------------------------------------------------------------- 1 | import "./market"; 2 | -------------------------------------------------------------------------------- /tasks/auth/index.ts: -------------------------------------------------------------------------------- 1 | import "./pool"; 2 | import "./user"; 3 | -------------------------------------------------------------------------------- /tasks/irm/index.ts: -------------------------------------------------------------------------------- 1 | import "./admin"; 2 | import "./jrm"; 3 | -------------------------------------------------------------------------------- /tasks/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import "./deploy"; 2 | import "./replace"; 3 | -------------------------------------------------------------------------------- /tasks/pool/index.ts: -------------------------------------------------------------------------------- 1 | import "./admin"; 2 | import "./upgrade"; 3 | -------------------------------------------------------------------------------- /docker/hh-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | npx hardhat test -------------------------------------------------------------------------------- /tasks/pool/upgrade/index.ts: -------------------------------------------------------------------------------- 1 | import "./upgrade"; 2 | import "./non-owner"; 3 | -------------------------------------------------------------------------------- /docker/forge-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | npm run test:forge -------------------------------------------------------------------------------- /docker/start-node.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | npx hardhat node -------------------------------------------------------------------------------- /tasks/pool/admin/index.ts: -------------------------------------------------------------------------------- 1 | import "./create"; 2 | import "./deprecate"; 3 | import "./roles-auth"; 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | contracts/oracles/keydonix/** 2 | contracts/liquidators/BalancerPoolTokenLiquidator.sol 3 | -------------------------------------------------------------------------------- /tasks/chain-specific/index.ts: -------------------------------------------------------------------------------- 1 | import "./bob"; 2 | import "./base"; 3 | import "./mode"; 4 | import "./fraxtal"; -------------------------------------------------------------------------------- /tasks/flywheel/index.ts: -------------------------------------------------------------------------------- 1 | import "./retire"; 2 | import "./deploy"; 3 | import "./approve"; 4 | import "./replace"; 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "arcanis.vscode-zipfs", 4 | "esbenp.prettier-vscode" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0xbcb5c437d5347be21add3972512679054416805274f80e85ab8053ae79340a69" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tasks/market/risk/index.ts: -------------------------------------------------------------------------------- 1 | import "./supply-caps"; 2 | import "./borrow-caps"; 3 | import "./debt-ceilings"; 4 | import "./asset-blacklisting"; 5 | -------------------------------------------------------------------------------- /tasks/vaults/index.ts: -------------------------------------------------------------------------------- 1 | // import "./adapters"; 2 | // import "./vault"; 3 | // import "./rewards"; 4 | // import "./registry"; 5 | // import "./deploy"; 6 | -------------------------------------------------------------------------------- /docker/start-bsc-node.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | FORK_URL=${FORK_URL_BSC} FORK_BLOCK_NUMBER=${FORK_BLOCK_NUMBER} FORK_CHAIN_ID=56 npx hardhat node -------------------------------------------------------------------------------- /tasks/chain-specific/fraxtal/index.ts: -------------------------------------------------------------------------------- 1 | import "./pool"; 2 | import "./markets"; 3 | 4 | export const COMPTROLLER = "0xB5141403e811fFFE02f4d49Ea8d4a7B0b9590658"; 5 | -------------------------------------------------------------------------------- /contracts/external/olympus/sOlympus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | abstract contract sOlympus { 5 | address public stakingContract; 6 | } 7 | -------------------------------------------------------------------------------- /docker/hh-bsc-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | sleep 15 6 | FORK_URL=${FORK_URL_BSC} FORK_BLOCK_NUMBER=${FORK_BLOCK_NUMBER} FORK_CHAIN_ID=56 npx hardhat test --network localhost -------------------------------------------------------------------------------- /rpc-cache-keyfile: -------------------------------------------------------------------------------- 1 | This file serves as the key for the github actions/cache 2 | 3 | Any change in this file will invalidate the cache in CI that stores RPC data. 4 | 5 | Last updated: 03-11-2022 -------------------------------------------------------------------------------- /scripts/move-to-monorepo.sh: -------------------------------------------------------------------------------- 1 | rm -rf ../monorepo/packages/sdk/deployments 2 | cp -r ./deploymentsBrief ../monorepo/packages/sdk/deployments 3 | cp generated.ts ../monorepo/packages/sdk/src/generated.ts 4 | -------------------------------------------------------------------------------- /tasks/admin/index.ts: -------------------------------------------------------------------------------- 1 | import "./replace-deployer"; 2 | import "./update-ionic-fee"; 3 | import "./edit-deployers"; 4 | import "./revenue"; 5 | import "./fix-flywheel-owners"; 6 | import "./configure-ap-strategies"; 7 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV1Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | interface IUniswapV1Factory { 5 | function getExchange(address token) external view returns (address); 6 | } 7 | -------------------------------------------------------------------------------- /tasks/index.ts: -------------------------------------------------------------------------------- 1 | import "./admin"; 2 | import "./auth"; 3 | import "./flywheel"; 4 | import "./irm"; 5 | import "./leverage/configurePair"; 6 | import "./plugin"; 7 | import "./pool"; 8 | import "./market"; 9 | import "./chain-specific"; 10 | import "./oracle"; -------------------------------------------------------------------------------- /tasks/chain-specific/base/index.ts: -------------------------------------------------------------------------------- 1 | import "./markets"; 2 | import "./single"; 3 | import "./rewards"; 4 | import "./tokens"; 5 | import "./disableFlywheel"; 6 | import "./removeFlywheel"; 7 | 8 | export const COMPTROLLER = "0x05c9C6417F246600f8f5f49fcA9Ee991bfF73D13"; 9 | -------------------------------------------------------------------------------- /contracts/external/api3/IProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IProxy { 5 | function read() external view returns (int224 value, uint32 timestamp); 6 | 7 | function api3ServerV1() external view returns (address); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/external/olympus/OlympusStaking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | abstract contract OlympusStaking { 5 | address public OHM; 6 | 7 | function unstake(uint256 _amount, bool _trigger) external virtual; 8 | } 9 | -------------------------------------------------------------------------------- /tasks/market/index.ts: -------------------------------------------------------------------------------- 1 | import "./set-plugin"; 2 | import "./admin"; 3 | import "./risk"; 4 | import "./upgrade"; 5 | import "./upgrade-all"; 6 | import "./deploy-dynamic-rewards-market"; 7 | import "./deploy-static-rewards-market"; 8 | import "./deploy"; 9 | import "./borrow"; 10 | -------------------------------------------------------------------------------- /wagmi.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@wagmi/cli"; 2 | import { foundry } from "@wagmi/cli/plugins"; 3 | 4 | export default defineConfig({ 5 | out: "generated.ts", 6 | contracts: [], 7 | plugins: [ 8 | foundry({ 9 | project: "./" 10 | }) 11 | ] 12 | }); 13 | -------------------------------------------------------------------------------- /contracts/external/chainlink/AggregatorV2V3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "./AggregatorInterface.sol"; 5 | import "./AggregatorV3Interface.sol"; 6 | 7 | interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {} 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/ionic/IFlashLoanReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface IFlashLoanReceiver { 5 | function receiveFlashLoan( 6 | address borrowedAsset, 7 | uint256 borrowedAmount, 8 | bytes calldata data 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test/abstracts/ITestConfigStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | interface ITestConfigStorage { 5 | function getTestConfig(uint256 i) external view returns (bytes memory); 6 | 7 | function getTestConfigLength() external view returns (uint256); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV2Callee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | interface IUniswapV2Callee { 5 | function uniswapV2Call( 6 | address sender, 7 | uint256 amount0, 8 | uint256 amount1, 9 | bytes calldata data 10 | ) external; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV1Exchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | interface IUniswapV1Exchange { 5 | function tokenToEthSwapInput( 6 | uint256 tokens_sold, 7 | uint256 min_eth, 8 | uint256 deadline 9 | ) external returns (uint256); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/external/jarvis/ISynthereumLiquidityPoolGeneral.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.4; 3 | 4 | import "./ISynthereumDeployment.sol"; 5 | 6 | interface ISynthereumLiquidityPoolGeneral is 7 | ISynthereumDeployment 8 | //, 9 | //ISynthereumLiquidityPoolInteraction 10 | {} 11 | -------------------------------------------------------------------------------- /contracts/external/curve/ICurveLiquidityGaugeV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface ICurveLiquidityGaugeV2 { 5 | function lp_token() external view returns (address); 6 | 7 | function deposit(uint256 _value) external; 8 | 9 | function withdraw(uint256 _value) external; 10 | } 11 | -------------------------------------------------------------------------------- /deploy/15-configure-levered-position-pairs.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | const func: DeployFunction = async ({ run }) => { 4 | // configure levered position pairs 5 | await run("levered-positions:configure-pairs"); 6 | }; 7 | 8 | func.tags = ["prod", "configure-pairs"]; 9 | 10 | export default func; 11 | -------------------------------------------------------------------------------- /contracts/ionic/SafeOwnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable2Step.sol"; 5 | 6 | abstract contract SafeOwnable is Ownable2Step { 7 | function renounceOwnership() public override onlyOwner { 8 | revert("renounce ownership not allowed"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | artifacts_forge 3 | cache 4 | out/ 5 | typechain/ 6 | .DS_Store 7 | .idea 8 | .env 9 | deployments/localhost/ 10 | deployments/hardhat/ 11 | deployments/rinkeby/ 12 | deployments/kovan/ 13 | contracts.iml 14 | node_modules/ 15 | ionic-contracts.iml 16 | cache_hardhat 17 | transactions.json 18 | *transactions.json 19 | deployments/local 20 | -------------------------------------------------------------------------------- /contracts/utils/TOUCHToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | // SPDX-License-Identifier: UNLICENSED 4 | 5 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 6 | 7 | contract TOUCHToken is ERC20 { 8 | constructor(uint256 initialSupply, address tokenOwner) ERC20("Midas TOUCH Token", "TOUCH", 18) { 9 | _mint(tokenOwner, initialSupply); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | flywheel/=lib/fuse-flywheel/lib/flywheel-v2/src/ 2 | solidity-bytes-utils/=lib/solidity-bytes-utils/ 3 | @openzeppelin=lib/openzeppelin-contracts/ 4 | @pythnetwork/pyth-sdk-solidity/=lib/pyth-sdk-solidity/ 5 | @pythnetwork/express-relay-sdk-solidity/=node_modules/@pythnetwork/express-relay-sdk-solidity 6 | adrastia-periphery/=lib/adrastia-periphery/contracts/ -------------------------------------------------------------------------------- /contracts/utils/TRIBEToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | // SPDX-License-Identifier: UNLICENSED 4 | 5 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 6 | 7 | contract TRIBEToken is ERC20 { 8 | constructor(uint256 initialSupply, address tokenOwner) ERC20("TRIBE Governance Token", "TRIBE", 18) { 9 | _mint(tokenOwner, initialSupply); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.sol", "*.ts", "*.tsx", ".js"], 5 | "options": { 6 | "printWidth": 120, 7 | "tabWidth": 2, 8 | "useTabs": false, 9 | "singleQuote": false, 10 | "bracketSpacing": true, 11 | "explicitTypes": "always", 12 | "trailingComma": "none" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /chainDeploy/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export { ChainDeployConfig, ChainlinkFeedBaseCurrency } from "../types"; 2 | export { deployIRMs } from "./irms"; 3 | export { deployChainlinkOracle } from "./oracles/chainlink"; 4 | export { deployFlywheelWithDynamicRewards } from "./dynamicFlywheels"; 5 | export { deployErc4626PriceOracle } from "./oracles/erc4626"; 6 | export { deployPythPriceOracle } from "./oracles/pyth"; 7 | -------------------------------------------------------------------------------- /contracts/external/mstable/ISavingsContractV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | /** 5 | * @title ISavingsContractV2 6 | */ 7 | interface ISavingsContractV2 { 8 | function redeemCredits(uint256 _amount) external returns (uint256 underlyingReturned); // V2 9 | 10 | function exchangeRate() external view returns (uint256); // V1 & V2 11 | } 12 | -------------------------------------------------------------------------------- /contracts/external/curve/ICurveRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface ICurveRegistry { 5 | function get_n_coins(address lp) external view returns (uint256); 6 | 7 | function get_coins(address pool) external view returns (address[8] memory); 8 | 9 | function get_pool_from_lp_token(address lp) external view returns (address); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/external/alpha/ISafeBoxETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface ISafeBoxETH is IERC20Upgradeable { 7 | function cToken() external view returns (address); 8 | 9 | function deposit() external payable; 10 | 11 | function withdraw(uint256 amount) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/external/balancer/IRateProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | interface IRateProvider { 5 | /** 6 | * @dev Returns an 18 decimal fixed point number that is the exchange rate of the token to some other underlying 7 | * token. The meaning of this rate depends on the context. 8 | */ 9 | function getRate() external view returns (uint256); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/external/curve/ICurveV2Pool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { ICurvePool } from "./ICurvePool.sol"; 5 | 6 | interface ICurveV2Pool is ICurvePool { 7 | function price_oracle() external view returns (uint256); 8 | 9 | function lp_price() external view returns (uint256); 10 | 11 | function coins(uint256 arg0) external view returns (address); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/adrastia/PrudentiaLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | library PrudentiaLib { 5 | struct PrudentiaConfig { 6 | address controller; // Adrastia Prudentia controller address 7 | uint8 offset; // Offset for delayed rate activation 8 | int8 decimalShift; // Positive values scale the rate up (in powers of 10), negative values scale the rate down 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/external/inverse/Stabilizer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity >=0.8.0; 3 | 4 | interface Stabilizer { 5 | function buyFee() external view returns (uint256); 6 | 7 | function synth() external view returns (address); 8 | 9 | function reserve() external view returns (address); 10 | 11 | function buy(uint256 amount) external; 12 | 13 | function sell(uint256 amount) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/external/yearn/IVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | interface IVault { 5 | function getPricePerFullShare() external view returns (uint256); 6 | 7 | function token() external view returns (address); 8 | 9 | function decimals() external view returns (uint8); 10 | 11 | function deposit(uint256 _amount) external; 12 | 13 | function withdraw(uint256 _shares) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/libraries/FixedPoint96.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.0; 3 | 4 | /// @title FixedPoint96 5 | /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) 6 | /// @dev Used in SqrtPriceMath.sol 7 | library FixedPoint96 { 8 | uint8 internal constant RESOLUTION = 96; 9 | uint256 internal constant Q96 = 0x1000000000000000000000000; // 2^96 10 | } 11 | -------------------------------------------------------------------------------- /contracts/external/redstone/IRedstoneOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | interface IRedstoneOracle { 5 | function priceOf(address asset) external view returns (uint256); 6 | 7 | function priceOfETH() external view returns (uint256); 8 | 9 | function getDataFeedIdForAsset(address asset) external view returns (bytes32); 10 | 11 | function getDataFeedIds() external view returns (bytes32[] memory dataFeedIds); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/external/yearn/IVaultV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | interface IVaultV2 { 5 | function pricePerShare() external view returns (uint256); 6 | 7 | function token() external view returns (address); 8 | 9 | function decimals() external view returns (uint8); 10 | 11 | function deposit(uint256 _amount) external returns (uint256); 12 | 13 | function withdraw(uint256 maxShares) external returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /deploy/16-upgrade-all-pools.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | const func: DeployFunction = async ({ run }) => { 4 | // upgrade any of the pools if necessary 5 | // the markets are also autoupgraded with this task 6 | try { 7 | await run("pools:all:upgrade"); 8 | } catch (error) { 9 | console.error("Could not deploy:", error); 10 | } 11 | }; 12 | 13 | func.tags = ["prod", "upgrade-pools"]; 14 | 15 | export default func; 16 | -------------------------------------------------------------------------------- /contracts/external/alpha/ISafeBox.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface ISafeBox is IERC20Upgradeable { 7 | function cToken() external view returns (address); 8 | 9 | function uToken() external view returns (address); 10 | 11 | function deposit(uint256 amount) external; 12 | 13 | function withdraw(uint256 amount) external; 14 | } 15 | -------------------------------------------------------------------------------- /contracts/external/curve/ICurveStableSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface ICurveStableSwap is IERC20Upgradeable { 7 | function get_balances() external view returns (uint256[2] memory); 8 | 9 | function remove_liquidity_one_coin( 10 | uint256 _token_amount, 11 | int128 i, 12 | uint256 min_amount 13 | ) external; 14 | } 15 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'contracts' 3 | bytecode_hash = 'none' 4 | solc_version = "0.8.10" 5 | evm_version = 'shanghai' 6 | libs = ["lib"] 7 | optimizer = true 8 | optimizer_runs = 200 9 | out = "out" 10 | 11 | [rpc_endpoints] 12 | mode = "${MODE_MAINNET_RPC_URL}" 13 | base = "${BASE_MAINNET__RPC_URL}" 14 | 15 | mode_archive = "${MODE_MAINNET_ARCHIVE_RPC_URL}" 16 | base_archive = "${BASE_MAINNET__RPC_URL}" 17 | 18 | # See more config options https://github.com/gakonst/foundry/tree/master/config 19 | -------------------------------------------------------------------------------- /contracts/external/sushi/SushiBar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | abstract contract SushiBar is IERC20Upgradeable { 7 | IERC20Upgradeable public sushi; 8 | 9 | // Enter the bar. Pay some SUSHIs. Earn some shares. 10 | function enter(uint256 _amount) public virtual; 11 | 12 | // Leave the bar. Claim back your SUSHIs. 13 | function leave(uint256 _share) public virtual; 14 | } 15 | -------------------------------------------------------------------------------- /tasks/chain-specific/fraxtal/pool.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | 3 | task("pool:create:fraxtal").setAction(async ({}, { run, deployments }) => { 4 | const oracle = await deployments.get("MasterPriceOracle"); 5 | console.log("oracle: ", oracle.address); 6 | await run("pool:create", { 7 | name: "Fraxtal Market", 8 | creator: "deployer", 9 | priceOracle: oracle.address, // MPO 10 | closeFactor: "50", 11 | liquidationIncentive: "8", 12 | enforceWhitelist: "false" 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /contracts/external/compound/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity >=0.8.0; 3 | 4 | import "./ICToken.sol"; 5 | 6 | interface IPriceOracle { 7 | /** 8 | * @notice Get the underlying price of a cToken asset 9 | * @param cToken The cToken to get the underlying price of 10 | * @return The underlying asset price mantissa (scaled by 1e18). 11 | * Zero means the price is unavailable. 12 | */ 13 | function getUnderlyingPrice(ICToken cToken) external view returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/interfaces/IQuoter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | interface IQuoter { 5 | function estimateMaxSwapUniswapV3( 6 | address _fromToken, 7 | address _toToken, 8 | uint256 _amount, 9 | uint24 _poolFee 10 | ) external view returns (uint256); 11 | 12 | function estimateMinSwapUniswapV3( 13 | address _fromToken, 14 | address _toToken, 15 | uint256 _amount, 16 | uint24 _poolFee 17 | ) external view returns (uint256); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/external/compound/IUnitroller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity >=0.8.0; 3 | 4 | /** 5 | * @title ComptrollerCore 6 | * @dev Storage for the comptroller is at this address, while execution is delegated to the `comptrollerImplementation`. 7 | * CTokens should reference this contract as their comptroller. 8 | */ 9 | interface IUnitroller { 10 | function _setPendingImplementation(address newPendingImplementation) external returns (uint256); 11 | 12 | function _setPendingAdmin(address newPendingAdmin) external returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom-oracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom Oracle 3 | about: Describe this issue template's purpose here. 4 | title: Custom Oracle for $XXX 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Implement Custom Oracle for the desired asset 11 | 12 | ### Oracle Description 13 | 14 | Brief description of what kind of assets this oracle would support 15 | 16 | ### Links and Documentation 17 | 18 | **Asset Support Issue**: N/A 19 | **Project Docs**: N/A 20 | **Other Relevant Links**: N/A 21 | 22 | ### Tasks 23 | 24 | - [ ] implement `getUnderlyingPrice()` 25 | - [ ] add forge tests 26 | -------------------------------------------------------------------------------- /contracts/utils/IW_NATIVE.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity >=0.8.0; 3 | 4 | interface IW_NATIVE { 5 | function deposit() external payable; 6 | 7 | function withdraw(uint256 amount) external; 8 | 9 | function approve(address spender, uint256 amount) external returns (bool); 10 | 11 | function transfer(address to, uint256 amount) external returns (bool); 12 | 13 | function transferFrom( 14 | address from, 15 | address to, 16 | uint256 amount 17 | ) external returns (bool); 18 | 19 | function balanceOf(address) external view returns (uint256); 20 | } 21 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | Any information that gives more context as to why this task exists, and why it matters. This might include: links to discussions, documentation, or other Jira issues / tasks 4 | 5 | ### Linked Issues & Documentation 6 | 7 | - List of linked issues, external links, etc 8 | 9 | ## Definition Of Done 10 | 11 | Describes in 1-2 sentences what the output of the Task is, so that it can be verified by another team member easily. “Feature X is implemented as described and can be verified by using it” 12 | 13 | ## Tasks 14 | 15 | - [ ] Task 1 16 | - [ ] Task 2 17 | - [ ] Task 3 18 | 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "editor.tabSize": 2, 5 | "typescript.tsdk": ".yarn/sdks/typescript/lib", 6 | "[typescript]": { 7 | "editor.defaultFormatter": "esbenp.prettier-vscode" 8 | }, 9 | "solidity.packageDefaultDependenciesContractsDirectory": "contracts", 10 | "solidity.packageDefaultDependenciesDirectory": "lib", 11 | "search.exclude": { 12 | "**/.yarn": true, 13 | "**/.pnp.*": true 14 | }, 15 | "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs", 16 | "typescript.enablePromptUseWorkspaceTsdk": true 17 | } 18 | -------------------------------------------------------------------------------- /contracts/external/compound/ICErc20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity >=0.8.0; 3 | 4 | import "./ICToken.sol"; 5 | 6 | /** 7 | * @title Compound's CErc20 Contract 8 | * @notice CTokens which wrap an EIP-20 underlying 9 | * @author Compound 10 | */ 11 | interface ICErc20Compound is ICToken { 12 | function underlying() external view returns (address); 13 | 14 | function liquidateBorrow( 15 | address borrower, 16 | uint256 repayAmount, 17 | ICToken cTokenCollateral 18 | ) external returns (uint256); 19 | 20 | function getTotalUnderlyingSupplied() external view returns (uint256); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/external/alpha/Bank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface Bank is IERC20Upgradeable { 7 | /// @dev Return the total ETH entitled to the token holders. Be careful of unaccrued interests. 8 | function totalETH() external view returns (uint256); 9 | 10 | /// @dev Add more ETH to the bank. Hope to get some good returns. 11 | function deposit() external payable; 12 | 13 | /// @dev Withdraw ETH from the bank by burning the share tokens. 14 | function withdraw(uint256 share) external; 15 | } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom-liquidation-strategy.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom Liquidation Strategy 3 | about: Liquidation strategy for custom asset 4 | title: Custom Liquidation Strategy for $XXX 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Implement Custom Liquidation Strategy for the desired asset 11 | 12 | ### Liquidation Strategy Description 13 | 14 | Brief description of the liquidation strategy for the custom asset 15 | 16 | ### Links and Documentation 17 | 18 | **Asset Support Issue**: N/A 19 | **Project Docs**: N/A 20 | **Other Relevant Links**: N/A 21 | 22 | ### Tasks 23 | 24 | - [ ] implement `redeem()` 25 | - [ ] add forge tests 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/erc-4626-strategy.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ERC-4626 Strategy 3 | about: 'Implements strategy for custom asset ' 4 | title: ERC-4626 Strategy for $XXX 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Implement ERC4626 strategy for the desired asset 11 | 12 | ### Strategy Description 13 | 14 | Brief description of what this strategy enables 15 | 16 | ### Links and Documentation 17 | 18 | **Asset Support Issue**: N/A 19 | **Project Docs**: N/A 20 | **Other Relevant Links**: N/A 21 | 22 | ### Tasks 23 | 24 | - [ ] implement beforeWithdraw 25 | - [ ] implement afterDeposit 26 | - [ ] implement totalAssets 27 | - [ ] add forge tests 28 | -------------------------------------------------------------------------------- /contracts/compound/PriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { ICErc20 } from "./CTokenInterfaces.sol"; 5 | 6 | abstract contract PriceOracle { 7 | /// @notice Indicator that this is a PriceOracle contract (for inspection) 8 | bool public constant isPriceOracle = true; 9 | 10 | /** 11 | * @notice Get the underlying price of a cToken asset 12 | * @param cToken The cToken to get the underlying price of 13 | * @return The underlying asset price mantissa (scaled by 1e18). 14 | * Zero means the price is unavailable. 15 | */ 16 | function getUnderlyingPrice(ICErc20 cToken) external view virtual returns (uint256); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/libraries/LiquidityMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Math library for liquidity 5 | library LiquidityMath { 6 | /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows 7 | /// @param x The liquidity before change 8 | /// @param y The delta by which liquidity should be changed 9 | /// @return z The liquidity delta 10 | function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { 11 | if (y < 0) { 12 | require((z = x - uint128(-y)) < x, "LS"); 13 | } else { 14 | require((z = x + uint128(y)) >= x, "LA"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/external/chainlink/AggregatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | interface AggregatorInterface { 5 | function latestAnswer() external view returns (int256); 6 | 7 | function latestTimestamp() external view returns (uint256); 8 | 9 | function latestRound() external view returns (uint256); 10 | 11 | function getAnswer(uint256 roundId) external view returns (int256); 12 | 13 | function getTimestamp(uint256 roundId) external view returns (uint256); 14 | 15 | event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); 16 | 17 | event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/liquidators/IFundsConversionStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./IRedemptionStrategy.sol"; 5 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 6 | 7 | interface IFundsConversionStrategy is IRedemptionStrategy { 8 | function convert( 9 | IERC20Upgradeable inputToken, 10 | uint256 inputAmount, 11 | bytes memory strategyData 12 | ) external returns (IERC20Upgradeable outputToken, uint256 outputAmount); 13 | 14 | function estimateInputAmount(uint256 outputAmount, bytes memory strategyData) 15 | external 16 | view 17 | returns (IERC20Upgradeable inputToken, uint256 inputAmount); 18 | } 19 | -------------------------------------------------------------------------------- /tasks/chain-specific/bob.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { assets as bobAssets } from "../../../monorepo/packages/chains/src/bob/assets"; 3 | 4 | task("market:set-cf:bob:main", "Sets caps on a market").setAction(async (_, { viem, run }) => { 5 | const COMPTROLLER = "0x9cFEe81970AA10CC593B83fB96eAA9880a6DF715"; 6 | for (const asset of bobAssets) { 7 | const pool = await viem.getContractAt("IonicComptroller", COMPTROLLER); 8 | const cToken = await pool.read.cTokensByUnderlying([asset.underlying]); 9 | console.log("cToken: ", cToken, asset.symbol); 10 | 11 | await run("market:set:ltv", { 12 | marketAddress: cToken, 13 | ltv: asset.initialCf 14 | }); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /tasks/oracle/admin.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Address } from "viem"; 3 | 4 | export default task("oracle:set:mpo", "Sets an oracle for an underlying asset on the MasterPriceOracle") 5 | .addParam("underlying", "Underlying to set", undefined, types.string) 6 | .addParam("oracle", "Oracle to set underlying to", undefined, types.string) 7 | .setAction(async ({ underlying, oracle }, { viem, deployments }) => { 8 | const mpo = await viem.getContractAt( 9 | "MasterPriceOracle", 10 | (await deployments.get("MasterPriceOracle")).address as Address 11 | ); 12 | const addTx = await mpo.write.add([[underlying], [oracle]]); 13 | console.log("addTx: ", addTx); 14 | }); 15 | -------------------------------------------------------------------------------- /contracts/external/pstake/IStakePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | // 1 stkBNB = (totalWei / poolTokenSupply) BNB 5 | // 1 BNB = (poolTokenSupply / totalWei) stkBNB 6 | // Over time, stkBNB appreciates in value as compared to BNB. 7 | struct ExchangeRateData { 8 | uint256 totalWei; // total amount of BNB managed by the pool 9 | uint256 poolTokenSupply; // total amount of stkBNB managed by the pool 10 | } 11 | 12 | // External protocols (eg: Wombat Exchange) that integrate with us, rely on this interface. 13 | // We must always ensure that StakePool conforms to this interface. 14 | interface IStakePool { 15 | function exchangeRate() external view returns (ExchangeRateData memory); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/libraries/UnsafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Math functions that do not check inputs or outputs 5 | /// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks 6 | library UnsafeMath { 7 | /// @notice Returns ceil(x / y) 8 | /// @dev division by 0 has unspecified behavior, and must be checked externally 9 | /// @param x The dividend 10 | /// @param y The divisor 11 | /// @return z The quotient, ceil(x / y) 12 | function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { 13 | assembly { 14 | z := add(div(x, y), gt(mod(x, y), 0)) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/utils/IMulticall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | /// @title Multicall interface 5 | /// @notice Enables calling multiple methods in a single call to the contract 6 | interface IMulticall { 7 | /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed 8 | /// @dev The `msg.value` should not be trusted for any method callable from multicall. 9 | /// @param data The encoded function data for each of the calls to make to this contract 10 | /// @return results The results from each of the calls passed in via data 11 | function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/external/stader/IStakeManager.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IStakeManager { 5 | function deposit() external payable; 6 | 7 | function getTotalPooledBnb() external view returns (uint256); 8 | 9 | function getContracts() 10 | external 11 | view 12 | returns ( 13 | address _manager, 14 | address _bnbX, 15 | address _tokenHub, 16 | address _bcDepositWallet 17 | ); 18 | 19 | function getExtraBnbInContract() external view returns (uint256 _extraBnb); 20 | 21 | function convertBnbToBnbX(uint256 _amount) external view returns (uint256); 22 | 23 | function convertBnbXToBnb(uint256 _amountInBnbX) external view returns (uint256); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/liquidators/KimUniV2Liquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./BaseUniswapV2Liquidator.sol"; 5 | 6 | contract KimUniV2Liquidator is BaseUniswapV2Liquidator { 7 | function _swap( 8 | IUniswapV2Router02 uniswapV2Router, 9 | uint256 inputAmount, 10 | address[] memory swapPath 11 | ) internal override { 12 | uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( 13 | inputAmount, 14 | 0, 15 | swapPath, 16 | address(this), 17 | address(0), // referrer 18 | block.timestamp 19 | ); 20 | } 21 | 22 | function name() public pure virtual returns (string memory) { 23 | return "KimUniV2Liquidator"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/BNBxPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { BNBxPriceOracle } from "../../../oracles/default/BNBxPriceOracle.sol"; 6 | 7 | contract BNBxPriceOracleTest is BaseTest { 8 | BNBxPriceOracle private oracle; 9 | address BNBx = 0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275; 10 | 11 | function afterForkSetUp() internal override { 12 | oracle = new BNBxPriceOracle(); 13 | oracle.initialize(); 14 | } 15 | 16 | function testBnbXOraclePrice() public forkAtBlock(BSC_MAINNET, 22332594) { 17 | uint256 priceBnbX = oracle.price(BNBx); 18 | assertGt(priceBnbX, 1e18); 19 | assertEq(priceBnbX, 1041708576933034575); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deploy/09-chain-deploy.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | import { chainDeployConfig } from "../chainDeploy"; 4 | 5 | const func: DeployFunction = async ({ viem, getNamedAccounts, deployments, getChainId }): Promise => { 6 | const chainId = parseInt(await getChainId()); 7 | 8 | if (!chainDeployConfig[chainId]) { 9 | throw new Error(`Config invalid for ${chainId}`); 10 | } 11 | const { deployFunc }: { deployFunc: any } = chainDeployConfig[chainId]; 12 | 13 | //// CHAIN SPECIFIC DEPLOYMENT 14 | console.log("Running deployment for chain: ", chainId); 15 | if (deployFunc) { 16 | await deployFunc({ run, viem, getNamedAccounts, deployments }); 17 | } 18 | }; 19 | 20 | func.tags = ["prod", "chain-deploy"]; 21 | 22 | export default func; 23 | -------------------------------------------------------------------------------- /contracts/external/balancer/IBalancerLinearPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | import { IBalancerVault } from "./IBalancerVault.sol"; 5 | 6 | interface IBalancerLinearPool { 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 getVault() external view returns (IBalancerVault); 14 | 15 | function getRate() external view returns (uint256); 16 | 17 | function getScalingFactros() external view returns (uint256[] memory); 18 | 19 | function getTokenRate(address token) external view returns (uint256); 20 | 21 | function getMainToken() external view returns (address); 22 | } 23 | -------------------------------------------------------------------------------- /scripts/prune.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | console.log("./scripts/prune.js - pruning JSON files to reduce bundle size"); 3 | const filePaths = process.argv.slice(2); 4 | filePaths.forEach((fp) => { 5 | const file = fs.readFileSync(fp); 6 | const obj = JSON.parse(file); 7 | 8 | prune(obj, [/abi/, /ArrakisERC4626.*/, /Beefy.*/]); 9 | 10 | fs.writeFileSync(fp, JSON.stringify(obj, 0, 2)); 11 | }); 12 | 13 | function prune(data, keysToDelete) { 14 | if (typeof data === "object") { 15 | for (const key in data) { 16 | if ( 17 | keysToDelete.some((k) => 18 | k instanceof RegExp ? k.test(key) : k === key, 19 | ) 20 | ) { 21 | delete data[key]; 22 | } else { 23 | prune(data[key], keysToDelete); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/WombatLpTokenPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { WombatLpTokenPriceOracle } from "../../../oracles/default/WombatLpTokenPriceOracle.sol"; 6 | 7 | contract WombatLpTokenPriceOracleTest is BaseTest { 8 | WombatLpTokenPriceOracle private oracle; 9 | 10 | function afterForkSetUp() internal override { 11 | oracle = new WombatLpTokenPriceOracle(); 12 | } 13 | 14 | function testPrice() public fork(BSC_MAINNET) { 15 | // price for Wombat Wrapped BNB asset 16 | vm.prank(ap.getAddress("MasterPriceOracle")); 17 | uint256 price = oracle.price(0x74f019A5C4eD2C2950Ce16FaD7Af838549092c5b); 18 | assertEq(price, 1e18); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/external/bomb/IXBomb.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | import { IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 4 | 5 | interface IXBomb is IERC20Upgradeable { 6 | function reward() external view returns (IERC20Upgradeable); 7 | 8 | function leave(uint256 _share) external; 9 | 10 | function enter(uint256 _amount) external; 11 | 12 | function getExchangeRate() external view returns (uint256); 13 | 14 | function toREWARD(uint256 stakedAmount) external view returns (uint256 rewardAmount); 15 | 16 | function toSTAKED(uint256 rewardAmount) external view returns (uint256 stakedAmount); 17 | 18 | function name() external view returns (string memory); 19 | 20 | function symbol() external view returns (string memory); 21 | } 22 | -------------------------------------------------------------------------------- /tasks/vaults/registry.ts: -------------------------------------------------------------------------------- 1 | // import { task } from "hardhat/config"; 2 | 3 | // export default task("optimized-vaults-registry:upgrade").setAction(async ({}, { deployments, getNamedAccounts }) => { 4 | // const { deployer } = await getNamedAccounts(); 5 | // const vaultsRegistry = await deployments.deploy("OptimizedVaultsRegistry", { 6 | // from: deployer, 7 | // log: true, 8 | // proxy: { 9 | // execute: { 10 | // init: { 11 | // methodName: "initialize", 12 | // args: [] 13 | // } 14 | // }, 15 | // proxyContract: "OpenZeppelinTransparentProxy", 16 | // owner: deployer 17 | // }, 18 | // waitConfirmations: 1 19 | // }); 20 | 21 | // console.log(`upgraded the optimized vaults registry at ${vaultsRegistry.address}`); 22 | // }); 23 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/StkBNBPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { StkBNBPriceOracle } from "../../../oracles/default/StkBNBPriceOracle.sol"; 6 | 7 | contract StkBNBPriceOracleTest is BaseTest { 8 | StkBNBPriceOracle private oracle; 9 | address stkBnb = 0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16; 10 | 11 | function afterForkSetUp() internal override { 12 | oracle = new StkBNBPriceOracle(); 13 | oracle.initialize(); 14 | } 15 | 16 | function testStkBnbOraclePrice() public forkAtBlock(BSC_MAINNET, 21952914) { 17 | uint256 priceStkBnb = oracle.price(stkBnb); 18 | 19 | assertGt(priceStkBnb, 1e18); 20 | assertEq(priceStkBnb, 1006482474298479702); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deploy/08-deploy-irms.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | import { ChainDeployConfig, chainDeployConfig } from "../chainDeploy"; 4 | import { deployIRMs } from "../chainDeploy/helpers"; 5 | 6 | const func: DeployFunction = async ({ run, viem, getNamedAccounts, deployments, getChainId }) => { 7 | const chainId = parseInt(await getChainId()); 8 | 9 | if (!chainDeployConfig[chainId]) { 10 | throw new Error(`Config invalid for ${chainId}`); 11 | } 12 | const { config: chainDeployParams }: { config: ChainDeployConfig } = chainDeployConfig[chainId]; 13 | 14 | /// 15 | //// IRM MODELS 16 | await deployIRMs({ run, viem, getNamedAccounts, deployments, deployConfig: chainDeployParams, chainId }); 17 | }; 18 | 19 | func.tags = ["prod", "deploy-irms"]; 20 | 21 | export default func; 22 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | interface IUniswapV2Factory { 5 | event PairCreated(address indexed token0, address indexed token1, address pair, uint256); 6 | 7 | function feeTo() external view returns (address); 8 | 9 | function feeToSetter() external view returns (address); 10 | 11 | function getPair(address tokenA, address tokenB) external view returns (address pair); 12 | 13 | function allPairs(uint256) external view returns (address pair); 14 | 15 | function allPairsLength() external view returns (uint256); 16 | 17 | function createPair(address tokenA, address tokenB) external returns (address pair); 18 | 19 | function setFeeTo(address) external; 20 | 21 | function setFeeToSetter(address) external; 22 | } 23 | -------------------------------------------------------------------------------- /chainDeploy/helpers/getCgPrice.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export const getCgPrice = async (coingeckoId: string) => { 4 | let usdPrice = NaN; 5 | 6 | try { 7 | const { data } = await axios.get( 8 | `https://api.coingecko.com/api/v3/simple/price?vs_currencies=usd&ids=${coingeckoId}` 9 | ); 10 | 11 | if (data[coingeckoId] && data[coingeckoId].usd) { 12 | usdPrice = data[coingeckoId].usd; 13 | } 14 | } catch (e) { 15 | const { data } = await axios.get(`https://coins.llama.fi/prices/current/coingecko:${coingeckoId}`); 16 | 17 | if (data.coins[`coingecko:${coingeckoId}`] && data.coins[`coingecko:${coingeckoId}`].price) { 18 | usdPrice = data.coins[`coingecko:${coingeckoId}`].price; 19 | } 20 | } 21 | 22 | if (usdPrice) { 23 | return usdPrice; 24 | } else { 25 | return 1; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /chainDeploy/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | import { SupportedAsset } from "../../../monorepo/packages/types"; 3 | 4 | export const underlying = (assets: SupportedAsset[], symbol: string): Address => { 5 | return assetFilter(assets, symbol).underlying; 6 | }; 7 | 8 | export const assetArrayToMap = (assets: SupportedAsset[]): { [key: string]: SupportedAsset } => 9 | assets.reduce( 10 | (acc, curr) => { 11 | acc[curr.underlying] = curr; 12 | return acc; 13 | }, 14 | {} as Record 15 | ); 16 | 17 | export const assetFilter = (assets: SupportedAsset[], symbol: string): SupportedAsset => { 18 | const asset = assets.find((a: SupportedAsset) => a.symbol === symbol); 19 | if (!asset) throw new Error(`no such SupportedAsset with symbol ${symbol} in assets ${JSON.stringify(assets)}`); 20 | return asset; 21 | }; 22 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IIonicFlywheel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 5 | 6 | interface IIonicFlywheel { 7 | function isRewardsDistributor() external returns (bool); 8 | 9 | function isFlywheel() external returns (bool); 10 | 11 | function flywheelPreSupplierAction(address market, address supplier) external; 12 | 13 | function flywheelPreBorrowerAction(address market, address borrower) external; 14 | 15 | function flywheelPreTransferAction(address market, address src, address dst) external; 16 | 17 | function compAccrued(address user) external view returns (uint256); 18 | 19 | function addMarketForRewards(ERC20 strategy) external; 20 | 21 | function marketState(ERC20 strategy) external view returns (uint224 index, uint32 lastUpdatedTimestamp); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/liquidators/UniswapV2Liquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./BaseUniswapV2Liquidator.sol"; 5 | 6 | /** 7 | * @title UniswapV2Liquidator 8 | * @notice Exchanges seized token collateral for underlying tokens via a Uniswap V2 router for use as a step in a liquidation. 9 | * @author David Lucid (https://github.com/davidlucid) 10 | */ 11 | contract UniswapV2Liquidator is BaseUniswapV2Liquidator { 12 | function _swap( 13 | IUniswapV2Router02 uniswapV2Router, 14 | uint256 inputAmount, 15 | address[] memory swapPath 16 | ) internal override { 17 | uniswapV2Router.swapExactTokensForTokens(inputAmount, 0, swapPath, address(this), block.timestamp); 18 | } 19 | 20 | function name() public pure virtual returns (string memory) { 21 | return "UniswapV2Liquidator"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deploy/07-address-provider.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | const func: DeployFunction = async ({ getNamedAccounts, deployments }) => { 4 | const { deployer, multisig } = await getNamedAccounts(); 5 | 6 | //// 7 | //// HELPERS - ADDRESSES PROVIDER 8 | try { 9 | await deployments.deploy("AddressesProvider", { 10 | from: deployer, 11 | log: true, 12 | proxy: { 13 | execute: { 14 | init: { 15 | methodName: "initialize", 16 | args: [deployer] 17 | } 18 | }, 19 | proxyContract: "OpenZeppelinTransparentProxy", 20 | owner: multisig 21 | }, 22 | waitConfirmations: 1 23 | }); 24 | } catch (error) { 25 | console.error("Could not deploy:", error); 26 | } 27 | }; 28 | 29 | func.tags = ["prod", "deploy-ap"]; 30 | 31 | export default func; 32 | -------------------------------------------------------------------------------- /contracts/ionic/levered/LeveredPositionStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import { ILeveredPositionFactory } from "./ILeveredPositionFactory.sol"; 5 | import { IonicComptroller } from "../../compound/ComptrollerInterface.sol"; 6 | import { ICErc20 } from "../../compound/CTokenInterfaces.sol"; 7 | 8 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 9 | 10 | contract LeveredPositionStorage { 11 | address public immutable positionOwner; 12 | ILeveredPositionFactory public factory; 13 | 14 | ICErc20 public collateralMarket; 15 | ICErc20 public stableMarket; 16 | IonicComptroller public pool; 17 | 18 | IERC20Upgradeable public collateralAsset; 19 | IERC20Upgradeable public stableAsset; 20 | 21 | constructor(address _positionOwner) { 22 | positionOwner = _positionOwner; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/default-issue-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Default Issue ' 3 | about: Describe this issue template's purpose here. 4 | title: New Issue 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Background 11 | 12 | Any information that gives more context as to why this task exists, and why it matters. This might include: links to discussions, documentation, or other GitHub issues / tasks 13 | 14 | ### Linked Issues & Documentation 15 | 16 | - List of linked issues, external links, etc 17 | 18 | ## Definition Of Done 19 | 20 | Describes in 1-2 sentences what the output of the Task is, so that it can be verified by another team member easily. “Feature X is implemented as described and can be verified by using it” 21 | 22 | ## Acceptance Criteria 23 | 24 | Describe criteria required for proper testing after 25 | 26 | ## Tasks 27 | 28 | - [ ] Task 1 29 | - [ ] Task 2 30 | - [ ] Task 3 31 | -------------------------------------------------------------------------------- /chainDeploy/index.ts: -------------------------------------------------------------------------------- 1 | import { base, fraxtal, mode } from "viem/chains"; 2 | import { ChainDeployConfig } from "./helpers"; 3 | import { deploy as deployBase, deployConfig as deployConfigBase } from "./mainnets/base"; 4 | import { deploy as deployMode, deployConfig as deployConfigMode } from "./mainnets/mode"; 5 | import { deploy as deployFrax, deployConfig as deployConfigFrax } from "./mainnets/fraxtal"; 6 | // import { deploy as deploy10, deployConfig as deployConfig10 } from "./mainnets/optimism"; 7 | 8 | export const chainDeployConfig: Record = { 9 | // mainnets 10 | [mode.id]: { config: deployConfigMode, deployFunc: deployMode }, 11 | [base.id]: { config: deployConfigBase, deployFunc: deployBase }, 12 | [fraxtal.id]: { config: deployConfigFrax, deployFunc: deployFrax } 13 | // testnets 14 | // local 15 | }; 16 | 17 | export * from "./types"; 18 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/WSTEthPriceOracleTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { WSTEthPriceOracle } from "../../../oracles/default/WSTEthPriceOracle.sol"; 6 | 7 | contract WSTEthPriceOracleTest is BaseTest { 8 | WSTEthPriceOracle private oracle; 9 | address wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; 10 | address mpo = 0xdD8d4e09Acb39C2B4DE9A84384389B79850f3271; 11 | 12 | function afterForkSetUp() internal override { 13 | oracle = new WSTEthPriceOracle(); 14 | oracle.initialize(); 15 | } 16 | 17 | function testWstEthOraclePrice() public forkAtBlock(ETHEREUM_MAINNET, 17469681) { 18 | vm.prank(mpo); 19 | uint256 priceWstEth = oracle.price(wstETH); 20 | 21 | assertGt(priceWstEth, 1e18); 22 | assertEq(priceWstEth, 1006482474298479702); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/WithdrawableFlywheelStaticRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { FlywheelStaticRewards } from "./FlywheelStaticRewards.sol"; 5 | import { IonicFlywheelCore } from "../IonicFlywheelCore.sol"; 6 | import { Auth, Authority } from "solmate/auth/Auth.sol"; 7 | import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; 8 | 9 | contract WithdrawableFlywheelStaticRewards is FlywheelStaticRewards { 10 | using SafeTransferLib for ERC20; 11 | 12 | constructor( 13 | IonicFlywheelCore _flywheel, 14 | address _owner, 15 | Authority _authority 16 | ) FlywheelStaticRewards(_flywheel, _owner, _authority) {} 17 | 18 | function withdraw(uint256 amount) external { 19 | require(msg.sender == flywheel.owner()); 20 | rewardToken.safeTransfer(address(flywheel.owner()), amount); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/external/balancer/IBalancerStablePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | import { IBalancerVault } from "./IBalancerVault.sol"; 5 | import { IRateProvider } from "./IRateProvider.sol"; 6 | 7 | interface IBalancerStablePool { 8 | function getActualSupply() external view returns (uint256); 9 | 10 | function getBptIndex() external view returns (uint256); 11 | 12 | function getPoolId() external view returns (bytes32); 13 | 14 | function getVault() external view returns (IBalancerVault); 15 | 16 | function getRate() external view returns (uint256); 17 | 18 | function getRateProviders() external view returns (IRateProvider[] memory); 19 | 20 | function getScalingFactros() external view returns (uint256[] memory); 21 | 22 | function getTokenRate(address token) external view returns (uint256); 23 | 24 | function updateTokenRateCache(address token) external; 25 | } 26 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/ReplacingFlywheelStaticRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { FlywheelStaticRewards } from "./FlywheelStaticRewards.sol"; 5 | import { IonicFlywheelCore } from "../IonicFlywheelCore.sol"; 6 | import { Auth, Authority } from "solmate/auth/Auth.sol"; 7 | import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; 8 | 9 | contract ReplacingFlywheelStaticRewards is FlywheelStaticRewards { 10 | using SafeTransferLib for ERC20; 11 | 12 | IonicFlywheelCore public replacedFlywheel; 13 | 14 | constructor( 15 | IonicFlywheelCore _replacedFlywheel, 16 | IonicFlywheelCore _flywheel, 17 | address _owner, 18 | Authority _authority 19 | ) FlywheelStaticRewards(_flywheel, _owner, _authority) { 20 | ERC20 _rewardToken = _flywheel.rewardToken(); 21 | _rewardToken.safeApprove(address(_replacedFlywheel), type(uint256).max); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deploy/13-configure-address-provider.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | import { ChainDeployConfig, chainDeployConfig } from "../chainDeploy"; 4 | import { configureAddressesProviderAddresses } from "../chainDeploy/helpers/liquidators/ionicLiquidator"; 5 | 6 | const func: DeployFunction = async ({ viem, getNamedAccounts, getChainId, deployments }) => { 7 | const chainId = parseInt(await getChainId()); 8 | 9 | if (!chainDeployConfig[chainId]) { 10 | throw new Error(`Config invalid for ${chainId}`); 11 | } 12 | const { config: chainDeployParams }: { config: ChainDeployConfig } = chainDeployConfig[chainId]; 13 | 14 | if (chainId !== 1) { 15 | await configureAddressesProviderAddresses({ 16 | viem, 17 | getNamedAccounts, 18 | chainId, 19 | deployConfig: chainDeployParams, 20 | deployments 21 | }); 22 | } 23 | }; 24 | 25 | func.tags = ["prod", "configure-ap"]; 26 | 27 | export default func; 28 | -------------------------------------------------------------------------------- /contracts/utils/Multicall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | import "./IMulticall.sol"; 5 | 6 | /// @title Multicall 7 | /// @notice Enables calling multiple methods in a single call to the contract 8 | abstract contract Multicall is IMulticall { 9 | /// @inheritdoc IMulticall 10 | function multicall(bytes[] calldata data) public payable virtual override returns (bytes[] memory results) { 11 | results = new bytes[](data.length); 12 | for (uint256 i = 0; i < data.length; i++) { 13 | (bool success, bytes memory result) = address(this).delegatecall(data[i]); 14 | 15 | if (!success) { 16 | // Next 5 lines from https://ethereum.stackexchange.com/a/83577 17 | if (result.length < 68) revert(); 18 | assembly { 19 | result := add(result, 0x04) 20 | } 21 | revert(abi.decode(result, (string))); 22 | } 23 | 24 | results[i] = result; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tasks/chain-specific/base/disableFlywheel.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Address, formatEther, parseEther } from "viem"; 3 | import { COMPTROLLER } from "."; 4 | 5 | task("market:base:disable-flywheel", "Deploys flywheel and adds rewards").setAction( 6 | async (_, { viem, run, deployments, getNamedAccounts }) => { 7 | const { deployer } = await getNamedAccounts(); 8 | const publicClient = await viem.getPublicClient(); 9 | 10 | const flywheel = await viem.getContractAt( 11 | "IonicFlywheel", 12 | (await deployments.get("IonicFlywheel_ION_v2")).address as Address 13 | ); 14 | 15 | const comptroller = await viem.getContractAt("IonicComptroller", COMPTROLLER); 16 | const addTx = await comptroller.write.addNonAccruingFlywheel([flywheel.address]); 17 | await publicClient.waitForTransactionReceipt({ hash: addTx }); 18 | console.log({ addTx }); 19 | console.log("Remove IonicFlywheel_ION_v2 from comptroller"); 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /tasks/chain-specific/base/removeFlywheel.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Address, formatEther, parseEther } from "viem"; 3 | import { COMPTROLLER } from "."; 4 | 5 | task("market:base:remove-flywheel", "Deploys flywheel and adds rewards").setAction( 6 | async (_, { viem, run, deployments, getNamedAccounts }) => { 7 | const { deployer } = await getNamedAccounts(); 8 | const publicClient = await viem.getPublicClient(); 9 | 10 | const flywheel = await viem.getContractAt( 11 | "IonicFlywheel", 12 | (await deployments.get("IonicFlywheel_ION_v2")).address as Address 13 | ); 14 | 15 | const comptroller = await viem.getContractAt("IonicComptroller", COMPTROLLER); 16 | const addTx = await comptroller.write._removeFlywheel([flywheel.address]); 17 | await publicClient.waitForTransactionReceipt({ hash: addTx }); 18 | console.log({ addTx }); 19 | console.log(`Remove IonicFlywheel_ION_v2 ${flywheel.address} from comptroller`); 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/brockelmore/forge-std 4 | [submodule "lib/openzeppelin-contracts-upgradeable"] 5 | path = lib/openzeppelin-contracts-upgradeable 6 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 7 | [submodule "lib/solmate"] 8 | path = lib/solmate 9 | url = https://github.com/rari-capital/solmate 10 | [submodule "lib/openzeppelin-contracts"] 11 | path = lib/openzeppelin-contracts 12 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 13 | [submodule "lib/pyth-sdk-solidity"] 14 | path = lib/pyth-sdk-solidity 15 | url = https://github.com/pyth-network/pyth-sdk-solidity 16 | [submodule "lib/solidity-bytes-utils"] 17 | path = lib/solidity-bytes-utils 18 | url = https://github.com/GNSPS/solidity-bytes-utils 19 | [submodule "lib/ops"] 20 | path = lib/ops 21 | url = https://github.com/gelatodigital/ops 22 | [submodule "lib/adrastia-periphery"] 23 | path = lib/adrastia-periphery 24 | url = https://github.com/adrastia-oracle/adrastia-periphery 25 | -------------------------------------------------------------------------------- /contracts/external/jarvis/ISynthereumFinder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.4; 3 | 4 | /** 5 | * @title Provides addresses of the contracts implementing certain interfaces. 6 | */ 7 | interface ISynthereumFinder { 8 | /** 9 | * @notice Updates the address of the contract that implements `interfaceName`. 10 | * @param interfaceName bytes32 encoding of the interface name that is either changed or registered. 11 | * @param implementationAddress address of the deployed contract that implements the interface. 12 | */ 13 | function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external; 14 | 15 | /** 16 | * @notice Gets the address of the contract that implements the given `interfaceName`. 17 | * @param interfaceName queried interface. 18 | * @return implementationAddress Address of the deployed contract that implements the interface. 19 | */ 20 | function getImplementationAddress(bytes32 interfaceName) external view returns (address); 21 | } 22 | -------------------------------------------------------------------------------- /tasks/pool/admin/deprecate.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | 3 | import { Address } from "viem"; 4 | 5 | export default task("pool:deprecate", "Whitelists a new comptroller implementation upgrade") 6 | .addOptionalParam("index", "Pool index for which to deprecate", undefined, types.string) 7 | .addOptionalParam("comptroller", "Pool address for which to deprecate", undefined, types.string) 8 | .setAction(async (taskArgs, { viem, deployments }) => { 9 | const poolDirectory = await viem.getContractAt( 10 | "PoolDirectory", 11 | (await deployments.get("PoolDirectory")).address as Address 12 | ); 13 | if (taskArgs.index) { 14 | const tx = await poolDirectory.write._deprecatePool(taskArgs.index); 15 | console.log("tx: ", tx); 16 | } else if (taskArgs.comptroller) { 17 | const tx = await poolDirectory.write._deprecatePool(taskArgs.comptroller); 18 | console.log("tx: ", tx); 19 | } else { 20 | throw new Error("Must provide either index or comptroller"); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /contracts/external/balancer/BConst.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | contract BConst { 5 | uint256 public constant BONE = 10**18; 6 | 7 | uint256 public constant MIN_BOUND_TOKENS = 2; 8 | uint256 public constant MAX_BOUND_TOKENS = 8; 9 | 10 | uint256 public constant MIN_FEE = BONE / 10**6; 11 | uint256 public constant MAX_FEE = BONE / 10; 12 | uint256 public constant EXIT_FEE = 0; 13 | 14 | uint256 public constant MIN_WEIGHT = BONE; 15 | uint256 public constant MAX_WEIGHT = BONE * 50; 16 | uint256 public constant MAX_TOTAL_WEIGHT = BONE * 50; 17 | uint256 public constant MIN_BALANCE = BONE / 10**12; 18 | 19 | uint256 public constant INIT_POOL_SUPPLY = BONE * 100; 20 | 21 | uint256 public constant MIN_BPOW_BASE = 1 wei; 22 | uint256 public constant MAX_BPOW_BASE = (2 * BONE) - 1 wei; 23 | uint256 public constant BPOW_PRECISION = BONE / 10**10; 24 | 25 | uint256 public constant MAX_IN_RATIO = BONE / 2; 26 | uint256 public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei; 27 | } 28 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Type of change 6 | 7 | 8 | 9 | - [ ] Docs change / dependency upgrade 10 | - [ ] Configuration / tooling changes 11 | - [ ] Refactoring 12 | - [ ] Bug fix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 15 | - [ ] Requires changes in customer code 16 | 17 | ## High-level change(s) description - from the user's perspective 18 | 19 | 20 | 21 | ## Related Issue(s) 22 | 23 | Fixes 24 | 25 | ## Related pull request(s) 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV3FlashCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Callback for IUniswapV3PoolActions#flash 5 | /// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface 6 | interface IUniswapV3FlashCallback { 7 | /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. 8 | /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. 9 | /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. 10 | /// @param fee0 The fee amount in token0 due to the pool by the end of the flash 11 | /// @param fee1 The fee amount in token1 due to the pool by the end of the flash 12 | /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call 13 | function uniswapV3FlashCallback( 14 | uint256 fee0, 15 | uint256 fee1, 16 | bytes calldata data 17 | ) external; 18 | } 19 | -------------------------------------------------------------------------------- /contracts/external/chainlink/AggregatorV3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | interface AggregatorV3Interface { 5 | function decimals() external view returns (uint8); 6 | 7 | function description() external view returns (string memory); 8 | 9 | function version() external view returns (uint256); 10 | 11 | // getRoundData and latestRoundData should both raise "No data present" 12 | // if they do not have data to report, instead of returning unset values 13 | // which could be misinterpreted as actual reported values. 14 | function getRoundData(uint80 _roundId) 15 | external 16 | view 17 | returns ( 18 | uint80 roundId, 19 | int256 answer, 20 | uint256 startedAt, 21 | uint256 updatedAt, 22 | uint80 answeredInRound 23 | ); 24 | 25 | function latestRoundData() 26 | external 27 | view 28 | returns ( 29 | uint80 roundId, 30 | int256 answer, 31 | uint256 startedAt, 32 | uint256 updatedAt, 33 | uint80 answeredInRound 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: For any new feature requests (resolves an issue, UX improvement or addition) 4 | etc) 5 | title: 'FEATURE: ' 6 | labels: ":sparkles: [Feature]" 7 | assignees: '' 8 | 9 | --- 10 | 11 | **Please describe the reasoning for this feature?** _Please mark one of the the checkboxes below, then put the description below that, screenshots preferred._
12 | - [ ] _Is this to resolve a process problem, bug. defect, error or glitch?_
13 | - [ ] _Is this for a new feature or improvement?_
14 | *** 15 | 16 | **Describe the solution or new feature you'd like.** _How can we improve our product? What will bring more users to our protocol?_
17 | 18 | 19 | **Describe alternatives that would satisfy requirements.** _What's an alternative that could satisfy the same requirements I was looking for? Will it be harder? Will it require more resources?._
20 | 21 | **Additional context.**
_Any information that's relevant that doesn't fit in the categories above?_
22 | -------------------------------------------------------------------------------- /contracts/external/curve/ICurvePool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface ICurvePool is IERC20Upgradeable { 7 | function get_virtual_price() external view returns (uint256); 8 | 9 | function remove_liquidity_one_coin( 10 | uint256 _token_amount, 11 | int128 i, 12 | uint256 min_amount 13 | ) external; 14 | 15 | function calc_withdraw_one_coin(uint256 _burn_amount, int128 i) external view returns (uint256); 16 | 17 | function add_liquidity(uint256[2] calldata _amounts, uint256 _min_mint_amount) external returns (uint256); 18 | 19 | function exchange( 20 | int128 i, 21 | int128 j, 22 | uint256 dx, 23 | uint256 min_dy 24 | ) external returns (uint256); 25 | 26 | function get_dy( 27 | int128 i, 28 | int128 j, 29 | uint256 _dx 30 | ) external view returns (uint256); 31 | 32 | function coins(uint256 index) external view returns (address); 33 | 34 | function lp_token() external view returns (address); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { FlywheelDynamicRewards } from "./FlywheelDynamicRewards.sol"; 5 | import { IonicFlywheelCore } from "../IonicFlywheelCore.sol"; 6 | import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; 7 | 8 | contract IonicFlywheelDynamicRewards is FlywheelDynamicRewards { 9 | using SafeTransferLib for ERC20; 10 | 11 | constructor(IonicFlywheelCore _flywheel, uint32 _cycleLength) 12 | FlywheelDynamicRewards(_flywheel, _cycleLength) 13 | {} 14 | 15 | function getNextCycleRewards(ERC20 strategy) 16 | internal 17 | override 18 | returns (uint192) 19 | { 20 | uint256 rewardAmount = rewardToken.balanceOf(address(strategy)); 21 | if (rewardAmount != 0) { 22 | rewardToken.safeTransferFrom( 23 | address(strategy), 24 | address(this), 25 | rewardAmount 26 | ); 27 | } 28 | return uint192(rewardAmount); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/liquidators/registry/LiquidatorsRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import "../../ionic/DiamondExtension.sol"; 5 | import "./LiquidatorsRegistryStorage.sol"; 6 | import "./LiquidatorsRegistryExtension.sol"; 7 | 8 | contract LiquidatorsRegistry is LiquidatorsRegistryStorage, DiamondBase { 9 | using EnumerableSet for EnumerableSet.AddressSet; 10 | 11 | constructor(AddressesProvider _ap) SafeOwnable() { 12 | ap = _ap; 13 | } 14 | 15 | /** 16 | * @dev register a logic extension 17 | * @param extensionToAdd the extension whose functions are to be added 18 | * @param extensionToReplace the extension whose functions are to be removed/replaced 19 | */ 20 | function _registerExtension(DiamondExtension extensionToAdd, DiamondExtension extensionToReplace) 21 | public 22 | override 23 | onlyOwner 24 | { 25 | LibDiamond.registerExtension(extensionToAdd, extensionToReplace); 26 | } 27 | 28 | function asExtension() public view returns (LiquidatorsRegistryExtension) { 29 | return LiquidatorsRegistryExtension(address(this)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /deploy/04-deploy-pool-directory.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | import { Hash } from "viem"; 3 | 4 | const func: DeployFunction = async ({ viem, getNamedAccounts, deployments }) => { 5 | const { deployer, multisig } = await getNamedAccounts(); 6 | const publicClient = await viem.getPublicClient(); 7 | 8 | let fpd; 9 | try { 10 | fpd = await deployments.deploy("PoolDirectory", { 11 | from: deployer, 12 | log: true, 13 | proxy: { 14 | proxyContract: "OpenZeppelinTransparentProxy", 15 | execute: { 16 | init: { 17 | methodName: "initialize", 18 | args: [false, []] 19 | } 20 | }, 21 | owner: multisig 22 | }, 23 | waitConfirmations: 1 24 | }); 25 | if (fpd.transactionHash) await publicClient.waitForTransactionReceipt({ hash: fpd.transactionHash as Hash }); 26 | console.log("PoolDirectory: ", fpd.address); 27 | } catch (error) { 28 | console.error("Could not deploy:", error); 29 | } 30 | }; 31 | 32 | func.tags = ["prod", "deply-pool-directory"]; 33 | 34 | export default func; 35 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/libraries/SafeCast.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Safe casting methods 5 | /// @notice Contains methods for safely casting between types 6 | library SafeCast { 7 | /// @notice Cast a uint256 to a uint160, revert on overflow 8 | /// @param y The uint256 to be downcasted 9 | /// @return z The downcasted integer, now type uint160 10 | function toUint160(uint256 y) internal pure returns (uint160 z) { 11 | require((z = uint160(y)) == y); 12 | } 13 | 14 | /// @notice Cast a int256 to a int128, revert on overflow or underflow 15 | /// @param y The int256 to be downcasted 16 | /// @return z The downcasted integer, now type int128 17 | function toInt128(int256 y) internal pure returns (int128 z) { 18 | require((z = int128(y)) == y); 19 | } 20 | 21 | /// @notice Cast a uint256 to a int256, revert on overflow 22 | /// @param y The uint256 to be casted 23 | /// @return z The casted integer, now type int256 24 | function toInt256(uint256 y) internal pure returns (int256 z) { 25 | require(y < 2**255); 26 | z = int256(y); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IonicFlywheelBorrowBooster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { ICErc20 } from "../../../compound/CTokenInterfaces.sol"; 5 | import "./IIonicFlywheelBorrowBooster.sol"; 6 | 7 | contract IonicFlywheelBorrowBooster is IIonicFlywheelBorrowBooster { 8 | string public constant BOOSTER_TYPE = "FlywheelBorrowBooster"; 9 | 10 | /** 11 | @notice calculate the boosted supply of a strategy. 12 | @param strategy the strategy to calculate boosted supply of 13 | @return the boosted supply 14 | */ 15 | function boostedTotalSupply(ICErc20 strategy) external view returns (uint256) { 16 | return strategy.totalBorrows(); 17 | } 18 | 19 | /** 20 | @notice calculate the boosted balance of a user in a given strategy. 21 | @param strategy the strategy to calculate boosted balance of 22 | @param user the user to calculate boosted balance of 23 | @return the boosted balance 24 | */ 25 | function boostedBalanceOf(ICErc20 strategy, address user) external view returns (uint256) { 26 | return strategy.borrowBalanceCurrent(user); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/oracles/BasePriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../compound/CTokenInterfaces.sol"; 5 | 6 | /** 7 | * @title BasePriceOracle 8 | * @notice Returns prices of underlying tokens directly without the caller having to specify a cToken address. 9 | * @dev Implements the `PriceOracle` interface. 10 | * @author David Lucid (https://github.com/davidlucid) 11 | */ 12 | interface BasePriceOracle { 13 | /** 14 | * @notice Get the price of an underlying asset. 15 | * @param underlying The underlying asset to get the price of. 16 | * @return The underlying asset price in ETH as a mantissa (scaled by 1e18). 17 | * Zero means the price is unavailable. 18 | */ 19 | function price(address underlying) external view returns (uint256); 20 | 21 | /** 22 | * @notice Get the underlying price of a cToken asset 23 | * @param cToken The cToken to get the underlying price of 24 | * @return The underlying asset price mantissa (scaled by 1e18). 25 | * Zero means the price is unavailable. 26 | */ 27 | function getUnderlyingPrice(ICErc20 cToken) external view returns (uint256); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/oracles/1337/MockRevertPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../BasePriceOracle.sol"; 5 | 6 | /** 7 | * @title MockRevertPriceOracle 8 | * @notice Mocks a failing price oracle. Used for testing purposes only 9 | * @author Jourdan Dunkley (https://github.com/jourdanDunkley) 10 | */ 11 | contract MockRevertPriceOracle is BasePriceOracle { 12 | constructor() {} 13 | 14 | /** 15 | * @dev Returns the price in ETH of `underlying` (implements `BasePriceOracle`). 16 | */ 17 | function price(address underlying) external view override returns (uint256) { 18 | revert("MockPriceOracle: price function is failing."); 19 | } 20 | 21 | /** 22 | * @notice Returns the price in ETH of the token underlying `cToken`. 23 | * @dev Implements the `PriceOracle` interface for Ionic pools (and Compound v2). 24 | * @return Price in ETH of the token underlying `cToken`, scaled by `10 ** (36 - underlyingDecimals)`. 25 | */ 26 | function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { 27 | revert("MockPriceOracle: getUnderlyingPrice function is failing."); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/external/gamma/IUniProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.7.5; 3 | 4 | interface IUniProxy { 5 | /// @notice Deposit into the given position 6 | /// @param deposit0 Amount of token0 to deposit 7 | /// @param deposit1 Amount of token1 to deposit 8 | /// @param to Address to receive liquidity tokens 9 | /// @param pos Hypervisor Address 10 | /// @return shares Amount of liquidity tokens received 11 | function deposit( 12 | uint256 deposit0, 13 | uint256 deposit1, 14 | address to, 15 | address pos, // IHypervisor 16 | uint256[4] memory minIn 17 | ) external returns (uint256 shares); 18 | 19 | /// @notice Get the amount of token to deposit for the given amount of pair token 20 | /// @param pos Hypervisor Address 21 | /// @param token Address of token to deposit 22 | /// @param _deposit Amount of token to deposit 23 | /// @return amountStart Minimum amounts of the pair token to deposit 24 | /// @return amountEnd Maximum amounts of the pair token to deposit 25 | function getDepositAmount( 26 | address pos, 27 | address token, 28 | uint256 _deposit 29 | ) external view returns (uint256 amountStart, uint256 amountEnd); 30 | } 31 | -------------------------------------------------------------------------------- /contracts/external/chainlink/Denominations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | library Denominations { 5 | address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 6 | address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; 7 | 8 | // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 9 | address public constant USD = address(840); 10 | address public constant GBP = address(826); 11 | address public constant EUR = address(978); 12 | address public constant JPY = address(392); 13 | address public constant KRW = address(410); 14 | address public constant CNY = address(156); 15 | address public constant AUD = address(36); 16 | address public constant CAD = address(124); 17 | address public constant CHF = address(756); 18 | address public constant ARS = address(32); 19 | address public constant PHP = address(608); 20 | address public constant NZD = address(554); 21 | address public constant SGD = address(702); 22 | address public constant NGN = address(566); 23 | address public constant ZAR = address(710); 24 | address public constant RUB = address(643); 25 | address public constant INR = address(356); 26 | address public constant BRL = address(986); 27 | } 28 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IonicFlywheelBorrow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 5 | import { IonicFlywheelCore } from "./IonicFlywheelCore.sol"; 6 | import "./IIonicFlywheel.sol"; 7 | 8 | contract IonicFlywheelBorrow is IonicFlywheelCore, IIonicFlywheel { 9 | bool public constant isRewardsDistributor = true; 10 | bool public constant isFlywheel = true; 11 | 12 | function flywheelPreSupplierAction(address market, address supplier) external {} 13 | 14 | function flywheelPreBorrowerAction(address market, address borrower) external { 15 | accrue(ERC20(market), borrower); 16 | } 17 | 18 | function flywheelPreTransferAction(address market, address src, address dst) external {} 19 | 20 | function compAccrued(address user) external view returns (uint256) { 21 | return _rewardsAccrued[user]; 22 | } 23 | 24 | function addMarketForRewards(ERC20 strategy) external onlyOwner { 25 | _addStrategyForRewards(strategy); 26 | } 27 | 28 | // TODO remove 29 | function marketState(ERC20 strategy) external view returns (uint224, uint32) { 30 | return (_strategyState[strategy].index, _strategyState[strategy].lastUpdatedTimestamp); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/liquidators/IRedemptionStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | /** 7 | * @title IRedemptionStrategy 8 | * @notice Redeems seized wrapped token collateral for an underlying token for use as a step in a liquidation. 9 | * @author David Lucid (https://github.com/davidlucid) 10 | */ 11 | interface IRedemptionStrategy { 12 | /** 13 | * @notice Redeems custom collateral `token` for an underlying token. 14 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 15 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 16 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 17 | * @return outputToken The underlying ERC20 token outputted. 18 | * @return outputAmount The quantity of underlying tokens outputted. 19 | */ 20 | function redeem( 21 | IERC20Upgradeable inputToken, 22 | uint256 inputAmount, 23 | bytes memory strategyData 24 | ) external returns (IERC20Upgradeable outputToken, uint256 outputAmount); 25 | 26 | function name() external view returns (string memory); 27 | } 28 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IonicFlywheel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 5 | import { IonicFlywheelCore } from "./IonicFlywheelCore.sol"; 6 | import "./IIonicFlywheel.sol"; 7 | 8 | contract IonicFlywheel is IonicFlywheelCore, IIonicFlywheel { 9 | bool public constant isRewardsDistributor = true; 10 | bool public constant isFlywheel = true; 11 | 12 | function flywheelPreSupplierAction(address market, address supplier) external { 13 | accrue(ERC20(market), supplier); 14 | } 15 | 16 | function flywheelPreBorrowerAction(address market, address borrower) external {} 17 | 18 | function flywheelPreTransferAction(address market, address src, address dst) external { 19 | accrue(ERC20(market), src, dst); 20 | } 21 | 22 | function compAccrued(address user) external view returns (uint256) { 23 | return _rewardsAccrued[user]; 24 | } 25 | 26 | function addMarketForRewards(ERC20 strategy) external onlyOwner { 27 | _addStrategyForRewards(strategy); 28 | } 29 | 30 | // TODO remove 31 | function marketState(ERC20 strategy) external view returns (uint224, uint32) { 32 | return (_strategyState[strategy].index, _strategyState[strategy].lastUpdatedTimestamp); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/oracles/default/FixedNativePriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; 5 | 6 | import "../BasePriceOracle.sol"; 7 | 8 | /** 9 | * @title FixedEthPriceOracle 10 | * @notice Returns fixed prices of 1 denominated in the chain's native token for all tokens (expected to be used under a `MasterPriceOracle`). 11 | * @dev Implements `PriceOracle` and `BasePriceOracle`. 12 | * @author David Lucid (https://github.com/davidlucid) 13 | */ 14 | contract FixedNativePriceOracle is BasePriceOracle { 15 | /** 16 | * @dev Returns the price in native token of `underlying` (implements `BasePriceOracle`). 17 | */ 18 | function price(address underlying) external view override returns (uint256) { 19 | return 1e18; 20 | } 21 | 22 | /** 23 | * @notice Returns the price in native token of the token underlying `cToken`. 24 | * @dev Implements the `PriceOracle` interface for Ionic pools (and Compound v2). 25 | * @return Price in native token of the token underlying `cToken`, scaled by `10 ** (36 - underlyingDecimals)`. 26 | */ 27 | function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { 28 | return 1e18; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/test/liquidators/AaveTokenLiquidatorTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../config/BaseTest.t.sol"; 5 | import "../../liquidators/AaveTokenLiquidator.sol"; 6 | 7 | contract AaveTokenLiquidatorTest is BaseTest { 8 | AaveTokenLiquidator public liquidator; 9 | address stable; 10 | address amUsdc = 0x1a13F4Ca1d028320A707D99520AbFefca3998b7F; 11 | uint256 inputAmount; 12 | 13 | function afterForkSetUp() internal override { 14 | liquidator = new AaveTokenLiquidator(); 15 | stable = ap.getAddress("stableToken"); 16 | } 17 | 18 | function testAmUsdcPolygon() public fork(POLYGON_MAINNET) { 19 | address amUsdcWhale = 0xe8599F3cc5D38a9aD6F3684cd5CEa72f10Dbc383; // curve pool 20 | inputAmount = 1000e6; 21 | 22 | IERC20Upgradeable amUsdcToken = IERC20Upgradeable(amUsdc); 23 | vm.prank(amUsdcWhale); 24 | amUsdcToken.transfer(address(liquidator), inputAmount); 25 | 26 | (IERC20Upgradeable outputToken, uint256 outputAmount) = liquidator.redeem( 27 | amUsdcToken, 28 | inputAmount, 29 | abi.encode(stable) 30 | ); 31 | 32 | assertEq(address(outputToken), stable, "!usdc output"); 33 | assertApproxEqRel(outputAmount, inputAmount, 8e16, "!output does not match input"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/GelatoGUniPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { GelatoGUniPriceOracle } from "../../../oracles/default/GelatoGUniPriceOracle.sol"; 6 | import { MasterPriceOracle } from "../../../oracles/MasterPriceOracle.sol"; 7 | 8 | contract GelatoGUniPriceOracleTest is BaseTest { 9 | GelatoGUniPriceOracle private oracle; 10 | MasterPriceOracle mpo; 11 | 12 | function afterForkSetUp() internal override { 13 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 14 | oracle = new GelatoGUniPriceOracle(ap.getAddress("wtoken")); 15 | } 16 | 17 | function testPriceGelatoGUni() public fork(POLYGON_MAINNET) { 18 | address PAR_USDC_ARRAKIS_VAULT = 0xC1DF4E2fd282e39346422e40C403139CD633Aacd; 19 | address WBTC_WETH_ARRAKIS_VAULT = 0x590217ef04BcB96FF6Da991AB070958b8F9E77f0; 20 | 21 | vm.prank(address(mpo)); 22 | uint256 price_PAR_USDC = oracle.price(PAR_USDC_ARRAKIS_VAULT); 23 | 24 | vm.prank(address(mpo)); 25 | uint256 price_WBTC_WETH = oracle.price(WBTC_WETH_ARRAKIS_VAULT); 26 | 27 | assertTrue(price_PAR_USDC > 0, "!Price Error"); 28 | assertTrue(price_WBTC_WETH > 0, "!Price Error"); 29 | assertGt(price_WBTC_WETH, price_PAR_USDC); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/external/harvest/IFarmVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface IFarmVault { 5 | function underlyingBalanceInVault() external view returns (uint256); 6 | 7 | function underlyingBalanceWithInvestment() external view returns (uint256); 8 | 9 | // function store() external view returns (address); 10 | function governance() external view returns (address); 11 | 12 | function controller() external view returns (address); 13 | 14 | function underlying() external view returns (address); 15 | 16 | function strategy() external view returns (address); 17 | 18 | function setStrategy(address _strategy) external; 19 | 20 | function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external; 21 | 22 | function deposit(uint256 amountWei) external; 23 | 24 | function depositFor(uint256 amountWei, address holder) external; 25 | 26 | function withdrawAll() external; 27 | 28 | function withdraw(uint256 numberOfShares) external; 29 | 30 | function getPricePerFullShare() external view returns (uint256); 31 | 32 | function underlyingBalanceWithInvestmentForHolder(address holder) external view returns (uint256); 33 | 34 | // hard work should be callable only by the controller (by the hard worker) or by governance 35 | function doHardWork() external; 36 | } 37 | -------------------------------------------------------------------------------- /contracts/test/helpers/BalancerReentrancyAttacker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import { IBalancerStablePool } from "../../external/balancer/IBalancerStablePool.sol"; 5 | import "../../external/balancer/IBalancerVault.sol"; 6 | import { MasterPriceOracle } from "../../oracles/MasterPriceOracle.sol"; 7 | 8 | contract BalancerReentrancyAttacker { 9 | IBalancerVault private immutable _vault; 10 | MasterPriceOracle private _mpo; 11 | address private _lpToken; 12 | 13 | constructor( 14 | IBalancerVault vault, 15 | MasterPriceOracle mpo, 16 | address lpToken 17 | ) { 18 | _vault = vault; 19 | _mpo = mpo; 20 | _lpToken = lpToken; 21 | } 22 | 23 | function startAttack() external payable { 24 | UserBalanceOp[] memory ops = new UserBalanceOp[](1); 25 | ops[0].kind = UserBalanceOpKind.DEPOSIT_INTERNAL; 26 | // Asking to deposit 1 ETH 27 | ops[0].amount = 1e18; 28 | ops[0].sender = address(this); 29 | ops[0].recipient = payable(address(this)); 30 | 31 | // but pass 2 eth, so there's an amount of exceding ETH and receive() callback is called 32 | _vault.manageUserBalance{ value: 2e18 }(ops); 33 | } 34 | 35 | receive() external payable { 36 | _reenterAttack(); 37 | } 38 | 39 | function _reenterAttack() internal view { 40 | _mpo.price(_lpToken); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/SimplePriceOracleTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { MasterPriceOracle } from "../../../oracles/MasterPriceOracle.sol"; 6 | import { SimplePriceOracle } from "../../../oracles/default/SimplePriceOracle.sol"; 7 | import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 8 | 9 | contract SimplePriceOracleTest is BaseTest { 10 | SimplePriceOracle oracle; 11 | MasterPriceOracle mpo; 12 | address someAdminAccount = address(94949); 13 | 14 | function afterForkSetUp() internal override { 15 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 16 | SimplePriceOracle impl = new SimplePriceOracle(); 17 | 18 | vm.prank(someAdminAccount); 19 | TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( 20 | address(impl), 21 | address(dpa), 22 | abi.encodePacked(impl.initialize.selector) 23 | ); 24 | oracle = SimplePriceOracle(address(proxy)); 25 | } 26 | 27 | function testSimplePO() public fork(BSC_MAINNET) { 28 | vm.expectRevert("Ownable: caller is not the owner"); 29 | oracle.setDirectPrice(address(1), 1); 30 | 31 | vm.prank(someAdminAccount); 32 | oracle.setDirectPrice(address(1), 1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/ionic/levered/LeveredPositionFactoryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import { SafeOwnable } from "../../ionic/SafeOwnable.sol"; 5 | import { IFeeDistributor } from "../../compound/IFeeDistributor.sol"; 6 | import { ILiquidatorsRegistry } from "../../liquidators/registry/ILiquidatorsRegistry.sol"; 7 | import { ICErc20 } from "../../compound/CTokenInterfaces.sol"; 8 | 9 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol"; 10 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 11 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; 12 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 13 | 14 | abstract contract LeveredPositionFactoryStorage is SafeOwnable { 15 | EnumerableSet.AddressSet internal accountsWithOpenPositions; 16 | mapping(address => EnumerableSet.AddressSet) internal positionsByAccount; 17 | EnumerableSet.AddressSet internal collateralMarkets; 18 | mapping(ICErc20 => EnumerableSet.AddressSet) internal borrowableMarketsByCollateral; 19 | 20 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => uint256)) private __unused; 21 | 22 | IFeeDistributor public feeDistributor; 23 | ILiquidatorsRegistry public liquidatorsRegistry; 24 | uint256 public blocksPerYear; 25 | } 26 | -------------------------------------------------------------------------------- /deploy/05-deploy-flywheel.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | import { Address } from "viem"; 3 | 4 | const func: DeployFunction = async ({ run, viem, getNamedAccounts, deployments }) => { 5 | const { deployer } = await getNamedAccounts(); 6 | const publicClient = await viem.getPublicClient(); 7 | 8 | const fpd = await viem.getContractAt("PoolDirectory", (await deployments.get("PoolDirectory")).address as Address); 9 | 10 | const mflrReceipt = await deployments.deploy("IonicFlywheelLensRouter", { 11 | from: deployer, 12 | args: [fpd.address], 13 | log: true, 14 | waitConfirmations: 1 15 | }); 16 | if (mflrReceipt.transactionHash) 17 | await publicClient.waitForTransactionReceipt({ hash: mflrReceipt.transactionHash as Address }); 18 | console.log("IonicFlywheelLensRouter: ", mflrReceipt.address); 19 | 20 | const booster = await deployments.deploy("LooplessFlywheelBooster", { 21 | from: deployer, 22 | log: true, 23 | args: [], 24 | waitConfirmations: 1 25 | }); 26 | if (booster.transactionHash) 27 | await publicClient.waitForTransactionReceipt({ hash: booster.transactionHash as Address }); 28 | console.log("LooplessFlywheelBooster: ", booster.address); 29 | if (booster.newlyDeployed) await run("flywheels:booster:update"); 30 | }; 31 | 32 | func.tags = ["prod", "deploy-flywheel"]; 33 | 34 | export default func; 35 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/erc4626.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | import { Erc4626OracleFnParams } from "../../types"; 3 | 4 | import { addUnderlyingsToMpo } from "./utils"; 5 | 6 | export const deployErc4626PriceOracle = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | erc4626Assets 11 | }: Erc4626OracleFnParams): Promise => { 12 | const { deployer } = await getNamedAccounts(); 13 | const publicClient = await viem.getPublicClient(); 14 | const walletClient = await viem.getWalletClient(deployer as Address); 15 | 16 | const mpo = await viem.getContractAt( 17 | "MasterPriceOracle", 18 | (await deployments.get("MasterPriceOracle")).address as Address 19 | ); 20 | 21 | const e4626o = await deployments.deploy("ERC4626Oracle", { 22 | from: deployer, 23 | args: [], 24 | log: true, 25 | proxy: { 26 | execute: { 27 | init: { 28 | methodName: "initialize", 29 | args: [] 30 | } 31 | }, 32 | owner: deployer, 33 | proxyContract: "OpenZeppelinTransparentProxy" 34 | } 35 | }); 36 | if (e4626o.transactionHash) await publicClient.waitForTransactionReceipt({ hash: e4626o.transactionHash as Address }); 37 | console.log("ERC4626Oracle: ", e4626o.address); 38 | 39 | const underlyings = erc4626Assets.map((f) => f.assetAddress); 40 | await addUnderlyingsToMpo(mpo, underlyings, e4626o.address, deployer, publicClient, walletClient); 41 | }; 42 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/BaseFlywheelRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import {SafeTransferLib, ERC20} from "solmate/utils/SafeTransferLib.sol"; 5 | import {IFlywheelRewards} from "./IFlywheelRewards.sol"; 6 | import {IonicFlywheelCore} from "../IonicFlywheelCore.sol"; 7 | 8 | /** 9 | @title Flywheel Reward Module 10 | @notice Determines how many rewards accrue to each strategy globally over a given time period. 11 | @dev approves the flywheel core for the reward token to allow balances to be managed by the module but claimed from core. 12 | */ 13 | abstract contract BaseFlywheelRewards is IFlywheelRewards { 14 | using SafeTransferLib for ERC20; 15 | 16 | /// @notice thrown when caller is not the flywheel 17 | error FlywheelError(); 18 | 19 | /// @notice the reward token paid 20 | ERC20 public immutable override rewardToken; 21 | 22 | /// @notice the flywheel core contract 23 | IonicFlywheelCore public immutable override flywheel; 24 | 25 | constructor(IonicFlywheelCore _flywheel) { 26 | flywheel = _flywheel; 27 | ERC20 _rewardToken = _flywheel.rewardToken(); 28 | rewardToken = _rewardToken; 29 | 30 | _rewardToken.safeApprove(address(_flywheel), type(uint256).max); 31 | } 32 | 33 | modifier onlyFlywheel() { 34 | if (msg.sender != address(flywheel)) revert FlywheelError(); 35 | _; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/UniswapTwapPriceOracleV2Resolver.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { MasterPriceOracle } from "../../../oracles/MasterPriceOracle.sol"; 5 | import { UniswapTwapPriceOracleV2Root } from "../../../oracles/default/UniswapTwapPriceOracleV2Root.sol"; 6 | import { IUniswapV2Factory } from "../../../external/uniswap/IUniswapV2Factory.sol"; 7 | import { UniswapTwapPriceOracleV2Resolver } from "../../../oracles/default/UniswapTwapPriceOracleV2Resolver.sol"; 8 | import { IUniswapV2Pair } from "../../../external/uniswap/IUniswapV2Pair.sol"; 9 | 10 | import { BaseTest } from "../../config/BaseTest.t.sol"; 11 | 12 | contract UniswapTwapOracleV2ResolverTest is BaseTest { 13 | UniswapTwapPriceOracleV2Root twapPriceOracleRoot; 14 | UniswapTwapPriceOracleV2Resolver resolver; 15 | IUniswapV2Factory uniswapV2Factory; 16 | MasterPriceOracle mpo; 17 | 18 | struct Observation { 19 | uint32 timestamp; 20 | uint256 price0Cumulative; 21 | uint256 price1Cumulative; 22 | } 23 | 24 | function afterForkSetUp() internal override { 25 | uniswapV2Factory = IUniswapV2Factory(ap.getAddress("IUniswapV2Factory")); 26 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 27 | } 28 | 29 | function getTokenTwapPrice(address tokenAddress) internal view returns (uint256) { 30 | // return the price denominated in W_NATIVE 31 | return mpo.price(tokenAddress); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/LooplessFlywheelBooster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import {IFlywheelBooster} from "./IFlywheelBooster.sol"; 5 | import { ICErc20 } from "../../../compound/CTokenInterfaces.sol"; 6 | import {ERC20} from "solmate/tokens/ERC20.sol"; 7 | 8 | contract LooplessFlywheelBooster is IFlywheelBooster { 9 | string public constant BOOSTER_TYPE = "LooplessFlywheelBooster"; 10 | 11 | /** 12 | @notice calculate the boosted supply of a strategy. 13 | @param strategy the strategy to calculate boosted supply of 14 | @return the boosted supply 15 | */ 16 | function boostedTotalSupply(ERC20 strategy) external view returns (uint256) { 17 | return strategy.totalSupply(); 18 | } 19 | 20 | /** 21 | @notice calculate the boosted balance of a user in a given strategy. 22 | @param strategy the strategy to calculate boosted balance of 23 | @param user the user to calculate boosted balance of 24 | @return the boosted balance 25 | */ 26 | function boostedBalanceOf(ERC20 strategy, address user) external view returns (uint256) { 27 | uint256 cTokensBalance = strategy.balanceOf(user); 28 | ICErc20 asMarket = ICErc20(address(strategy)); 29 | uint256 cTokensBorrow = (asMarket.borrowBalanceCurrent(user) * 1e18) / asMarket.exchangeRateCurrent(); 30 | return (cTokensBalance > cTokensBorrow) ? cTokensBalance - cTokensBorrow : 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/IonicFlywheelDynamicRewardsPlugin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import "./FlywheelDynamicRewards.sol"; 5 | 6 | interface ICERC20 { 7 | function plugin() external returns (address); 8 | } 9 | 10 | interface IPlugin_FDR { 11 | function claimRewards() external; 12 | } 13 | 14 | /** 15 | @title Ionic Flywheel Dynamic Reward Stream 16 | @notice Determines rewards based on reward cycle 17 | Each cycle, claims rewards on the plugin before getting the reward amount 18 | */ 19 | contract IonicFlywheelDynamicRewardsPlugin is FlywheelDynamicRewards { 20 | using SafeTransferLib for ERC20; 21 | 22 | constructor(IonicFlywheelCore _flywheel, uint32 _cycleLength) 23 | FlywheelDynamicRewards(_flywheel, _cycleLength) 24 | {} 25 | 26 | function getNextCycleRewards(ERC20 strategy) 27 | internal 28 | override 29 | returns (uint192) 30 | { 31 | IPlugin_FDR plugin = IPlugin_FDR(ICERC20(address(strategy)).plugin()); 32 | try plugin.claimRewards() {} catch {} 33 | 34 | uint256 rewardAmount = rewardToken.balanceOf(address(strategy)); 35 | if (rewardAmount != 0) { 36 | rewardToken.safeTransferFrom( 37 | address(strategy), 38 | address(this), 39 | rewardAmount 40 | ); 41 | } 42 | return uint192(rewardAmount); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/liquidators/registry/LiquidatorsRegistryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import "../IRedemptionStrategy.sol"; 5 | import { SafeOwnable } from "../../ionic/SafeOwnable.sol"; 6 | import { AddressesProvider } from "../../ionic/AddressesProvider.sol"; 7 | 8 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 9 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 10 | 11 | abstract contract LiquidatorsRegistryStorage is SafeOwnable { 12 | AddressesProvider public ap; 13 | 14 | EnumerableSet.AddressSet internal redemptionStrategies; 15 | mapping(string => IRedemptionStrategy) public redemptionStrategiesByName; 16 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => IRedemptionStrategy)) public redemptionStrategiesByTokens; 17 | mapping(IERC20Upgradeable => IERC20Upgradeable) public defaultOutputToken; 18 | mapping(IERC20Upgradeable => EnumerableSet.AddressSet) internal inputTokensByOutputToken; 19 | EnumerableSet.AddressSet internal outputTokensSet; 20 | 21 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => uint256)) internal conversionSlippage; 22 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => uint256)) internal conversionSlippageUpdated; 23 | 24 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => uint24)) public uniswapV3Fees; 25 | mapping(IERC20Upgradeable => mapping(IERC20Upgradeable => address)) public customUniV3Router; 26 | } 27 | -------------------------------------------------------------------------------- /contracts/external/algebra/IAlgebraSwapCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Callback for IAlgebraPoolActions#swap 5 | /// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface 6 | /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: 7 | /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces 8 | interface IAlgebraSwapCallback { 9 | /// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap. 10 | /// @dev In the implementation you must pay the pool tokens owed for the swap. 11 | /// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory. 12 | /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. 13 | /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by 14 | /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. 15 | /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by 16 | /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. 17 | /// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call 18 | function algebraSwapCallback( 19 | int256 amount0Delta, 20 | int256 amount1Delta, 21 | bytes calldata data 22 | ) external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/external/compound/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Compound Labs, Inc. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /contracts/test/liquidators/UniswapV2LiquidatorFunderTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../config/BaseTest.t.sol"; 5 | import { UniswapV2LiquidatorFunder } from "../../liquidators/UniswapV2LiquidatorFunder.sol"; 6 | import { IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 7 | 8 | contract UniswapV2LiquidatorFunderTest is BaseTest { 9 | address maiAddress; 10 | address usdcAddress; 11 | UniswapV2LiquidatorFunder uv2lf; 12 | address uniswapV2Router; 13 | 14 | function afterForkSetUp() internal override { 15 | uv2lf = new UniswapV2LiquidatorFunder(); 16 | uniswapV2Router = ap.getAddress("IUniswapV2Router02"); 17 | usdcAddress = 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d; 18 | maiAddress = 0x3F56e0c36d275367b8C502090EDF38289b3dEa0d; 19 | } 20 | 21 | function testConvertUsdcMai() public fork(BSC_MAINNET) { 22 | address[] memory swapPath = new address[](2); 23 | swapPath[0] = maiAddress; 24 | swapPath[1] = usdcAddress; 25 | bytes memory strategyData = abi.encode(uniswapV2Router, swapPath); 26 | 27 | uint256 outputUsdcExpected = 1e10; 28 | (IERC20Upgradeable inputToken, uint256 inputMaiRequired) = uv2lf.estimateInputAmount( 29 | outputUsdcExpected, 30 | strategyData 31 | ); 32 | 33 | assertApproxEqAbs(inputMaiRequired, outputUsdcExpected, 1e9); 34 | assertEq(address(inputToken), maiAddress, "!mai address"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/compound/IFeeDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../ionic/AuthoritiesRegistry.sol"; 5 | 6 | interface IFeeDistributor { 7 | function minBorrowEth() external view returns (uint256); 8 | 9 | function maxUtilizationRate() external view returns (uint256); 10 | 11 | function interestFeeRate() external view returns (uint256); 12 | 13 | function latestComptrollerImplementation(address oldImplementation) external view returns (address); 14 | 15 | function latestCErc20Delegate(uint8 delegateType) 16 | external 17 | view 18 | returns (address cErc20Delegate, bytes memory becomeImplementationData); 19 | 20 | function latestPluginImplementation(address oldImplementation) external view returns (address); 21 | 22 | function getComptrollerExtensions(address comptroller) external view returns (address[] memory); 23 | 24 | function getCErc20DelegateExtensions(address cErc20Delegate) external view returns (address[] memory); 25 | 26 | function deployCErc20( 27 | uint8 delegateType, 28 | bytes calldata constructorData, 29 | bytes calldata becomeImplData 30 | ) external returns (address); 31 | 32 | function canCall( 33 | address pool, 34 | address user, 35 | address target, 36 | bytes4 functionSig 37 | ) external view returns (bool); 38 | 39 | function authoritiesRegistry() external view returns (AuthoritiesRegistry); 40 | 41 | fallback() external payable; 42 | 43 | receive() external payable; 44 | } 45 | -------------------------------------------------------------------------------- /contracts/oracles/default/StkBNBPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { IStakePool, ExchangeRateData } from "../../external/pstake/IStakePool.sol"; 5 | 6 | import "../../ionic/SafeOwnableUpgradeable.sol"; 7 | import "../BasePriceOracle.sol"; 8 | 9 | contract StkBNBPriceOracle is SafeOwnableUpgradeable, BasePriceOracle { 10 | IStakePool public stakingPool; 11 | address public stkBnb; 12 | 13 | function initialize() public initializer { 14 | __SafeOwnable_init(msg.sender); 15 | stakingPool = IStakePool(0xC228CefDF841dEfDbD5B3a18dFD414cC0dbfa0D8); 16 | stkBnb = 0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16; 17 | } 18 | 19 | function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { 20 | // Get underlying token address 21 | address underlying = cToken.underlying(); 22 | require(underlying == stkBnb, "Invalid underlying"); 23 | // no need to scale as stkBNB has 18 decimals 24 | return _price(); 25 | } 26 | 27 | function price(address underlying) external view override returns (uint256) { 28 | require(underlying == stkBnb, "Invalid underlying"); 29 | return _price(); 30 | } 31 | 32 | function _price() internal view returns (uint256) { 33 | // 1 stkBNB = (totalWei / poolTokenSupply) BNB 34 | ExchangeRateData memory exchangeRate = stakingPool.exchangeRate(); 35 | uint256 stkBNBPrice = (exchangeRate.totalWei * 1e18) / exchangeRate.poolTokenSupply; 36 | return stkBNBPrice; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/external/mstable/MassetStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | interface MassetStructs { 5 | struct BassetPersonal { 6 | // Address of the bAsset 7 | address addr; 8 | // Address of the bAsset 9 | address integrator; 10 | // An ERC20 can charge transfer fee, for example USDT, DGX tokens. 11 | bool hasTxFee; // takes a byte in storage 12 | // Status of the bAsset 13 | BassetStatus status; 14 | } 15 | 16 | struct BassetData { 17 | // 1 Basset * ratio / ratioScale == x Masset (relative value) 18 | // If ratio == 10e8 then 1 bAsset = 10 mAssets 19 | // A ratio is divised as 10^(18-tokenDecimals) * measurementMultiple(relative value of 1 base unit) 20 | uint128 ratio; 21 | // Amount of the Basset that is held in Collateral 22 | uint128 vaultBalance; 23 | } 24 | 25 | // Status of the Basset - has it broken its peg? 26 | enum BassetStatus { 27 | Default, 28 | Normal, 29 | BrokenBelowPeg, 30 | BrokenAbovePeg, 31 | Blacklisted, 32 | Liquidating, 33 | Liquidated, 34 | Failed 35 | } 36 | 37 | struct BasketState { 38 | bool undergoingRecol; 39 | bool failed; 40 | } 41 | 42 | struct InvariantConfig { 43 | uint256 a; 44 | WeightLimits limits; 45 | } 46 | 47 | struct WeightLimits { 48 | uint128 min; 49 | uint128 max; 50 | } 51 | 52 | struct AmpData { 53 | uint64 initialA; 54 | uint64 targetA; 55 | uint64 rampStartTime; 56 | uint64 rampEndTime; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/liquidators/UniswapV2LiquidatorFunder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { UniswapV2Liquidator } from "./UniswapV2Liquidator.sol"; 5 | import "./IFundsConversionStrategy.sol"; 6 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 7 | import "../external/uniswap/IUniswapV2Router02.sol"; 8 | 9 | contract UniswapV2LiquidatorFunder is UniswapV2Liquidator, IFundsConversionStrategy { 10 | function convert( 11 | IERC20Upgradeable inputToken, 12 | uint256 inputAmount, 13 | bytes memory strategyData 14 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 15 | return _convert(inputToken, inputAmount, strategyData); 16 | } 17 | 18 | function estimateInputAmount(uint256 outputAmount, bytes memory strategyData) 19 | external 20 | view 21 | returns (IERC20Upgradeable inputToken, uint256 inputAmount) 22 | { 23 | (IUniswapV2Router02 uniswapV2Router, address[] memory swapPath) = abi.decode( 24 | strategyData, 25 | (IUniswapV2Router02, address[]) 26 | ); 27 | require(swapPath.length >= 2, "Invalid UniswapLiquidator swap path."); 28 | 29 | uint256[] memory amounts = uniswapV2Router.getAmountsIn(outputAmount, swapPath); 30 | 31 | inputAmount = amounts[0]; 32 | inputToken = IERC20Upgradeable(swapPath[0]); 33 | } 34 | 35 | function name() public pure override(UniswapV2Liquidator, IRedemptionStrategy) returns (string memory) { 36 | return "UniswapV2LiquidatorFunder"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/compound/InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | /** 5 | * @title Compound's InterestRateModel Interface 6 | * @author Compound 7 | */ 8 | abstract contract InterestRateModel { 9 | /// @notice Indicator that this is an InterestRateModel contract (for inspection) 10 | bool public constant isInterestRateModel = true; 11 | 12 | /** 13 | * @notice Calculates the current borrow interest rate per block 14 | * @param cash The total amount of cash the market has 15 | * @param borrows The total amount of borrows the market has outstanding 16 | * @param reserves The total amount of reserves the market has 17 | * @return The borrow rate per block (as a percentage, and scaled by 1e18) 18 | */ 19 | function getBorrowRate( 20 | uint256 cash, 21 | uint256 borrows, 22 | uint256 reserves 23 | ) public view virtual returns (uint256); 24 | 25 | /** 26 | * @notice Calculates the current supply interest rate per block 27 | * @param cash The total amount of cash the market has 28 | * @param borrows The total amount of borrows the market has outstanding 29 | * @param reserves The total amount of reserves the market has 30 | * @param reserveFactorMantissa The current reserve factor the market has 31 | * @return The supply rate per block (as a percentage, and scaled by 1e18) 32 | */ 33 | function getSupplyRate( 34 | uint256 cash, 35 | uint256 borrows, 36 | uint256 reserves, 37 | uint256 reserveFactorMantissa 38 | ) public view virtual returns (uint256); 39 | } 40 | -------------------------------------------------------------------------------- /contracts/external/compound/IRewardsDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity >=0.8.0; 3 | 4 | import "./ICToken.sol"; 5 | 6 | /** 7 | * @title RewardsDistributor 8 | * @author Compound 9 | */ 10 | interface IRewardsDistributor { 11 | /// @dev The token to reward (i.e., COMP) 12 | function rewardToken() external view returns (address); 13 | 14 | /// @notice The portion of compRate that each market currently receives 15 | function compSupplySpeeds(address) external view returns (uint256); 16 | 17 | /// @notice The portion of compRate that each market currently receives 18 | function compBorrowSpeeds(address) external view returns (uint256); 19 | 20 | /// @notice The COMP accrued but not yet transferred to each user 21 | function compAccrued(address) external view returns (uint256); 22 | 23 | /** 24 | * @notice Keeps the flywheel moving pre-mint and pre-redeem 25 | * @dev Called by the Comptroller 26 | * @param cToken The relevant market 27 | * @param supplier The minter/redeemer 28 | */ 29 | function flywheelPreSupplierAction(address cToken, address supplier) external; 30 | 31 | /** 32 | * @notice Keeps the flywheel moving pre-borrow and pre-repay 33 | * @dev Called by the Comptroller 34 | * @param cToken The relevant market 35 | * @param borrower The borrower 36 | */ 37 | function flywheelPreBorrowerAction(address cToken, address borrower) external; 38 | 39 | /** 40 | * @notice Returns an array of all markets. 41 | */ 42 | function getAllMarkets() external view returns (ICToken[] memory); 43 | } 44 | -------------------------------------------------------------------------------- /contracts/oracles/default/RecursivePriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../../external/compound/IPriceOracle.sol"; 5 | import "../../external/compound/ICToken.sol"; 6 | import "../../external/compound/ICErc20.sol"; 7 | import "../../external/compound/IComptroller.sol"; 8 | 9 | /** 10 | * @title RecursivePriceOracle 11 | * @notice Returns prices from other cTokens (from Ionic). 12 | * @dev Implements `PriceOracle`. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract RecursivePriceOracle is IPriceOracle { 16 | /** 17 | * @notice Returns the price in ETH of the token underlying `cToken`. 18 | * @dev Implements the `PriceOracle` interface for Ionic pools (and Compound v2). 19 | * @return Price in ETH of the token underlying `cToken`, scaled by `10 ** (36 - underlyingDecimals)`. 20 | */ 21 | function getUnderlyingPrice(ICToken cToken) external view override returns (uint256) { 22 | // Get cToken's underlying cToken 23 | ICToken underlying = ICToken(ICErc20Compound(address(cToken)).underlying()); 24 | 25 | // Get Comptroller 26 | IComptroller comptroller = IComptroller(underlying.comptroller()); 27 | 28 | // If cETH, return cETH/ETH exchange rate 29 | if (underlying.isCEther()) { 30 | return underlying.exchangeRateStored(); 31 | } 32 | 33 | // Ionic cTokens: cToken/token price * token/ETH price = cToken/ETH price 34 | return (underlying.exchangeRateStored() * comptroller.oracle().getUnderlyingPrice(underlying)) / 1e18; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/test/SafeOwnableUpgradeableTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "./config/BaseTest.t.sol"; 5 | 6 | import { SafeOwnableUpgradeable } from "../ionic/SafeOwnableUpgradeable.sol"; 7 | import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 8 | 9 | contract SomeOwnable is SafeOwnableUpgradeable { 10 | function initialize() public initializer { 11 | __SafeOwnable_init(msg.sender); 12 | } 13 | } 14 | 15 | contract SafeOwnableUpgradeableTest is BaseTest { 16 | function testSafeOwnableUpgradeable() public { 17 | SomeOwnable someOwnable = new SomeOwnable(); 18 | // deploy as a proxy/implementation 19 | { 20 | TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( 21 | address(someOwnable), 22 | address(dpa), 23 | abi.encodeWithSelector(someOwnable.initialize.selector) 24 | ); 25 | someOwnable = SomeOwnable(address(proxy)); 26 | } 27 | 28 | address joe = address(1234); 29 | 30 | address initOwner = someOwnable.owner(); 31 | assertEq(initOwner, address(this), "owner init value"); 32 | 33 | someOwnable._setPendingOwner(joe); 34 | 35 | address currentOwner = someOwnable.owner(); 36 | assertEq(currentOwner, address(this), "owner should not change yet"); 37 | 38 | vm.prank(joe); 39 | someOwnable._acceptOwner(); 40 | 41 | address ownerAfter = someOwnable.owner(); 42 | 43 | assertEq(ownerAfter, joe, "ownership transfer failed"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/test/irm/AdjustableJumpRateModelTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../config/BaseTest.t.sol"; 5 | 6 | import { AdjustableJumpRateModel, InterestRateModelParams } from "../../ionic/irms/AdjustableJumpRateModel.sol"; 7 | 8 | contract InterestRateModelTest is BaseTest { 9 | AdjustableJumpRateModel adjustableJumpRateModel; 10 | InterestRateModelParams params; 11 | InterestRateModelParams newParams; 12 | 13 | function setUp() public { 14 | params = InterestRateModelParams({ 15 | blocksPerYear: 10512000, 16 | baseRatePerYear: 0.5e16, 17 | multiplierPerYear: 0.18e18, 18 | jumpMultiplierPerYear: 4e18, 19 | kink: 0.8e18 20 | }); 21 | adjustableJumpRateModel = new AdjustableJumpRateModel(params); 22 | } 23 | 24 | function testUpdateJrmParams() public { 25 | assertEq(adjustableJumpRateModel.blocksPerYear(), params.blocksPerYear); 26 | assertEq(adjustableJumpRateModel.baseRatePerBlock(), params.baseRatePerYear / params.blocksPerYear); 27 | 28 | newParams = InterestRateModelParams({ 29 | blocksPerYear: 512000, 30 | baseRatePerYear: 0.7e16, 31 | multiplierPerYear: 0.18e18, 32 | jumpMultiplierPerYear: 4e18, 33 | kink: 0.8e18 34 | }); 35 | 36 | adjustableJumpRateModel._setIrmParameters(newParams); 37 | vm.roll(1); 38 | 39 | assertEq(adjustableJumpRateModel.blocksPerYear(), newParams.blocksPerYear); 40 | assertEq(adjustableJumpRateModel.baseRatePerBlock(), newParams.baseRatePerYear / newParams.blocksPerYear); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tasks/flywheel/replace.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Address, zeroAddress } from "viem"; 3 | 4 | task("flywheels:booster:update").setAction(async ({}, { viem, getChainId, deployments }) => { 5 | const publicClient = await viem.getPublicClient(); 6 | const poolDirectory = await viem.getContractAt( 7 | "PoolDirectory", 8 | (await deployments.get("PoolDirectory")).address as Address 9 | ); 10 | const newBooster = await viem.getContractAt( 11 | "LooplessFlywheelBooster", 12 | (await deployments.get("LooplessFlywheelBooster")).address as Address 13 | ); 14 | 15 | const [ids, poolDatas] = await poolDirectory.read.getActivePools(); 16 | for (const poolData of poolDatas) { 17 | const pool = await viem.getContractAt("ComptrollerFirstExtension", poolData.comptroller); 18 | const fws = await pool.read.getAccruingFlywheels(); 19 | 20 | for (const fw of fws) { 21 | const flywheel = await viem.getContractAt("IonicFlywheel", fw); 22 | const currentBooster = await flywheel.read.flywheelBooster(); 23 | if (currentBooster != zeroAddress && currentBooster != newBooster.address) { 24 | const tx = await flywheel.write.setBooster([newBooster.address]); 25 | await publicClient.waitForTransactionReceipt({ hash: tx }); 26 | console.log(`replaced ${currentBooster} with ${newBooster.address} for ${flywheel.address}`); 27 | } else { 28 | console.log( 29 | `current booster ${currentBooster} NOT REPLACED with ${newBooster.address} for ${flywheel.address}` 30 | ); 31 | } 32 | } 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /contracts/external/gelato/GUniPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.8.0; 3 | 4 | interface GUniPool { 5 | function token0() external view returns (address); 6 | 7 | function token1() external view returns (address); 8 | 9 | /// @notice compute total underlying holdings of the G-UNI token supply 10 | /// includes current liquidity invested in uniswap position, current fees earned 11 | /// and any uninvested leftover (but does not include manager or gelato fees accrued) 12 | /// @return amount0Current current total underlying balance of token0 13 | /// @return amount1Current current total underlying balance of token1 14 | function getUnderlyingBalancesAtPrice(uint160 sqrtRatioX96) 15 | external 16 | view 17 | returns (uint256 amount0Current, uint256 amount1Current); 18 | 19 | /// @notice burn G-UNI tokens (fractional shares of a Uniswap V3 position) and receive tokens 20 | /// @param burnAmount The number of G-UNI tokens to burn 21 | /// @param receiver The account to receive the underlying amounts of token0 and token1 22 | /// @return amount0 amount of token0 transferred to receiver for burning `burnAmount` 23 | /// @return amount1 amount of token1 transferred to receiver for burning `burnAmount` 24 | /// @return liquidityBurned amount of liquidity removed from the underlying Uniswap V3 position 25 | // solhint-disable-next-line function-max-lines 26 | function burn(uint256 burnAmount, address receiver) 27 | external 28 | returns ( 29 | uint256 amount0, 30 | uint256 amount1, 31 | uint128 liquidityBurned 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /tasks/pool/upgrade/non-owner.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Address } from "viem"; 3 | 4 | task("non-owner-pool:upgrade") 5 | .addParam("comptrollerAddress", "The comptroller implementation address", undefined, types.string) 6 | .addParam("poolAddress", "The pool address", undefined, types.string) 7 | .setAction(async ({ comptrollerAddress, poolAddress }, { viem }) => { 8 | const publicClient = await viem.getPublicClient(); 9 | // pools to upgrade 10 | const pools: Address[] = [poolAddress]; 11 | 12 | const comptrollerImpl = await viem.getContractAt("Comptroller", comptrollerAddress); 13 | 14 | for (let i = 0; i < pools.length; i++) { 15 | const asUnitroller = await viem.getContractAt("Unitroller", pools[i]); 16 | 17 | const currentImpl = await asUnitroller.read.comptrollerImplementation(); 18 | if (currentImpl != comptrollerImpl.address) { 19 | console.log(`current impl is ${currentImpl}`); 20 | console.log(`should be ${comptrollerImpl.address}`); 21 | 22 | let tx = await asUnitroller.write._registerExtension([comptrollerImpl.address, currentImpl]); 23 | await publicClient.waitForTransactionReceipt({ hash: tx }); 24 | console.log(`new comptroller set with ${tx}`); 25 | console.log(`updating the extensions`); 26 | 27 | tx = await asUnitroller.write._upgrade(); 28 | await publicClient.waitForTransactionReceipt({ hash: tx }); 29 | console.log(`extensions updated ${tx}`); 30 | } else { 31 | console.log(`already the needed impl ${currentImpl}`); 32 | } 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /contracts/test/liquidators/XBombLiquidatorTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../../external/bomb/IXBomb.sol"; 5 | import "../../liquidators/XBombLiquidatorFunder.sol"; 6 | import { BaseTest } from "../config/BaseTest.t.sol"; 7 | 8 | contract XBombLiquidatorTest is BaseTest { 9 | // the Pancake BOMB/xBOMB pair 10 | address holder = 0x6aE0Fb5D98911cF5AF6A8CE0AeCE426227d41103; 11 | IXBomb xbombToken = IXBomb(0xAf16cB45B8149DA403AF41C63AbFEBFbcd16264b); 12 | address bombTokenAddress = 0x522348779DCb2911539e76A1042aA922F9C47Ee3; // BOMB 13 | XBombLiquidatorFunder liquidator; 14 | 15 | function afterForkSetUp() internal override { 16 | liquidator = new XBombLiquidatorFunder(); 17 | } 18 | 19 | function testRedeem() public debuggingOnly fork(BSC_MAINNET) { 20 | // make sure we're testing with at least some tokens 21 | uint256 balance = xbombToken.balanceOf(holder); 22 | assertTrue(balance > 0); 23 | 24 | // impersonate the holder 25 | vm.prank(holder); 26 | 27 | // fund the liquidator so it can redeem the tokens 28 | xbombToken.transfer(address(liquidator), balance); 29 | 30 | bytes memory data = abi.encode(address(xbombToken), address(xbombToken), bombTokenAddress); 31 | // redeem the underlying reward token 32 | (IERC20Upgradeable outputToken, uint256 outputAmount) = liquidator.redeem( 33 | IERC20Upgradeable(address(xbombToken)), 34 | balance, 35 | data 36 | ); 37 | 38 | assertEq(address(outputToken), bombTokenAddress); 39 | assertEq(outputAmount, xbombToken.toREWARD(balance)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/external/gamma/IHypervisor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface IHypervisor is IERC20Upgradeable { 7 | function baseLower() external view returns (int24); 8 | 9 | function baseUpper() external view returns (int24); 10 | 11 | function limitLower() external view returns (int24); 12 | 13 | function limitUpper() external view returns (int24); 14 | 15 | function pool() external view returns (address); 16 | 17 | function token0() external view returns (address); 18 | 19 | function token1() external view returns (address); 20 | 21 | function directDeposit() external view returns (bool); 22 | 23 | function getBasePosition() 24 | external 25 | view 26 | returns ( 27 | uint256 liquidity, 28 | uint256 total0, 29 | uint256 total1 30 | ); 31 | 32 | function getTotalAmounts() external view returns (uint256 total0, uint256 total1); 33 | 34 | function setWhitelist(address _address) external; 35 | 36 | function setFee(uint8 newFee) external; 37 | 38 | function removeWhitelisted() external; 39 | 40 | function transferOwnership(address newOwner) external; 41 | 42 | function withdraw( 43 | uint256 shares, 44 | address to, 45 | address from, 46 | uint256[4] memory minAmounts 47 | ) external returns (uint256 amount0, uint256 amount1); 48 | 49 | function deposit( 50 | uint256 deposit0, 51 | uint256 deposit1, 52 | address to, 53 | address from, 54 | uint256[4] memory inMin 55 | ) external returns (uint256 shares); 56 | } 57 | -------------------------------------------------------------------------------- /contracts/external/jarvis/ISynthereumDeployment.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.4; 3 | 4 | import { IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | import "./ISynthereumFinder.sol"; 6 | 7 | /** 8 | * @title Interface that a pool MUST have in order to be included in the deployer 9 | */ 10 | interface ISynthereumDeployment { 11 | /** 12 | * @notice Get Synthereum finder of the pool/self-minting derivative 13 | * @return finder Returns finder contract 14 | */ 15 | function synthereumFinder() external view returns (ISynthereumFinder finder); 16 | 17 | /** 18 | * @notice Get Synthereum version 19 | * @return poolVersion Returns the version of this pool/self-minting derivative 20 | */ 21 | function version() external view returns (uint8 poolVersion); 22 | 23 | /** 24 | * @notice Get the collateral token of this pool/self-minting derivative 25 | * @return collateralCurrency The ERC20 collateral token 26 | */ 27 | function collateralToken() external view returns (IERC20Upgradeable collateralCurrency); 28 | 29 | /** 30 | * @notice Get the synthetic token associated to this pool/self-minting derivative 31 | * @return syntheticCurrency The ERC20 synthetic token 32 | */ 33 | function syntheticToken() external view returns (IERC20Upgradeable syntheticCurrency); 34 | 35 | /** 36 | * @notice Get the synthetic token symbol associated to this pool/self-minting derivative 37 | * @return symbol The ERC20 synthetic token symbol 38 | */ 39 | function syntheticTokenSymbol() external view returns (string memory symbol); 40 | } 41 | -------------------------------------------------------------------------------- /contracts/compound/CErc20RewardsDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./CErc20Delegate.sol"; 5 | import "./EIP20Interface.sol"; 6 | 7 | contract CErc20RewardsDelegate is CErc20Delegate { 8 | function _getExtensionFunctions() public pure virtual override returns (bytes4[] memory functionSelectors) { 9 | uint8 fnsCount = 2; 10 | 11 | bytes4[] memory superFunctionSelectors = super._getExtensionFunctions(); 12 | functionSelectors = new bytes4[](superFunctionSelectors.length + fnsCount); 13 | 14 | for (uint256 i = 0; i < superFunctionSelectors.length; i++) { 15 | functionSelectors[i] = superFunctionSelectors[i]; 16 | } 17 | 18 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.claim.selector; 19 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.approve.selector; 20 | 21 | require(fnsCount == 0, "use the correct array length"); 22 | } 23 | 24 | /// @notice A reward token claim function 25 | /// to be overridden for use cases where rewardToken needs to be pulled in 26 | function claim() external {} 27 | 28 | /// @notice token approval function 29 | function approve(address _token, address _spender) external { 30 | require(hasAdminRights(), "!admin"); 31 | require(_token != underlying, "!underlying"); 32 | 33 | EIP20Interface(_token).approve(_spender, type(uint256).max); 34 | } 35 | 36 | function delegateType() public pure virtual override returns (uint8) { 37 | return 3; 38 | } 39 | 40 | function contractType() external pure override returns (string memory) { 41 | return "CErc20RewardsDelegate"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/liquidators/WSTEthLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/lido/IWstETH.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title WSTEthLiquidator 12 | * @notice Redeems wstETH for underlying stETH for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract WSTEthLiquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Unwrap wstETH (and store output stETH as new collateral) 30 | IWstETH token = IWstETH(address(inputToken)); 31 | token.unwrap(inputAmount); 32 | outputToken = IERC20Upgradeable(token.stETH()); 33 | outputAmount = inputAmount; 34 | } 35 | 36 | function name() public pure returns (string memory) { 37 | return "WSTEthLiquidator"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/compound/CErc20PluginRewardsDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./CErc20PluginDelegate.sol"; 5 | 6 | contract CErc20PluginRewardsDelegate is CErc20PluginDelegate { 7 | function _getExtensionFunctions() public pure virtual override returns (bytes4[] memory functionSelectors) { 8 | uint8 fnsCount = 2; 9 | 10 | bytes4[] memory superFunctionSelectors = super._getExtensionFunctions(); 11 | functionSelectors = new bytes4[](superFunctionSelectors.length + fnsCount); 12 | 13 | for (uint256 i = 0; i < superFunctionSelectors.length; i++) { 14 | functionSelectors[i] = superFunctionSelectors[i]; 15 | } 16 | 17 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.claim.selector; 18 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.approve.selector; 19 | 20 | require(fnsCount == 0, "use the correct array length"); 21 | } 22 | 23 | /// @notice A reward token claim function 24 | /// to be overridden for use cases where rewardToken needs to be pulled in 25 | function claim() external {} 26 | 27 | /// @notice token approval function 28 | function approve(address _token, address _spender) external { 29 | require(hasAdminRights(), "!admin"); 30 | require(_token != underlying && _token != address(plugin), "!token"); 31 | 32 | EIP20Interface(_token).approve(_spender, type(uint256).max); 33 | } 34 | 35 | function delegateType() public pure virtual override returns (uint8) { 36 | return 4; 37 | } 38 | 39 | function contractType() external pure override returns (string memory) { 40 | return "CErc20PluginRewardsDelegate"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/redstoneFallbacks.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | import { RedStoneDeployFnParams } from "../../types"; 3 | 4 | import { addUnderlyingsToMpoFallback } from "./utils"; 5 | 6 | export const addRedstoneFallbacks = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | redStoneAddress, 11 | redStoneAssets 12 | }: RedStoneDeployFnParams): Promise<{ redStoneOracle: any }> => { 13 | const { deployer } = await getNamedAccounts(); 14 | const publicClient = await viem.getPublicClient(); 15 | const walletClient = await viem.getWalletClient(deployer as Address); 16 | 17 | const mpo = await viem.getContractAt( 18 | "MasterPriceOracle", 19 | (await deployments.get("MasterPriceOracle")).address as Address 20 | ); 21 | 22 | //// RedStone Oracle 23 | const redStone = await deployments.deploy("RedstoneAdapterPriceOracle", { 24 | from: deployer, 25 | args: [redStoneAddress], 26 | log: true, 27 | waitConfirmations: 1 28 | }); 29 | 30 | if (redStone.transactionHash) 31 | await publicClient.waitForTransactionReceipt({ hash: redStone.transactionHash as Address }); 32 | console.log("RedstoneAdapterPriceOracle: ", redStone.address); 33 | 34 | const redStoneOracle = await viem.getContractAt( 35 | "RedstoneAdapterPriceOracle", 36 | (await deployments.get("RedstoneAdapterPriceOracle")).address as Address 37 | ); 38 | 39 | const underlyings = redStoneAssets.map((f) => f.underlying); 40 | await addUnderlyingsToMpoFallback( 41 | mpo as any, 42 | underlyings, 43 | redStoneOracle.address, 44 | deployer as Address, 45 | publicClient, 46 | walletClient 47 | ); 48 | return { redStoneOracle }; 49 | }; 50 | -------------------------------------------------------------------------------- /contracts/liquidators/SaddleLpTokenLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./IRedemptionStrategy.sol"; 5 | import { IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 6 | import "../external/saddle/ISwap.sol"; 7 | import { SaddleLpPriceOracle } from "../oracles/default/SaddleLpPriceOracle.sol"; 8 | import { WETH } from "solmate/tokens/WETH.sol"; 9 | 10 | contract SaddleLpTokenLiquidator is IRedemptionStrategy { 11 | function redeem( 12 | IERC20Upgradeable inputToken, 13 | uint256 inputAmount, 14 | bytes memory strategyData 15 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 16 | (address outputTokenAddress, SaddleLpPriceOracle oracle, address payable wtoken) = abi.decode( 17 | strategyData, 18 | (address, SaddleLpPriceOracle, address) 19 | ); 20 | 21 | ISwap pool = ISwap(oracle.poolOf(address(inputToken))); 22 | uint8 index = pool.getTokenIndex(outputTokenAddress); 23 | 24 | outputAmount = pool.removeLiquidityOneToken(inputAmount, index, 1, block.timestamp); 25 | 26 | // Convert to W_NATIVE if ETH 27 | if (outputTokenAddress == address(0) || outputTokenAddress == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) { 28 | WETH(wtoken).deposit{ value: outputAmount }(); 29 | outputToken = IERC20Upgradeable(wtoken); 30 | } else { 31 | outputToken = IERC20Upgradeable(outputTokenAddress); 32 | } 33 | 34 | outputToken = IERC20Upgradeable(outputTokenAddress); 35 | } 36 | 37 | function name() public pure returns (string memory) { 38 | return "SaddleLpTokenLiquidator"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/AnkrCertificateTokenPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BaseTest } from "../../config/BaseTest.t.sol"; 5 | import { AnkrCertificateTokenPriceOracle } from "../../../oracles/default/AnkrCertificateTokenPriceOracle.sol"; 6 | import { MasterPriceOracle } from "../../../oracles/MasterPriceOracle.sol"; 7 | 8 | contract AnkrCertificateTokenPriceOracleTest is BaseTest { 9 | AnkrCertificateTokenPriceOracle private oracle; 10 | MasterPriceOracle mpo; 11 | address wtoken; 12 | address aFTMc = 0xCfC785741Dc0e98ad4c9F6394Bb9d43Cd1eF5179; 13 | address ankrBNB = 0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827; 14 | address aMATICc = 0x0E9b89007eEE9c958c0EDA24eF70723C2C93dD58; 15 | 16 | function afterForkSetUp() internal override { 17 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 18 | wtoken = ap.getAddress("wtoken"); 19 | oracle = new AnkrCertificateTokenPriceOracle(); 20 | if (block.chainid == BSC_MAINNET) { 21 | oracle.initialize(ankrBNB); 22 | } else if (block.chainid == POLYGON_MAINNET) { 23 | oracle.initialize(aMATICc); 24 | } 25 | } 26 | 27 | function testAnkrBSCOracle() public forkAtBlock(BSC_MAINNET, 24150586) { 28 | uint256 priceAnkrBNB = oracle.price(ankrBNB); 29 | assertGt(priceAnkrBNB, 1e18); 30 | assertEq(priceAnkrBNB, 1040035572321529337); 31 | } 32 | 33 | function testAnkrPolygonOracle() public fork(POLYGON_MAINNET) { 34 | uint256 priceAnkrMATICc = oracle.price(aMATICc); 35 | uint256 pricWmatic = mpo.price(wtoken); 36 | assertGt(priceAnkrMATICc, 1e18); 37 | assertGt(priceAnkrMATICc, pricWmatic); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /deploy/11-deploy-liquidators.ts: -------------------------------------------------------------------------------- 1 | import { DeployFunction } from "hardhat-deploy/types"; 2 | 3 | import { ChainDeployConfig, chainDeployConfig } from "../chainDeploy"; 4 | import { 5 | configureIonicLiquidator, 6 | deployIonicLiquidator, 7 | deployIonicUniV3Liquidator 8 | } from "../chainDeploy/helpers/liquidators/ionicLiquidator"; 9 | 10 | const func: DeployFunction = async ({ run, viem, getNamedAccounts, deployments, getChainId }) => { 11 | const chainId = parseInt(await getChainId()); 12 | 13 | if (!chainDeployConfig[chainId]) { 14 | throw new Error(`Config invalid for ${chainId}`); 15 | } 16 | const { config: chainDeployParams }: { config: ChainDeployConfig } = chainDeployConfig[chainId]; 17 | 18 | //// Liquidator 19 | let liquidatorContractName: string | undefined; 20 | if (chainId === 34443) { 21 | liquidatorContractName = await deployIonicUniV3Liquidator({ 22 | run, 23 | viem, 24 | getNamedAccounts, 25 | deployments, 26 | deployConfig: chainDeployParams, 27 | chainId 28 | }); 29 | } else if (chainId === 60808) { 30 | // TODO 31 | } else { 32 | liquidatorContractName = await deployIonicLiquidator({ 33 | run, 34 | viem, 35 | getNamedAccounts, 36 | deployments, 37 | deployConfig: chainDeployParams, 38 | chainId 39 | }); 40 | } 41 | 42 | //// Configure Liquidator 43 | if (liquidatorContractName) { 44 | await configureIonicLiquidator({ 45 | contractName: liquidatorContractName, 46 | viem, 47 | getNamedAccounts, 48 | chainId, 49 | deployments 50 | }); 51 | } 52 | }; 53 | 54 | func.tags = ["prod", "deploy-liquidators"]; 55 | 56 | export default func; 57 | -------------------------------------------------------------------------------- /contracts/compound/CErc20Delegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./CToken.sol"; 5 | 6 | /** 7 | * @title Compound's CErc20Delegate Contract 8 | * @notice CTokens which wrap an EIP-20 underlying and are delegated to 9 | * @author Compound 10 | */ 11 | contract CErc20Delegate is CErc20 { 12 | function _getExtensionFunctions() public pure virtual override returns (bytes4[] memory functionSelectors) { 13 | uint8 fnsCount = 3; 14 | 15 | bytes4[] memory superFunctionSelectors = super._getExtensionFunctions(); 16 | functionSelectors = new bytes4[](superFunctionSelectors.length + fnsCount); 17 | 18 | for (uint256 i = 0; i < superFunctionSelectors.length; i++) { 19 | functionSelectors[i] = superFunctionSelectors[i]; 20 | } 21 | 22 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.contractType.selector; 23 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this.delegateType.selector; 24 | functionSelectors[--fnsCount + superFunctionSelectors.length] = this._becomeImplementation.selector; 25 | 26 | require(fnsCount == 0, "use the correct array length"); 27 | } 28 | 29 | /** 30 | * @notice Called by the delegator on a delegate to initialize it for duty 31 | */ 32 | function _becomeImplementation(bytes memory) public virtual override { 33 | require(msg.sender == address(this) || hasAdminRights(), "!self || !admin"); 34 | } 35 | 36 | function delegateType() public pure virtual override returns (uint8) { 37 | return 1; 38 | } 39 | 40 | function contractType() external pure virtual override returns (string memory) { 41 | return "CErc20Delegate"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/redstoneWeETHFallbacks.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | 3 | import { addUnderlyingsToMpoFallback } from "./utils"; 4 | import { RedStoneDeployFnParams } from "../../types"; 5 | 6 | export const addRedstoneWeETHFallbacks = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | redStoneAddress, 11 | redStoneAssets 12 | }: RedStoneDeployFnParams): Promise<{ redStoneOracle: any }> => { 13 | const { deployer } = await getNamedAccounts(); 14 | const publicClient = await viem.getPublicClient(); 15 | const walletClient = await viem.getWalletClient(deployer as Address); 16 | 17 | const mpo = await viem.getContractAt( 18 | "MasterPriceOracle", 19 | (await deployments.get("MasterPriceOracle")).address as Address 20 | ); 21 | 22 | //// RedStone Oracle 23 | const redStone = await deployments.deploy("RedstoneAdapterPriceOracleWeETH", { 24 | from: deployer, 25 | args: [redStoneAddress], 26 | log: true, 27 | waitConfirmations: 1 28 | }); 29 | 30 | if (redStone.transactionHash) 31 | await publicClient.waitForTransactionReceipt({ hash: redStone.transactionHash as Address }); 32 | console.log("RedstoneAdapterPriceOracleWeETH: ", redStone.address); 33 | 34 | const redStoneOracle = await viem.getContractAt( 35 | "RedstoneAdapterPriceOracleWeETH", 36 | (await deployments.get("RedstoneAdapterPriceOracleWeETH")).address as Address 37 | ); 38 | 39 | const underlyings = redStoneAssets.map((f) => f.underlying); 40 | await addUnderlyingsToMpoFallback( 41 | mpo as any, 42 | underlyings, 43 | redStoneOracle.address, 44 | deployer as Address, 45 | publicClient, 46 | walletClient 47 | ); 48 | return { redStoneOracle }; 49 | }; 50 | -------------------------------------------------------------------------------- /contracts/liquidators/BalancerSwapLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "./IRedemptionStrategy.sol"; 5 | import "../external/balancer/IBalancerPool.sol"; 6 | import "../external/balancer/IBalancerVault.sol"; 7 | 8 | import { IERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 9 | 10 | contract BalancerSwapLiquidator is IRedemptionStrategy { 11 | function redeem( 12 | IERC20Upgradeable inputToken, 13 | uint256 inputAmount, 14 | bytes memory strategyData 15 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 16 | (address outputTokenAddress, IBalancerPool pool) = abi.decode(strategyData, (address, IBalancerPool)); 17 | 18 | IBalancerVault vault = pool.getVault(); 19 | bytes32 poolId = pool.getPoolId(); 20 | 21 | SingleSwap memory singleSwap = SingleSwap( 22 | poolId, 23 | SwapKind.GIVEN_IN, 24 | IAsset(address(inputToken)), 25 | IAsset(address(outputTokenAddress)), 26 | inputAmount, 27 | "" 28 | ); 29 | 30 | FundManagement memory funds = FundManagement( 31 | address(this), 32 | false, // fromInternalBalance 33 | payable(address(this)), 34 | false // toInternalBalance 35 | ); 36 | 37 | inputToken.approve(address(vault), inputAmount); 38 | vault.swap(singleSwap, funds, 0, block.timestamp + 10); 39 | outputAmount = IERC20Upgradeable(outputTokenAddress).balanceOf(address(this)); 40 | return (IERC20Upgradeable(outputTokenAddress), outputAmount); 41 | } 42 | 43 | function name() public pure returns (string memory) { 44 | return "BalancerSwapLiquidator"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/liquidators/SushiBarLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/sushi/SushiBar.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title SushiBarLiquidator 12 | * @notice Redeems SushiBar (xSUSHI) for underlying SUSHI for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract SushiBarLiquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Unstake sOHM (and store output OHM as new collateral) 30 | SushiBar sushiBar = SushiBar(address(inputToken)); 31 | sushiBar.leave(inputAmount); 32 | outputToken = IERC20Upgradeable(sushiBar.sushi()); 33 | outputAmount = outputToken.balanceOf(address(this)); 34 | } 35 | 36 | function name() public pure returns (string memory) { 37 | return "SushiBarLiquidator"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/liquidators/YearnYVaultV2Liquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/yearn/IVaultV2.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title YearnYVaultV2Liquidator 12 | * @notice Exchanges seized Yearn yVault V2 token collateral for underlying tokens for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract YearnYVaultV2Liquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Redeem yVault token for underlying token (and store output as new collateral) 30 | IVaultV2 yVault = IVaultV2(address(inputToken)); 31 | outputAmount = yVault.withdraw(inputAmount); 32 | outputToken = IERC20Upgradeable(yVault.token()); 33 | } 34 | 35 | function name() public pure returns (string memory) { 36 | return "YearnYVaultV2Liquidator"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/test/oracles/RedstoneAdapterOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | import { MasterPriceOracle } from "../../oracles/MasterPriceOracle.sol"; 7 | import { RedstoneAdapterPriceOracle } from "../../oracles/default/RedstoneAdapterPriceOracle.sol"; 8 | 9 | import { BaseTest } from "../config/BaseTest.t.sol"; 10 | 11 | contract RedstoneAdapterOracleTest is BaseTest { 12 | MasterPriceOracle public mpo; 13 | RedstoneAdapterPriceOracle public oracle; 14 | address public redstoneOracleAddress; 15 | address MODE_USDC = 0xd988097fb8612cc24eeC14542bC03424c656005f; 16 | address MODE_EZETH = 0x2416092f143378750bb29b79eD961ab195CcEea5; 17 | address MODE_WBTC = 0xcDd475325D6F564d27247D1DddBb0DAc6fA0a5CF; 18 | address MODE_WEETH = 0x028227c4dd1e5419d11Bb6fa6e661920c519D4F5; 19 | 20 | function afterForkSetUp() internal override { 21 | if (block.chainid == MODE_MAINNET) { 22 | redstoneOracleAddress = 0x7C1DAAE7BB0688C9bfE3A918A4224041c7177256; 23 | } 24 | 25 | oracle = new RedstoneAdapterPriceOracle(redstoneOracleAddress); 26 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 27 | } 28 | 29 | function testPrintPricesMode() public fork(MODE_MAINNET) { 30 | emit log_named_uint("ezETH price (18 dec)", oracle.price(MODE_EZETH)); 31 | emit log_named_uint("WBTC price (8 dec)", oracle.price(MODE_WBTC)); 32 | emit log_named_uint("weETH price (18 dec)", oracle.price(MODE_WEETH)); 33 | } 34 | 35 | function testPrintMpoPricesMode() public fork(MODE_MAINNET) { 36 | emit log_named_uint("weETH price (18 dec)", mpo.price(MODE_WEETH)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | import "./IUniswapV2Router01.sol"; 5 | 6 | interface IUniswapV2Router02 is IUniswapV2Router01 { 7 | function removeLiquidityETHSupportingFeeOnTransferTokens( 8 | address token, 9 | uint256 liquidity, 10 | uint256 amountTokenMin, 11 | uint256 amountETHMin, 12 | address to, 13 | uint256 deadline 14 | ) external returns (uint256 amountETH); 15 | 16 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 17 | address token, 18 | uint256 liquidity, 19 | uint256 amountTokenMin, 20 | uint256 amountETHMin, 21 | address to, 22 | uint256 deadline, 23 | bool approveMax, 24 | uint8 v, 25 | bytes32 r, 26 | bytes32 s 27 | ) external returns (uint256 amountETH); 28 | 29 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 30 | uint256 amountIn, 31 | uint256 amountOutMin, 32 | address[] calldata path, 33 | address to, 34 | uint256 deadline 35 | ) external; 36 | 37 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 38 | uint256 amountIn, 39 | uint256 amountOutMin, 40 | address[] calldata path, 41 | address to, 42 | address referrer, 43 | uint256 deadline 44 | ) external; 45 | 46 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 47 | uint256 amountOutMin, 48 | address[] calldata path, 49 | address to, 50 | uint256 deadline 51 | ) external payable; 52 | 53 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 54 | uint256 amountIn, 55 | uint256 amountOutMin, 56 | address[] calldata path, 57 | address to, 58 | uint256 deadline 59 | ) external; 60 | } 61 | -------------------------------------------------------------------------------- /contracts/liquidators/YearnYVaultV1Liquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/yearn/IVault.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title YearnYVaultV1Liquidator 12 | * @notice Exchanges seized Yearn yVault V1 token collateral for underlying tokens for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract YearnYVaultV1Liquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Redeem yVault token for underlying token (and store output as new collateral) 30 | IVault yVault = IVault(address(inputToken)); 31 | yVault.withdraw(inputAmount); 32 | outputToken = IERC20Upgradeable(yVault.token()); 33 | outputAmount = outputToken.balanceOf(address(this)); 34 | } 35 | 36 | function name() public pure returns (string memory) { 37 | return "YearnYVaultV1Liquidator"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/redstone.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | 3 | import { RedStoneDeployFnParams } from "../../types"; 4 | import { addUnderlyingsToMpo } from "./utils"; 5 | 6 | export const deployRedStonePriceOracle = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | redStoneAddress, 11 | redStoneAssets 12 | }: RedStoneDeployFnParams): Promise<{ redStoneOracle: any }> => { 13 | const { deployer } = await getNamedAccounts(); 14 | const publicClient = await viem.getPublicClient(); 15 | const walletClient = await viem.getWalletClient(deployer as Address); 16 | 17 | const mpo = await viem.getContractAt( 18 | "MasterPriceOracle", 19 | (await deployments.get("MasterPriceOracle")).address as Address 20 | ); 21 | 22 | //// RedStone Oracle 23 | const redStone = await deployments.deploy("RedstoneAdapterPriceOracle", { 24 | from: deployer, 25 | args: [redStoneAddress], 26 | log: true, 27 | waitConfirmations: 1 28 | }); 29 | 30 | if (redStone.transactionHash) 31 | await publicClient.waitForTransactionReceipt({ hash: redStone.transactionHash as Address }); 32 | console.log("RedstoneAdapterPriceOracle: ", redStone.address); 33 | 34 | const redStoneOracle = await viem.getContractAt( 35 | "RedstoneAdapterPriceOracle", 36 | (await deployments.get("RedstoneAdapterPriceOracle")).address as Address 37 | ); 38 | 39 | const underlyings = redStoneAssets.map((f) => f.underlying); 40 | const mpoAdmin = await mpo.read.admin(); 41 | if (mpoAdmin != deployer) { 42 | console.error(`failed to update mpo - use gnosis multisig? ${mpoAdmin} ${deployer}`); 43 | } else { 44 | await addUnderlyingsToMpo(mpo as any, underlyings, redStoneOracle.address, deployer, publicClient, walletClient); 45 | } 46 | return { redStoneOracle }; 47 | }; 48 | -------------------------------------------------------------------------------- /tasks/auth/user.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Address, zeroAddress } from "viem"; 3 | 4 | enum Roles { 5 | REGISTRY_ROLE, 6 | SUPPLIER_ROLE, 7 | BORROWER_ROLE, 8 | LIQUIDATOR_ROLE, 9 | LEVERED_POSITION_ROLE 10 | } 11 | 12 | task("auth:set-user-role", "Sets the role of a new user") 13 | .addParam("pool", "Address of pool", undefined, types.string) 14 | .addParam("user", "Address of user", undefined, types.string) 15 | .addParam("role", "Enum of the role to use", undefined, types.int) 16 | .addParam("enabled", "If the user", undefined, types.boolean) 17 | .setAction(async ({ pool, user, role, enabled }, { viem, deployments }) => { 18 | const publicClient = await viem.getPublicClient(); 19 | console.log({ pool, user, role: Roles[role], enabled }); 20 | const authRegistry = await viem.getContractAt( 21 | "AuthoritiesRegistry", 22 | (await deployments.get("AuthoritiesRegistry")).address as Address 23 | ); 24 | const poolAuthAddress = await authRegistry.read.poolsAuthorities([pool]); 25 | if (poolAuthAddress === zeroAddress) { 26 | console.log(`Pool authority for pool ${pool} does not exist`); 27 | return; 28 | } 29 | const poolAuth = await viem.getContractAt("PoolRolesAuthority", poolAuthAddress); 30 | const userHasCapability = await poolAuth.read.doesUserHaveRole([user, role]); 31 | if (userHasCapability === enabled) { 32 | console.log(`User ${user} already has ${Roles[role]} role for pool ${pool}`); 33 | return; 34 | } else { 35 | const tx = await authRegistry.write.setUserRole([pool, user, role, enabled]); 36 | await publicClient.waitForTransactionReceipt({ hash: tx }); 37 | console.log(`Set user ${user} role: ${Roles[role]} pool ${pool}: ${tx} to ${enabled}`); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IFlywheelBooster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import {ERC20} from "solmate/tokens/ERC20.sol"; 5 | 6 | /** 7 | @title Balance Booster Module for Flywheel 8 | @notice Flywheel is a general framework for managing token incentives. 9 | It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies. 10 | 11 | The Booster module is an optional module for virtually boosting or otherwise transforming user balances. 12 | If a booster is not configured, the strategies ERC-20 balanceOf/totalSupply will be used instead. 13 | 14 | Boosting logic can be associated with referrals, vote-escrow, or other strategies. 15 | 16 | SECURITY NOTE: similar to how Core needs to be notified any time the strategy user composition changes, the booster would need to be notified of any conditions which change the boosted balances atomically. 17 | This prevents gaming of the reward calculation function by using manipulated balances when accruing. 18 | */ 19 | interface IFlywheelBooster { 20 | /** 21 | @notice calculate the boosted supply of a strategy. 22 | @param strategy the strategy to calculate boosted supply of 23 | @return the boosted supply 24 | */ 25 | function boostedTotalSupply(ERC20 strategy) external view returns (uint256); 26 | 27 | /** 28 | @notice calculate the boosted balance of a user in a given strategy. 29 | @param strategy the strategy to calculate boosted balance of 30 | @param user the user to calculate boosted balance of 31 | @return the boosted balance 32 | */ 33 | function boostedBalanceOf(ERC20 strategy, address user) external view returns (uint256); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/rewards/ReplacingFlywheelDynamicRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { FlywheelDynamicRewards } from "./FlywheelDynamicRewards.sol"; 5 | import { IonicFlywheelCore } from "../IonicFlywheelCore.sol"; 6 | import { Auth, Authority } from "solmate/auth/Auth.sol"; 7 | import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; 8 | 9 | interface ICERC20_RFDR { 10 | function plugin() external returns (address); 11 | } 12 | 13 | interface IPlugin { 14 | function claimRewards() external; 15 | } 16 | 17 | contract ReplacingFlywheelDynamicRewards is FlywheelDynamicRewards { 18 | using SafeTransferLib for ERC20; 19 | 20 | IonicFlywheelCore public replacedFlywheel; 21 | 22 | constructor( 23 | IonicFlywheelCore _replacedFlywheel, 24 | IonicFlywheelCore _flywheel, 25 | uint32 _cycleLength 26 | ) FlywheelDynamicRewards(_flywheel, _cycleLength) { 27 | replacedFlywheel = _replacedFlywheel; 28 | // rewardToken.safeApprove(address(_replacedFlywheel), type(uint256).max); 29 | } 30 | 31 | function getNextCycleRewards(ERC20 strategy) internal override returns (uint192) { 32 | if (msg.sender == address(replacedFlywheel)) { 33 | return 0; 34 | } else { 35 | // make it work for both pulled (claimed) and pushed (transferred some other way) rewards 36 | try ICERC20_RFDR(address(strategy)).plugin() returns (address plugin) { 37 | try IPlugin(plugin).claimRewards() {} catch {} 38 | } catch {} 39 | 40 | uint256 rewardAmount = rewardToken.balanceOf(address(strategy)); 41 | if (rewardAmount != 0) { 42 | rewardToken.safeTransferFrom(address(strategy), address(this), rewardAmount); 43 | } 44 | return uint192(rewardAmount); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/redstoneWeETH.ts: -------------------------------------------------------------------------------- 1 | import { RedStoneDeployFnParams } from "../../types"; 2 | import { addUnderlyingsToMpo } from "./utils"; 3 | 4 | import { Address } from "viem"; 5 | 6 | export const deployRedStonePriceOracle = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | redStoneAddress, 11 | redStoneAssets 12 | }: RedStoneDeployFnParams): Promise<{ redStoneOracle: any }> => { 13 | const { deployer } = await getNamedAccounts(); 14 | const publicClient = await viem.getPublicClient(); 15 | const walletClient = await viem.getWalletClient(deployer as Address); 16 | 17 | const mpo = await viem.getContractAt( 18 | "MasterPriceOracle", 19 | (await deployments.get("MasterPriceOracle")).address as Address 20 | ); 21 | 22 | //// RedStone Oracle 23 | const redStone = await deployments.deploy("RedstoneAdapterPriceOracleWeETH", { 24 | from: deployer, 25 | args: [redStoneAddress], 26 | log: true, 27 | waitConfirmations: 1 28 | }); 29 | 30 | if (redStone.transactionHash) 31 | await publicClient.waitForTransactionReceipt({ hash: redStone.transactionHash as Address }); 32 | console.log("RedstoneAdapterPriceOracleWeETH: ", redStone.address); 33 | 34 | const redStoneOracle = await viem.getContractAt( 35 | "RedstoneAdapterPriceOracleWeETH", 36 | (await deployments.get("RedstoneAdapterPriceOracleWeETH")).address as Address 37 | ); 38 | 39 | const underlyings = redStoneAssets.map((f) => f.underlying); 40 | const mpoAdmin = await mpo.read.admin(); 41 | if (mpoAdmin != deployer) { 42 | console.error(`failed to update mpo - use gnosis multisig? ${mpoAdmin} ${deployer}`); 43 | } else { 44 | await addUnderlyingsToMpo(mpo as any, underlyings, redStoneOracle.address, deployer, publicClient, walletClient); 45 | } 46 | return { redStoneOracle }; 47 | }; 48 | -------------------------------------------------------------------------------- /contracts/oracles/default/VelodromePriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "../BasePriceOracle.sol"; 5 | 6 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | interface Prices { 9 | function getRateToEth(address srcToken, bool useSrcWrappers) external view returns (uint256 weightedRate); 10 | } 11 | 12 | contract VelodromePriceOracle is BasePriceOracle { 13 | Prices immutable prices; 14 | 15 | constructor(address _prices) { 16 | prices = Prices(_prices); 17 | } 18 | /** 19 | * @notice Fetches the token/ETH price, with 18 decimals of precision. 20 | * @param underlying The underlying token address for which to get the price. 21 | * @return Price denominated in ETH (scaled by 1e18) 22 | */ 23 | function price(address underlying) external view override returns (uint256) { 24 | return _price(underlying); 25 | } 26 | 27 | /** 28 | * @notice Returns the price in ETH of the token underlying `cToken`. 29 | * @dev Implements the `PriceOracle` interface for Ionic pools (and Compound v2). 30 | * @return Price in ETH of the token underlying `cToken`, scaled by `10 ** (36 - underlyingDecimals)`. 31 | */ 32 | function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { 33 | address underlying = cToken.underlying(); 34 | // Comptroller needs prices to be scaled by 1e(36 - decimals) 35 | // Since `_price` returns prices scaled by 18 decimals, we must scale them by 1e(36 - 18 - decimals) 36 | return (_price(underlying)); 37 | } 38 | 39 | /** 40 | * @notice Fetches the token/ETH price, with 18 decimals of precision. 41 | */ 42 | function _price(address token) internal view returns (uint256) { 43 | return prices.getRateToEth(token, false); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/external/uniswap/quoter/libraries/LowGasSafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0; 3 | 4 | /// @title Optimized overflow and underflow safe math operations 5 | /// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost 6 | library LowGasSafeMath { 7 | /// @notice Returns x + y, reverts if sum overflows uint256 8 | /// @param x The augend 9 | /// @param y The addend 10 | /// @return z The sum of x and y 11 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 12 | require((z = x + y) >= x); 13 | } 14 | 15 | /// @notice Returns x - y, reverts if underflows 16 | /// @param x The minuend 17 | /// @param y The subtrahend 18 | /// @return z The difference of x and y 19 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 20 | require((z = x - y) <= x); 21 | } 22 | 23 | /// @notice Returns x * y, reverts if overflows 24 | /// @param x The multiplicand 25 | /// @param y The multiplier 26 | /// @return z The product of x and y 27 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 28 | require(x == 0 || (z = x * y) / x == y); 29 | } 30 | 31 | /// @notice Returns x + y, reverts if overflows or underflows 32 | /// @param x The augend 33 | /// @param y The addend 34 | /// @return z The sum of x and y 35 | function add(int256 x, int256 y) internal pure returns (int256 z) { 36 | require((z = x + y) >= x == (y >= 0)); 37 | } 38 | 39 | /// @notice Returns x - y, reverts if overflows or underflows 40 | /// @param x The minuend 41 | /// @param y The subtrahend 42 | /// @return z The difference of x and y 43 | function sub(int256 x, int256 y) internal pure returns (int256 z) { 44 | require((z = x - y) <= x == (y >= 0)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tasks/admin/configure-ap-strategies.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | 3 | import { ChainDeployConfig, chainDeployConfig } from "../../chainDeploy"; 4 | import { 5 | configureAddressesProviderAddresses, 6 | configureIonicLiquidator 7 | } from "../../chainDeploy/helpers/liquidators/ionicLiquidator"; 8 | import { configureLiquidatorsRegistry } from "../../chainDeploy/helpers/liquidators/registry"; 9 | 10 | export default task( 11 | "config:strategies", 12 | "Configure the redemption and funding strategies in the AddressesProvider for testing purposes" 13 | ).setAction(async ({}, { viem, getNamedAccounts, getChainId, deployments }) => { 14 | const chainId = parseInt(await getChainId()); 15 | const { deployer } = await getNamedAccounts(); 16 | console.log(`deployer ${deployer}`); 17 | 18 | if (!chainDeployConfig[chainId]) { 19 | throw new Error(`Config invalid for ${chainId}`); 20 | } 21 | const { config: chainDeployParams }: { config: ChainDeployConfig; deployFunc: CallableFunction } = 22 | chainDeployConfig[chainId]; 23 | console.log("chainDeployParams: ", chainDeployParams); 24 | 25 | await configureAddressesProviderAddresses({ 26 | viem, 27 | getNamedAccounts, 28 | chainId, 29 | deployConfig: chainDeployParams, 30 | deployments 31 | }); 32 | 33 | //// Configure Liquidators Registry 34 | await configureLiquidatorsRegistry({ 35 | viem, 36 | getNamedAccounts, 37 | chainId, 38 | deployments 39 | }); 40 | }); 41 | 42 | task("config:ionic:liquidator").setAction(async ({}, { viem, deployments, getNamedAccounts, getChainId }) => { 43 | const chainId = parseInt(await getChainId()); 44 | await configureIonicLiquidator({ 45 | contractName: "IonicUniV3Liquidator", 46 | viem, 47 | getNamedAccounts, 48 | chainId, 49 | deployments 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /chainDeploy/helpers/oracles/redstoneWrsETH.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | 3 | import { RedStoneDeployFnParams } from "../../types"; 4 | import { addUnderlyingsToMpo } from "./utils"; 5 | 6 | export const deployRedStoneWrsETHPriceOracle = async ({ 7 | viem, 8 | getNamedAccounts, 9 | deployments, 10 | redStoneAddress, 11 | redStoneAssets 12 | }: RedStoneDeployFnParams): Promise<{ redStoneOracle: any }> => { 13 | const { deployer } = await getNamedAccounts(); 14 | const publicClient = await viem.getPublicClient(); 15 | const walletClient = await viem.getWalletClient(deployer as Address); 16 | 17 | const mpo = await viem.getContractAt( 18 | "MasterPriceOracle", 19 | (await deployments.get("MasterPriceOracle")).address as Address 20 | ); 21 | 22 | //// RedStone Oracle 23 | const redStone = await deployments.deploy("RedstoneAdapterPriceOracleWrsETH", { 24 | from: deployer, 25 | args: [redStoneAddress], 26 | log: true, 27 | waitConfirmations: 1 28 | }); 29 | 30 | if (redStone.transactionHash) 31 | await publicClient.waitForTransactionReceipt({ hash: redStone.transactionHash as Address }); 32 | console.log("RedstoneAdapterPriceOracleWrsETH: ", redStone.address); 33 | 34 | const redStoneOracle = await viem.getContractAt( 35 | "RedstoneAdapterPriceOracleWrsETH", 36 | (await deployments.get("RedstoneAdapterPriceOracleWrsETH")).address as Address 37 | ); 38 | 39 | const underlyings = redStoneAssets.map((f) => f.underlying); 40 | const mpoAdmin = await mpo.read.admin(); 41 | if (mpoAdmin != deployer) { 42 | console.error(`failed to update mpo - use gnosis multisig? ${mpoAdmin} ${deployer}`); 43 | } else { 44 | await addUnderlyingsToMpo(mpo as any, underlyings, redStoneOracle.address, deployer, publicClient, walletClient); 45 | } 46 | return { redStoneOracle }; 47 | }; 48 | -------------------------------------------------------------------------------- /contracts/liquidators/UniswapV3LiquidatorFunder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol"; 5 | import { IFundsConversionStrategy } from "./IFundsConversionStrategy.sol"; 6 | import { IRedemptionStrategy } from "./IRedemptionStrategy.sol"; 7 | import "./UniswapV3Liquidator.sol"; 8 | 9 | import { Quoter } from "../external/uniswap/quoter/Quoter.sol"; 10 | 11 | contract UniswapV3LiquidatorFunder is UniswapV3Liquidator, IFundsConversionStrategy { 12 | using FixedPointMathLib for uint256; 13 | 14 | function convert( 15 | IERC20Upgradeable inputToken, 16 | uint256 inputAmount, 17 | bytes memory strategyData 18 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 19 | return _convert(inputToken, inputAmount, strategyData); 20 | } 21 | 22 | /** 23 | * @dev Estimates the needed input amount of the input token for the conversion to return the desired output amount. 24 | * @param outputAmount the desired output amount 25 | * @param strategyData the input token 26 | */ 27 | function estimateInputAmount(uint256 outputAmount, bytes memory strategyData) 28 | external 29 | view 30 | returns (IERC20Upgradeable inputToken, uint256 inputAmount) 31 | { 32 | (address _inputToken, address _outputToken, uint24 fee, , Quoter quoter) = abi.decode( 33 | strategyData, 34 | (address, address, uint24, ISwapRouter, Quoter) 35 | ); 36 | 37 | inputAmount = quoter.estimateMinSwapUniswapV3(_inputToken, _outputToken, outputAmount, fee); 38 | inputToken = IERC20Upgradeable(_inputToken); 39 | } 40 | 41 | function name() public pure override(UniswapV3Liquidator, IRedemptionStrategy) returns (string memory) { 42 | return "UniswapV3LiquidatorFunder"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/oracles/default/BNBxPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { ERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; 5 | import { IStakeManager } from "../../external/stader/IStakeManager.sol"; 6 | 7 | import "../../ionic/SafeOwnableUpgradeable.sol"; 8 | import "../BasePriceOracle.sol"; 9 | 10 | /** 11 | * @title BNBxPriceOracle 12 | * @author Carlo Mazzaferro (https://github.com/carlomazzaferro) 13 | * @notice BNBxPriceOracle is a price oracle for BNBx liquid staked tokens. 14 | * @dev Implements the `PriceOracle` interface used by Midas pools (and Compound v2). 15 | */ 16 | 17 | contract BNBxPriceOracle is SafeOwnableUpgradeable, BasePriceOracle { 18 | IStakeManager public stakeManager; 19 | address public BNBx; 20 | 21 | function initialize() public initializer { 22 | __SafeOwnable_init(msg.sender); 23 | stakeManager = IStakeManager(0x7276241a669489E4BBB76f63d2A43Bfe63080F2F); 24 | (, address _bnbX, , ) = stakeManager.getContracts(); 25 | BNBx = _bnbX; 26 | } 27 | 28 | function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { 29 | // Get underlying token address 30 | address underlying = cToken.underlying(); 31 | require(underlying == BNBx, "Invalid underlying"); 32 | // no need to scale as BNBx has 18 decimals 33 | return _price(); 34 | } 35 | 36 | function price(address underlying) external view override returns (uint256) { 37 | require(underlying == BNBx, "Invalid underlying"); 38 | return _price(); 39 | } 40 | 41 | function _price() internal view returns (uint256) { 42 | uint256 oneBNB = 1e18; 43 | uint256 exchangeRate = stakeManager.convertBnbXToBnb(oneBNB); 44 | return exchangeRate; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tasks/plugin/deploy.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Hash } from "viem"; 3 | 4 | task("plugin:deploy", "Deploy ERC4626 Strategy") 5 | .addParam("contractName", "Name of the ERC4626 strategy", undefined, types.string) 6 | .addParam("deploymentName", "Name of the ERC4626 contract", undefined, types.string) 7 | .addParam("underlying", "Address of the underlying token", undefined, types.string) 8 | .addParam("creator", "Deployer Address", "deployer", types.string) 9 | .addOptionalParam( 10 | "otherParams", 11 | "other params that might be required to construct the strategy", 12 | undefined, 13 | types.string 14 | ) 15 | .setAction(async (taskArgs, { viem, deployments, getNamedAccounts }) => { 16 | const publicClient = await viem.getPublicClient(); 17 | const { deployer } = await getNamedAccounts(); 18 | const otherParams = taskArgs.otherParams ? taskArgs.otherParams.split(",") : null; 19 | let deployArgs; 20 | if (otherParams) { 21 | deployArgs = [taskArgs.underlying, ...otherParams]; 22 | } else { 23 | deployArgs = [taskArgs.underlying]; 24 | } 25 | 26 | const deployment = await deployments.deploy(taskArgs.deploymentName, { 27 | contract: taskArgs.contractName, 28 | from: deployer, 29 | proxy: { 30 | proxyContract: "OpenZeppelinTransparentProxy", 31 | execute: { 32 | init: { 33 | methodName: "initialize", 34 | args: deployArgs 35 | } 36 | }, 37 | owner: deployer 38 | }, 39 | log: true 40 | }); 41 | 42 | if (deployment.transactionHash) 43 | await publicClient.waitForTransactionReceipt({ hash: deployment.transactionHash as Hash }); 44 | 45 | console.log("ERC4626 Strategy: ", deployment.address); 46 | return deployment.address; 47 | }); 48 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/flywheel/IIonicFlywheelBorrowBooster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.10; 3 | 4 | import { ICErc20 } from "../../../compound/CTokenInterfaces.sol"; 5 | 6 | /** 7 | @title Balance Booster Module for Flywheel 8 | @notice Flywheel is a general framework for managing token incentives. 9 | It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies. 10 | 11 | The Booster module is an optional module for virtually boosting or otherwise transforming user balances. 12 | If a booster is not configured, the strategies ERC-20 balanceOf/totalSupply will be used instead. 13 | 14 | Boosting logic can be associated with referrals, vote-escrow, or other strategies. 15 | 16 | SECURITY NOTE: similar to how Core needs to be notified any time the strategy user composition changes, the booster would need to be notified of any conditions which change the boosted balances atomically. 17 | This prevents gaming of the reward calculation function by using manipulated balances when accruing. 18 | */ 19 | interface IIonicFlywheelBorrowBooster { 20 | /** 21 | @notice calculate the boosted supply of a strategy. 22 | @param strategy the strategy to calculate boosted supply of 23 | @return the boosted supply 24 | */ 25 | function boostedTotalSupply(ICErc20 strategy) external view returns (uint256); 26 | 27 | /** 28 | @notice calculate the boosted balance of a user in a given strategy. 29 | @param strategy the strategy to calculate boosted balance of 30 | @param user the user to calculate boosted balance of 31 | @return the boosted balance 32 | */ 33 | function boostedBalanceOf(ICErc20 strategy, address user) external view returns (uint256); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/liquidators/AlphaHomoraV2SafeBoxLiquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/alpha/ISafeBox.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title AlphaHomoraV2SafeBoxLiquidator 12 | * @notice Redeems seized Alpha Homora v2 "ibTokenV2" or SafeBox tokens (e.g., ibDAIv2) for underlying tokens for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract AlphaHomoraV2SafeBoxLiquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Redeem ibTokenV2 for underlying ERC20 token (and store output as new collateral) 30 | ISafeBox safeBox = ISafeBox(address(inputToken)); 31 | safeBox.withdraw(inputAmount); 32 | outputToken = IERC20Upgradeable(safeBox.uToken()); 33 | outputAmount = outputToken.balanceOf(address(this)); 34 | } 35 | 36 | function name() public pure returns (string memory) { 37 | return "AlphaHomoraV2SafeBoxLiquidator"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/oracles/default/UniswapV3PriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BasePriceOracle } from "../BasePriceOracle.sol"; 5 | import { ERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; 6 | import { ConcentratedLiquidityBasePriceOracle } from "./ConcentratedLiquidityBasePriceOracle.sol"; 7 | 8 | import "../../external/uniswap/TickMath.sol"; 9 | import "../../external/uniswap/FullMath.sol"; 10 | import "../../external/uniswap/IUniswapV3Pool.sol"; 11 | 12 | /** 13 | * @title UniswapV3PriceOracle 14 | * @author Carlo Mazzaferro (https://github.com/carlomazzaferro) 15 | * @notice UniswapV3PriceOracle is a price oracle for Uniswap V3 pairs. 16 | * @dev Implements the `PriceOracle` interface used by Ionic pools (and Compound v2). 17 | */ 18 | contract UniswapV3PriceOracle is ConcentratedLiquidityBasePriceOracle { 19 | /** 20 | * @dev Fetches the price for a token from Algebra pools. 21 | */ 22 | 23 | function _price(address token) internal view override returns (uint256) { 24 | uint32[] memory secondsAgos = new uint32[](2); 25 | uint256 twapWindow = poolFeeds[token].twapWindow; 26 | address baseToken = poolFeeds[token].baseToken; 27 | 28 | secondsAgos[0] = uint32(twapWindow); 29 | secondsAgos[1] = 0; 30 | 31 | IUniswapV3Pool pool = IUniswapV3Pool(poolFeeds[token].poolAddress); 32 | (int56[] memory tickCumulatives, ) = pool.observe(secondsAgos); 33 | 34 | int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int256(twapWindow))); 35 | uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick); 36 | 37 | uint256 tokenPrice = getPriceX96FromSqrtPriceX96(pool.token0(), token, sqrtPriceX96); 38 | return scalePrices(baseToken, token, tokenPrice); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tasks/admin/edit-deployers.ts: -------------------------------------------------------------------------------- 1 | import { task } from "hardhat/config"; 2 | import { Address } from "viem"; 3 | 4 | export default task("edit-deployers", "Edit deployers") 5 | .addParam("deployers", "Comma-separated deployers") 6 | .addOptionalParam("status", "Add or remove deployer") 7 | .setAction(async ({ deployers: _deployers, status: _status }, { viem, deployments }) => { 8 | const status = _status ? _status === "true" : true; 9 | console.log("status: ", status); 10 | const deployers = _deployers.split(","); 11 | console.log("deployers: ", deployers); 12 | 13 | const fpd = await viem.getContractAt("PoolDirectory", (await deployments.get("PoolDirectory")).address as Address); 14 | const tx = await fpd._editDeployerWhitelist(deployers, status); 15 | 16 | console.log("_editDeployerWhitelist tx: ", tx); 17 | const receipt = await tx.wait(); 18 | console.log("_editDeployerWhitelist tx mined: ", receipt.transactionHash); 19 | }); 20 | 21 | task("edit-deployer-whitelist-enforcement", "Edit deployer whitelist enforcement") 22 | .addParam("enforce", "Enforce whitelist?") 23 | .setAction(async ({ enforce: _enforce }, { viem, deployments }) => { 24 | const enforce = _enforce === "true"; 25 | 26 | const fpd = await viem.getContractAt("PoolDirectory", (await deployments.get("PoolDirectory")).address as Address); 27 | const current = await fpd.enforceDeployerWhitelist(); 28 | console.log("current: ", current); 29 | if (current === enforce) { 30 | console.log("Already set to ", enforce); 31 | return; 32 | } 33 | const tx = await fpd._setDeployerWhitelistEnforcement(enforce); 34 | 35 | console.log("_setDeployerWhitelistEnforcement tx: ", tx); 36 | const receipt = await tx.wait(); 37 | console.log("_setDeployerWhitelistEnforcement tx mined: ", receipt.transactionHash); 38 | }); 39 | -------------------------------------------------------------------------------- /contracts/external/lido/IWstETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | /** 5 | * @title StETH token wrapper with static balances. 6 | * @dev It's an ERC20 token that represents the account's share of the total 7 | * supply of stETH tokens. WstETH token's balance only changes on transfers, 8 | * unlike StETH that is also changed when oracles report staking rewards and 9 | * penalties. It's a "power user" token for DeFi protocols which don't 10 | * support rebasable tokens. 11 | * 12 | * The contract is also a trustless wrapper that accepts stETH tokens and mints 13 | * wstETH in return. Then the user unwraps, the contract burns user's wstETH 14 | * and sends user locked stETH in return. 15 | * 16 | * The contract provides the staking shortcut: user can send ETH with regular 17 | * transfer and get wstETH in return. The contract will send ETH to Lido submit 18 | * method, staking it and wrapping the received stETH. 19 | * 20 | */ 21 | interface IWstETH { 22 | function stETH() external view returns (address); 23 | 24 | /** 25 | * @notice Get amount of stETH for a one wstETH 26 | * @return Amount of stETH for 1 wstETH 27 | */ 28 | function stEthPerToken() external view returns (uint256); 29 | 30 | /** 31 | * @notice Get amount of wstETH for a one stETH 32 | * @return Amount of wstETH for a 1 stETH 33 | */ 34 | function tokensPerStEth() external view returns (uint256); 35 | 36 | /** 37 | * @notice Exchanges wstETH to stETH 38 | * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH 39 | * @dev Requirements: 40 | * - `_wstETHAmount` must be non-zero 41 | * - msg.sender must have at least `_wstETHAmount` wstETH. 42 | * @return Amount of stETH user receives after unwrap 43 | */ 44 | function unwrap(uint256 _wstETHAmount) external returns (uint256); 45 | } 46 | -------------------------------------------------------------------------------- /contracts/oracles/default/AlgebraPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { BasePriceOracle } from "../BasePriceOracle.sol"; 5 | import { ERC20Upgradeable } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; 6 | import { ConcentratedLiquidityBasePriceOracle } from "./ConcentratedLiquidityBasePriceOracle.sol"; 7 | import { IAlgebraPool } from "../../external/algebra/IAlgebraPool.sol"; 8 | 9 | import "../../external/uniswap/TickMath.sol"; 10 | import "../../external/uniswap/FullMath.sol"; 11 | 12 | /** 13 | * @title UniswapV3PriceOracle 14 | * @author Carlo Mazzaferro (https://github.com/carlomazzaferro) 15 | * @notice AlgebraPriceOracle is a price oracle for Algebra pairs. 16 | * @dev Implements the `PriceOracle` interface used by Ionic pools (and Compound v2). 17 | */ 18 | contract AlgebraPriceOracle is ConcentratedLiquidityBasePriceOracle { 19 | /** 20 | * @dev Fetches the price for a token from Algebra pools 21 | */ 22 | function _price(address token) internal view override returns (uint256) { 23 | uint32[] memory secondsAgos = new uint32[](2); 24 | uint256 twapWindow = poolFeeds[token].twapWindow; 25 | address baseToken = poolFeeds[token].baseToken; 26 | 27 | secondsAgos[0] = uint32(twapWindow); 28 | secondsAgos[1] = 0; 29 | 30 | IAlgebraPool pool = IAlgebraPool(poolFeeds[token].poolAddress); 31 | (int56[] memory tickCumulatives, , , ) = pool.getTimepoints(secondsAgos); 32 | 33 | int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int256(twapWindow))); 34 | uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick); 35 | 36 | uint256 tokenPrice = getPriceX96FromSqrtPriceX96(pool.token0(), token, sqrtPriceX96); 37 | return scalePrices(baseToken, token, tokenPrice); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/test/oracles/default/SaddleLpPriceOracleTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { ISwap } from "../../../external/saddle/ISwap.sol"; 5 | import { SaddleLpPriceOracle } from "../../../oracles/default/SaddleLpPriceOracle.sol"; 6 | import { MasterPriceOracle } from "../../../oracles/MasterPriceOracle.sol"; 7 | import { ICErc20 } from "../../../compound/CTokenInterfaces.sol"; 8 | 9 | import { BaseTest } from "../../config/BaseTest.t.sol"; 10 | 11 | contract SaddleLpPriceOracleTest is BaseTest { 12 | SaddleLpPriceOracle oracle; 13 | address usdc = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; 14 | address frax = 0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F; 15 | address fraxUsdc_lp = 0x896935B02D3cBEb152192774e4F1991bb1D2ED3f; 16 | address fraxUsdc_pool = 0x401AFbc31ad2A3Bc0eD8960d63eFcDEA749b4849; 17 | // TODO: add test once this is deployed 18 | // ICErc20 fraxUsdc_c = ICErc20(0x); 19 | MasterPriceOracle mpo; 20 | 21 | function afterForkSetUp() internal override { 22 | mpo = MasterPriceOracle(ap.getAddress("MasterPriceOracle")); 23 | 24 | address[] memory lpTokens = new address[](1); 25 | lpTokens[0] = fraxUsdc_lp; 26 | address[] memory pools = new address[](1); 27 | pools[0] = fraxUsdc_pool; 28 | address[][] memory underlyings = new address[][](1); 29 | underlyings[0] = new address[](2); 30 | underlyings[0][0] = usdc; 31 | underlyings[0][1] = frax; 32 | 33 | vm.startPrank(mpo.admin()); 34 | oracle = new SaddleLpPriceOracle(); 35 | oracle.initialize(lpTokens, pools, underlyings); 36 | vm.stopPrank(); 37 | } 38 | 39 | function testSaddleLpTokenPriceOracle() public debuggingOnly forkAtBlock(ARBITRUM_ONE, 44898730) { 40 | vm.prank(address(mpo)); 41 | uint256 price = oracle.price(fraxUsdc_lp); 42 | assertEq(price, 785240575939374); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tasks/flywheel/retire.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Address } from "viem"; 3 | 4 | export default task("flywheel:nonaccruing", "Sets a flywheel as non-accruing in the comptroller") 5 | .addParam("flywheel", "address of flywheel", undefined, types.string) 6 | .addParam("pool", "address of comptroller", undefined, types.string) 7 | .setAction(async (taskArgs, { viem }) => { 8 | const publicClient = await viem.getPublicClient(); 9 | const comptroller = await viem.getContractAt("ComptrollerFirstExtension", taskArgs.pool); 10 | 11 | const tx = await comptroller.write.addNonAccruingFlywheel([taskArgs.flywheel]); 12 | await publicClient.waitForTransactionReceipt({ hash: tx }); 13 | console.log(`added the flywheel to the non-accruing with tx ${tx}`); 14 | }); 15 | 16 | task("flywheel:remove", "remove a rewards distributor from a pool") 17 | .addParam("flywheel", "address of flywheel", undefined, types.string) 18 | .addParam("pool", "address of comptroller", undefined, types.string) 19 | .setAction(async (taskArgs, { viem, getNamedAccounts }) => { 20 | const { deployer } = await getNamedAccounts(); 21 | const publicClient = await viem.getPublicClient(); 22 | // extract the leftover rewards to the deployer 23 | const flywheel = await viem.getContractAt("IonicFlywheel", taskArgs.flywheel); 24 | let tx = await flywheel.write.setFlywheelRewards([deployer as Address]); 25 | await publicClient.waitForTransactionReceipt({ hash: tx }); 26 | console.log("setFlywheelRewards: ", tx); 27 | 28 | const asComptrollerExtension = await viem.getContractAt("ComptrollerFirstExtension", taskArgs.pool); 29 | 30 | tx = await asComptrollerExtension.write._removeFlywheel([taskArgs.flywheel]); 31 | await publicClient.waitForTransactionReceipt({ hash: tx }); 32 | console.log("_removeFlywheel: ", tx); 33 | }); 34 | -------------------------------------------------------------------------------- /tasks/market/set-plugin.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | import { Address } from "viem"; 3 | 4 | export default task("market:set-plugin", "Set's the plugin of a market") 5 | .addParam("comptrollerAddress", "Address of the comptroller of the market", undefined, types.string) // TODO I would rather use id or comptroller address directly. 6 | .addParam("underlying", "Underlying asset symbol or address", undefined, types.string) 7 | .addParam("pluginAddress", "The address of the deployed plugin", "", types.string) 8 | .addOptionalParam("signer", "Named account that is an admin of the pool", "deployer", types.string) 9 | .setAction(async (taskArgs, { viem, deployments, getNamedAccounts }) => { 10 | const publicClient = await viem.getPublicClient(); 11 | const { comptrollerAddress, underlying, pluginAddress } = taskArgs; 12 | 13 | const comptroller = await viem.getContractAt("IonicComptroller", comptrollerAddress as Address); 14 | 15 | const allMarkets = await comptroller.read.getAllMarkets(); 16 | 17 | const cTokenInstances = await Promise.all( 18 | allMarkets.map(async (marketAddress) => { 19 | return await viem.getContractAt("ICErc20Plugin", marketAddress); 20 | }) 21 | ); 22 | 23 | const cTokenInstance = cTokenInstances.find(async (cToken) => { 24 | return (await cToken.read.underlying()) == underlying; 25 | }); 26 | 27 | console.log(`Setting plugin to ${pluginAddress}`); 28 | const setPluginTx = await cTokenInstance!.write._updatePlugin(pluginAddress); 29 | 30 | const receipt = await publicClient.waitForTransactionReceipt({ 31 | hash: setPluginTx 32 | }); 33 | if (receipt.status !== "success") { 34 | throw `Failed set plugin to ${pluginAddress}`; 35 | } 36 | console.log(`Plugin successfully set to ${pluginAddress} for market ${cTokenInstance!.address}`); 37 | }); 38 | -------------------------------------------------------------------------------- /contracts/ionic/strategies/MockERC4626.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity ^0.8.0; 3 | 4 | import { ERC20 } from "solmate/tokens/ERC20.sol"; 5 | import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol"; 6 | 7 | import { ERC4626 } from "solmate/mixins/ERC4626.sol"; 8 | import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol"; 9 | 10 | /** 11 | * @title Mock ERC4626 Contract 12 | * @notice ERC4626 wrapper for Tribe Token 13 | * @author carlomazzaferro 14 | * 15 | */ 16 | contract MockERC4626 is ERC4626 { 17 | using SafeTransferLib for ERC20; 18 | using FixedPointMathLib for uint256; 19 | 20 | /** 21 | @notice Creates a new Vault that accepts a specific underlying token. 22 | @param _asset The ERC20 compliant token the Vault should accept. 23 | */ 24 | constructor(ERC20 _asset) 25 | ERC4626( 26 | _asset, 27 | string(abi.encodePacked("Midas ", _asset.name(), " Vault")), 28 | string(abi.encodePacked("mv", _asset.symbol())) 29 | ) 30 | {} 31 | 32 | /* ========== VIEWS ========== */ 33 | 34 | /// @notice Calculates the total amount of underlying tokens the Vault holds. 35 | /// @return The total amount of underlying tokens the Vault holds. 36 | function totalAssets() public view override returns (uint256) { 37 | return asset.balanceOf(address(this)); 38 | } 39 | 40 | /// @notice Calculates the total amount of underlying tokens the user holds. 41 | /// @return The total amount of underlying tokens the user holds. 42 | function balanceOfUnderlying(address account) public view returns (uint256) { 43 | return convertToAssets(balanceOf[account]); 44 | } 45 | 46 | /* ========== INTERNAL FUNCTIONS ========== */ 47 | 48 | function afterDeposit(uint256 amount, uint256) internal override {} 49 | 50 | function beforeWithdraw(uint256, uint256 shares) internal override {} 51 | } 52 | -------------------------------------------------------------------------------- /contracts/liquidators/CErc20Liquidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | import "../external/compound/ICErc20.sol"; 7 | 8 | import "./IRedemptionStrategy.sol"; 9 | 10 | /** 11 | * @title CErc20Liquidator 12 | * @notice Redeems seized Compound/Cream/Ionic CErc20 cTokens for underlying tokens for use as a step in a liquidation. 13 | * @author David Lucid (https://github.com/davidlucid) 14 | */ 15 | contract CErc20Liquidator is IRedemptionStrategy { 16 | /** 17 | * @notice Redeems custom collateral `token` for an underlying token. 18 | * @param inputToken The input wrapped token to be redeemed for an underlying token. 19 | * @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token. 20 | * @param strategyData The ABI-encoded data to be used in the redemption strategy logic. 21 | * @return outputToken The underlying ERC20 token outputted. 22 | * @return outputAmount The quantity of underlying tokens outputted. 23 | */ 24 | function redeem( 25 | IERC20Upgradeable inputToken, 26 | uint256 inputAmount, 27 | bytes memory strategyData 28 | ) external override returns (IERC20Upgradeable outputToken, uint256 outputAmount) { 29 | // Redeem cErc20 for underlying ERC20 token (and store output as new collateral) 30 | ICErc20Compound cErc20 = ICErc20Compound(address(inputToken)); 31 | uint256 redeemResult = cErc20.redeem(inputAmount); 32 | require(redeemResult == 0, "Error calling redeeming seized cErc20: error code not equal to 0"); 33 | outputToken = IERC20Upgradeable(cErc20.underlying()); 34 | outputAmount = outputToken.balanceOf(address(this)); 35 | } 36 | 37 | function name() public pure returns (string memory) { 38 | return "CErc20Liquidator"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/external/uniswap/IUniswapV3PoolImmutables.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | /// @title Pool state that never changes 5 | /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values 6 | interface IUniswapV3PoolImmutables { 7 | /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface 8 | /// @return The contract address 9 | function factory() external view returns (address); 10 | 11 | /// @notice The first of the two tokens of the pool, sorted by address 12 | /// @return The token contract address 13 | function token0() external view returns (address); 14 | 15 | /// @notice The second of the two tokens of the pool, sorted by address 16 | /// @return The token contract address 17 | function token1() external view returns (address); 18 | 19 | /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 20 | /// @return The fee 21 | function fee() external view returns (uint24); 22 | 23 | /// @notice The pool tick spacing 24 | /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive 25 | /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... 26 | /// This value is an int24 to avoid casting even though it is always positive. 27 | /// @return The tick spacing 28 | function tickSpacing() external view returns (int24); 29 | 30 | /// @notice The maximum amount of position liquidity that can use any tick in the range 31 | /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and 32 | /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool 33 | /// @return The max amount of liquidity per tick 34 | function maxLiquidityPerTick() external view returns (uint128); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/external/balancer/IBalancerPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.8.0; 3 | 4 | import { IBalancerVault } from "./IBalancerVault.sol"; 5 | 6 | interface IBalancerPool { 7 | function getFinalTokens() external view returns (address[] memory); 8 | 9 | function getNormalizedWeight(address token) external view returns (uint256); 10 | 11 | function getNormalizedWeights() external view returns (uint256[] memory); 12 | 13 | function getSwapFee() external view returns (uint256); 14 | 15 | function getNumTokens() external view returns (uint256); 16 | 17 | function getBalance(address token) external view returns (uint256); 18 | 19 | function totalSupply() external view returns (uint256); 20 | 21 | function getPoolId() external view returns (bytes32); 22 | 23 | function getVault() external view returns (IBalancerVault); 24 | 25 | function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external; 26 | 27 | function swapExactAmountIn( 28 | address tokenIn, 29 | uint256 tokenAmountIn, 30 | address tokenOut, 31 | uint256 minAmountOut, 32 | uint256 maxPrice 33 | ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter); 34 | 35 | function swapExactAmountOut( 36 | address tokenIn, 37 | uint256 maxAmountIn, 38 | address tokenOut, 39 | uint256 tokenAmountOut, 40 | uint256 maxPrice 41 | ) external returns (uint256 tokenAmountIn, uint256 spotPriceAfter); 42 | 43 | function joinswapExternAmountIn( 44 | address tokenIn, 45 | uint256 tokenAmountIn, 46 | uint256 minPoolAmountOut 47 | ) external returns (uint256 poolAmountOut); 48 | 49 | function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external; 50 | 51 | function exitswapExternAmountOut( 52 | address tokenOut, 53 | uint256 tokenAmountOut, 54 | uint256 maxPoolAmountIn 55 | ) external returns (uint256 poolAmountIn); 56 | } 57 | -------------------------------------------------------------------------------- /contracts/test/PoolDirectoryTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | import { PoolDirectory } from "../PoolDirectory.sol"; 5 | 6 | import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 7 | 8 | import { BaseTest } from "./config/BaseTest.t.sol"; 9 | 10 | contract PoolDirectoryTest is BaseTest { 11 | PoolDirectory fpd; 12 | 13 | function afterForkSetUp() internal override { 14 | address fpdAddress = ap.getAddress("PoolDirectory"); 15 | fpd = PoolDirectory(fpdAddress); 16 | 17 | // upgrade to the current changes impl 18 | { 19 | PoolDirectory newImpl = new PoolDirectory(); 20 | TransparentUpgradeableProxy proxy = TransparentUpgradeableProxy(payable(fpdAddress)); 21 | bytes32 bytesAtSlot = vm.load(address(proxy), _ADMIN_SLOT); 22 | address admin = address(uint160(uint256(bytesAtSlot))); 23 | vm.prank(admin); 24 | proxy.upgradeTo(address(newImpl)); 25 | } 26 | } 27 | 28 | function testDeprecatePool() public fork(BSC_MAINNET) { 29 | _testDeprecatePool(); 30 | } 31 | 32 | function _testDeprecatePool() internal { 33 | PoolDirectory.Pool[] memory allPools = fpd.getAllPools(); 34 | 35 | PoolDirectory.Pool memory poolToDeprecate; 36 | 37 | // BOMB pool https://app.midascapital.xyz/56/pool/0 38 | uint256 index = 0; 39 | 40 | poolToDeprecate = allPools[index]; 41 | 42 | vm.prank(fpd.owner()); 43 | fpd._deprecatePool(index); 44 | 45 | (, PoolDirectory.Pool[] memory allPoolsAfter) = fpd.getActivePools(); 46 | 47 | bool poolStillThere = false; 48 | for (uint256 i = 0; i < allPoolsAfter.length; i++) { 49 | if (allPoolsAfter[i].comptroller == poolToDeprecate.comptroller) { 50 | poolStillThere = true; 51 | break; 52 | } 53 | } 54 | 55 | assertTrue(!poolStillThere, "deprecated pool is still there"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tasks/irm/admin.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config"; 2 | 3 | import { chainDeployConfig } from "../../chainDeploy"; 4 | import { Address } from "viem"; 5 | 6 | export default task("irm:set", "Set new IRM to ctoken") 7 | .addParam("ctokens", "cToken for which to set the IRM", undefined, types.string) 8 | .addParam("irmAddress", "Irm address to use ", undefined, types.string) 9 | .setAction(async ({ ctokens: _ctokens, irmAddress: _irmAddress }, { viem }) => { 10 | const publicClient = await viem.getPublicClient(); 11 | const ctokens: Address[] = _ctokens.split(","); 12 | 13 | for (const cTokenAddress of ctokens) { 14 | const cToken = await viem.getContractAt("ICErc20", cTokenAddress); 15 | 16 | const tx = await cToken.write._setInterestRateModel([_irmAddress]); 17 | await publicClient.waitForTransactionReceipt({ hash: tx }); 18 | console.log(`Set IRM of ${await cToken.read.underlying()} to ${_irmAddress}`); 19 | } 20 | }); 21 | 22 | task("irm:set-non-owner", "Set new IRM to ctoken") 23 | .addParam("ctokens", "cToken for which to set the IRM", undefined, types.string) 24 | .addParam("irmAddress", "Irm address to use ", undefined, types.string) 25 | .setAction(async ({ ctokens: _ctokens, irmAddress: _irmAddress }, { viem, deployments }) => { 26 | const publicClient = await viem.getPublicClient(); 27 | const feeDistributor = await viem.getContractAt( 28 | "FeeDistributor", 29 | (await deployments.get("FeeDistributor")).address as Address 30 | ); 31 | const sliced = _irmAddress.slice(2); 32 | const cTokens = _ctokens.split(","); 33 | 34 | for (const cToken of cTokens) { 35 | // cToken._setInterestRateModel(irmAddress); 36 | const tx = await feeDistributor.write._callPool([[cToken], [`0xf2b3abbd000000000000000000000000${sliced}`]]); 37 | await publicClient.waitForTransactionReceipt({ hash: tx }); 38 | console.log(`become with ${tx}`); 39 | } 40 | }); 41 | --------------------------------------------------------------------------------