├── .env.example ├── .gitattributes ├── .gitignore ├── .mocharc.js ├── .prettierrc ├── .solcover.adapters.js ├── .solcover.js ├── .solcover.registry.js ├── .solhint.json ├── .solhintignore ├── .vscode ├── launch.json └── settings.json ├── @types └── augmentations.d.ts ├── README.md ├── contracts ├── AdapterRegistry.sol ├── BatchRebalancer.sol ├── NirnVaultFactory.sol ├── OwnableProxyImplementation.sol ├── adapters │ ├── AbstractErc20Adapter.sol │ ├── AbstractEtherAdapter.sol │ ├── aave-v1 │ │ ├── AaveV1Erc20Adapter.sol │ │ └── AaveV1EtherAdapter.sol │ ├── aave-v2 │ │ ├── AaveV2Erc20Adapter.sol │ │ └── AaveV2EtherAdapter.sol │ ├── compound │ │ ├── C1Erc20Adapter.sol │ │ ├── CErc20Adapter.sol │ │ └── CEtherAdapter.sol │ ├── cream │ │ ├── CrErc20Adapter.sol │ │ └── CrEtherAdapter.sol │ ├── dydx │ │ ├── DyDxErc20Adapter.sol │ │ └── DyDxEtherAdapter.sol │ ├── fulcrum │ │ ├── FulcrumErc20Adapter.sol │ │ └── FulcrumEtherAdapter.sol │ ├── fuse │ │ ├── FuseErc20Adapter.sol │ │ └── FuseEtherAdapter.sol │ └── ironbank │ │ ├── CyErc20Adapter.sol │ │ └── CyEtherAdapter.sol ├── interfaces │ ├── AaveV1Interfaces.sol │ ├── AaveV2Interfaces.sol │ ├── CompoundInterfaces.sol │ ├── DyDxInterfaces.sol │ ├── FulcrumInterfaces.sol │ ├── FuseInterfaces.sol │ ├── IAdapterRegistry.sol │ ├── IERC20.sol │ ├── IERC20Metadata.sol │ ├── INirnVault.sol │ ├── IProtocolAdapter.sol │ ├── IProxyManager.sol │ ├── IRewardsSeller.sol │ ├── ITokenAdapter.sol │ └── IWETH.sol ├── libraries │ ├── AdapterHelper.sol │ ├── ArrayHelper.sol │ ├── C1TokenParams.sol │ ├── CTokenParams.sol │ ├── CloneLibrary.sol │ ├── CyTokenParams.sol │ ├── DynamicArrays.sol │ ├── Fraction.sol │ ├── LowGasSafeMath.sol │ ├── MinimalSignedMath.sol │ ├── RayDiv.sol │ ├── RayMul.sol │ ├── RebalanceValidation.sol │ ├── ReserveConfigurationLib.sol │ ├── SafeCast.sol │ ├── SortLibrary.sol │ ├── SymbolHelper.sol │ └── TransferHelper.sol ├── protocols │ ├── AaveV1ProtocolAdapter.sol │ ├── AaveV2ProtocolAdapter.sol │ ├── AbstractProtocolAdapter.sol │ ├── CompoundProtocolAdapter.sol │ ├── CreamProtocolAdapter.sol │ ├── DyDxProtocolAdapter.sol │ ├── FulcrumProtocolAdapter.sol │ ├── FusePoolAdapter.sol │ └── IronBankProtocolAdapter.sol ├── test │ ├── CallForwarder.sol │ ├── IIndexPool.sol │ ├── IUniswapV2Pair.sol │ ├── SendEth.sol │ ├── TestAdapter.sol │ ├── TestAdapterHelper.sol │ ├── TestArrayHelper.sol │ ├── TestBatcherRevert.sol │ ├── TestComptrollerLens.sol │ ├── TestDynamicArrays.sol │ ├── TestERC20.sol │ ├── TestFactory.sol │ ├── TestMapper.sol │ ├── TestNirnVault.sol │ ├── TestProtocolAdapter.sol │ ├── TestProxyManager.sol │ ├── TestRewardsSeller.sol │ └── TestVault.sol └── vaults │ ├── ERC20.sol │ ├── NirnVault.sol │ └── NirnVaultBase.sol ├── deploy ├── aave-v1.deploy.js ├── aave-v2.deploy.js ├── batcher.deploy.js ├── compound.deploy.js ├── cream.deploy.js ├── factory.deploy.js ├── fulcrum.deploy.js ├── iron-bank.deploy.js ├── registry.deploy.js └── vault.deploy.js ├── deployments ├── kovan │ └── .chainId ├── mainnet │ ├── .chainId │ ├── AaveV1ProtocolAdapter.json │ ├── AaveV2ProtocolAdapter.json │ ├── AdapterRegistry.json │ ├── BatchRebalancer.json │ ├── CompoundProtocolAdapter.json │ ├── CreamProtocolAdapter.json │ ├── FulcrumProtocolAdapter.json │ ├── IronBankProtocolAdapter.json │ ├── NirnVault.json │ ├── NirnVaultFactory.json │ └── solcInputs │ │ ├── 06d89cdc5f2bce9f6e0e1e7b7ef4e3a6.json │ │ ├── 15c5eb146eed5e149f0fa39b934f8cde.json │ │ ├── 8ef4372f6a42b1fc2976b04a41fd0d3e.json │ │ ├── 9bffdedaeba4603e348d0b7b645d9a82.json │ │ └── d9f57fa73085ba5fabd60a40ef0d5c65.json └── rinkeby │ └── .chainId ├── docs └── Token-Adapters.md ├── hardhat.config.ts ├── package.json ├── test ├── AdapterRegistry.spec.ts ├── BatchRebalancer.spec.ts ├── Erc20AdapterBehavior.spec.ts ├── EtherAdapterBehavior.spec.ts ├── NirnVault.spec.ts ├── NirnVaultFactory.spec.ts ├── TestDeploy.ts ├── TestMapper.ts ├── adapters │ ├── aave-v1 │ │ ├── AaveV1Erc20Adapter.spec.ts │ │ └── AaveV1EtherAdapter.spec.ts │ ├── aave-v2 │ │ ├── AaveV2Erc20Adapter.spec.ts │ │ └── AaveV2EtherAdapter.spec.ts │ ├── compound │ │ ├── C1Erc20Adapter.spec.ts │ │ ├── CErc20Adapter.spec.ts │ │ └── CEtherAdapter.spec.ts │ ├── cream │ │ ├── CrErc20Adapter.spec.ts │ │ └── CrEtherAdapter.spec.ts │ ├── fulcrum │ │ ├── FulcrumErc20Adapter.spec.ts │ │ └── FulcrumEtherAdapter.spec.ts │ └── ironbank │ │ ├── CyErc20Adapter.spec.ts │ │ └── CyEtherAdapter.spec.ts ├── libraries │ ├── AdapterHelper.spec.ts │ ├── ArrayHelper.spec.ts │ └── DynamicArrays.spec.ts └── shared │ ├── coder.ts │ ├── conversion.ts │ ├── fixtures.ts │ ├── index.ts │ ├── time.ts │ ├── tokens.ts │ └── utils.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY= 2 | ALCHEMY_API_KEY= 3 | INFURA_API_KEY= 4 | RINKEBY_PVT_KEY= 5 | MAINNET_PVT_KEY= 6 | KOVAN_PVT_KEY= -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts/ 2 | cache/ 3 | node_modules/ 4 | typechain/ 5 | .env 6 | coverage/ 7 | coverage.json -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | require: "hardhat/register", 3 | timeout: 20000, 4 | }; 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "tabWidth": 2 6 | } 7 | -------------------------------------------------------------------------------- /.solcover.adapters.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | module.exports = { 4 | skipFiles: ["interfaces/", "protocols/", "test/", "libraries/"], 5 | // Options for forking mainnet 6 | providerOptions: { 7 | host: "localhost", 8 | port: 8545, 9 | network_id: "1234567890", 10 | networkCheckTimeout: 60000, 11 | fork: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`, 12 | fork_block_number: 12667185 13 | } 14 | } -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | norpc: true, 3 | skipFiles: ["mocks/", "interfaces/", "test/"], 4 | } -------------------------------------------------------------------------------- /.solcover.registry.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | norpc: false, 3 | skipFiles: ["interfaces/", "test/", "protocols/", "adapters/", "libraries/", "vaults/"], 4 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "avoid-suicide": "error", 6 | "avoid-sha3": "warn", 7 | "code-complexity": ["warn", 7], 8 | "compiler-version": "off", 9 | "max-states-count": ["error", 18], 10 | "max-line-length": ["warn", 145], 11 | "not-rely-on-time": "warn", 12 | "quotes": ["warn", "double"], 13 | "prettier/prettier": "off", 14 | "indent": ["error", 2] 15 | } 16 | } -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | # folders 2 | .yarn/ 3 | build/ 4 | dist/ 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "hardhat node", 8 | "skipFiles": ["/**"], 9 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/hardhat", 10 | "args": ["node"], 11 | "cwd": "${workspaceFolder}" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll": true 6 | }, 7 | "mochaExplorer.files": "test/**/*.{j,t}s", 8 | "mochaExplorer.fullTrace": true, 9 | "mochaExplorer.exit": true, 10 | "solidity.linter": "solhint", 11 | "solidity.enableLocalNodeCompiler": false, 12 | "solidity.compileUsingRemoteVersion": "v0.7.6+commit.7338295f", 13 | "solidity.packageDefaultDependenciesContractsDirectory": "", 14 | "solidity.enabledAsYouTypeCompilationErrorCheck": true, 15 | "solidity.validationDelay": 1500, 16 | "solidity.packageDefaultDependenciesDirectory": "node_modules", 17 | "solidity-va.deco.statevars": true, 18 | "solidity-va.preview.markdown": true, 19 | } 20 | -------------------------------------------------------------------------------- /@types/augmentations.d.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "@ethersproject/wallet"; 2 | import { BigNumber } from "ethers"; 3 | import { IERC20 } from "../typechain/IERC20"; 4 | import { IErc20Adapter } from "../typechain/IErc20Adapter"; 5 | 6 | export interface ConvertHelper { 7 | liquidityHolder(token: IERC20): Promise; 8 | toWrapped(token: IERC20, amount: BigNumber, withdrawUnderlying?: boolean): Promise; 9 | toUnderlying(token: IERC20, amount: BigNumber): Promise; 10 | getRewardsTokenAndAPR?: (adapter: IErc20Adapter) => Promise<[string, BigNumber]> 11 | protocolName: string; 12 | symbolPrefix: string; 13 | useWrappedEther?: boolean; 14 | } 15 | 16 | declare module "mocha" { 17 | interface Context { 18 | // Util functions 19 | getImplementation: () => Promise 20 | getExpectedLiquidity: () => Promise 21 | initialize: (adapter: IErc20Adapter, underlying: IERC20, token: IERC20) => Promise 22 | resetTests: (deposit?: boolean) => Promise 23 | toUnderlying: (amount: BigNumber) => Promise 24 | toWrapped: (amount: BigNumber, withdrawUnderlying?: boolean) => Promise 25 | getTokenAmount: (n: number) => BigNumber 26 | getTokens: (n: number) => Promise 27 | 28 | // Context data 29 | symbol: string; 30 | decimals: number 31 | /** @param depositSenderWrapped Address that the minted tokens are transferred from. */ 32 | depositSenderWrapped: string 33 | /** @param depositReceiverWrapped Address that receives the minted tokens. */ 34 | depositReceiverWrapped: string 35 | /** @param withdrawalSenderUnderlying Address that sends the redeemed tokens for a withdrawal. */ 36 | withdrawalSenderUnderlying: string 37 | wallet: Wallet 38 | wallet1: Wallet 39 | wallet2: Wallet 40 | amountDeposited: BigNumber 41 | 42 | // Contracts 43 | adapter: IErc20Adapter 44 | wrapper: IERC20 45 | underlying: IERC20 46 | converter: ConvertHelper 47 | } 48 | } -------------------------------------------------------------------------------- /contracts/BatchRebalancer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "./interfaces/INirnVault.sol"; 6 | import "./interfaces/IAdapterRegistry.sol"; 7 | 8 | 9 | contract BatchRebalancer { 10 | bytes4 internal constant rebalance = INirnVault.rebalance.selector; 11 | bytes4 internal constant rebalanceWithNewWeights = INirnVault.rebalanceWithNewWeights.selector; 12 | bytes4 internal constant rebalanceWithNewAdapters = INirnVault.rebalanceWithNewAdapters.selector; 13 | 14 | IAdapterRegistry public immutable registry; 15 | 16 | constructor(address _registry) { 17 | registry = IAdapterRegistry(_registry); 18 | } 19 | 20 | function revertWithReturnData(bytes memory _returnData) internal pure { 21 | // Taken from BoringCrypto 22 | // If the _res length is less than 68, then the transaction failed silently (without a revert message) 23 | if (_returnData.length < 68) revert("silent revert"); 24 | 25 | assembly { 26 | // Slice the sighash. 27 | _returnData := add(_returnData, 0x04) 28 | } 29 | revert(abi.decode(_returnData, (string))); // All that remains is the revert string 30 | } 31 | 32 | function batchExecuteRebalance(address[] calldata vaults, bytes[] calldata calldatas) external { 33 | require(msg.sender == tx.origin, "!EOA"); 34 | uint256 len = vaults.length; 35 | require(calldatas.length == len, "bad lengths"); 36 | for (uint256 i; i < len; i++) { 37 | INirnVault vault = INirnVault(vaults[i]); 38 | require( 39 | registry.vaultsByUnderlying(vault.underlying()) == address(vault), 40 | "bad vault" 41 | ); 42 | bytes memory data = calldatas[i]; 43 | bytes4 sig; 44 | assembly { sig := mload(add(data, 32)) } 45 | require( 46 | sig == rebalance || 47 | sig == rebalanceWithNewWeights || 48 | sig == rebalanceWithNewAdapters, 49 | "fn not allowed" 50 | ); 51 | (bool success, bytes memory returnData) = address(vault).call(data); 52 | if (!success) revertWithReturnData(returnData); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /contracts/NirnVaultFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 6 | import "./interfaces/IProxyManager.sol"; 7 | import "./interfaces/IAdapterRegistry.sol"; 8 | import "./interfaces/INirnVault.sol"; 9 | import "./libraries/ArrayHelper.sol"; 10 | 11 | 12 | contract NirnVaultFactory is Ownable() { 13 | using EnumerableSet for EnumerableSet.AddressSet; 14 | using ArrayHelper for EnumerableSet.AddressSet; 15 | 16 | /* ========== Events ========== */ 17 | 18 | event TokenApproved(address token); 19 | 20 | event SetDefaultRewardsSeller(address defaultRewardsSeller); 21 | 22 | event SetDefaultFeeRecipient(address defaultFeeRecipient); 23 | 24 | /* ========== Constants ========== */ 25 | 26 | address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 27 | uint256 public constant minimumAdapters = 2; 28 | bytes32 public constant erc20VaultImplementationId = keccak256("NirnVaultV1.sol"); 29 | bytes32 public constant ethVaultImplementationId = keccak256("EthNirnVaultV1.sol"); 30 | IProxyManager public immutable proxyManager; 31 | IAdapterRegistry public immutable registry; 32 | 33 | /* ========== Storage ========== */ 34 | 35 | EnumerableSet.AddressSet internal _approvedTokens; 36 | address public defaultFeeRecipient; 37 | address public defaultRewardsSeller; 38 | 39 | /* ========== Constructor ========== */ 40 | 41 | constructor(address _proxyManager, address _registry) { 42 | proxyManager = IProxyManager(_proxyManager); 43 | registry = IAdapterRegistry(_registry); 44 | } 45 | 46 | /* ========== Configuration ========== */ 47 | 48 | function approveToken(address token) external onlyOwner { 49 | require(!_approvedTokens.contains(token), "already approved"); 50 | require(token != address(0), "null address"); 51 | _approvedTokens.add(token); 52 | emit TokenApproved(token); 53 | } 54 | 55 | function setDefaultRewardsSeller(address _defaultRewardsSeller) external onlyOwner { 56 | require(_defaultRewardsSeller != address(0), "null address"); 57 | defaultRewardsSeller = _defaultRewardsSeller; 58 | emit SetDefaultRewardsSeller(_defaultRewardsSeller); 59 | } 60 | 61 | function setDefaultFeeRecipient(address _defaultFeeRecipient) external onlyOwner { 62 | require(_defaultFeeRecipient != address(0), "null address"); 63 | defaultFeeRecipient = _defaultFeeRecipient; 64 | emit SetDefaultFeeRecipient(_defaultFeeRecipient); 65 | } 66 | 67 | /* ========== Queries ========== */ 68 | 69 | function isTokenApproved(address token) external view returns (bool) { 70 | return _approvedTokens.contains(token); 71 | } 72 | 73 | function getApprovedTokens() external view returns (address[] memory approvedTokens) { 74 | approvedTokens = _approvedTokens.toArray(); 75 | } 76 | 77 | function computeVaultAddress(address underlying) external view returns (address vault) { 78 | bytes32 implementationId = getImplementationId(underlying); 79 | bytes32 salt = keccak256(abi.encode(underlying)); 80 | vault = proxyManager.computeProxyAddressManyToOne(address(this), implementationId, salt); 81 | } 82 | 83 | function getImplementationId(address underlying) internal pure returns (bytes32 implementationId) { 84 | return underlying == weth 85 | ? ethVaultImplementationId 86 | : erc20VaultImplementationId; 87 | } 88 | 89 | /* ========== Actions ========== */ 90 | 91 | function deployVault(address underlying) external { 92 | require(_approvedTokens.contains(underlying), "!approved"); 93 | require(registry.getAdaptersCount(underlying) >= minimumAdapters, "insufficient adapters"); 94 | address _defaultFeeRecipient = defaultFeeRecipient; 95 | require(_defaultFeeRecipient != address(0), "null default"); 96 | bytes32 implementationId = getImplementationId(underlying); 97 | bytes32 salt = keccak256(abi.encode(underlying)); 98 | address vault = proxyManager.deployProxyManyToOne(implementationId, salt); 99 | INirnVault(vault).initialize(underlying, defaultRewardsSeller, _defaultFeeRecipient, owner()); 100 | registry.addVault(vault); 101 | } 102 | } -------------------------------------------------------------------------------- /contracts/OwnableProxyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | 5 | /** 6 | * @dev Contract module which provides a basic access control mechanism, where 7 | * there is an account (an owner) that can be granted exclusive access to 8 | * specific functions. 9 | * 10 | * This is a modified implementation of OpenZeppelin's Ownable.sol. 11 | * The modifications allow the contract to be inherited by a proxy's logic contract. 12 | * Any owner-only functions on the base implementation will be unusable. 13 | * 14 | * By default, the owner account will be a null address which can be set by invoking 15 | * a function with the `initializer` modifier. The owner can later be changed with 16 | * {transferOwnership}. 17 | * 18 | * This module is used through inheritance. It will make available the modifier 19 | * `onlyOwner`, which can be applied to your functions to restrict their use to 20 | * the owner. It also makes available the `initializer` modifier, which will set 21 | * the owner to `msg.sender` the first time the function is invoked, and will 22 | * revert if the owner has already been set. 23 | * 24 | * Note: This contract should only be inherited by proxy implementation contracts 25 | * where the implementation will only ever be used as the logic address for proxies. 26 | * The constructor permanently locks the owner of the implementation contract, but the 27 | * owner of the proxies can be configured by the first caller. 28 | */ 29 | contract OwnableProxyImplementation { 30 | address private _owner; 31 | 32 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 33 | 34 | constructor() { 35 | _owner = address(1); 36 | emit OwnershipTransferred(address(0), address(1)); 37 | } 38 | 39 | /** 40 | * @dev Returns the address of the current owner. 41 | */ 42 | function owner() public view returns (address) { 43 | return _owner; 44 | } 45 | 46 | /** 47 | * @dev Throws if called by any account other than the owner. 48 | */ 49 | modifier onlyOwner { 50 | require(_owner == msg.sender, "!owner"); 51 | _; 52 | } 53 | 54 | /** 55 | * @dev Initializes the contract setting `initialOwner` as the initial owner. 56 | * Reverts if owner has already been set. 57 | */ 58 | modifier initializer(address initialOwner) { 59 | require(_owner == address(0), "already initialized"); 60 | _owner = initialOwner; 61 | emit OwnershipTransferred(address(0), initialOwner); 62 | _; 63 | } 64 | 65 | /** 66 | * @dev Leaves the contract without owner. It will not be possible to call 67 | * `onlyOwner` functions anymore. Can only be called by the current owner. 68 | * 69 | * NOTE: Renouncing ownership will leave the contract without an owner, 70 | * thereby removing any functionality that is only available to the owner. 71 | */ 72 | function renounceOwnership() public virtual onlyOwner { 73 | // Modified from OZ contract - sets owner to address(1) to prevent 74 | // the initializer from being invoked after ownership is revoked. 75 | emit OwnershipTransferred(_owner, address(1)); 76 | _owner = address(1); 77 | } 78 | 79 | /** 80 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 81 | * Can only be called by the current owner. 82 | */ 83 | function transferOwnership(address newOwner) public virtual onlyOwner { 84 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 85 | emit OwnershipTransferred(_owner, newOwner); 86 | _owner = newOwner; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/adapters/AbstractErc20Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../libraries/TransferHelper.sol"; 5 | import "../libraries/SymbolHelper.sol"; 6 | import "../interfaces/IERC20Metadata.sol"; 7 | import "../interfaces/IERC20.sol"; 8 | 9 | 10 | abstract contract AbstractErc20Adapter { 11 | using SymbolHelper for address; 12 | using TransferHelper for address; 13 | 14 | /* ========== Storage ========== */ 15 | 16 | address public underlying; 17 | address public token; 18 | 19 | /* ========== Initializer ========== */ 20 | 21 | function initialize(address _underlying, address _token) public virtual { 22 | require(underlying == address(0) && token == address(0), "initialized"); 23 | require(_underlying != address(0) && _token != address(0), "bad address"); 24 | underlying = _underlying; 25 | token = _token; 26 | _approve(); 27 | } 28 | 29 | /* ========== Internal Queries ========== */ 30 | 31 | function _protocolName() internal view virtual returns (string memory); 32 | 33 | /* ========== Metadata ========== */ 34 | 35 | function name() external view virtual returns (string memory) { 36 | return string(abi.encodePacked( 37 | bytes(_protocolName()), 38 | " ", 39 | bytes(underlying.getSymbol()), 40 | " Adapter" 41 | )); 42 | } 43 | 44 | function availableLiquidity() public view virtual returns (uint256); 45 | 46 | /* ========== Conversion Queries ========== */ 47 | 48 | function toUnderlyingAmount(uint256 tokenAmount) external view virtual returns (uint256); 49 | 50 | function toWrappedAmount(uint256 underlyingAmount) external view virtual returns (uint256); 51 | 52 | /* ========== Performance Queries ========== */ 53 | 54 | function getAPR() public view virtual returns (uint256); 55 | 56 | function getHypotheticalAPR(int256 _deposit) external view virtual returns (uint256); 57 | 58 | function getRevenueBreakdown() 59 | external 60 | view 61 | virtual 62 | returns ( 63 | address[] memory assets, 64 | uint256[] memory aprs 65 | ) 66 | { 67 | assets = new address[](1); 68 | aprs = new uint256[](1); 69 | assets[0] = underlying; 70 | aprs[0] = getAPR(); 71 | } 72 | 73 | /* ========== Caller Balance Queries ========== */ 74 | 75 | function balanceWrapped() public view virtual returns (uint256) { 76 | return IERC20(token).balanceOf(msg.sender); 77 | } 78 | 79 | function balanceUnderlying() external view virtual returns (uint256); 80 | 81 | /* ========== Token Actions ========== */ 82 | 83 | function deposit(uint256 amountUnderlying) external virtual returns (uint256 amountMinted) { 84 | require(amountUnderlying > 0, "deposit 0"); 85 | underlying.safeTransferFrom(msg.sender, address(this), amountUnderlying); 86 | amountMinted = _mint(amountUnderlying); 87 | token.safeTransfer(msg.sender, amountMinted); 88 | } 89 | 90 | function withdraw(uint256 amountToken) public virtual returns (uint256 amountReceived) { 91 | require(amountToken > 0, "withdraw 0"); 92 | token.safeTransferFrom(msg.sender, address(this), amountToken); 93 | amountReceived = _burn(amountToken); 94 | underlying.safeTransfer(msg.sender, amountReceived); 95 | } 96 | 97 | function withdrawAll() public virtual returns (uint256 amountReceived) { 98 | return withdraw(balanceWrapped()); 99 | } 100 | 101 | function withdrawUnderlying(uint256 amountUnderlying) external virtual returns (uint256 amountBurned) { 102 | require(amountUnderlying > 0, "withdraw 0"); 103 | amountBurned = _burnUnderlying(amountUnderlying); 104 | underlying.safeTransfer(msg.sender, amountUnderlying); 105 | } 106 | 107 | function withdrawUnderlyingUpTo(uint256 amountUnderlying) external virtual returns (uint256 amountReceived) { 108 | require(amountUnderlying > 0, "withdraw 0"); 109 | uint256 amountAvailable = availableLiquidity(); 110 | amountReceived = amountAvailable < amountUnderlying ? amountAvailable : amountUnderlying; 111 | _burnUnderlying(amountReceived); 112 | underlying.safeTransfer(msg.sender, amountReceived); 113 | } 114 | 115 | /* ========== Internal Actions ========== */ 116 | 117 | function _approve() internal virtual; 118 | 119 | /** 120 | * @dev Deposit `amountUnderlying` into the wrapper and return the amount of wrapped tokens received. 121 | * Note: 122 | * - Called after the underlying token is transferred. 123 | * - Should not transfer minted token to caller. 124 | */ 125 | function _mint(uint256 amountUnderlying) internal virtual returns (uint256 amountMinted); 126 | 127 | /** 128 | * @dev Burn `amountToken` of `token` and return the amount of `underlying` received. 129 | * Note: 130 | * - Called after the wrapper token is transferred. 131 | * - Should not transfer underlying token to caller. 132 | */ 133 | function _burn(uint256 amountToken) internal virtual returns (uint256 amountReceived); 134 | 135 | /** 136 | * @dev Redeem `amountUnderlying` of the underlying token and return the amount of wrapper tokens burned. 137 | * Note: 138 | * - Should transfer the wrapper token from the caller. 139 | * - Should not transfer underlying token to caller. 140 | */ 141 | function _burnUnderlying(uint256 amountUnderlying) internal virtual returns (uint256 amountBurned); 142 | } -------------------------------------------------------------------------------- /contracts/adapters/AbstractEtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../libraries/TransferHelper.sol"; 5 | import "../interfaces/IERC20Metadata.sol"; 6 | import "../interfaces/IERC20.sol"; 7 | import "./AbstractErc20Adapter.sol"; 8 | 9 | 10 | abstract contract AbstractEtherAdapter is AbstractErc20Adapter { 11 | using TransferHelper for address; 12 | 13 | /* ========== Metadata ========== */ 14 | 15 | function name() external view virtual override returns (string memory) { 16 | return string(abi.encodePacked( 17 | bytes(_protocolName()), 18 | " ETH Adapter" 19 | )); 20 | } 21 | 22 | /* ========== Fallback ========== */ 23 | 24 | fallback() external payable { return; } 25 | 26 | receive() external payable { return; } 27 | 28 | /* ========== Token Actions ========== */ 29 | 30 | function deposit(uint256 amountUnderlying) 31 | external 32 | virtual 33 | override 34 | returns (uint256 amountMinted) 35 | { 36 | underlying.safeTransferFrom(msg.sender, address(this), amountUnderlying); 37 | _afterReceiveWETH(amountUnderlying); 38 | amountMinted = _mint(amountUnderlying); 39 | token.safeTransfer(msg.sender, amountMinted); 40 | } 41 | 42 | function depositETH() external virtual payable returns (uint256 amountMinted) { 43 | _afterReceiveETH(msg.value); 44 | amountMinted = _mint(msg.value); 45 | token.safeTransfer(msg.sender, amountMinted); 46 | } 47 | 48 | function withdraw(uint256 amountToken) public virtual override returns (uint256 amountReceived) { 49 | token.safeTransferFrom(msg.sender, address(this), amountToken); 50 | amountReceived = _burn(amountToken); 51 | _beforeSendWETH(amountReceived); 52 | underlying.safeTransfer(msg.sender, amountReceived); 53 | } 54 | 55 | function withdrawAsETH(uint256 amountToken) public virtual returns (uint256 amountReceived) { 56 | token.safeTransferFrom(msg.sender, address(this), amountToken); 57 | amountReceived = _burn(amountToken); 58 | _beforeSendETH(amountReceived); 59 | address(msg.sender).safeTransferETH(amountReceived); 60 | } 61 | 62 | function withdrawAllAsETH() public virtual returns (uint256 amountReceived) { 63 | return withdrawAsETH(balanceWrapped()); 64 | } 65 | 66 | function withdrawUnderlying(uint256 amountUnderlying) external virtual override returns (uint256 amountBurned) { 67 | amountBurned = _burnUnderlying(amountUnderlying); 68 | _beforeSendWETH(amountUnderlying); 69 | underlying.safeTransfer(msg.sender, amountUnderlying); 70 | } 71 | 72 | function withdrawUnderlyingAsETH(uint256 amountUnderlying) external virtual returns (uint256 amountBurned) { 73 | amountBurned = _burnUnderlying(amountUnderlying); 74 | _beforeSendETH(amountUnderlying); 75 | address(msg.sender).safeTransferETH(amountUnderlying); 76 | } 77 | 78 | function withdrawUnderlyingUpTo(uint256 amountUnderlying) external virtual override returns (uint256 amountReceived) { 79 | require(amountUnderlying > 0, "withdraw 0"); 80 | uint256 amountAvailable = availableLiquidity(); 81 | amountReceived = amountAvailable < amountUnderlying ? amountAvailable : amountUnderlying; 82 | _burnUnderlying(amountReceived); 83 | _beforeSendWETH(amountReceived); 84 | underlying.safeTransfer(msg.sender, amountReceived); 85 | } 86 | 87 | /* ========== Internal Ether Handlers ========== */ 88 | 89 | // Convert to WETH if contract takes WETH 90 | function _afterReceiveETH(uint256 amount) internal virtual; 91 | 92 | // Convert to WETH if contract takes ETH 93 | function _afterReceiveWETH(uint256 amount) internal virtual; 94 | 95 | // Convert to ETH if contract returns WETH 96 | function _beforeSendETH(uint256 amount) internal virtual; 97 | 98 | // Convert to WETH if contract returns ETH 99 | function _beforeSendWETH(uint256 amount) internal virtual; 100 | } -------------------------------------------------------------------------------- /contracts/adapters/aave-v1/AaveV1Erc20Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractErc20Adapter.sol"; 5 | import "../../interfaces/AaveV1Interfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../libraries/LowGasSafeMath.sol"; 8 | import "../../libraries/TransferHelper.sol"; 9 | import "../../libraries/MinimalSignedMath.sol"; 10 | import "../../libraries/RayDiv.sol"; 11 | 12 | 13 | contract AaveV1Erc20Adapter is AbstractErc20Adapter { 14 | using MinimalSignedMath for uint256; 15 | using LowGasSafeMath for uint256; 16 | using RayDiv for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Constants ========== */ 20 | 21 | // ILendingPoolAddressesProvider public immutable aave; 22 | ILendingPool public immutable pool; 23 | ILendingPoolCore public immutable core; 24 | 25 | /* ========== Constructor ========== */ 26 | 27 | constructor(ILendingPoolAddressesProvider _aave) { 28 | pool = _aave.getLendingPool(); 29 | core = _aave.getLendingPoolCore(); 30 | } 31 | 32 | /* ========== Internal Queries ========== */ 33 | 34 | function _protocolName() internal view virtual override returns (string memory) { 35 | return "Aave V1"; 36 | } 37 | 38 | /* ========== Metadata ========== */ 39 | 40 | function availableLiquidity() public view override returns (uint256) { 41 | return IERC20(underlying).balanceOf(address(core)); 42 | } 43 | 44 | /* ========== Conversion Queries ========== */ 45 | 46 | function toUnderlyingAmount(uint256 tokenAmount) external view virtual override returns (uint256) { 47 | return tokenAmount; 48 | } 49 | 50 | function toWrappedAmount(uint256 underlyingAmount) external view virtual override returns (uint256) { 51 | return underlyingAmount; 52 | } 53 | 54 | /* ========== Performance Queries ========== */ 55 | 56 | function getAPR() public view virtual override returns (uint256 apr) { 57 | apr = core.getReserveCurrentLiquidityRate(underlying) / 1e9; 58 | } 59 | 60 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256 apr) { 61 | address reserve = underlying; 62 | (uint256 liquidityRate,,) = core.getReserveInterestRateStrategyAddress(reserve).calculateInterestRates( 63 | reserve, 64 | core.getReserveAvailableLiquidity(reserve).add(liquidityDelta), 65 | core.getReserveTotalBorrowsStable(reserve), 66 | core.getReserveTotalBorrowsVariable(reserve), 67 | core.getReserveCurrentAverageStableBorrowRate(reserve) 68 | ); 69 | return liquidityRate / 1e9; 70 | } 71 | 72 | /* ========== Caller Balance Queries ========== */ 73 | 74 | function balanceUnderlying() external view virtual override returns (uint256) { 75 | return IERC20(token).balanceOf(msg.sender); 76 | } 77 | 78 | /* ========== Internal Actions ========== */ 79 | 80 | function _approve() internal virtual override { 81 | underlying.safeApproveMax(address(core)); 82 | } 83 | 84 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 85 | amountMinted = amountUnderlying; 86 | pool.deposit(underlying, amountUnderlying, 0); 87 | } 88 | 89 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 90 | amountReceived = amountToken; 91 | IAToken(token).redeem(amountToken); 92 | } 93 | 94 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 95 | token.safeTransferFrom(msg.sender, address(this), amountUnderlying); 96 | amountBurned = amountUnderlying; 97 | IAToken(token).redeem(amountUnderlying); 98 | } 99 | } -------------------------------------------------------------------------------- /contracts/adapters/aave-v1/AaveV1EtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractEtherAdapter.sol"; 5 | import "../../interfaces/AaveV1Interfaces.sol"; 6 | import "../../interfaces/IWETH.sol"; 7 | import "../../interfaces/IERC20.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import "../../libraries/RayDiv.sol"; 12 | 13 | 14 | contract AaveV1EtherAdapter is AbstractEtherAdapter { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using RayDiv for uint256; 18 | using TransferHelper for address; 19 | 20 | /* ========== Constants ========== */ 21 | 22 | address public constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 23 | 24 | /* ========== Constants ========== */ 25 | 26 | ILendingPool public immutable pool; 27 | ILendingPoolCore public immutable core; 28 | 29 | /* ========== Constructor ========== */ 30 | 31 | constructor(ILendingPoolAddressesProvider _aave) { 32 | pool = _aave.getLendingPool(); 33 | core = _aave.getLendingPoolCore(); 34 | } 35 | 36 | /* ========== Internal Queries ========== */ 37 | 38 | function _protocolName() internal view virtual override returns (string memory) { 39 | return "Aave V1"; 40 | } 41 | 42 | /* ========== Metadata ========== */ 43 | 44 | function availableLiquidity() public view override returns (uint256) { 45 | return address(core).balance; 46 | } 47 | 48 | /* ========== Conversion Queries ========== */ 49 | 50 | function toUnderlyingAmount(uint256 tokenAmount) external view virtual override returns (uint256) { 51 | return tokenAmount; 52 | } 53 | 54 | function toWrappedAmount(uint256 underlyingAmount) external view virtual override returns (uint256) { 55 | return underlyingAmount; 56 | } 57 | 58 | /* ========== Performance Queries ========== */ 59 | 60 | function getAPR() public view virtual override returns (uint256 apr) { 61 | apr = core.getReserveCurrentLiquidityRate(ETH_RESERVE_ADDRESS) / 1e9; 62 | } 63 | 64 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256 apr) { 65 | (uint256 liquidityRate,,) = core.getReserveInterestRateStrategyAddress(ETH_RESERVE_ADDRESS).calculateInterestRates( 66 | ETH_RESERVE_ADDRESS, 67 | core.getReserveAvailableLiquidity(ETH_RESERVE_ADDRESS).add(liquidityDelta), 68 | core.getReserveTotalBorrowsStable(ETH_RESERVE_ADDRESS), 69 | core.getReserveTotalBorrowsVariable(ETH_RESERVE_ADDRESS), 70 | core.getReserveCurrentAverageStableBorrowRate(ETH_RESERVE_ADDRESS) 71 | ); 72 | return liquidityRate / 1e9; 73 | } 74 | 75 | /* ========== Caller Balance Queries ========== */ 76 | 77 | function balanceUnderlying() external view virtual override returns (uint256) { 78 | return IERC20(token).balanceOf(msg.sender); 79 | } 80 | 81 | /* ========== Internal Ether Handlers ========== */ 82 | 83 | // Convert to WETH if contract takes WETH 84 | function _afterReceiveETH(uint256 amount) internal virtual override {} 85 | 86 | // Convert to WETH if contract takes ETH 87 | function _afterReceiveWETH(uint256 amount) internal virtual override { 88 | IWETH(underlying).withdraw(amount); 89 | } 90 | 91 | // Convert to ETH if contract returns WETH 92 | function _beforeSendETH(uint256 amount) internal virtual override {} 93 | 94 | // Convert to WETH if contract returns ETH 95 | function _beforeSendWETH(uint256 amount) internal virtual override { 96 | IWETH(underlying).deposit{value: amount}(); 97 | } 98 | 99 | /* ========== Internal Actions ========== */ 100 | 101 | function _approve() internal virtual override {} 102 | 103 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 104 | amountMinted = amountUnderlying; 105 | pool.deposit{value: amountUnderlying}(ETH_RESERVE_ADDRESS, amountUnderlying, 0); 106 | } 107 | 108 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 109 | amountReceived = amountToken; 110 | IAToken(token).redeem(amountToken); 111 | } 112 | 113 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 114 | token.safeTransferFrom(msg.sender, address(this), amountUnderlying); 115 | amountBurned = amountUnderlying; 116 | IAToken(token).redeem(amountUnderlying); 117 | } 118 | } -------------------------------------------------------------------------------- /contracts/adapters/cream/CrErc20Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractErc20Adapter.sol"; 5 | import "../../interfaces/CompoundInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CTokenParams } from "../../libraries/CTokenParams.sol"; 12 | 13 | 14 | contract CrErc20Adapter is AbstractErc20Adapter() { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Internal Queries ========== */ 20 | 21 | function _protocolName() internal view virtual override returns (string memory) { 22 | return "Cream"; 23 | } 24 | 25 | /* ========== Metadata ========== */ 26 | 27 | function availableLiquidity() public view override returns (uint256) { 28 | return IERC20(underlying).balanceOf(token); 29 | } 30 | 31 | /* ========== Conversion Queries ========== */ 32 | 33 | function toUnderlyingAmount(uint256 tokenAmount) public view override returns (uint256) { 34 | return ( 35 | tokenAmount 36 | .mul(CTokenParams.currentExchangeRate(token)) 37 | / uint256(1e18) 38 | ); 39 | } 40 | 41 | function toWrappedAmount(uint256 underlyingAmount) public view override returns (uint256) { 42 | return underlyingAmount 43 | .mul(1e18) 44 | / CTokenParams.currentExchangeRate(token); 45 | } 46 | 47 | /* ========== Performance Queries ========== */ 48 | 49 | function getAPR() public view virtual override returns (uint256) { 50 | return ICToken(token).supplyRatePerBlock().mul(2102400); 51 | } 52 | 53 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 54 | ICToken cToken = ICToken(token); 55 | ( 56 | address model, 57 | uint256 cashPrior, 58 | uint256 borrowsPrior, 59 | uint256 reservesPrior, 60 | uint256 reserveFactorMantissa 61 | ) = CTokenParams.getInterestRateParameters(address(cToken)); 62 | return IInterestRateModel(model).getSupplyRate( 63 | cashPrior.add(liquidityDelta), 64 | borrowsPrior, 65 | reservesPrior, 66 | reserveFactorMantissa 67 | ).mul(2102400); 68 | } 69 | 70 | /* ========== Caller Balance Queries ========== */ 71 | 72 | function balanceUnderlying() external view virtual override returns (uint256) { 73 | return toUnderlyingAmount(ICToken(token).balanceOf(msg.sender)); 74 | } 75 | 76 | /* ========== Internal Actions ========== */ 77 | 78 | function _approve() internal virtual override { 79 | underlying.safeApproveMax(token); 80 | } 81 | 82 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 83 | address _token = token; 84 | require(ICToken(_token).mint(amountUnderlying) == 0, "CrErc20: Mint failed"); 85 | amountMinted = IERC20(_token).balanceOf(address(this)); 86 | } 87 | 88 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 89 | require(ICToken(token).redeem(amountToken) == 0, "CrErc20: Burn failed"); 90 | amountReceived = IERC20(underlying).balanceOf(address(this)); 91 | } 92 | 93 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 94 | amountBurned = toWrappedAmount(amountUnderlying); 95 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 96 | require(ICToken(token).redeemUnderlying(amountUnderlying) == 0, "CrErc20: Burn failed"); 97 | } 98 | } -------------------------------------------------------------------------------- /contracts/adapters/cream/CrEtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractEtherAdapter.sol"; 5 | import "../../interfaces/CompoundInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CTokenParams } from "../../libraries/CTokenParams.sol"; 12 | 13 | 14 | contract CrEtherAdapter is AbstractEtherAdapter() { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Internal Queries ========== */ 20 | 21 | function _protocolName() internal view virtual override returns (string memory) { 22 | return "Cream"; 23 | } 24 | 25 | /* ========== Metadata ========== */ 26 | 27 | function availableLiquidity() public view override returns (uint256) { 28 | return address(token).balance; 29 | } 30 | 31 | /* ========== Conversion Queries ========== */ 32 | 33 | function toUnderlyingAmount(uint256 tokenAmount) public view override returns (uint256) { 34 | return ( 35 | tokenAmount 36 | .mul(CTokenParams.currentExchangeRate(token)) 37 | / uint256(1e18) 38 | ); 39 | } 40 | 41 | function toWrappedAmount(uint256 underlyingAmount) public view override returns (uint256) { 42 | return underlyingAmount 43 | .mul(1e18) 44 | / CTokenParams.currentExchangeRate(token); 45 | } 46 | 47 | /* ========== Performance Queries ========== */ 48 | 49 | function getAPR() public view virtual override returns (uint256) { 50 | return ICToken(token).supplyRatePerBlock().mul(2102400); 51 | } 52 | 53 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 54 | ICToken cToken = ICToken(token); 55 | ( 56 | address model, 57 | uint256 cashPrior, 58 | uint256 borrowsPrior, 59 | uint256 reservesPrior, 60 | uint256 reserveFactorMantissa 61 | ) = CTokenParams.getInterestRateParameters(address(cToken)); 62 | return IInterestRateModel(model).getSupplyRate( 63 | cashPrior.add(liquidityDelta), 64 | borrowsPrior, 65 | reservesPrior, 66 | reserveFactorMantissa 67 | ).mul(2102400); 68 | } 69 | 70 | /* ========== Caller Balance Queries ========== */ 71 | 72 | function balanceUnderlying() external view virtual override returns (uint256) { 73 | return toUnderlyingAmount(ICToken(token).balanceOf(msg.sender)); 74 | } 75 | 76 | /* ========== Internal Ether Handlers ========== */ 77 | 78 | // Convert to WETH if contract takes WETH 79 | function _afterReceiveETH(uint256 amount) internal virtual override {} 80 | 81 | // Convert to WETH if contract takes ETH 82 | function _afterReceiveWETH(uint256 amount) internal virtual override { 83 | IWETH(underlying).withdraw(amount); 84 | } 85 | 86 | // Convert to ETH if contract returns WETH 87 | function _beforeSendETH(uint256 amount) internal virtual override {} 88 | 89 | // Convert to WETH if contract returns ETH 90 | function _beforeSendWETH(uint256 amount) internal virtual override { 91 | IWETH(underlying).deposit{value: amount}(); 92 | } 93 | 94 | /* ========== Internal Actions ========== */ 95 | 96 | function _approve() internal virtual override {} 97 | 98 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 99 | address _token = token; 100 | ICToken(_token).mint{value: amountUnderlying}(); 101 | amountMinted = IERC20(_token).balanceOf(address(this)); 102 | } 103 | 104 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 105 | require(ICToken(token).redeem(amountToken) == 0, "CEther: Burn failed"); 106 | amountReceived = address(this).balance; 107 | } 108 | 109 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 110 | amountBurned = toWrappedAmount(amountUnderlying); 111 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 112 | require(ICToken(token).redeemUnderlying(amountUnderlying) == 0, "CEther: Burn failed"); 113 | } 114 | } -------------------------------------------------------------------------------- /contracts/adapters/dydx/DyDxEtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "./DyDxErc20Adapter.sol"; 6 | import "../../interfaces/IWETH.sol"; 7 | import "../../libraries/MinimalSignedMath.sol"; 8 | 9 | 10 | contract DyDxEtherAdapter is DyDxErc20Adapter, IEtherAdapter { 11 | using MinimalSignedMath for uint256; 12 | using LowGasSafeMath for uint256; 13 | using TransferHelper for address; 14 | 15 | /* ========== Constructor & Initializer ========== */ 16 | 17 | constructor(uint96 _marketId) { 18 | underlying = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 19 | marketId = _marketId; 20 | underlying.safeApproveMax(address(dydx)); 21 | } 22 | 23 | function initialize(address, uint256) external virtual override { 24 | return; 25 | } 26 | 27 | /* ========== Metadata Queries ========== */ 28 | 29 | function name() external pure override(DyDxErc20Adapter, IErc20Adapter) returns (string memory) { 30 | return "DyDx ETH Adapter"; 31 | } 32 | 33 | /* ========== Token Actions ========== */ 34 | 35 | function depositETH() external payable virtual override returns (uint256 shares) { 36 | require(msg.value > 0, "DyDx: Mint failed"); 37 | shares = toWrappedAmount(msg.value); 38 | IWETH(underlying).deposit{value: msg.value}(); 39 | _mint(msg.sender, shares); 40 | _deposit(msg.value); 41 | } 42 | 43 | function withdrawAsETH(uint256 shares) public virtual override returns (uint256 amountOut) { 44 | amountOut = toUnderlyingAmount(shares); 45 | _burn(msg.sender, shares); 46 | _withdraw(amountOut, false); 47 | IWETH(underlying).withdraw(amountOut); 48 | address(msg.sender).safeTransferETH(amountOut); 49 | } 50 | 51 | function withdrawAllAsETH() external virtual override returns (uint256 amountReceived) { 52 | return withdrawAsETH(balanceWrapped()); 53 | } 54 | 55 | function withdrawUnderlyingAsETH(uint256 amountUnderlying) external virtual override returns (uint256 shares) { 56 | require(amountUnderlying > 0, "DyDx: Burn failed"); 57 | shares = toWrappedAmount(amountUnderlying); 58 | _burn(msg.sender, shares); 59 | _withdraw(amountUnderlying, false); 60 | IWETH(underlying).withdraw(amountUnderlying); 61 | address(msg.sender).safeTransferETH(amountUnderlying); 62 | } 63 | } -------------------------------------------------------------------------------- /contracts/adapters/fuse/FuseErc20Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractErc20Adapter.sol"; 5 | import "../../interfaces/FuseInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CTokenParams } from "../../libraries/CTokenParams.sol"; 12 | 13 | 14 | contract FuseErc20Adapter is AbstractErc20Adapter { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Storage ========== */ 20 | 21 | string internal __protocolName; 22 | 23 | /* ========== Initializer ========== */ 24 | 25 | function initialize( 26 | address _underlying, 27 | address _token, 28 | string memory protocolName 29 | ) public { 30 | super.initialize(_underlying, _token); 31 | __protocolName = protocolName; 32 | } 33 | 34 | 35 | /* ========== Internal Queries ========== */ 36 | 37 | function _protocolName() internal view virtual override returns (string memory) { 38 | return __protocolName; 39 | } 40 | 41 | /* ========== Metadata ========== */ 42 | 43 | function availableLiquidity() public view override returns (uint256) { 44 | return IERC20(underlying).balanceOf(token); 45 | } 46 | 47 | /* ========== Conversion Queries ========== */ 48 | 49 | function toUnderlyingAmount(uint256 tokenAmount) public view override returns (uint256) { 50 | return ( 51 | tokenAmount 52 | .mul(CTokenParams.currentExchangeRate(token)) 53 | / uint256(1e18) 54 | ); 55 | } 56 | 57 | function toWrappedAmount(uint256 underlyingAmount) public view override returns (uint256) { 58 | return underlyingAmount 59 | .mul(1e18) 60 | / CTokenParams.currentExchangeRate(token); 61 | } 62 | 63 | /* ========== Performance Queries ========== */ 64 | 65 | function getAPR() public view virtual override returns (uint256) { 66 | return IFToken(token).supplyRatePerBlock().mul(2102400); 67 | } 68 | 69 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 70 | IFToken fToken = IFToken(token); 71 | ( 72 | address model, 73 | uint256 cashPrior, 74 | uint256 borrowsPrior, 75 | uint256 reservesPrior, 76 | uint256 reserveFactorMantissa 77 | ) = CTokenParams.getInterestRateParameters(address(fToken)); 78 | return IInterestRateModel(model).getSupplyRate( 79 | cashPrior.add(liquidityDelta), 80 | borrowsPrior, 81 | reservesPrior.add(fToken.totalFuseFees()).add(fToken.totalAdminFees()), 82 | reserveFactorMantissa.add(fToken.fuseFeeMantissa()).add(fToken.adminFeeMantissa()) 83 | ).mul(2102400); 84 | } 85 | 86 | /* ========== Caller Balance Queries ========== */ 87 | 88 | function balanceUnderlying() external view virtual override returns (uint256) { 89 | return IFToken(token).balanceOf(msg.sender).mul(IFToken(token).exchangeRateStored()) / 1e18; 90 | } 91 | 92 | /* ========== Internal Actions ========== */ 93 | 94 | function _approve() internal virtual override { 95 | underlying.safeApproveMax(token); 96 | } 97 | 98 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 99 | require(IFToken(token).mint(amountUnderlying) == 0, "CErc20: Mint failed"); 100 | amountMinted = IERC20(token).balanceOf(address(this)); 101 | } 102 | 103 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 104 | require(IFToken(token).redeem(amountToken) == 0, "CErc20: Burn failed"); 105 | amountReceived = IERC20(underlying).balanceOf(address(this)); 106 | } 107 | 108 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 109 | amountBurned = amountUnderlying.mul(1e18).divCeil(IFToken(token).exchangeRateCurrent()); 110 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 111 | require(IFToken(token).redeemUnderlying(amountUnderlying) == 0, "CErc20: Burn failed"); 112 | } 113 | } -------------------------------------------------------------------------------- /contracts/adapters/fuse/FuseEtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractEtherAdapter.sol"; 5 | import "../../interfaces/FuseInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CTokenParams } from "../../libraries/CTokenParams.sol"; 12 | 13 | 14 | contract FuseEtherAdapter is AbstractEtherAdapter() { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Storage ========== */ 20 | 21 | string internal __protocolName; 22 | 23 | /* ========== Initializer ========== */ 24 | 25 | function initialize( 26 | address _underlying, 27 | address _token, 28 | string memory protocolName 29 | ) public { 30 | super.initialize(_underlying, _token); 31 | __protocolName = protocolName; 32 | } 33 | 34 | 35 | /* ========== Internal Queries ========== */ 36 | 37 | function _protocolName() internal view virtual override returns (string memory) { 38 | return __protocolName; 39 | } 40 | 41 | /* ========== Metadata ========== */ 42 | 43 | function availableLiquidity() public view override returns (uint256) { 44 | return address(token).balance; 45 | } 46 | 47 | /* ========== Conversion Queries ========== */ 48 | 49 | function toUnderlyingAmount(uint256 tokenAmount) external view virtual override returns (uint256) { 50 | return tokenAmount; 51 | } 52 | 53 | function toWrappedAmount(uint256 underlyingAmount) external view virtual override returns (uint256) { 54 | return underlyingAmount; 55 | } 56 | 57 | /* ========== Performance Queries ========== */ 58 | 59 | function getAPR() public view virtual override returns (uint256) { 60 | return IFToken(token).supplyRatePerBlock().mul(2102400); 61 | } 62 | 63 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 64 | IFToken fToken = IFToken(token); 65 | ( 66 | address model, 67 | uint256 cashPrior, 68 | uint256 borrowsPrior, 69 | uint256 reservesPrior, 70 | uint256 reserveFactorMantissa 71 | ) = CTokenParams.getInterestRateParameters(address(fToken)); 72 | return IInterestRateModel(model).getSupplyRate( 73 | cashPrior.add(liquidityDelta), 74 | borrowsPrior, 75 | reservesPrior.add(fToken.totalFuseFees()).add(fToken.totalAdminFees()), 76 | reserveFactorMantissa.add(fToken.fuseFeeMantissa()).add(fToken.adminFeeMantissa()) 77 | ).mul(2102400); 78 | } 79 | 80 | /* ========== Caller Balance Queries ========== */ 81 | 82 | function balanceUnderlying() external view virtual override returns (uint256) { 83 | return IFToken(token).balanceOf(msg.sender).mul(IFToken(token).exchangeRateStored()) / 1e18; 84 | } 85 | 86 | /* ========== Internal Ether Handlers ========== */ 87 | 88 | // Convert to WETH if contract takes WETH 89 | function _afterReceiveETH(uint256 amount) internal virtual override {} 90 | 91 | // Convert to WETH if contract takes ETH 92 | function _afterReceiveWETH(uint256 amount) internal virtual override { 93 | IWETH(underlying).withdraw(amount); 94 | } 95 | 96 | // Convert to ETH if contract returns WETH 97 | function _beforeSendETH(uint256 amount) internal virtual override {} 98 | 99 | // Convert to WETH if contract returns ETH 100 | function _beforeSendWETH(uint256 amount) internal virtual override { 101 | IWETH(underlying).deposit{value: amount}(); 102 | } 103 | 104 | /* ========== Internal Actions ========== */ 105 | 106 | function _approve() internal virtual override {} 107 | 108 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 109 | require(IFToken(token).mint{value: amountUnderlying}() == 0, "CEther: Mint failed"); 110 | amountMinted = IERC20(token).balanceOf(address(this)); 111 | } 112 | 113 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 114 | require(IFToken(token).redeem(amountToken) == 0, "CEther: Burn failed"); 115 | amountReceived = IERC20(underlying).balanceOf(address(this)); 116 | } 117 | 118 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 119 | amountBurned = amountUnderlying.mul(1e18).divCeil(IFToken(token).exchangeRateCurrent()); 120 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 121 | require(IFToken(token).redeemUnderlying(amountUnderlying) == 0, "CEther: Burn failed"); 122 | } 123 | } -------------------------------------------------------------------------------- /contracts/adapters/ironbank/CyErc20Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractErc20Adapter.sol"; 5 | import "../../interfaces/CompoundInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CyTokenParams } from "../../libraries/CyTokenParams.sol"; 12 | 13 | 14 | contract CyErc20Adapter is AbstractErc20Adapter() { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Internal Queries ========== */ 20 | 21 | function _protocolName() internal view virtual override returns (string memory) { 22 | return "IronBank"; 23 | } 24 | 25 | /* ========== Metadata ========== */ 26 | 27 | function availableLiquidity() public view override returns (uint256) { 28 | return IERC20(underlying).balanceOf(token); 29 | } 30 | 31 | /* ========== Conversion Queries ========== */ 32 | 33 | function toUnderlyingAmount(uint256 tokenAmount) public view override returns (uint256) { 34 | return ( 35 | tokenAmount 36 | .mul(CyTokenParams.currentExchangeRate(token)) 37 | / uint256(1e18) 38 | ); 39 | } 40 | 41 | function toWrappedAmount(uint256 underlyingAmount) public view override returns (uint256) { 42 | return underlyingAmount 43 | .mul(1e18) 44 | / CyTokenParams.currentExchangeRate(token); 45 | } 46 | 47 | /* ========== Performance Queries ========== */ 48 | 49 | function getAPR() public view virtual override returns (uint256) { 50 | return CyTokenParams.getSupplyRate(token, 0); 51 | } 52 | 53 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 54 | return CyTokenParams.getSupplyRate(token, liquidityDelta); 55 | } 56 | 57 | /* ========== Caller Balance Queries ========== */ 58 | 59 | function balanceUnderlying() external view virtual override returns (uint256) { 60 | return toUnderlyingAmount(ICToken(token).balanceOf(msg.sender)); 61 | } 62 | 63 | /* ========== Internal Actions ========== */ 64 | 65 | function _approve() internal virtual override { 66 | underlying.safeApproveMax(token); 67 | } 68 | 69 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 70 | address _token = token; 71 | require(ICToken(_token).mint(amountUnderlying) == 0, "CErc20: Mint failed"); 72 | amountMinted = IERC20(_token).balanceOf(address(this)); 73 | } 74 | 75 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 76 | require(ICToken(token).redeem(amountToken) == 0, "CErc20: Burn failed"); 77 | amountReceived = IERC20(underlying).balanceOf(address(this)); 78 | } 79 | 80 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 81 | amountBurned = toWrappedAmount(amountUnderlying); 82 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 83 | require(ICToken(token).redeemUnderlying(amountUnderlying) == 0, "CrErc20: Burn failed"); 84 | } 85 | } -------------------------------------------------------------------------------- /contracts/adapters/ironbank/CyEtherAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../AbstractEtherAdapter.sol"; 5 | import "../../interfaces/CompoundInterfaces.sol"; 6 | import "../../interfaces/IERC20.sol"; 7 | import "../../interfaces/IWETH.sol"; 8 | import "../../libraries/LowGasSafeMath.sol"; 9 | import "../../libraries/TransferHelper.sol"; 10 | import "../../libraries/MinimalSignedMath.sol"; 11 | import { CyTokenParams } from "../../libraries/CyTokenParams.sol"; 12 | 13 | 14 | contract CyEtherAdapter is AbstractEtherAdapter() { 15 | using MinimalSignedMath for uint256; 16 | using LowGasSafeMath for uint256; 17 | using TransferHelper for address; 18 | 19 | /* ========== Internal Queries ========== */ 20 | 21 | function _protocolName() internal view virtual override returns (string memory) { 22 | return "IronBank"; 23 | } 24 | 25 | /* ========== Metadata ========== */ 26 | 27 | function availableLiquidity() public view override returns (uint256) { 28 | return IERC20(underlying).balanceOf(token); 29 | } 30 | 31 | /* ========== Conversion Queries ========== */ 32 | 33 | function toUnderlyingAmount(uint256 tokenAmount) public view override returns (uint256) { 34 | return ( 35 | tokenAmount 36 | .mul(CyTokenParams.currentExchangeRate(token)) 37 | / uint256(1e18) 38 | ); 39 | } 40 | 41 | function toWrappedAmount(uint256 underlyingAmount) public view override returns (uint256) { 42 | return underlyingAmount 43 | .mul(1e18) 44 | / CyTokenParams.currentExchangeRate(token); 45 | } 46 | 47 | /* ========== Performance Queries ========== */ 48 | 49 | function getAPR() public view virtual override returns (uint256) { 50 | return CyTokenParams.getSupplyRate(token, 0); 51 | } 52 | 53 | function getHypotheticalAPR(int256 liquidityDelta) external view virtual override returns (uint256) { 54 | return CyTokenParams.getSupplyRate(token, liquidityDelta); 55 | } 56 | 57 | /* ========== Caller Balance Queries ========== */ 58 | 59 | function balanceUnderlying() external view virtual override returns (uint256) { 60 | return toUnderlyingAmount(ICToken(token).balanceOf(msg.sender)); 61 | } 62 | 63 | /* ========== Internal Ether Handlers ========== */ 64 | 65 | // Convert to WETH if contract takes WETH 66 | function _afterReceiveETH(uint256 amount) internal virtual override { 67 | IWETH(underlying).deposit{value: amount}(); 68 | } 69 | 70 | // Convert to ETH if contract takes ETH 71 | function _afterReceiveWETH(uint256 amount) internal virtual override {} 72 | 73 | // Convert to ETH if contract returns WETH 74 | function _beforeSendETH(uint256 amount) internal virtual override { 75 | IWETH(underlying).withdraw(amount); 76 | } 77 | 78 | // Convert to WETH if contract returns ETH 79 | function _beforeSendWETH(uint256 amount) internal virtual override {} 80 | 81 | /* ========== Internal Actions ========== */ 82 | 83 | function _approve() internal virtual override { 84 | underlying.safeApproveMax(token); 85 | } 86 | 87 | function _mint(uint256 amountUnderlying) internal virtual override returns (uint256 amountMinted) { 88 | address _token = token; 89 | require(ICToken(_token).mint(amountUnderlying) == 0, "CErc20: Mint failed"); 90 | amountMinted = IERC20(_token).balanceOf(address(this)); 91 | } 92 | 93 | function _burn(uint256 amountToken) internal virtual override returns (uint256 amountReceived) { 94 | require(ICToken(token).redeem(amountToken) == 0, "CErc20: Burn failed"); 95 | amountReceived = IERC20(underlying).balanceOf(address(this)); 96 | } 97 | 98 | function _burnUnderlying(uint256 amountUnderlying) internal virtual override returns (uint256 amountBurned) { 99 | amountBurned = toWrappedAmount(amountUnderlying); 100 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 101 | require(ICToken(token).redeemUnderlying(amountUnderlying) == 0, "CrErc20: Burn failed"); 102 | } 103 | } -------------------------------------------------------------------------------- /contracts/interfaces/AaveV1Interfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | pragma abicoder v2; 4 | 5 | 6 | interface IReserveInterestRateStrategy { 7 | function calculateInterestRates( 8 | address _reserve, 9 | uint256 _availableLiquidity, 10 | uint256 _totalBorrowsStable, 11 | uint256 _totalBorrowsVariable, 12 | uint256 _averageStableBorrowRate 13 | ) 14 | external 15 | view 16 | returns 17 | ( 18 | uint256 liquidityRate, 19 | uint256 stableBorrowRate, 20 | uint256 variableBorrowRate 21 | ); 22 | } 23 | 24 | 25 | interface ILendingPoolAddressesProvider { 26 | function getLendingPoolCore() external view returns (ILendingPoolCore); 27 | function getLendingPool() external view returns (ILendingPool); 28 | } 29 | 30 | 31 | interface ILendingPool { 32 | function deposit(address reserve, uint256 amount, uint16 referralCode) external payable; 33 | } 34 | 35 | 36 | interface IAToken { 37 | function redeem(uint256 amount) external; 38 | } 39 | 40 | 41 | interface ILendingPoolCore { 42 | function getReserves() external view returns (address[] memory); 43 | function getReserveIsFreezed(address _reserve) external view returns (bool); 44 | function getReserveATokenAddress(address) external view returns (address); 45 | function getReserveCurrentLiquidityRate(address token) external view returns (uint256); 46 | function getReserveAvailableLiquidity(address token) external view returns (uint256); 47 | function getReserveTotalBorrowsStable(address token) external view returns (uint256); 48 | function getReserveTotalBorrowsVariable(address token) external view returns (uint256); 49 | function getReserveCurrentAverageStableBorrowRate(address token) external view returns (uint256); 50 | function getReserveInterestRateStrategyAddress(address token) external view returns (IReserveInterestRateStrategy); 51 | } -------------------------------------------------------------------------------- /contracts/interfaces/AaveV2Interfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | pragma abicoder v2; 4 | 5 | 6 | interface ILendingPoolAddressesProvider { 7 | function getLendingPool() external view returns (ILendingPool); 8 | 9 | function getPriceOracle() external view returns (IPriceOracle); 10 | } 11 | 12 | 13 | interface IVariableDebtToken { 14 | function scaledTotalSupply() external view returns (uint256); 15 | } 16 | 17 | 18 | interface IReserveInterestRateStrategy { 19 | function calculateInterestRates( 20 | address reserve, 21 | uint256 availableLiquidity, 22 | uint256 totalStableDebt, 23 | uint256 totalVariableDebt, 24 | uint256 averageStableBorrowRate, 25 | uint256 reserveFactor 26 | ) external 27 | view 28 | returns ( 29 | uint256 liquidityRate, 30 | uint256 stableBorrowRate, 31 | uint256 variableBorrowRate 32 | ); 33 | } 34 | 35 | 36 | interface IStableDebtToken { 37 | function getTotalSupplyAndAvgRate() external view returns (uint256, uint256); 38 | } 39 | 40 | 41 | interface ILendingPool { 42 | struct ReserveConfigurationMap { 43 | uint256 data; 44 | } 45 | 46 | struct ReserveData { 47 | ReserveConfigurationMap configuration; 48 | uint128 liquidityIndex; 49 | uint128 variableBorrowIndex; 50 | uint128 currentLiquidityRate; 51 | uint128 currentVariableBorrowRate; 52 | uint128 currentStableBorrowRate; 53 | uint40 lastUpdateTimestamp; 54 | address aTokenAddress; 55 | IStableDebtToken stableDebtToken; 56 | IVariableDebtToken variableDebtToken; 57 | IReserveInterestRateStrategy interestRateStrategy; 58 | uint8 id; 59 | } 60 | 61 | function getReserveNormalizedIncome(address asset) external view returns (uint128); 62 | 63 | function getReserveData(address asset) external view returns (ReserveData memory); 64 | 65 | function getReservesList() external view returns (address[] memory); 66 | 67 | function getConfiguration(address asset) external view returns (ReserveConfigurationMap memory); 68 | 69 | function deposit( 70 | address asset, 71 | uint256 amount, 72 | address onBehalfOf, 73 | uint16 referralCode 74 | ) external; 75 | 76 | function withdraw( 77 | address asset, 78 | uint256 amount, 79 | address to 80 | ) external; 81 | } 82 | 83 | 84 | interface IAaveDistributionManager { 85 | function getAssetData(address asset) external view returns (uint256 index, uint256 emissionPerSecond, uint256 lastUpdateTimestamp); 86 | 87 | function getUserUnclaimedRewards(address account) external view returns (uint256); 88 | 89 | function getRewardsBalance(address[] calldata assets, address user) 90 | external 91 | view 92 | returns (uint256); 93 | 94 | function claimRewards( 95 | address[] calldata assets, 96 | uint256 amount, 97 | address to 98 | ) external returns (uint256); 99 | } 100 | 101 | 102 | interface IPriceOracle { 103 | function getAssetPrice(address asset) external view returns (uint256); 104 | } 105 | 106 | interface IStakedAave { 107 | function COOLDOWN_SECONDS() external view returns (uint256); 108 | 109 | function stake(address to, uint256 amount) external; 110 | 111 | function redeem(address to, uint256 amount) external; 112 | 113 | function cooldown() external; 114 | 115 | function claimRewards(address to, uint256 amount) external; 116 | 117 | function stakerRewardsToClaim(address account) external view returns (uint256); 118 | 119 | function stakersCooldowns(address account) external view returns (uint256); 120 | 121 | function getTotalRewardsBalance(address staker) external view returns (uint256); 122 | 123 | function getNextCooldownTimestamp( 124 | uint256 fromCooldownTimestamp, 125 | uint256 amountToReceive, 126 | address toAddress, 127 | uint256 toBalance 128 | ) external returns (uint256); 129 | } -------------------------------------------------------------------------------- /contracts/interfaces/CompoundInterfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | pragma abicoder v2; 4 | 5 | 6 | interface ICToken { 7 | function comptroller() external view returns (address); 8 | function underlying() external view returns (address); 9 | function name() external view returns (string memory); 10 | function totalSupply() external view returns (uint256); 11 | 12 | function supplyRatePerBlock() external view returns (uint256); 13 | function borrowRatePerBlock() external view returns (uint256); 14 | 15 | function getCash() external view returns (uint256); 16 | function totalBorrows() external view returns (uint256); 17 | function totalReserves() external view returns (uint256); 18 | function reserveFactorMantissa() external view returns (uint256); 19 | function exchangeRateCurrent() external returns (uint256); 20 | function exchangeRateStored() external view returns (uint256); 21 | function accrualBlockNumber() external view returns (uint256); 22 | function borrowBalanceStored(address account) external view returns (uint); 23 | function interestRateModel() external view returns (IInterestRateModel); 24 | 25 | function balanceOf(address account) external view returns (uint256); 26 | function balanceOfUnderlying(address owner) external returns (uint); 27 | 28 | function mint(uint256 mintAmount) external returns (uint256); 29 | function mint() external payable; 30 | function redeem(uint256 tokenAmount) external returns (uint256); 31 | function redeemUnderlying(uint256 underlyingAmount) external returns (uint256); 32 | function borrow(uint borrowAmount) external returns (uint); 33 | 34 | // Used to check if a cream market is for an SLP token 35 | function sushi() external view returns (address); 36 | } 37 | 38 | 39 | interface IInterestRateModel { 40 | function getBorrowRate( 41 | uint cash, 42 | uint borrows, 43 | uint reserves 44 | ) external view returns (uint); 45 | 46 | function getSupplyRate( 47 | uint256 cash, 48 | uint256 borrows, 49 | uint256 reserves, 50 | uint256 reserveFactorMantissa 51 | ) external view returns (uint); 52 | } 53 | 54 | 55 | interface IComptroller { 56 | function getAllMarkets() external view returns (ICToken[] memory); 57 | function mintGuardianPaused(address cToken) external view returns (bool); 58 | function compSpeeds(address cToken) external view returns (uint256); 59 | function oracle() external view returns (IPriceOracle); 60 | function compAccrued(address) external view returns (uint); 61 | function markets(address cToken) external view returns ( 62 | bool isListed, 63 | uint collateralFactorMantissa, 64 | bool isComped 65 | ); 66 | function claimComp(address[] memory holders, address[] memory cTokens, bool borrowers, bool suppliers) external; 67 | function refreshCompSpeeds() external; 68 | } 69 | 70 | 71 | interface IPriceOracle { 72 | function getUnderlyingPrice(address cToken) external view returns (uint); 73 | } -------------------------------------------------------------------------------- /contracts/interfaces/DyDxInterfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | pragma abicoder v2; 4 | 5 | 6 | interface DyDxStructs { 7 | struct Val { 8 | uint256 value; 9 | } 10 | 11 | struct Set { 12 | uint128 borrow; 13 | uint128 supply; 14 | } 15 | 16 | enum ActionType { 17 | Deposit, // supply tokens 18 | Withdraw // borrow tokens 19 | } 20 | 21 | enum AssetDenomination { 22 | Wei // the amount is denominated in wei 23 | } 24 | 25 | enum AssetReference { 26 | Delta // the amount is given as a delta from the current value 27 | } 28 | 29 | struct AssetAmount { 30 | bool sign; // true if positive 31 | AssetDenomination denomination; 32 | AssetReference ref; 33 | uint256 value; 34 | } 35 | 36 | struct ActionArgs { 37 | ActionType actionType; 38 | uint256 accountId; 39 | AssetAmount amount; 40 | uint256 primaryMarketId; 41 | uint256 secondaryMarketId; 42 | address otherAddress; 43 | uint256 otherAccountId; 44 | bytes data; 45 | } 46 | 47 | struct Info { 48 | address owner; // The address that owns the account 49 | uint256 number; // A nonce that allows a single address to control many accounts 50 | } 51 | 52 | struct Wei { 53 | bool sign; // true if positive 54 | uint256 value; 55 | } 56 | } 57 | 58 | 59 | interface IDyDx is DyDxStructs { 60 | function getEarningsRate() external view returns (Val memory); 61 | 62 | function getNumMarkets() external view returns (uint256); 63 | 64 | function getMarketIsClosing(uint256 marketId) external view returns (bool); 65 | 66 | function getMarketTokenAddress(uint256) external view returns (address); 67 | 68 | function getMarketInterestRate(uint256 marketId) external view returns (Val memory); 69 | 70 | function getMarketTotalPar(uint256 marketId) external view returns (Set memory); 71 | 72 | function getAccountWei(Info memory account, uint256 marketId) external view returns (Wei memory); 73 | 74 | function operate(Info[] memory, ActionArgs[] memory) external; 75 | } 76 | -------------------------------------------------------------------------------- /contracts/interfaces/FulcrumInterfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | interface IBZX { 6 | function getLoanPoolsList(uint256 start, uint256 count) external view returns (address[] memory loanPoolsList); 7 | function loanPoolToUnderlying(address pool) external view returns (address underlying); 8 | function underlyingToLoanPool(address pool) external view returns (IToken underlying); 9 | function getLenderInterestData(address lender, address loanToken) 10 | external 11 | view 12 | returns ( 13 | uint256 interestPaid, 14 | uint256 interestPaidDate, 15 | uint256 interestOwedPerDay, 16 | uint256 interestUnPaid, 17 | uint256 interestFeePercent, 18 | uint256 principalTotal 19 | ); 20 | } 21 | 22 | 23 | interface IToken { 24 | function supplyInterestRate() external view returns (uint256); 25 | function totalAssetSupply() external view returns (uint256); 26 | function totalSupplyInterestRate(uint256 assetSupply) external view returns (uint256); 27 | function nextSupplyInterestRate(uint256 supplyAmount) external view returns (uint256); 28 | function mint(address receiver, uint256 amount) external payable returns (uint256 mintAmount); 29 | function burn(address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid); 30 | function assetBalanceOf(address _owner) external view returns (uint256 balance); 31 | function tokenPrice() external view returns (uint256); 32 | function mintWithEther(address receiver) external payable returns (uint256 mintAmount); 33 | function burnToEther(address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid); 34 | } -------------------------------------------------------------------------------- /contracts/interfaces/FuseInterfaces.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | pragma abicoder v2; 4 | 5 | 6 | interface IFusePoolDirectory { 7 | struct FusePool { 8 | string name; 9 | address creator; 10 | IFusePool comptroller; 11 | uint256 blockPosted; 12 | uint256 timestampPosted; 13 | } 14 | function pools(uint256) external view returns (string memory name, address creator, IFusePool comptroller, uint256 blockPosted, uint256 timestampPosted); 15 | function getAllPools() external view returns (FusePool[] memory); 16 | } 17 | 18 | 19 | interface IFusePool { 20 | function enforceWhitelist() external view returns (bool); 21 | function getAllMarkets() external view returns (IFToken[] memory); 22 | function mintGuardianPaused(address cToken) external view returns (bool); 23 | function oracle() external view returns (address); 24 | } 25 | 26 | 27 | interface IFToken { 28 | function underlying() external view returns (address); 29 | function name() external view returns (string memory); 30 | 31 | function supplyRatePerBlock() external view returns (uint256); 32 | 33 | function getCash() external view returns (uint256); 34 | function totalBorrows() external view returns (uint256); 35 | function totalReserves() external view returns (uint256); 36 | 37 | function reserveFactorMantissa() external view returns (uint256); 38 | function fuseFeeMantissa() external view returns(uint256); 39 | function adminFeeMantissa() external view returns(uint256); 40 | 41 | function exchangeRateCurrent() external returns (uint256); 42 | function exchangeRateStored() external view returns (uint256); 43 | 44 | function totalFuseFees() external view returns(uint256); 45 | function totalAdminFees() external view returns(uint256); 46 | 47 | function interestRateModel() external view returns (IInterestRateModel); 48 | function balanceOf(address account) external view returns (uint256); 49 | function mint(uint256 mintAmount) external returns (uint256); 50 | function mint() external payable returns (uint256); 51 | function redeem(uint256 tokenAmount) external returns (uint256); 52 | function redeemUnderlying(uint256 underlyingAmount) external returns (uint256); 53 | } 54 | 55 | 56 | interface IInterestRateModel { 57 | function getSupplyRate( 58 | uint256 cash, 59 | uint256 borrows, 60 | uint256 reserves, 61 | uint256 reserveFactorMantissa 62 | ) external view returns (uint); 63 | } -------------------------------------------------------------------------------- /contracts/interfaces/IAdapterRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | 5 | interface IAdapterRegistry { 6 | /* ========== Events ========== */ 7 | 8 | event ProtocolAdapterAdded(uint256 protocolId, address protocolAdapter); 9 | 10 | event ProtocolAdapterRemoved(uint256 protocolId); 11 | 12 | event TokenAdapterAdded(address adapter, uint256 protocolId, address underlying, address wrapper); 13 | 14 | event TokenAdapterRemoved(address adapter, uint256 protocolId, address underlying, address wrapper); 15 | 16 | event TokenSupportAdded(address underlying); 17 | 18 | event TokenSupportRemoved(address underlying); 19 | 20 | event VaultFactoryAdded(address factory); 21 | 22 | event VaultFactoryRemoved(address factory); 23 | 24 | event VaultAdded(address underlying, address vault); 25 | 26 | event VaultRemoved(address underlying, address vault); 27 | 28 | /* ========== Structs ========== */ 29 | 30 | struct TokenAdapter { 31 | address adapter; 32 | uint96 protocolId; 33 | } 34 | 35 | /* ========== Storage ========== */ 36 | 37 | function protocolsCount() external view returns (uint256); 38 | 39 | function protocolAdapters(uint256 id) external view returns (address protocolAdapter); 40 | 41 | function protocolAdapterIds(address protocolAdapter) external view returns (uint256 id); 42 | 43 | function vaultsByUnderlying(address underlying) external view returns (address vault); 44 | 45 | function approvedVaultFactories(address factory) external view returns (bool approved); 46 | 47 | /* ========== Vault Factory Management ========== */ 48 | 49 | function addVaultFactory(address _factory) external; 50 | 51 | function removeVaultFactory(address _factory) external; 52 | 53 | /* ========== Vault Management ========== */ 54 | 55 | function addVault(address vault) external; 56 | 57 | function removeVault(address vault) external; 58 | 59 | /* ========== Protocol Adapter Management ========== */ 60 | 61 | function addProtocolAdapter(address protocolAdapter) external returns (uint256 id); 62 | 63 | function removeProtocolAdapter(address protocolAdapter) external; 64 | 65 | /* ========== Token Adapter Management ========== */ 66 | 67 | function addTokenAdapter(address adapter) external; 68 | 69 | function addTokenAdapters(address[] calldata adapters) external; 70 | 71 | function removeTokenAdapter(address adapter) external; 72 | 73 | /* ========== Vault Queries ========== */ 74 | 75 | function getVaultsList() external view returns (address[] memory); 76 | 77 | function haveVaultFor(address underlying) external view returns (bool); 78 | 79 | /* ========== Protocol Queries ========== */ 80 | 81 | function getProtocolAdaptersAndIds() external view returns (address[] memory adapters, uint256[] memory ids); 82 | 83 | function getProtocolMetadata(uint256 id) external view returns (address protocolAdapter, string memory name); 84 | 85 | function getProtocolForTokenAdapter(address adapter) external view returns (address protocolAdapter); 86 | 87 | /* ========== Supported Token Queries ========== */ 88 | 89 | function isSupported(address underlying) external view returns (bool); 90 | 91 | function getSupportedTokens() external view returns (address[] memory list); 92 | 93 | /* ========== Token Adapter Queries ========== */ 94 | 95 | function isApprovedAdapter(address adapter) external view returns (bool); 96 | 97 | function getAdaptersList(address underlying) external view returns (address[] memory list); 98 | 99 | function getAdapterForWrapperToken(address wrapperToken) external view returns (address); 100 | 101 | function getAdaptersCount(address underlying) external view returns (uint256); 102 | 103 | function getAdaptersSortedByAPR(address underlying) 104 | external 105 | view 106 | returns (address[] memory adapters, uint256[] memory aprs); 107 | 108 | function getAdaptersSortedByAPRWithDeposit( 109 | address underlying, 110 | uint256 deposit, 111 | address excludingAdapter 112 | ) 113 | external 114 | view 115 | returns (address[] memory adapters, uint256[] memory aprs); 116 | 117 | function getAdapterWithHighestAPR(address underlying) external view returns (address adapter, uint256 apr); 118 | 119 | function getAdapterWithHighestAPRForDeposit( 120 | address underlying, 121 | uint256 deposit, 122 | address excludingAdapter 123 | ) external view returns (address adapter, uint256 apr); 124 | } 125 | 126 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | interface IERC20 { 6 | event Transfer(address indexed from, address indexed to, uint256 value); 7 | event Approval(address indexed owner, address indexed spender, uint256 value); 8 | 9 | function totalSupply() external view returns (uint256); 10 | function balanceOf(address account) external view returns (uint256); 11 | function transfer(address recipient, uint256 amount) external returns (bool); 12 | function allowance(address owner, address spender) external view returns (uint256); 13 | function approve(address spender, uint256 amount) external returns (bool); 14 | function increaseAllowance(address spender, uint256 addedValue) external returns (bool); 15 | function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); 16 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 17 | } -------------------------------------------------------------------------------- /contracts/interfaces/IERC20Metadata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | interface IERC20Metadata { 6 | function name() external view returns (string memory); 7 | function symbol() external view returns (string memory); 8 | function decimals() external view returns (uint8); 9 | } 10 | 11 | 12 | interface IERC20MetadataBytes32 { 13 | function name() external view returns (bytes32); 14 | function symbol() external view returns (bytes32); 15 | } -------------------------------------------------------------------------------- /contracts/interfaces/IProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.10; 3 | import "./IAdapterRegistry.sol"; 4 | 5 | 6 | interface IProtocolAdapter { 7 | event MarketFrozen(address token); 8 | 9 | event MarketUnfrozen(address token); 10 | 11 | event AdapterFrozen(address adapter); 12 | 13 | event AdapterUnfrozen(address adapter); 14 | 15 | function registry() external view returns (IAdapterRegistry); 16 | 17 | function frozenAdapters(uint256 index) external view returns (address); 18 | 19 | function frozenTokens(uint256 index) external view returns (address); 20 | 21 | function totalMapped() external view returns (uint256); 22 | 23 | function protocol() external view returns (string memory); 24 | 25 | function getUnmapped() external view returns (address[] memory tokens); 26 | 27 | function getUnmappedUpTo(uint256 max) external view returns (address[] memory tokens); 28 | 29 | function map(uint256 max) external; 30 | 31 | function unfreezeAdapter(uint256 index) external; 32 | 33 | function unfreezeToken(uint256 index) external; 34 | 35 | function freezeAdapter(address adapter) external; 36 | } -------------------------------------------------------------------------------- /contracts/interfaces/IProxyManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.10; 3 | 4 | 5 | interface IProxyManager { 6 | function deployProxyManyToOne( 7 | bytes32 implementationID, 8 | bytes32 suppliedSalt 9 | ) external returns(address proxyAddress); 10 | 11 | function computeProxyAddressManyToOne( 12 | address originator, 13 | bytes32 implementationID, 14 | bytes32 suppliedSalt 15 | ) external view returns (address); 16 | } -------------------------------------------------------------------------------- /contracts/interfaces/IRewardsSeller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | 5 | interface IRewardsSeller { 6 | /** 7 | * @dev Sell `rewardsToken` for `underlyingToken`. 8 | * Should only be called after `rewardsToken` is transferred. 9 | * @param sender - Address of account that initially triggered the call. Can be used to restrict who can trigger a sale. 10 | * @param rewardsToken - Address of the token to sell. 11 | * @param underlyingToken - Address of the token to buy. 12 | * @param params - Any additional data that the caller provided. 13 | */ 14 | function sellRewards( 15 | address sender, 16 | address rewardsToken, 17 | address underlyingToken, 18 | bytes calldata params 19 | ) external; 20 | } -------------------------------------------------------------------------------- /contracts/interfaces/ITokenAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | interface IErc20Adapter { 6 | /* ========== Metadata ========== */ 7 | 8 | function underlying() external view returns (address); 9 | 10 | function token() external view returns (address); 11 | 12 | function name() external view returns (string memory); 13 | 14 | function availableLiquidity() external view returns (uint256); 15 | 16 | /* ========== Conversion ========== */ 17 | 18 | function toUnderlyingAmount(uint256 tokenAmount) external view returns (uint256); 19 | 20 | function toWrappedAmount(uint256 underlyingAmount) external view returns (uint256); 21 | 22 | /* ========== Performance Queries ========== */ 23 | 24 | function getAPR() external view returns (uint256); 25 | 26 | function getHypotheticalAPR(int256 liquidityDelta) external view returns (uint256); 27 | 28 | function getRevenueBreakdown() 29 | external 30 | view 31 | returns ( 32 | address[] memory assets, 33 | uint256[] memory aprs 34 | ); 35 | 36 | /* ========== Caller Balance Queries ========== */ 37 | 38 | function balanceWrapped() external view returns (uint256); 39 | 40 | function balanceUnderlying() external view returns (uint256); 41 | 42 | /* ========== Interactions ========== */ 43 | 44 | function deposit(uint256 amountUnderlying) external returns (uint256 amountMinted); 45 | 46 | function withdraw(uint256 amountToken) external returns (uint256 amountReceived); 47 | 48 | function withdrawAll() external returns (uint256 amountReceived); 49 | 50 | function withdrawUnderlying(uint256 amountUnderlying) external returns (uint256 amountBurned); 51 | 52 | function withdrawUnderlyingUpTo(uint256 amountUnderlying) external returns (uint256 amountReceived); 53 | } 54 | 55 | interface IEtherAdapter is IErc20Adapter { 56 | function depositETH() external payable returns (uint256 amountMinted); 57 | 58 | function withdrawAsETH(uint256 amountToken) external returns (uint256 amountReceived); 59 | 60 | function withdrawAllAsETH() external returns (uint256 amountReceived); 61 | 62 | function withdrawUnderlyingAsETH(uint256 amountUnderlying) external returns (uint256 amountBurned); 63 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | interface IWETH { 6 | function deposit() external payable; 7 | function withdraw(uint) external; 8 | } -------------------------------------------------------------------------------- /contracts/libraries/CTokenParams.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/CompoundInterfaces.sol"; 5 | import "./LowGasSafeMath.sol"; 6 | import "./MinimalSignedMath.sol"; 7 | 8 | 9 | library CTokenParams { 10 | using LowGasSafeMath for uint256; 11 | using MinimalSignedMath for uint256; 12 | 13 | uint256 internal constant EXP_SCALE = 1e18; 14 | uint256 internal constant HALF_EXP_SCALE = 5e17; 15 | 16 | function getInterestRateParameters(address token) internal view returns ( 17 | address model, 18 | uint256 cashPrior, 19 | uint256 borrowsPrior, 20 | uint256 reservesPrior, 21 | uint256 reserveFactorMantissa 22 | ) { 23 | ICToken cToken = ICToken(token); 24 | model = address(cToken.interestRateModel()); 25 | 26 | cashPrior = cToken.getCash(); 27 | borrowsPrior = cToken.totalBorrows(); 28 | reservesPrior = cToken.totalReserves(); 29 | uint256 accrualBlockNumber = cToken.accrualBlockNumber(); 30 | uint256 blockDelta = block.number - accrualBlockNumber; 31 | reserveFactorMantissa = cToken.reserveFactorMantissa(); 32 | if (blockDelta > 0) { 33 | uint256 borrowRateMantissa = getBorrowRate(address(model), cashPrior, borrowsPrior, reservesPrior); 34 | uint256 interestAccumulated = mulScalarTruncate(borrowRateMantissa.mul(blockDelta), borrowsPrior); 35 | borrowsPrior = borrowsPrior.add(interestAccumulated); 36 | reservesPrior = mulScalarTruncate(reserveFactorMantissa, interestAccumulated).add(reservesPrior); 37 | } 38 | } 39 | 40 | function currentExchangeRate(address token) internal view returns (uint256 exchangeRate) { 41 | ICToken cToken = ICToken(token); 42 | uint256 blockDelta = block.number - cToken.accrualBlockNumber(); 43 | if (blockDelta == 0) { 44 | return cToken.exchangeRateStored(); 45 | } 46 | 47 | IInterestRateModel model = cToken.interestRateModel(); 48 | 49 | uint256 cashPrior = cToken.getCash(); 50 | uint256 borrowsPrior = cToken.totalBorrows(); 51 | uint256 reservesPrior = cToken.totalReserves(); 52 | uint256 reserveFactorMantissa = cToken.reserveFactorMantissa(); 53 | if (blockDelta > 0) { 54 | uint256 borrowRateMantissa = getBorrowRate(address(model), cashPrior, borrowsPrior, reservesPrior); 55 | uint256 interestAccumulated = mulScalarTruncate(borrowRateMantissa.mul(blockDelta), borrowsPrior); 56 | borrowsPrior = borrowsPrior.add(interestAccumulated); 57 | reservesPrior = mulScalarTruncate(reserveFactorMantissa, interestAccumulated).add(reservesPrior); 58 | } 59 | 60 | return cashPrior.add(borrowsPrior).sub(reservesPrior).mul(1e18) / ICToken(token).totalSupply(); 61 | } 62 | 63 | 64 | function truncate(uint256 x) internal pure returns (uint256) { 65 | return x / EXP_SCALE; 66 | } 67 | 68 | function mulScalarTruncate(uint256 x, uint256 y) internal pure returns (uint256) { 69 | return truncate(x.mul(y)); 70 | } 71 | 72 | function mulScalarTruncateAddUInt(uint256 x, uint256 y, uint256 z) internal pure returns (uint256) { 73 | return mulScalarTruncate(x, y).add(z); 74 | } 75 | 76 | function getBorrowRate( 77 | address model, 78 | uint256 cash, 79 | uint256 borrows, 80 | uint256 reserves 81 | ) internal view returns (uint256 borrowRateMantissa) { 82 | (bool success, bytes memory retData) = model.staticcall( 83 | abi.encodeWithSelector( 84 | IInterestRateModel.getBorrowRate.selector, 85 | cash, 86 | borrows, 87 | reserves 88 | ) 89 | ); 90 | if (!success) revert(abi.decode(retData, (string))); 91 | assembly { 92 | switch lt(mload(retData), 64) 93 | case 0 {borrowRateMantissa := mload(add(retData, 64))} 94 | default {borrowRateMantissa := mload(add(retData, 32))} 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /contracts/libraries/CloneLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.7.6; 3 | 4 | /* 5 | The MIT License (MIT) 6 | Copyright (c) 2018 Murray Software, LLC. 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * EIP 1167 Proxy Deployment 27 | * Originally from https://github.com/optionality/clone-factory/ 28 | */ 29 | library CloneLibrary { 30 | function getCreateCode(address target) internal pure returns (bytes memory createCode) { 31 | // Reserve 55 bytes for the deploy code + 17 bytes as a buffer to prevent overwriting 32 | // other memory in the final mstore 33 | createCode = new bytes(72); 34 | assembly { 35 | let clone := add(createCode, 32) 36 | mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) 37 | mstore(add(clone, 0x14), shl(96, target)) 38 | mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) 39 | mstore(createCode, 55) 40 | } 41 | } 42 | 43 | function createClone(address target) internal returns (address result) { 44 | bytes memory createCode = getCreateCode(target); 45 | assembly { result := create(0, add(createCode, 32), 55) } 46 | } 47 | 48 | function createClone(address target, bytes32 salt) internal returns (address result) { 49 | bytes memory createCode = getCreateCode(target); 50 | assembly { result := create2(0, add(createCode, 32), 55, salt) } 51 | } 52 | 53 | function isClone(address target, address query) internal view returns (bool result) { 54 | assembly { 55 | let clone := mload(0x40) 56 | mstore(clone, 0x363d3d373d3d3d363d7300000000000000000000000000000000000000000000) 57 | mstore(add(clone, 0xa), shl(96, target)) 58 | mstore(add(clone, 0x1e), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) 59 | 60 | let other := add(clone, 0x40) 61 | extcodecopy(query, other, 0, 0x2d) 62 | result := and( 63 | eq(mload(clone), mload(other)), 64 | eq(mload(add(clone, 0xd)), mload(add(other, 0xd))) 65 | ) 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /contracts/libraries/CyTokenParams.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/CompoundInterfaces.sol"; 5 | import "./LowGasSafeMath.sol"; 6 | import "./MinimalSignedMath.sol"; 7 | 8 | 9 | library CyTokenParams { 10 | using LowGasSafeMath for uint256; 11 | using MinimalSignedMath for uint256; 12 | 13 | uint256 internal constant EXP_SCALE = 1e18; 14 | 15 | function getInterestRateParameters(address token) internal view returns ( 16 | address model, 17 | uint256 cashPrior, 18 | uint256 borrowsPrior, 19 | uint256 reservesPrior, 20 | uint256 reserveFactorMantissa 21 | ) { 22 | ICToken cToken = ICToken(token); 23 | model = address(cToken.interestRateModel()); 24 | 25 | cashPrior = cToken.getCash(); 26 | borrowsPrior = cToken.totalBorrows(); 27 | uint256 borrowsPriorForInterestCalculation = borrowsPrior.sub(cToken.borrowBalanceStored(0x560A8E3B79d23b0A525E15C6F3486c6A293DDAd2)); 28 | reservesPrior = cToken.totalReserves(); 29 | uint256 accrualBlockNumber = cToken.accrualBlockNumber(); 30 | uint256 blockDelta = block.number - accrualBlockNumber; 31 | reserveFactorMantissa = cToken.reserveFactorMantissa(); 32 | if (blockDelta > 0) { 33 | uint256 borrowRateMantissa = getBorrowRate(address(model), cashPrior, borrowsPriorForInterestCalculation, reservesPrior); 34 | uint256 interestAccumulated = mulScalarTruncate(borrowRateMantissa.mul(blockDelta), borrowsPriorForInterestCalculation); 35 | borrowsPrior = borrowsPrior.add(interestAccumulated); 36 | reservesPrior = mulScalarTruncate(reserveFactorMantissa, interestAccumulated).add(reservesPrior); 37 | } 38 | } 39 | 40 | function getSupplyRate(address token, int256 liquidityDelta) internal view returns (uint256) { 41 | ( 42 | address model, 43 | uint256 cashPrior, 44 | uint256 borrowsPrior, 45 | uint256 reservesPrior, 46 | uint256 reserveFactorMantissa 47 | ) = getInterestRateParameters(token); 48 | return IInterestRateModel(model).getSupplyRate( 49 | cashPrior.add(liquidityDelta), 50 | borrowsPrior, 51 | reservesPrior, 52 | reserveFactorMantissa 53 | ).mul(2102400); 54 | } 55 | 56 | function currentExchangeRate(address token) internal view returns (uint256 exchangeRate) { 57 | ICToken cToken = ICToken(token); 58 | uint256 blockDelta = block.number - cToken.accrualBlockNumber(); 59 | if (blockDelta == 0) { 60 | return cToken.exchangeRateStored(); 61 | } 62 | 63 | IInterestRateModel model = cToken.interestRateModel(); 64 | uint256 cashPrior = cToken.getCash(); 65 | uint256 borrowsPrior = cToken.totalBorrows(); 66 | uint256 borrowsPriorForInterestCalculation = borrowsPrior.sub(cToken.borrowBalanceStored(0x560A8E3B79d23b0A525E15C6F3486c6A293DDAd2)); 67 | uint256 reservesPrior = cToken.totalReserves(); 68 | uint256 reserveFactorMantissa = cToken.reserveFactorMantissa(); 69 | if (blockDelta > 0) { 70 | uint256 borrowRateMantissa = getBorrowRate(address(model), cashPrior, borrowsPriorForInterestCalculation, reservesPrior); 71 | uint256 interestAccumulated = mulScalarTruncate(borrowRateMantissa.mul(blockDelta), borrowsPriorForInterestCalculation); 72 | borrowsPrior = borrowsPrior.add(interestAccumulated); 73 | reservesPrior = mulScalarTruncate(reserveFactorMantissa, interestAccumulated).add(reservesPrior); 74 | } 75 | 76 | return cashPrior.add(borrowsPrior).sub(reservesPrior).mul(1e18) / ICToken(token).totalSupply(); 77 | } 78 | 79 | function truncate(uint256 x) internal pure returns (uint256) { 80 | return x / EXP_SCALE; 81 | } 82 | 83 | function mulScalarTruncate(uint256 x, uint256 y) internal pure returns (uint256) { 84 | return truncate(x.mul(y)); 85 | } 86 | 87 | function mulScalarTruncateAddUInt(uint256 x, uint256 y, uint256 z) internal pure returns (uint256) { 88 | return mulScalarTruncate(x, y).add(z); 89 | } 90 | 91 | function getBorrowRate( 92 | address model, 93 | uint256 cash, 94 | uint256 borrows, 95 | uint256 reserves 96 | ) internal view returns (uint256 borrowRateMantissa) { 97 | (bool success, bytes memory retData) = model.staticcall( 98 | abi.encodeWithSelector( 99 | IInterestRateModel.getBorrowRate.selector, 100 | cash, 101 | borrows, 102 | reserves 103 | ) 104 | ); 105 | if (!success) revert(abi.decode(retData, (string))); 106 | assembly { 107 | switch lt(mload(retData), 64) 108 | case 0 {borrowRateMantissa := mload(add(retData, 64))} 109 | default {borrowRateMantissa := mload(add(retData, 32))} 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /contracts/libraries/DynamicArrays.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.5.0; 3 | 4 | /** 5 | * @dev Library for handling dynamic in-memory arrays. 6 | * 7 | * There is a very good reason for Solidity not supporting this by default -- dynamic 8 | * arrays in memory completely break memory management for Solidity when used improperly; 9 | * however, they can be created manually in a safe way so long as the maximum size is known 10 | * beforehand. 11 | * 12 | * This applies primarily to situations where a subset is taken from an existing array 13 | * by some filtering process. 14 | * 15 | * This library should not be used to bypass Solidity's lack of dynamic memory array 16 | * support in any situation where the code could potentially cause the array to exceed 17 | * the maximum size assigned in the array creation call. Doing so is likely to have 18 | * unintended and unpredictable side effects. 19 | */ 20 | library DynamicArrays { 21 | /** 22 | * @dev Reserves space in memory for an array of length `size`, but sets the length to 0. 23 | * This can be safely used for a dynamic array so long as the maximum possible size is 24 | * known beforehand. If the array can exceed `size`, pushing to it will corrupt memory. 25 | */ 26 | function dynamicAddressArray(uint256 size) internal pure returns (address[] memory arr) { 27 | arr = new address[](size); 28 | assembly { mstore(arr, 0) } 29 | } 30 | 31 | /** 32 | * @dev Reserves space in memory for an array of length `size`, but sets the length to 0. 33 | * This can be safely used for a dynamic array so long as the maximum possible size is 34 | * known beforehand. If the array can exceed length `size`, pushing to it will corrupt memory. 35 | */ 36 | function dynamicUint256Array(uint256 size) internal pure returns (uint256[] memory arr) { 37 | arr = new uint256[](size); 38 | assembly { mstore(arr, 0) } 39 | } 40 | 41 | /** 42 | * @dev Pushes an address to an in-memory array by reassigning the array length and storing 43 | * the element in the position used by solidity for the current array index. 44 | * Note: This should ONLY be used on an array created with `dynamicAddressArray`. Using it 45 | * on a typical array created with `new address[]()` will almost certainly have unintended 46 | * and unpredictable side effects. 47 | */ 48 | function dynamicPush(address[] memory arr, address element) internal pure { 49 | assembly { 50 | let size := mload(arr) 51 | let ptr := add( 52 | add(arr, 32), 53 | mul(size, 32) 54 | ) 55 | mstore(ptr, element) 56 | mstore(arr, add(size, 1)) 57 | } 58 | } 59 | 60 | /** 61 | * @dev Pushes a uint256 to an in-memory array by reassigning the array length and storing 62 | * the element in the position used by solidity for the current array index. 63 | * Note: This should ONLY be used on an array created with `dynamicUint256Array`. Using it 64 | * on a typical array created with `new uint256[]()` will almost certainly have unintended 65 | * and unpredictable side effects. 66 | */ 67 | function dynamicPush(uint256[] memory arr, uint256 element) internal pure { 68 | assembly { 69 | let size := mload(arr) 70 | let ptr := add( 71 | add(arr, 32), 72 | mul(size, 32) 73 | ) 74 | mstore(ptr, element) 75 | mstore(arr, add(size, 1)) 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /contracts/libraries/Fraction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../libraries/LowGasSafeMath.sol"; 5 | 6 | 7 | library Fraction { 8 | using LowGasSafeMath for uint256; 9 | 10 | uint256 internal constant ONE_E18 = 1e18; 11 | 12 | function mulFractionE18(uint256 a, uint256 fraction) internal pure returns (uint256) { 13 | return a.mul(fraction) / ONE_E18; 14 | } 15 | 16 | function mulSubFractionE18(uint256 a, uint256 fraction) internal pure returns (uint256) { 17 | return a.sub(a.mul(fraction) / ONE_E18); 18 | } 19 | 20 | function toFractionE18(uint256 a, uint256 b) internal pure returns (uint256) { 21 | return a.mul(ONE_E18) / b; 22 | } 23 | } -------------------------------------------------------------------------------- /contracts/libraries/LowGasSafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.7.0; 3 | 4 | /************************************************************************************************ 5 | Originally from https://github.com/Uniswap/uniswap-v3-core/blob/main/contracts/libraries/LowGasSafeMath.sol 6 | 7 | This source code has been modified from the original, which was copied from the github repository 8 | at commit hash b83fcf497e895ae59b97c9d04e997023f69b5e97. 9 | 10 | Subject to the GPL-2.0 license 11 | *************************************************************************************************/ 12 | 13 | 14 | /// @title Optimized overflow and underflow safe math operations 15 | /// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost 16 | library LowGasSafeMath { 17 | /// @notice Returns x + y, reverts if sum overflows uint256 18 | /// @param x The augend 19 | /// @param y The addend 20 | /// @return z The sum of x and y 21 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 22 | require((z = x + y) >= x); 23 | } 24 | 25 | /// @notice Returns x + y, reverts if sum overflows uint256 26 | /// @param x The augend 27 | /// @param y The addend 28 | /// @return z The sum of x and y 29 | function add(uint256 x, uint256 y, string memory errorMessage) internal pure returns (uint256 z) { 30 | require((z = x + y) >= x, errorMessage); 31 | } 32 | 33 | /// @notice Returns x - y, reverts if underflows 34 | /// @param x The minuend 35 | /// @param y The subtrahend 36 | /// @return z The difference of x and y 37 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 38 | require(y <= x); 39 | z = x - y; 40 | } 41 | 42 | /// @notice Returns x - y, reverts if underflows 43 | /// @param x The minuend 44 | /// @param y The subtrahend 45 | /// @return z The difference of x and y 46 | function sub(uint256 x, uint256 y, string memory errorMessage) internal pure returns (uint256 z) { 47 | require(y <= x, errorMessage); 48 | z = x - y; 49 | } 50 | 51 | /// @notice Returns x * y, reverts if overflows 52 | /// @param x The multiplicand 53 | /// @param y The multiplier 54 | /// @return z The product of x and y 55 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 56 | if (x == 0) return 0; 57 | z = x * y; 58 | require(z / x == y); 59 | } 60 | 61 | /// @notice Returns x * y, reverts if overflows 62 | /// @param x The multiplicand 63 | /// @param y The multiplier 64 | /// @return z The product of x and y 65 | function mul(uint256 x, uint256 y, string memory errorMessage) internal pure returns (uint256 z) { 66 | if (x == 0) return 0; 67 | z = x * y; 68 | require(z / x == y, errorMessage); 69 | } 70 | 71 | /// @notice Returns ceil(x / y) 72 | /// @param x The numerator 73 | /// @param y The denominator 74 | /// @return z The quotient of x and y 75 | function divCeil(uint256 x, uint256 y) internal pure returns (uint256 z) { 76 | z = x % y == 0 ? x / y : (x/y) + 1; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /contracts/libraries/MinimalSignedMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | library MinimalSignedMath { 6 | function add(int256 a, int256 b) internal pure returns (int256) { 7 | int256 c = a + b; 8 | require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); 9 | 10 | return c; 11 | } 12 | 13 | function sub(int256 a, int256 b) internal pure returns (int256) { 14 | int256 c = a - b; 15 | require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); 16 | 17 | return c; 18 | } 19 | 20 | function add(uint256 a, int256 b) internal pure returns (uint256) { 21 | require(a < 2**255); 22 | int256 _a = int256(a); 23 | int256 c = _a + b; 24 | require((b >= 0 && c >= _a) || (b < 0 && c < _a)); 25 | if (c < 0) return 0; 26 | return uint256(c); 27 | } 28 | } -------------------------------------------------------------------------------- /contracts/libraries/RayDiv.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | import "./LowGasSafeMath.sol"; 4 | 5 | 6 | library RayDiv { 7 | using LowGasSafeMath for uint256; 8 | uint256 internal constant RAY = 1e27; 9 | 10 | 11 | function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { 12 | uint256 halfB = b / 2; 13 | return halfB.add(a.mul(RAY)) / b; 14 | } 15 | } -------------------------------------------------------------------------------- /contracts/libraries/RayMul.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | 5 | library RayMul { 6 | uint256 internal constant RAY = 1e27; 7 | uint256 internal constant halfRAY = RAY / 2; 8 | 9 | /** 10 | * @dev Multiplies two ray, rounding half up to the nearest ray 11 | * @param a Ray 12 | * @param b Ray 13 | * @return The result of a*b, in ray 14 | **/ 15 | function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { 16 | if (a == 0 || b == 0) { 17 | return 0; 18 | } 19 | 20 | require(a <= (type(uint256).max - halfRAY) / b, "rayMul overflow"); 21 | 22 | return (a * b + halfRAY) / RAY; 23 | } 24 | } -------------------------------------------------------------------------------- /contracts/libraries/RebalanceValidation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.6; 3 | 4 | import "../interfaces/IAdapterRegistry.sol"; 5 | import "../interfaces/ITokenAdapter.sol"; 6 | import "../libraries/LowGasSafeMath.sol"; 7 | import "../libraries/Fraction.sol"; 8 | 9 | 10 | 11 | library RebalanceValidation { 12 | using LowGasSafeMath for uint256; 13 | using Fraction for uint256; 14 | 15 | function validateSufficientImprovement( 16 | uint256 currentAPR, 17 | uint256 newAPR, 18 | uint256 minImprovement 19 | ) internal pure { 20 | require( 21 | newAPR.sub(currentAPR, "!increased").toFractionE18(currentAPR) >= minImprovement, 22 | "insufficient improvement" 23 | ); 24 | } 25 | 26 | function validateProposedWeights( 27 | uint256[] memory currentWeights, 28 | uint256[] memory proposedWeights 29 | ) internal pure { 30 | uint256 len = currentWeights.length; 31 | require(proposedWeights.length == len, "bad lengths"); 32 | uint256 _sum; 33 | for (uint256 i; i < len; i++) { 34 | uint256 weight = proposedWeights[i]; 35 | _sum = _sum.add(weight); 36 | if (weight == 0) { 37 | require(currentWeights[i] == 0, "can not set null weight"); 38 | } else { 39 | require(weight >= 5e16, "weight < 5%"); 40 | } 41 | } 42 | require(_sum == 1e18, "weights != 100%"); 43 | } 44 | 45 | function validateAdaptersAndWeights( 46 | IAdapterRegistry registry, 47 | address underlying, 48 | IErc20Adapter[] memory adapters, 49 | uint256[] memory weights 50 | ) internal view { 51 | uint256 len = adapters.length; 52 | require(weights.length == len, "bad lengths"); 53 | uint256 totalWeight; 54 | for (uint256 i; i < len; i++) { 55 | IErc20Adapter adapter = adapters[i]; 56 | require(registry.isApprovedAdapter(address(adapter)), "!approved"); 57 | require(adapter.underlying() == underlying, "bad adapter"); 58 | for (uint256 j = i + 1; j < len; j++) { 59 | require(address(adapter) != address(adapters[j]), "duplicate adapter"); 60 | } 61 | uint256 weight = weights[i]; 62 | totalWeight = totalWeight.add(weight); 63 | require(weight >= 5e16, "weight < 5%"); 64 | } 65 | require(totalWeight == 1e18, "weights != 100%"); 66 | } 67 | } -------------------------------------------------------------------------------- /contracts/libraries/ReserveConfigurationLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/AaveV2Interfaces.sol"; 5 | 6 | 7 | library ReserveConfigurationLib { 8 | uint256 internal constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore 9 | uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore 10 | uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64; 11 | 12 | /** 13 | * @dev Gets the reserve factor of the reserve 14 | * @param self The reserve configuration 15 | * @return The reserve factor 16 | **/ 17 | function getReserveFactor(ILendingPool.ReserveConfigurationMap memory self) 18 | internal 19 | pure 20 | returns (uint256) 21 | { 22 | return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; 23 | } 24 | 25 | function isFrozen(ILendingPool.ReserveConfigurationMap memory self) 26 | internal 27 | pure 28 | returns (bool) 29 | { 30 | return (self.data & ~FROZEN_MASK) != 0; 31 | } 32 | } -------------------------------------------------------------------------------- /contracts/libraries/SafeCast.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity >=0.5.0; 3 | 4 | /************************************************************************************************ 5 | Originally from https://github.com/Uniswap/uniswap-v3-core/blob/main/contracts/libraries/SafeCast.sol 6 | 7 | This source code has been modified from the original, which was copied from the github repository 8 | at commit hash b83fcf497e895ae59b97c9d04e997023f69b5e97. 9 | 10 | Subject to the GPL-2.0 license 11 | *************************************************************************************************/ 12 | 13 | 14 | /// @title Safe casting methods 15 | /// @notice Contains methods for safely casting between types 16 | library SafeCast { 17 | /// @notice Cast a uint256 to a uint160, revert on overflow 18 | /// @param y The uint256 to be downcasted 19 | /// @return z The downcasted integer, now type uint160 20 | function toUint160(uint256 y) internal pure returns (uint160 z) { 21 | require((z = uint160(y)) == y); 22 | } 23 | 24 | /// @notice Cast a uint256 to a uint128, revert on overflow 25 | /// @param y The uint256 to be downcasted 26 | /// @return z The downcasted integer, now type uint128 27 | function toUint128(uint256 y) internal pure returns (uint128 z) { 28 | require((z = uint128(y)) == y); 29 | } 30 | 31 | /// @notice Cast a uint256 to a uint96, revert on overflow 32 | /// @param y The uint256 to be downcasted 33 | /// @return z The downcasted integer, now type uint96 34 | function toUint96(uint256 y) internal pure returns (uint96 z) { 35 | require((z = uint96(y)) == y); 36 | } 37 | 38 | /// @notice Cast a int256 to a int128, revert on overflow or underflow 39 | /// @param y The int256 to be downcasted 40 | /// @return z The downcasted integer, now type int128 41 | function toInt128(int256 y) internal pure returns (int128 z) { 42 | require((z = int128(y)) == y); 43 | } 44 | 45 | /// @notice Cast a uint256 to a int256, revert on overflow 46 | /// @param y The uint256 to be casted 47 | /// @return z The casted integer, now type int256 48 | function toInt256(uint256 y) internal pure returns (int256 z) { 49 | require(y < 2**255); 50 | z = int256(y); 51 | } 52 | 53 | /// @notice Cast an int256 to a uint256, revert on overflow 54 | /// @param y The uint256 to be downcasted 55 | /// @return z The downcasted integer, now type uint160 56 | function toUint256(int256 y) internal pure returns (uint256 z) { 57 | require(y >= 0); 58 | z = uint256(y); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/libraries/SortLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.5.0; 3 | 4 | 5 | library SortLibrary { 6 | /** 7 | * @dev Given a list of tokens and their scores, sort by scores 8 | * in descending order. 9 | */ 10 | function sortByDescendingScore( 11 | address[] memory tokens, 12 | uint256[] memory scores 13 | ) internal pure { 14 | uint256 len = tokens.length; 15 | for (uint256 i = 0; i < len; i++) { 16 | uint256 score = scores[i]; 17 | address token = tokens[i]; 18 | uint256 j = i - 1; 19 | while (int(j) >= 0 && scores[j] < score) { 20 | scores[j + 1] = scores[j]; 21 | tokens[j + 1] = tokens[j]; 22 | j--; 23 | } 24 | scores[j + 1] = score; 25 | tokens[j + 1] = token; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /contracts/libraries/SymbolHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/IERC20Metadata.sol"; 5 | 6 | 7 | library SymbolHelper { 8 | 9 | /** 10 | * @dev Returns the index of the lowest bit set in `self`. 11 | * Note: Requires that `self != 0` 12 | */ 13 | function lowestBitSet(uint256 self) internal pure returns (uint256 _z) { 14 | require (self > 0, "Bits::lowestBitSet: Value 0 has no bits set"); 15 | uint256 _magic = 0x00818283848586878898a8b8c8d8e8f929395969799a9b9d9e9faaeb6bedeeff; 16 | uint256 val = (self & -self) * _magic >> 248; 17 | uint256 _y = val >> 5; 18 | _z = ( 19 | _y < 4 20 | ? _y < 2 21 | ? _y == 0 22 | ? 0x753a6d1b65325d0c552a4d1345224105391a310b29122104190a110309020100 23 | : 0xc976c13bb96e881cb166a933a55e490d9d56952b8d4e801485467d2362422606 24 | : _y == 2 25 | ? 0xe39ed557db96902cd38ed14fad815115c786af479b7e83247363534337271707 26 | : 0xf7cae577eec2a03cf3bad76fb589591debb2dd67e0aa9834bea6925f6a4a2e0e 27 | : _y < 6 28 | ? _y == 4 29 | ? 0xc8c0b887b0a8a4489c948c7f847c6125746c645c544c444038302820181008ff 30 | : 0xf6e4ed9ff2d6b458eadcdf97bd91692de2d4da8fd2d0ac50c6ae9a8272523616 31 | : _y == 6 32 | ? 0xf5ecf1b3e9debc68e1d9cfabc5997135bfb7a7a3938b7b606b5b4b3f2f1f0ffe 33 | : 0xf8f9cbfae6cc78fbefe7cdc3a1793dfcf4f0e8bbd8cec470b6a28a7a5a3e1efd 34 | ); 35 | _z >>= (val & 0x1f) << 3; 36 | return _z & 0xff; 37 | } 38 | 39 | function getSymbol(address token) internal view returns (string memory) { 40 | (bool success, bytes memory data) = token.staticcall(abi.encodeWithSignature("symbol()")); 41 | if (!success) return "UNKNOWN"; 42 | if (data.length != 32) return abi.decode(data, (string)); 43 | uint256 symbol = abi.decode(data, (uint256)); 44 | if (symbol == 0) return "UNKNOWN"; 45 | uint256 emptyBits = 255 - lowestBitSet(symbol); 46 | uint256 size = (emptyBits / 8) + (emptyBits % 8 > 0 ? 1 : 0); 47 | assembly { mstore(data, size) } 48 | return string(data); 49 | } 50 | 51 | function getName(address token) internal view returns (string memory) { 52 | (bool success, bytes memory data) = token.staticcall(abi.encodeWithSignature("name()")); 53 | if (!success) return "UNKNOWN"; 54 | if (data.length != 32) return abi.decode(data, (string)); 55 | uint256 symbol = abi.decode(data, (uint256)); 56 | if (symbol == 0) return "UNKNOWN"; 57 | uint256 emptyBits = 255 - lowestBitSet(symbol); 58 | uint256 size = (emptyBits / 8) + (emptyBits % 8 > 0 ? 1 : 0); 59 | assembly { mstore(data, size) } 60 | return string(data); 61 | } 62 | 63 | function getPrefixedSymbol(string memory prefix, address token) internal view returns (string memory prefixedSymbol) { 64 | prefixedSymbol = string(abi.encodePacked( 65 | prefix, 66 | getSymbol(token) 67 | )); 68 | } 69 | 70 | function getPrefixedName(string memory prefix, address token) internal view returns (string memory prefixedName) { 71 | prefixedName = string(abi.encodePacked( 72 | prefix, 73 | getName(token) 74 | )); 75 | } 76 | } -------------------------------------------------------------------------------- /contracts/libraries/TransferHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | /************************************************************************************************ 5 | Originally from https://github.com/Uniswap/uniswap-lib/blob/master/contracts/libraries/TransferHelper.sol 6 | 7 | This source code has been modified from the original, which was copied from the github repository 8 | at commit hash cfedb1f55864dcf8cc0831fdd8ec18eb045b7fd1. 9 | 10 | Subject to the MIT license 11 | *************************************************************************************************/ 12 | 13 | 14 | library TransferHelper { 15 | function safeApproveMax(address token, address to) internal { 16 | safeApprove(token, to, type(uint256).max); 17 | } 18 | 19 | function safeUnapprove(address token, address to) internal { 20 | safeApprove(token, to, 0); 21 | } 22 | 23 | function safeApprove(address token, address to, uint value) internal { 24 | // bytes4(keccak256(bytes("approve(address,uint256)"))); 25 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 26 | require(success && (data.length == 0 || abi.decode(data, (bool))), "TH:SA"); 27 | } 28 | 29 | function safeTransfer(address token, address to, uint value) internal { 30 | // bytes4(keccak256(bytes("transfer(address,uint256)"))); 31 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 32 | require(success && (data.length == 0 || abi.decode(data, (bool))), "TH:ST"); 33 | } 34 | 35 | function safeTransferFrom(address token, address from, address to, uint value) internal { 36 | // bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); 37 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 38 | require(success && (data.length == 0 || abi.decode(data, (bool))), "TH:STF"); 39 | } 40 | 41 | function safeTransferETH(address to, uint256 value) internal { 42 | (bool success, ) = to.call{value: value}(""); 43 | require(success, "TH:STE"); 44 | } 45 | } -------------------------------------------------------------------------------- /contracts/protocols/AaveV1ProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/AaveV1Interfaces.sol"; 6 | import "../adapters/aave-v1/AaveV1Erc20Adapter.sol"; 7 | import "../adapters/aave-v1/AaveV1EtherAdapter.sol"; 8 | import "./AbstractProtocolAdapter.sol"; 9 | 10 | 11 | contract AaveV1ProtocolAdapter is AbstractProtocolAdapter { 12 | using CloneLibrary for address; 13 | 14 | /* ========== Constants ========== */ 15 | 16 | ILendingPoolAddressesProvider public constant aave = ILendingPoolAddressesProvider(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8); 17 | ILendingPoolCore public immutable core; 18 | address public constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 19 | address public immutable erc20AdapterImplementation; 20 | address public immutable etherAdapterImplementation; 21 | 22 | /* ========== Constructor ========== */ 23 | 24 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 25 | core = aave.getLendingPoolCore(); 26 | erc20AdapterImplementation = address(new AaveV1Erc20Adapter(aave)); 27 | etherAdapterImplementation = address(new AaveV1EtherAdapter(aave)); 28 | } 29 | 30 | /* ========== Internal Actions ========== */ 31 | 32 | function deployAdapter(address underlying) internal virtual override returns (address adapter) { 33 | if (underlying == ETH_RESERVE_ADDRESS) { 34 | adapter = etherAdapterImplementation.createClone(); 35 | AaveV1EtherAdapter(payable(adapter)).initialize(weth, core.getReserveATokenAddress(underlying)); 36 | } else { 37 | adapter = erc20AdapterImplementation.createClone(); 38 | AaveV1Erc20Adapter(adapter).initialize(underlying, core.getReserveATokenAddress(underlying)); 39 | } 40 | } 41 | 42 | /* ========== Public Queries ========== */ 43 | 44 | function protocol() external pure virtual override returns (string memory) { 45 | return "Aave V1"; 46 | } 47 | 48 | function getUnmapped() public view virtual override returns (address[] memory tokens) { 49 | tokens = core.getReserves(); 50 | uint256 len = tokens.length; 51 | uint256 prevLen = totalMapped; 52 | if (len == prevLen) { 53 | assembly { mstore(tokens, 0) } 54 | } else { 55 | assembly { 56 | tokens := add(tokens, mul(prevLen, 32)) 57 | mstore(tokens, sub(len, prevLen)) 58 | } 59 | } 60 | } 61 | 62 | /* ========== Internal Queries ========== */ 63 | 64 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 65 | return isTokenMarketFrozen(IErc20Adapter(adapter).underlying()); 66 | } 67 | 68 | function isTokenMarketFrozen(address underlying) internal view virtual override returns (bool) { 69 | return core.getReserveIsFreezed(underlying); 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /contracts/protocols/AaveV2ProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/AaveV2Interfaces.sol"; 6 | import "../adapters/aave-v2/AaveV2Erc20Adapter.sol"; 7 | import "../adapters/aave-v2/AaveV2EtherAdapter.sol"; 8 | import "../libraries/ReserveConfigurationLib.sol"; 9 | import "./AbstractProtocolAdapter.sol"; 10 | 11 | 12 | contract AaveV2ProtocolAdapter is AbstractProtocolAdapter { 13 | using ReserveConfigurationLib for ILendingPool.ReserveConfigurationMap; 14 | 15 | /* ========== Constants ========== */ 16 | 17 | ILendingPoolAddressesProvider public constant aave = ILendingPoolAddressesProvider(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5); 18 | ILendingPool public immutable pool; 19 | address public immutable erc20AdapterImplementation; 20 | 21 | /* ========== Constructor ========== */ 22 | 23 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 24 | erc20AdapterImplementation = address(new AaveV2Erc20Adapter(aave)); 25 | pool = aave.getLendingPool(); 26 | } 27 | 28 | /* ========== Internal Actions ========== */ 29 | 30 | function deployAdapter(address underlying) internal override returns (address adapter) { 31 | address aToken = pool.getReserveData(underlying).aTokenAddress; 32 | if (underlying == weth) { 33 | adapter = address(new AaveV2EtherAdapter(aave, underlying, aToken)); 34 | } else { 35 | adapter = CloneLibrary.createClone(erc20AdapterImplementation); 36 | AaveV2Erc20Adapter(adapter).initialize(underlying, aToken); 37 | } 38 | } 39 | 40 | /* ========== Public Queries ========== */ 41 | 42 | function protocol() external pure virtual override returns (string memory) { 43 | return "Aave V2"; 44 | } 45 | 46 | function getUnmapped() public view virtual override returns (address[] memory tokens) { 47 | tokens = pool.getReservesList(); 48 | uint256 len = tokens.length; 49 | uint256 prevLen = totalMapped; 50 | if (len == prevLen) { 51 | assembly { mstore(tokens, 0) } 52 | } else { 53 | assembly { 54 | tokens := add(tokens, mul(prevLen, 32)) 55 | mstore(tokens, sub(len, prevLen)) 56 | } 57 | } 58 | } 59 | 60 | /* ========== Internal Queries ========== */ 61 | 62 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 63 | return isTokenMarketFrozen(IErc20Adapter(adapter).underlying()); 64 | } 65 | 66 | function isTokenMarketFrozen(address underlying) internal view virtual override returns (bool) { 67 | return pool.getConfiguration(underlying).isFrozen(); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /contracts/protocols/CompoundProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/CompoundInterfaces.sol"; 6 | import "../adapters/compound/C1Erc20Adapter.sol"; 7 | import "../adapters/compound/CErc20Adapter.sol"; 8 | import "../adapters/compound/CEtherAdapter.sol"; 9 | import "./AbstractProtocolAdapter.sol"; 10 | 11 | 12 | contract CompoundProtocolAdapter is AbstractProtocolAdapter { 13 | using CloneLibrary for address; 14 | 15 | /* ========== Constants ========== */ 16 | 17 | IComptroller public constant comptroller = IComptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); 18 | address public constant interestRateModelV1 = 0xBAE04CbF96391086dC643e842b517734E214D698; 19 | address public immutable erc20AdapterImplementationV1; 20 | address public immutable erc20AdapterImplementation; 21 | address public immutable etherAdapterImplementation; 22 | 23 | /* ========== Constructor ========== */ 24 | 25 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 26 | erc20AdapterImplementationV1 = address(new C1Erc20Adapter()); 27 | erc20AdapterImplementation = address(new CErc20Adapter()); 28 | etherAdapterImplementation = address(new CEtherAdapter()); 29 | } 30 | 31 | /* ========== Internal Actions ========== */ 32 | 33 | function deployAdapter(address cToken) internal virtual override returns (address adapter) { 34 | address underlying; 35 | // The call to underlying() will use all the gas sent if it fails, 36 | // so we specify a maximum of 25k gas. The contract will only use ~2k 37 | // but this protects against all likely changes to the gas schedule. 38 | try ICToken(cToken).underlying{gas: 25000}() returns (address _underlying) { 39 | underlying = _underlying; 40 | if (underlying == address(0)) { 41 | underlying = weth; 42 | } 43 | } catch { 44 | underlying = weth; 45 | } 46 | if (underlying == weth) { 47 | adapter = etherAdapterImplementation.createClone(); 48 | } else if (address(ICToken(cToken).interestRateModel()) == interestRateModelV1) { 49 | adapter = erc20AdapterImplementationV1.createClone(); 50 | } else { 51 | adapter = erc20AdapterImplementation.createClone(); 52 | } 53 | CErc20Adapter(adapter).initialize(underlying, cToken); 54 | } 55 | 56 | /* ========== Public Queries ========== */ 57 | 58 | function protocol() external pure virtual override returns (string memory) { 59 | return "Compound"; 60 | } 61 | 62 | function getUnmapped() public view virtual override returns (address[] memory cTokens) { 63 | cTokens = toAddressArray(comptroller.getAllMarkets()); 64 | uint256 len = cTokens.length; 65 | uint256 prevLen = totalMapped; 66 | if (len == prevLen) { 67 | assembly { mstore(cTokens, 0) } 68 | } else { 69 | assembly { 70 | cTokens := add(cTokens, mul(prevLen, 32)) 71 | mstore(cTokens, sub(len, prevLen)) 72 | } 73 | } 74 | } 75 | 76 | function toAddressArray(ICToken[] memory cTokens) internal pure returns (address[] memory arr) { 77 | assembly { arr := cTokens } 78 | } 79 | 80 | /* ========== Internal Queries ========== */ 81 | 82 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 83 | return isTokenMarketFrozen(IErc20Adapter(adapter).token()); 84 | } 85 | 86 | function isTokenMarketFrozen(address cToken) internal view virtual override returns (bool) { 87 | if (comptroller.mintGuardianPaused(cToken)) { 88 | return true; 89 | } 90 | return IERC20(cToken).totalSupply() == 0; 91 | } 92 | } -------------------------------------------------------------------------------- /contracts/protocols/CreamProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/CompoundInterfaces.sol"; 6 | import "../adapters/cream/CrErc20Adapter.sol"; 7 | import "../adapters/cream/CrEtherAdapter.sol"; 8 | import "./AbstractProtocolAdapter.sol"; 9 | 10 | 11 | contract CreamProtocolAdapter is AbstractProtocolAdapter { 12 | using CloneLibrary for address; 13 | 14 | /* ========== Constants ========== */ 15 | 16 | IComptroller public constant comptroller = IComptroller(0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258); 17 | address public immutable erc20AdapterImplementation; 18 | address public immutable etherAdapterImplementation; 19 | 20 | /* ========== Constructor ========== */ 21 | 22 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 23 | erc20AdapterImplementation = address(new CrErc20Adapter()); 24 | etherAdapterImplementation = address(new CrEtherAdapter()); 25 | } 26 | 27 | /* ========== Internal Actions ========== */ 28 | 29 | function deployAdapter(address cToken) internal virtual override returns (address adapter) { 30 | address underlying; 31 | // The call to underlying will use all the gas sent if it fails, 32 | // so we specify a maximum of 25k gas. The contract will only use ~2k 33 | // but this protects against all likely changes to the gas schedule. 34 | try ICToken(cToken).underlying{gas: 25000}() returns (address _underlying) { 35 | underlying = _underlying; 36 | if (underlying == address(0)) { 37 | underlying = weth; 38 | } 39 | } catch { 40 | underlying = weth; 41 | } 42 | if (underlying == weth) { 43 | adapter = CloneLibrary.createClone(etherAdapterImplementation); 44 | } else { 45 | adapter = CloneLibrary.createClone(erc20AdapterImplementation); 46 | } 47 | CrErc20Adapter(adapter).initialize(underlying, address(cToken)); 48 | } 49 | 50 | /* ========== Public Queries ========== */ 51 | 52 | function protocol() external pure virtual override returns (string memory) { 53 | return "Cream"; 54 | } 55 | 56 | function getUnmapped() public view virtual override returns (address[] memory cTokens) { 57 | cTokens = toAddressArray(comptroller.getAllMarkets()); 58 | uint256 len = cTokens.length; 59 | uint256 prevLen = totalMapped; 60 | if (len == prevLen) { 61 | assembly { mstore(cTokens, 0) } 62 | } else { 63 | assembly { 64 | cTokens := add(cTokens, mul(prevLen, 32)) 65 | mstore(cTokens, sub(len, prevLen)) 66 | } 67 | } 68 | } 69 | 70 | function toAddressArray(ICToken[] memory cTokens) internal pure returns (address[] memory arr) { 71 | assembly { arr := cTokens } 72 | } 73 | 74 | /* ========== Internal Queries ========== */ 75 | 76 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 77 | return comptroller.mintGuardianPaused(IErc20Adapter(adapter).token()); 78 | } 79 | 80 | function isTokenMarketFrozen(address cToken) internal view virtual override returns (bool) { 81 | // Return true if market is paused in comptroller 82 | bool isFrozen = comptroller.mintGuardianPaused(cToken); 83 | if (isFrozen) return true; 84 | // Return true if market is for an SLP token, which the adapter can not handle. 85 | // The call to `sushi()` will use all the gas sent if it fails, so we specify a 86 | // maximum of 25k gas to ensure it will not use all the gas in the transaction, but 87 | // can still be executed with any foreseeable changes to the gas schedule. 88 | try ICToken(cToken).sushi{gas:25000}() returns (address) { 89 | return true; 90 | } catch { 91 | // Return true is supply is 0. 92 | return IERC20(cToken).totalSupply() == 0; 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /contracts/protocols/FulcrumProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/FulcrumInterfaces.sol"; 6 | import "../adapters/fulcrum/FulcrumErc20Adapter.sol"; 7 | import "../adapters/fulcrum/FulcrumEtherAdapter.sol"; 8 | import "../interfaces/IAdapterRegistry.sol"; 9 | import "./AbstractProtocolAdapter.sol"; 10 | 11 | 12 | contract FulcrumProtocolAdapter is AbstractProtocolAdapter { 13 | 14 | /* ========== Constants ========== */ 15 | 16 | IBZX public constant bzx = IBZX(0xD8Ee69652E4e4838f2531732a46d1f7F584F0b7f); 17 | address public immutable erc20AdapterImplementation; 18 | 19 | /* ========== Constructor ========== */ 20 | 21 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 22 | address _erc20AdapterImplementation = address(new FulcrumErc20Adapter()); 23 | erc20AdapterImplementation = _erc20AdapterImplementation; 24 | 25 | address[] memory loanPoolsZeroAndOne = bzx.getLoanPoolsList(0, 2); 26 | address underlying0 = bzx.loanPoolToUnderlying(loanPoolsZeroAndOne[0]); 27 | address adapter0 = CloneLibrary.createClone(_erc20AdapterImplementation); 28 | FulcrumErc20Adapter(adapter0).initialize(underlying0, loanPoolsZeroAndOne[0]); 29 | 30 | _registry.addTokenAdapter(adapter0); 31 | _registry.addTokenAdapter(address(new FulcrumEtherAdapter(weth, loanPoolsZeroAndOne[1]))); 32 | 33 | totalMapped = 2; 34 | } 35 | 36 | /* ========== Internal Actions ========== */ 37 | 38 | function deployAdapter(address loanPool) internal virtual override returns (address adapter) { 39 | address underlying = bzx.loanPoolToUnderlying(loanPool); 40 | adapter = CloneLibrary.createClone(erc20AdapterImplementation); 41 | FulcrumErc20Adapter(adapter).initialize(underlying, loanPool); 42 | } 43 | 44 | /* ========== Public Queries ========== */ 45 | 46 | function protocol() external pure virtual override returns (string memory) { 47 | return "Fulcrum"; 48 | } 49 | 50 | function getUnmapped() public view virtual override returns (address[] memory loanPools) { 51 | loanPools = bzx.getLoanPoolsList(totalMapped, 1e18); 52 | } 53 | 54 | function getUnmappedUpTo(uint256 max) public view virtual override returns (address[] memory loanPools) { 55 | loanPools = bzx.getLoanPoolsList(totalMapped, max); 56 | } 57 | 58 | /* ========== Internal Queries ========== */ 59 | 60 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 61 | return isTokenMarketFrozen(IErc20Adapter(adapter).token()); 62 | } 63 | 64 | function isTokenMarketFrozen(address loanPool) internal view virtual override returns (bool) { 65 | return IERC20(loanPool).totalSupply() == 0; 66 | } 67 | } -------------------------------------------------------------------------------- /contracts/protocols/FusePoolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/FuseInterfaces.sol"; 6 | import "../adapters/fuse/FuseErc20Adapter.sol"; 7 | import "../adapters/fuse/FuseEtherAdapter.sol"; 8 | import "./AbstractProtocolAdapter.sol"; 9 | 10 | 11 | contract FuseProtocolAdapter is AbstractProtocolAdapter { 12 | using CloneLibrary for address; 13 | 14 | /* ========== Constants ========== */ 15 | 16 | address public immutable fuseProtocolAdapter; 17 | address public immutable erc20AdapterImplementation; 18 | address public immutable etherAdapterImplementation; 19 | 20 | /* ========== Storage ========== */ 21 | 22 | IFusePool public pool; 23 | string internal _fusePoolName; 24 | 25 | /* ========== Constructor ========== */ 26 | 27 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 28 | erc20AdapterImplementation = address(new FuseErc20Adapter()); 29 | etherAdapterImplementation = address(new FuseEtherAdapter()); 30 | fuseProtocolAdapter = msg.sender; 31 | } 32 | 33 | function initialize(IFusePool _pool, string memory fusePoolName) external { 34 | require(msg.sender == fuseProtocolAdapter, "!fuse adapter"); 35 | require(address(pool) == address(0), "already initialized"); 36 | pool = _pool; 37 | _fusePoolName = fusePoolName; 38 | } 39 | 40 | /* ========== Internal Actions ========== */ 41 | 42 | function deployAdapter(address fToken) internal virtual override returns (address adapter) { 43 | address underlying; 44 | // The call to underlying will use all the gas sent if it fails, 45 | // so we specify a maximum of 25k gas. The contract will only use ~2k 46 | // but this protects against all likely changes to the gas schedule. 47 | try IFToken(fToken).underlying{gas: 25000}() returns (address _underlying) { 48 | underlying = _underlying; 49 | if (underlying == address(0)) { 50 | underlying = weth; 51 | } 52 | } catch { 53 | underlying = weth; 54 | } 55 | if (underlying == weth) { 56 | adapter = CloneLibrary.createClone(etherAdapterImplementation); 57 | } else { 58 | adapter = CloneLibrary.createClone(erc20AdapterImplementation); 59 | } 60 | FuseErc20Adapter(adapter).initialize(underlying, fToken, _fusePoolName); 61 | } 62 | 63 | /* ========== Public Queries ========== */ 64 | 65 | function protocol() external view virtual override returns (string memory) { 66 | return _fusePoolName; 67 | } 68 | 69 | function getUnmapped() public view virtual override returns (address[] memory fTokens) { 70 | fTokens = toAddressArray(pool.getAllMarkets()); 71 | uint256 len = fTokens.length; 72 | uint256 prevLen = totalMapped; 73 | if (len == prevLen) { 74 | assembly { mstore(fTokens, 0) } 75 | } else { 76 | assembly { 77 | fTokens := add(fTokens, mul(prevLen, 32)) 78 | mstore(fTokens, sub(len, prevLen)) 79 | } 80 | } 81 | } 82 | 83 | function toAddressArray(IFToken[] memory fTokens) internal pure returns (address[] memory arr) { 84 | assembly { arr := fTokens } 85 | } 86 | 87 | /* ========== Internal Queries ========== */ 88 | 89 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 90 | return isTokenMarketFrozen(IErc20Adapter(adapter).token()); 91 | } 92 | 93 | function isTokenMarketFrozen(address fToken) internal view virtual override returns (bool) { 94 | return pool.mintGuardianPaused(fToken); 95 | } 96 | } -------------------------------------------------------------------------------- /contracts/protocols/IronBankProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../interfaces/CompoundInterfaces.sol"; 6 | import "../adapters/ironbank/CyErc20Adapter.sol"; 7 | import "../adapters/ironbank/CyEtherAdapter.sol"; 8 | import "./AbstractProtocolAdapter.sol"; 9 | 10 | 11 | contract IronBankProtocolAdapter is AbstractProtocolAdapter { 12 | using CloneLibrary for address; 13 | 14 | /* ========== Constants ========== */ 15 | 16 | IComptroller public constant comptroller = IComptroller(0xAB1c342C7bf5Ec5F02ADEA1c2270670bCa144CbB); 17 | address public immutable erc20AdapterImplementation; 18 | address public immutable etherAdapterImplementation; 19 | 20 | /* ========== Constructor ========== */ 21 | 22 | constructor(IAdapterRegistry _registry) AbstractProtocolAdapter(_registry) { 23 | erc20AdapterImplementation = address(new CyErc20Adapter()); 24 | etherAdapterImplementation = address(new CyEtherAdapter()); 25 | } 26 | 27 | /* ========== Internal Actions ========== */ 28 | 29 | function deployAdapter(address cToken) internal virtual override returns (address adapter) { 30 | address underlying; 31 | // The call to underlying will use all the gas sent if it fails, 32 | // so we specify a maximum of 25k gas. The contract will only use ~2k 33 | // but this protects against all likely changes to the gas schedule. 34 | try ICToken(cToken).underlying{gas: 25000}() returns (address _underlying) { 35 | underlying = _underlying; 36 | if (underlying == address(0)) { 37 | underlying = weth; 38 | } 39 | } catch { 40 | underlying = weth; 41 | } 42 | if (underlying == weth) { 43 | adapter = CloneLibrary.createClone(etherAdapterImplementation); 44 | } else { 45 | adapter = CloneLibrary.createClone(erc20AdapterImplementation); 46 | } 47 | CyErc20Adapter(adapter).initialize(underlying, address(cToken)); 48 | } 49 | 50 | /* ========== Public Queries ========== */ 51 | 52 | function protocol() external pure virtual override returns (string memory) { 53 | return "Iron Bank"; 54 | } 55 | 56 | function getUnmapped() public view virtual override returns (address[] memory cTokens) { 57 | cTokens = toAddressArray(comptroller.getAllMarkets()); 58 | uint256 len = cTokens.length; 59 | uint256 prevLen = totalMapped; 60 | if (len == prevLen) { 61 | assembly { mstore(cTokens, 0) } 62 | } else { 63 | assembly { 64 | cTokens := add(cTokens, mul(prevLen, 32)) 65 | mstore(cTokens, sub(len, prevLen)) 66 | } 67 | } 68 | } 69 | 70 | function toAddressArray(ICToken[] memory cTokens) internal pure returns (address[] memory arr) { 71 | assembly { arr := cTokens } 72 | } 73 | 74 | /* ========== Internal Queries ========== */ 75 | 76 | function isAdapterMarketFrozen(address adapter) internal view virtual override returns (bool) { 77 | return isTokenMarketFrozen(IErc20Adapter(adapter).token()); 78 | } 79 | 80 | function isTokenMarketFrozen(address cToken) internal view virtual override returns (bool) { 81 | if (comptroller.mintGuardianPaused(cToken)) { 82 | return true; 83 | } 84 | return IERC20(cToken).totalSupply() == 0; 85 | } 86 | } -------------------------------------------------------------------------------- /contracts/test/CallForwarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | 5 | contract CallForwarder { 6 | function execute(address to, bytes memory data) external { 7 | assembly { 8 | let result := call(gas(), to, 0, add(data, 32), mload(data), 0, 0) 9 | returndatacopy(0, 0, returndatasize()) 10 | switch result 11 | case 0 { revert(0, returndatasize()) } 12 | default { return(0, returndatasize()) } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /contracts/test/IIndexPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | interface IIndexPool { 5 | function getCurrentTokens() external view returns (address[] memory tokens); 6 | 7 | function getDenormalizedWeight(address token) external view returns (uint256 denorm); 8 | 9 | function getTotalDenormalizedWeight() external view returns (uint256); 10 | } -------------------------------------------------------------------------------- /contracts/test/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | interface IUniswapV2Pair { 5 | event Approval(address indexed owner, address indexed spender, uint256 value); 6 | event Transfer(address indexed from, address indexed to, uint256 value); 7 | 8 | function name() external pure returns (string memory); 9 | 10 | function symbol() external pure returns (string memory); 11 | 12 | function decimals() external pure returns (uint8); 13 | 14 | function totalSupply() external view returns (uint256); 15 | 16 | function balanceOf(address owner) external view returns (uint256); 17 | 18 | function allowance(address owner, address spender) external view returns (uint256); 19 | 20 | function approve(address spender, uint256 value) external returns (bool); 21 | 22 | function transfer(address to, uint256 value) external returns (bool); 23 | 24 | function transferFrom( 25 | address from, 26 | address to, 27 | uint256 value 28 | ) external returns (bool); 29 | 30 | function DOMAIN_SEPARATOR() external view returns (bytes32); 31 | 32 | function PERMIT_TYPEHASH() external pure returns (bytes32); 33 | 34 | function nonces(address owner) external view returns (uint256); 35 | 36 | function permit( 37 | address owner, 38 | address spender, 39 | uint256 value, 40 | uint256 deadline, 41 | uint8 v, 42 | bytes32 r, 43 | bytes32 s 44 | ) external; 45 | 46 | event Mint(address indexed sender, uint256 amount0, uint256 amount1); 47 | event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); 48 | event Swap( 49 | address indexed sender, 50 | uint256 amount0In, 51 | uint256 amount1In, 52 | uint256 amount0Out, 53 | uint256 amount1Out, 54 | address indexed to 55 | ); 56 | event Sync(uint112 reserve0, uint112 reserve1); 57 | 58 | function MINIMUM_LIQUIDITY() external pure returns (uint256); 59 | 60 | function factory() external view returns (address); 61 | 62 | function token0() external view returns (address); 63 | 64 | function token1() external view returns (address); 65 | 66 | function getReserves() 67 | external 68 | view 69 | returns ( 70 | uint112 reserve0, 71 | uint112 reserve1, 72 | uint32 blockTimestampLast 73 | ); 74 | 75 | function price0CumulativeLast() external view returns (uint256); 76 | 77 | function price1CumulativeLast() external view returns (uint256); 78 | 79 | function kLast() external view returns (uint256); 80 | 81 | function mint(address to) external returns (uint256 liquidity); 82 | 83 | function burn(address to) external returns (uint256 amount0, uint256 amount1); 84 | 85 | function swap( 86 | uint256 amount0Out, 87 | uint256 amount1Out, 88 | address to, 89 | bytes calldata data 90 | ) external; 91 | 92 | function skim(address to) external; 93 | 94 | function sync() external; 95 | 96 | function initialize(address, address) external; 97 | } 98 | -------------------------------------------------------------------------------- /contracts/test/SendEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | contract SendEth { 5 | constructor(address target) payable { 6 | assembly { selfdestruct(target) } 7 | } 8 | } -------------------------------------------------------------------------------- /contracts/test/TestAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.6; 3 | 4 | import "./TestERC20.sol"; 5 | import "./TestVault.sol"; 6 | import "../libraries/MinimalSignedMath.sol"; 7 | 8 | contract TestAdapter { 9 | using LowGasSafeMath for uint256; 10 | using MinimalSignedMath for uint256; 11 | using TransferHelper for address; 12 | 13 | address public immutable underlying; 14 | address public immutable token; 15 | uint256 public annualInterest; 16 | bool internal restrictedLiquidity; 17 | uint256 internal _availableLiquidity; 18 | bool internal revertOnAPRQQuery; 19 | address[] internal revenueTokens; 20 | uint256[] internal revenueAPRs; 21 | 22 | constructor(address _underlying, address _token, uint256 _annualInterest) { 23 | underlying = _underlying; 24 | token = _token; 25 | annualInterest = _annualInterest; 26 | _underlying.safeApproveMax(_token); 27 | } 28 | 29 | function setRevenueBreakdown( 30 | address[] memory _revenueTokens, 31 | uint256[] memory _revenueAPRs 32 | ) external { 33 | revenueTokens = _revenueTokens; 34 | revenueAPRs = _revenueAPRs; 35 | } 36 | 37 | function getRevenueBreakdown() 38 | external 39 | view 40 | returns ( 41 | address[] memory _revenueTokens, 42 | uint256[] memory _revenueAPRs 43 | ) 44 | { 45 | _revenueTokens = revenueTokens; 46 | _revenueAPRs = revenueAPRs; 47 | } 48 | 49 | function availableLiquidity() public view returns (uint256) { 50 | if (restrictedLiquidity) return _availableLiquidity; 51 | return IERC20(underlying).balanceOf(token); 52 | } 53 | 54 | function setAvailableLiquidity(uint256 amount) external { 55 | restrictedLiquidity = true; 56 | _availableLiquidity = amount; 57 | } 58 | function setRevertOnAPRQQuery(bool _revertOnAPRQQuery) external { 59 | revertOnAPRQQuery = _revertOnAPRQQuery; 60 | } 61 | 62 | function toWrappedAmount(uint256 underlyingAmount) public view returns (uint256) { 63 | TestVault vault = TestVault(token); 64 | uint256 bal = vault.balance(); 65 | uint256 supply = vault.totalSupply(); 66 | return supply == 0 ? underlyingAmount : (underlyingAmount.mul(supply) / bal); 67 | } 68 | 69 | function toUnderlyingAmount(uint256 wrappedAmount) public view returns (uint256) { 70 | TestVault vault = TestVault(token); 71 | return vault.balance().mul(wrappedAmount) / vault.totalSupply(); 72 | } 73 | 74 | function setAnnualInterest(uint256 _annualInterest) external { 75 | annualInterest = _annualInterest; 76 | } 77 | 78 | function getAPR() external view returns (uint256) { 79 | if (revertOnAPRQQuery) revert(); 80 | return annualInterest.mul(1e18) / TestVault(token).balance(); 81 | } 82 | 83 | function getHypotheticalAPR(int256 liquidityDelta) external view returns (uint256) { 84 | if (revertOnAPRQQuery) revert(); 85 | return annualInterest.mul(1e18) / TestVault(token).balance().add(liquidityDelta); 86 | } 87 | 88 | function balanceWrapped() public view returns (uint256) { 89 | return IERC20(token).balanceOf(msg.sender); 90 | } 91 | 92 | function balanceUnderlying() external view returns (uint256) { 93 | return toUnderlyingAmount(balanceWrapped()); 94 | } 95 | 96 | function mintTo(address to, uint256 amount) external { 97 | TestERC20(underlying).mint(address(this), amount); 98 | uint256 amountMinted = TestVault(token).deposit(amount); 99 | token.safeTransfer(to, amountMinted); 100 | } 101 | 102 | function deposit(uint256 amountUnderlying) external virtual returns (uint256 amountMinted) { 103 | require(amountUnderlying > 0, "deposit 0"); 104 | underlying.safeTransferFrom(msg.sender, address(this), amountUnderlying); 105 | amountMinted = TestVault(token).deposit(amountUnderlying); 106 | token.safeTransfer(msg.sender, amountMinted); 107 | } 108 | 109 | function withdraw(uint256 amountToken) public virtual returns (uint256 amountReceived) { 110 | require(amountToken > 0, "withdraw 0"); 111 | token.safeTransferFrom(msg.sender, address(this), amountToken); 112 | amountReceived = TestVault(token).withdraw(amountToken); 113 | underlying.safeTransfer(msg.sender, amountReceived); 114 | } 115 | 116 | function withdrawAll() public virtual returns (uint256 amountReceived) { 117 | return withdraw(balanceWrapped()); 118 | } 119 | 120 | function withdrawUnderlying(uint256 amountUnderlying) public returns (uint256 amountBurned) { 121 | TestVault vault = TestVault(token); 122 | uint256 bal = vault.balance(); 123 | uint256 supply = vault.totalSupply(); 124 | amountBurned = amountUnderlying.mul(supply).divCeil(bal); 125 | token.safeTransferFrom(msg.sender, address(this), amountBurned); 126 | TestVault(token).withdraw(amountBurned); 127 | underlying.safeTransfer(msg.sender, amountUnderlying); 128 | } 129 | 130 | function withdrawUnderlyingUpTo(uint256 amountToWithdraw) external returns (uint256 amountReceived) { 131 | uint256 available = availableLiquidity(); 132 | amountReceived = amountToWithdraw > available ? available : amountToWithdraw; 133 | if (amountReceived == 0) return 0; 134 | withdrawUnderlying(amountReceived); 135 | } 136 | } -------------------------------------------------------------------------------- /contracts/test/TestAdapterHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.6; 3 | 4 | import "../libraries/AdapterHelper.sol"; 5 | import "../libraries/TransferHelper.sol"; 6 | 7 | contract TestAdapterHelper { 8 | using TransferHelper for address; 9 | using AdapterHelper for IErc20Adapter; 10 | using AdapterHelper for IErc20Adapter[]; 11 | using AdapterHelper for uint256; 12 | using AdapterHelper for bytes32; 13 | using AdapterHelper for bytes32[]; 14 | 15 | function approve(IErc20Adapter adapter) external { 16 | adapter.underlying().safeApproveMax(address(adapter)); 17 | address(adapter.token()).safeApproveMax(address(adapter)); 18 | } 19 | 20 | function testDeposit(IErc20Adapter adapter, uint256 amount) external { 21 | adapter.underlying().safeApproveMax(address(adapter)); 22 | adapter.deposit(amount); 23 | address(adapter.token()).safeApproveMax(address(adapter)); 24 | } 25 | 26 | function packAdapterAndWeight( 27 | IErc20Adapter adapter, 28 | uint256 weight 29 | ) 30 | external 31 | pure 32 | returns (bytes32 encoded) 33 | { 34 | return adapter.packAdapterAndWeight(weight); 35 | } 36 | 37 | function packAdaptersAndWeights( 38 | IErc20Adapter[] memory adapters, 39 | uint256[] memory weights 40 | ) 41 | external 42 | pure 43 | returns (bytes32[] memory encodedArray) 44 | { 45 | return adapters.packAdaptersAndWeights(weights); 46 | } 47 | 48 | function unpackAdapterAndWeight(bytes32 encoded) 49 | external 50 | pure 51 | returns ( 52 | IErc20Adapter adapter, 53 | uint256 weight 54 | ) 55 | { 56 | return encoded.unpackAdapterAndWeight(); 57 | } 58 | 59 | function unpackAdaptersAndWeights(bytes32[] memory encodedArray) 60 | external 61 | pure 62 | returns ( 63 | IErc20Adapter[] memory adapters, 64 | uint256[] memory weights 65 | ) 66 | { 67 | return encodedArray.unpackAdaptersAndWeights(); 68 | } 69 | 70 | function getNetAPR( 71 | IErc20Adapter[] memory adapters, 72 | uint256[] memory weights, 73 | int256[] memory liquidityDeltas 74 | ) external view returns (uint256 netAPR) 75 | { 76 | return adapters.getNetAPR(weights, liquidityDeltas); 77 | } 78 | 79 | function getLiquidityDeltas( 80 | uint256 totalProductiveBalance, 81 | uint256[] memory balances, 82 | uint256[] memory weights 83 | ) external pure returns (int256[] memory deltas) 84 | { 85 | return totalProductiveBalance.getLiquidityDeltas(balances, weights); 86 | } 87 | 88 | function getBalances(IErc20Adapter[] memory adapters) 89 | external 90 | view 91 | returns (uint256[] memory balances) 92 | { 93 | return adapters.getBalances(); 94 | } 95 | 96 | function getExcludedAdapterIndices( 97 | IErc20Adapter[] memory oldAdapters, 98 | IErc20Adapter[] memory newAdapters 99 | ) external pure returns (uint256[] memory excludedAdapterIndices) 100 | { 101 | return oldAdapters.getExcludedAdapterIndices(newAdapters); 102 | } 103 | 104 | function rebalance( 105 | IErc20Adapter[] memory adapters, 106 | uint256[] memory weights, 107 | int256[] memory liquidityDeltas, 108 | uint256 reserveBalance 109 | ) external returns (uint256[] memory removedIndices) 110 | { 111 | return adapters.rebalance(weights, liquidityDeltas, reserveBalance); 112 | } 113 | } -------------------------------------------------------------------------------- /contracts/test/TestArrayHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.5.0; 3 | 4 | import "../libraries/ArrayHelper.sol"; 5 | 6 | 7 | contract TestArrayHelper { 8 | using ArrayHelper for uint256[]; 9 | using ArrayHelper for bytes32[]; 10 | using ArrayHelper for address[]; 11 | using ArrayHelper for IErc20Adapter[]; 12 | using ArrayHelper for EnumerableSet.AddressSet; 13 | using EnumerableSet for EnumerableSet.AddressSet; 14 | 15 | EnumerableSet.AddressSet internal addressSet; 16 | address[] internal addressArray; 17 | bytes32[] internal bytes32Array; 18 | 19 | function toArray() external view returns (address[] memory arr) { 20 | arr = addressSet.toArray(); 21 | } 22 | 23 | function setAddressSet(address[] memory arr) external { 24 | for (uint256 i; i < arr.length; i++) addressSet.add(arr[i]); 25 | } 26 | 27 | function setAddressArray(address[] memory arr) external { 28 | addressArray = arr; 29 | } 30 | 31 | function setBytes32Array(bytes32[] memory arr) external { 32 | bytes32Array = arr; 33 | } 34 | 35 | function getAddressArray() external view returns (address[] memory arr) { 36 | arr = addressArray; 37 | } 38 | 39 | function getBytes32Array() external view returns (bytes32[] memory arr) { 40 | arr = bytes32Array; 41 | } 42 | function sum(uint256[] memory arr) external pure returns (uint256) { 43 | return arr.sum(); 44 | } 45 | 46 | function mremove(uint256[] memory arr, uint256 index) external pure returns (uint256[] memory) { 47 | arr.mremove(index); 48 | return arr; 49 | } 50 | 51 | function mremove(address[] memory arr, uint256 index) external pure returns (address[] memory) { 52 | arr.mremove(index); 53 | return arr; 54 | } 55 | 56 | function mremoveAdapters(IErc20Adapter[] memory arr, uint256 index) external pure returns (IErc20Adapter[] memory) { 57 | arr.mremove(index); 58 | return arr; 59 | } 60 | 61 | function removeBytes32(uint256 index) external { 62 | bytes32Array.remove(index); 63 | } 64 | 65 | function removeAddress(uint256 index) external { 66 | addressArray.remove(index); 67 | } 68 | 69 | function indexOf(address[] memory arr, address find) external pure returns (uint256) { 70 | return arr.indexOf(find); 71 | } 72 | 73 | function sortByDescendingScore( 74 | address[] memory addresses, 75 | uint256[] memory scores 76 | ) external pure returns (address[] memory, uint256[] memory) { 77 | addresses.sortByDescendingScore(scores); 78 | return (addresses, scores); 79 | } 80 | } -------------------------------------------------------------------------------- /contracts/test/TestBatcherRevert.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | contract TestBatcherRevert { 5 | address public underlying = address(1); 6 | } -------------------------------------------------------------------------------- /contracts/test/TestComptrollerLens.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/CompoundInterfaces.sol"; 5 | import "../interfaces/IERC20.sol"; 6 | 7 | 8 | contract TestComptrollerLens { 9 | IComptroller internal constant comptroller = IComptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); 10 | IERC20 internal constant comp = IERC20(0xc00e94Cb662C3520282E6f5717214004A7f26888); 11 | 12 | function getPendingRewards(address account, address cToken) external returns (uint256) { 13 | uint256 compBefore = comp.balanceOf(account); 14 | address[] memory holders = new address[](1); 15 | address[] memory cTokens = new address[](1); 16 | holders[0] = account; 17 | cTokens[0] = cToken; 18 | comptroller.claimComp(holders, cTokens, false, true); 19 | uint256 compAfter = comp.balanceOf(account); 20 | return compAfter - compBefore; 21 | } 22 | } -------------------------------------------------------------------------------- /contracts/test/TestDynamicArrays.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.7.6; 2 | 3 | import "../libraries/DynamicArrays.sol"; 4 | 5 | 6 | contract TestDynamicArrays { 7 | using DynamicArrays for address[]; 8 | using DynamicArrays for uint256[]; 9 | 10 | function buildDynamicAddressArray( 11 | uint256 size, 12 | address[] calldata elements 13 | ) external pure returns (address[] memory arr) { 14 | arr = DynamicArrays.dynamicAddressArray(size); 15 | for (uint256 i; i < elements.length; i++) { 16 | arr.dynamicPush(elements[i]); 17 | } 18 | } 19 | 20 | function testOverflowAddressArray() external pure { 21 | address[] memory arr = DynamicArrays.dynamicAddressArray(0); 22 | bytes memory b = new bytes(0); 23 | arr.dynamicPush(address(100)); 24 | require(b.length == 100, "Did not overflow as expected"); 25 | } 26 | 27 | function buildDynamicUint256Array( 28 | uint256 size, 29 | uint256[] calldata elements 30 | ) external pure returns (uint256[] memory arr) { 31 | arr = DynamicArrays.dynamicUint256Array(size); 32 | for (uint256 i; i < elements.length; i++) { 33 | arr.dynamicPush(elements[i]); 34 | } 35 | } 36 | 37 | function testOverflowUint256Array() external pure { 38 | uint256[] memory arr = DynamicArrays.dynamicUint256Array(0); 39 | bytes memory b = new bytes(0); 40 | arr.dynamicPush(100); 41 | require(b.length == 100, "Did not overflow as expected"); 42 | } 43 | } -------------------------------------------------------------------------------- /contracts/test/TestERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.7.6; 2 | 3 | import "../vaults/ERC20.sol"; 4 | 5 | contract TestERC20 is ERC20 { 6 | string public name; 7 | string public symbol; 8 | 9 | constructor(string memory _name, string memory _symbol, uint256 initBalance) { 10 | name = _name; 11 | symbol = _symbol; 12 | if (initBalance > 0) _mint(msg.sender, initBalance); 13 | } 14 | 15 | function mint(address to, uint256 amount) external { 16 | _mint(to, amount); 17 | } 18 | } -------------------------------------------------------------------------------- /contracts/test/TestFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | import "../libraries/CloneLibrary.sol"; 4 | 5 | 6 | contract TestFactory { 7 | address public last; 8 | function clone(address implementation) external { 9 | last = CloneLibrary.createClone(implementation); 10 | } 11 | } -------------------------------------------------------------------------------- /contracts/test/TestNirnVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | pragma abicoder v2; 4 | 5 | import "../vaults/NirnVault.sol"; 6 | 7 | 8 | contract TestNirnVault is NirnVault { 9 | constructor( 10 | address _registry, 11 | address _eoaSafeCaller 12 | ) NirnVault(_registry, _eoaSafeCaller) {} 13 | 14 | function setAdaptersAndWeightsInternal( 15 | IErc20Adapter[] calldata adapters, 16 | uint256[] calldata weights 17 | ) external { 18 | beforeAddAdapters(adapters); 19 | setAdaptersAndWeights(adapters, weights); 20 | } 21 | 22 | function removeAdaptersInternal(uint256[] calldata removeIndices) external { 23 | removeAdapters(removeIndices); 24 | } 25 | 26 | function withdrawToMatchAmountInternal( 27 | IErc20Adapter[] calldata adapters, 28 | uint256[] calldata weights, 29 | uint256[] calldata balances, 30 | uint256 _reserveBalance, 31 | uint256 amount, 32 | uint256 newReserves 33 | ) external { 34 | return withdrawToMatchAmount( 35 | adapters, 36 | weights, 37 | balances, 38 | _reserveBalance, 39 | amount, 40 | newReserves 41 | ); 42 | } 43 | 44 | function balanceSheetInternal() external view returns (BalanceSheet memory) { 45 | (IErc20Adapter[] memory adapters,) = getAdaptersAndWeights(); 46 | return getBalanceSheet(adapters); 47 | } 48 | 49 | function processProposedDistributionInternal( 50 | DistributionParameters calldata currentParams, 51 | uint256 totalProductiveBalance, 52 | IErc20Adapter[] calldata proposedAdapters, 53 | uint256[] calldata proposedWeights 54 | ) external view returns (DistributionParameters memory params) { 55 | return processProposedDistribution( 56 | currentParams, 57 | totalProductiveBalance, 58 | proposedAdapters, 59 | proposedWeights 60 | ); 61 | } 62 | } -------------------------------------------------------------------------------- /contracts/test/TestProtocolAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.6; 3 | 4 | 5 | contract TestProtocolAdapter { 6 | string public protocol = "Test Protocol"; 7 | } -------------------------------------------------------------------------------- /contracts/test/TestProxyManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../interfaces/IProxyManager.sol"; 5 | import "../libraries/CloneLibrary.sol"; 6 | 7 | 8 | contract TestProxyManager is IProxyManager { 9 | event DeployedProxy(bytes32 implementationId, address proxy); 10 | 11 | mapping (bytes32 => address) public implementations; 12 | 13 | function addImplementation(bytes32 implementationId, address implementation) external { 14 | implementations[implementationId] = implementation; 15 | } 16 | 17 | function deployProxyManyToOne(bytes32 implementationId, bytes32 suppliedSalt) external override returns (address proxy) { 18 | bytes32 salt = keccak256(abi.encode(implementationId, suppliedSalt)); 19 | proxy = CloneLibrary.createClone(implementations[implementationId], salt); 20 | emit DeployedProxy(implementationId, proxy); 21 | } 22 | 23 | function computeProxyAddressManyToOne( 24 | address, bytes32 implementationId, bytes32 suppliedSalt 25 | ) external view override returns (address proxy) { 26 | bytes32 salt = keccak256(abi.encode(implementationId, suppliedSalt)); 27 | address implementation = implementations[implementationId]; 28 | bytes32 initCodeHash = keccak256(CloneLibrary.getCreateCode(implementation)); 29 | bytes32 _data = keccak256( 30 | abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash) 31 | ); 32 | return address(uint160(uint256(_data))); 33 | } 34 | } -------------------------------------------------------------------------------- /contracts/test/TestRewardsSeller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.6; 3 | 4 | 5 | contract TestRewardsSeller { 6 | event RewardsSold( 7 | address originator, 8 | address rewardsToken, 9 | address underlying, 10 | bytes params 11 | ); 12 | 13 | function sellRewards( 14 | address originator, 15 | address rewardsToken, 16 | address underlying, 17 | bytes calldata params 18 | ) external { 19 | emit RewardsSold(originator, rewardsToken, underlying, params); 20 | } 21 | } -------------------------------------------------------------------------------- /contracts/test/TestVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../vaults/ERC20.sol"; 5 | import "../libraries/TransferHelper.sol"; 6 | import "../libraries/SymbolHelper.sol"; 7 | 8 | contract TestVault is ERC20 { 9 | using LowGasSafeMath for uint256; 10 | using TransferHelper for address; 11 | using SymbolHelper for address; 12 | 13 | address public underlying; 14 | string public name; 15 | string public symbol; 16 | 17 | constructor(address _underlying) { 18 | underlying = _underlying; 19 | name = string(abi.encodePacked( 20 | "Test Vault ", 21 | _underlying.getName() 22 | )); 23 | symbol = string(abi.encodePacked( 24 | "tv", 25 | _underlying.getSymbol() 26 | )); 27 | } 28 | 29 | function balance() public view returns (uint256) { 30 | return IERC20(underlying).balanceOf(address(this)); 31 | } 32 | 33 | function price() public view returns (uint256) { 34 | return balance().mul(1e18) / totalSupply; 35 | } 36 | 37 | function deposit(uint256 amount) external returns (uint256 shares) { 38 | uint256 bal = balance(); 39 | underlying.safeTransferFrom(msg.sender, address(this), amount); 40 | uint256 supply = totalSupply; 41 | shares = supply == 0 ? amount : (amount.mul(supply) / bal); 42 | _mint(msg.sender, shares); 43 | } 44 | 45 | function withdraw(uint256 shares) external returns (uint256 amount) { 46 | uint256 bal = balance(); 47 | amount = bal.mul(shares) / totalSupply; 48 | _burn(msg.sender, shares); 49 | underlying.safeTransfer(msg.sender, amount); 50 | } 51 | 52 | function withdrawUnderlying(uint256 amount) external returns (uint256 shares) { 53 | uint256 bal = balance(); 54 | shares = amount.mul(totalSupply) / bal; 55 | _burn(msg.sender, shares); 56 | underlying.safeTransfer(msg.sender, amount); 57 | } 58 | } -------------------------------------------------------------------------------- /contracts/vaults/ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.7.6; 3 | 4 | import "../libraries/LowGasSafeMath.sol"; 5 | import "../interfaces/IERC20.sol"; 6 | 7 | 8 | contract ERC20 is IERC20 { 9 | using LowGasSafeMath for uint256; 10 | 11 | mapping(address => uint256) public override balanceOf; 12 | 13 | mapping(address => mapping(address => uint256)) public override allowance; 14 | 15 | uint256 public override totalSupply; 16 | 17 | function transfer(address recipient, uint256 amount) external override returns (bool) { 18 | _transfer(msg.sender, recipient, amount); 19 | return true; 20 | } 21 | 22 | function approve(address spender, uint256 amount) external override returns (bool) { 23 | _approve(msg.sender, spender, amount); 24 | return true; 25 | } 26 | 27 | function transferFrom( 28 | address sender, 29 | address recipient, 30 | uint256 amount 31 | ) external override returns (bool) { 32 | _transfer(sender, recipient, amount); 33 | _approve( 34 | sender, 35 | msg.sender, 36 | allowance[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance") 37 | ); 38 | return true; 39 | } 40 | 41 | function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) { 42 | _approve(msg.sender, spender, allowance[msg.sender][spender].add(addedValue)); 43 | return true; 44 | } 45 | 46 | function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) { 47 | _approve( 48 | msg.sender, 49 | spender, 50 | allowance[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero") 51 | ); 52 | return true; 53 | } 54 | 55 | function _transfer( 56 | address sender, 57 | address recipient, 58 | uint256 amount 59 | ) internal { 60 | require(sender != address(0), "ERC20: transfer from the zero address"); 61 | require(recipient != address(0), "ERC20: transfer to the zero address"); 62 | 63 | balanceOf[sender] = balanceOf[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 64 | balanceOf[recipient] = balanceOf[recipient].add(amount); 65 | emit Transfer(sender, recipient, amount); 66 | } 67 | 68 | function _mint(address account, uint256 amount) internal { 69 | require(account != address(0), "ERC20: mint to the zero address"); 70 | 71 | totalSupply = totalSupply.add(amount); 72 | balanceOf[account] = balanceOf[account].add(amount); 73 | emit Transfer(address(0), account, amount); 74 | } 75 | 76 | function _burn(address account, uint256 amount) internal { 77 | require(account != address(0), "ERC20: burn from the zero address"); 78 | 79 | balanceOf[account] = balanceOf[account].sub(amount, "ERC20: burn amount exceeds balance"); 80 | totalSupply = totalSupply.sub(amount); 81 | emit Transfer(account, address(0), amount); 82 | } 83 | 84 | function _approve( 85 | address owner, 86 | address spender, 87 | uint256 amount 88 | ) internal { 89 | require(owner != address(0), "ERC20: approve from the zero address"); 90 | require(spender != address(0), "ERC20: approve to the zero address"); 91 | 92 | allowance[owner][spender] = amount; 93 | emit Approval(owner, spender, amount); 94 | } 95 | 96 | function _burnFrom(address account, uint256 amount) internal { 97 | _burn(account, amount); 98 | _approve( 99 | account, 100 | msg.sender, 101 | allowance[account][msg.sender].sub(amount, "ERC20: burn amount exceeds allowance") 102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /deploy/aave-v1.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployAaveV1ProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('AaveV1ProtocolAdapter', { 6 | from: deployer, 7 | gasLimit: 4000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Protocols', 'AaveV1'] -------------------------------------------------------------------------------- /deploy/aave-v2.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployAaveV2ProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('AaveV2ProtocolAdapter', { 6 | from: deployer, 7 | gasLimit: 7000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Protocols', 'AaveV2'] -------------------------------------------------------------------------------- /deploy/batcher.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployAaveV1ProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('BatchRebalancer', { 6 | from: deployer, 7 | gasLimit: 1000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Batcher'] -------------------------------------------------------------------------------- /deploy/compound.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployCompoundProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('CompoundProtocolAdapter', { 6 | from: deployer, 7 | gasLimit: 8000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Protocols', 'Compound'] -------------------------------------------------------------------------------- /deploy/cream.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployCreamProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('CreamProtocolAdapter', { 6 | from: deployer, 7 | gasLimit: 6000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Protocols', 'Cream'] -------------------------------------------------------------------------------- /deploy/factory.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployCompoundProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | const proxyManager = '0xd23dedc599bd56767e42d48484d6ca96ab01c115' 6 | await deployments.deploy('NirnVaultFactory', { 7 | from: deployer, 8 | gasLimit: 2500000, 9 | args: [proxyManager, registry.address], 10 | }) 11 | } 12 | 13 | module.exports.tags = ['Factory'] -------------------------------------------------------------------------------- /deploy/fulcrum.deploy.js: -------------------------------------------------------------------------------- 1 | const {getContractAddress} = require("@ethersproject/address") 2 | 3 | module.exports = async function deployFulcrumProtocolAdapter(hre) { 4 | const { getNamedAccounts, deployments, ethers } = hre 5 | const [wallet] = await ethers.getSigners() 6 | const nonce = await wallet.getTransactionCount() 7 | // const nextAddress = getContractAddress({ from: wallet.address, nonce: nonce + 1 }) 8 | const { deployer } = await getNamedAccounts() 9 | const registry = await ethers.getContract('AdapterRegistry') 10 | // await registry.addProtocolAdapter(nextAddress) 11 | await deployments.deploy('FulcrumProtocolAdapter', { 12 | from: deployer, 13 | gasLimit: 7000000, 14 | args: [registry.address] 15 | }) 16 | } 17 | 18 | module.exports.tags = ['Protocols', "Fulcrum"] -------------------------------------------------------------------------------- /deploy/iron-bank.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployIronBankProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | await deployments.deploy('IronBankProtocolAdapter', { 6 | from: deployer, 7 | gasLimit: 6000000, 8 | args: [registry.address] 9 | }) 10 | } 11 | 12 | module.exports.tags = ['Protocols', "IronBank"] -------------------------------------------------------------------------------- /deploy/registry.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployRegistry(hre) { 2 | const { 3 | getNamedAccounts, 4 | deployments: { deploy } 5 | } = hre; 6 | const { deployer } = await getNamedAccounts() 7 | await deploy('AdapterRegistry', { 8 | from: deployer, 9 | gasLimit: 4000000 10 | }) 11 | } 12 | 13 | module.exports.tags = ['Registry'] -------------------------------------------------------------------------------- /deploy/vault.deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function deployCompoundProtocolAdapter(hre) { 2 | const { getNamedAccounts, deployments } = hre 3 | const { deployer } = await getNamedAccounts() 4 | const registry = await deployments.get('AdapterRegistry') 5 | const batcher = await deployments.get('BatchRebalancer') 6 | await deployments.deploy('NirnVault', { 7 | from: deployer, 8 | gasLimit: 6500000, 9 | args: [registry.address, batcher.address], 10 | }) 11 | } 12 | 13 | module.exports.tags = ['Vault'] -------------------------------------------------------------------------------- /deployments/kovan/.chainId: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /deployments/mainnet/.chainId: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /deployments/rinkeby/.chainId: -------------------------------------------------------------------------------- 1 | 4 -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import "@nomiclabs/hardhat-etherscan"; 3 | import "@nomiclabs/hardhat-solhint"; 4 | import 'hardhat-typechain' 5 | import '@nomiclabs/hardhat-ethers' 6 | import '@nomiclabs/hardhat-waffle' 7 | import 'hardhat-deploy' 8 | import 'solidity-coverage' 9 | import 'hardhat-gas-reporter' 10 | import { randomBytes } from 'crypto'; 11 | 12 | const configureNetwork = (network: string, chainId: number) => ({ 13 | url: `https://${network}.infura.io/v3/${process.env.INFURA_API_KEY}`, 14 | chainId, 15 | accounts: [process.env[`${network.toUpperCase()}_PVT_KEY`] ?? randomBytes(32).toString('hex')] 16 | }); 17 | 18 | export default { 19 | gasReporter: { 20 | enabled: true, 21 | }, 22 | etherscan: { 23 | apiKey: process.env.ETHERSCAN_API_KEY, 24 | }, 25 | namedAccounts: { 26 | deployer: { 27 | default: 0, 28 | }, 29 | }, 30 | mocha: { 31 | timeout: 200000 32 | }, 33 | networks: { 34 | coverage: { 35 | url: 'http://localhost:8555' 36 | }, 37 | hardhat: { 38 | allowUnlimitedContractSize: false, 39 | forking: { 40 | url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`, 41 | blockNumber: 12667185 42 | } 43 | }, 44 | mainnet: configureNetwork('mainnet', 1), 45 | kovan: configureNetwork('kovan', 42), 46 | rinkeby: configureNetwork('rinkeby', 4) 47 | }, 48 | solidity: { 49 | version: '0.7.6', 50 | settings: { 51 | optimizer: { 52 | enabled: true, 53 | runs: 800, 54 | }, 55 | metadata: { 56 | bytecodeHash: 'none', 57 | }, 58 | }, 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@indexed-finance/apr-oracles", 3 | "description": "", 4 | "license": "MIT", 5 | "version": "0.0.2", 6 | "homepage": "https://github.com/indexed-finance/apr-oracles#readme", 7 | "keywords": [], 8 | "files": [ 9 | "contracts/interfaces", 10 | "contracts/libraries", 11 | "artifacts/contracts/interfaces/**/*.json", 12 | "!artifacts/contracts/interfaces/**/*.dbg.json" 13 | ], 14 | "engines": { 15 | "node": ">=10" 16 | }, 17 | "dependencies": { 18 | "shelljs": "^0.8.4" 19 | }, 20 | "devDependencies": { 21 | "@ethersproject/bignumber": "^5.0.8", 22 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", 23 | "@nomiclabs/hardhat-etherscan": "^2.1.1", 24 | "@nomiclabs/hardhat-solhint": "^2.0.0", 25 | "@nomiclabs/hardhat-waffle": "^2.0.0", 26 | "@openzeppelin/contracts": "3.4.1-solc-0.7-2", 27 | "@typechain/ethers-v5": "^4.0.0", 28 | "@types/chai": "^4.2.6", 29 | "@types/dotenv": "^8.2.0", 30 | "@types/fs-extra": "^9.0.1", 31 | "@types/mocha": "^5.2.7", 32 | "@types/node": "^14.11.8", 33 | "@typescript-eslint/eslint-plugin": "^3.10.1", 34 | "@typescript-eslint/parser": "^3.10.1", 35 | "chai": "^4.2.0", 36 | "dotenv": "^8.2.0", 37 | "eslint": "^7.11.0", 38 | "eslint-config-prettier": "^6.12.0", 39 | "ethereum-waffle": "^3.0.2", 40 | "ethers": "^5.0.8", 41 | "hardhat": "^2.5.0", 42 | "hardhat-deploy": "^0.7.0-beta.56", 43 | "hardhat-gas-reporter": "^1.0.4", 44 | "hardhat-typechain": "^0.3.3", 45 | "husky": "^4.3.0", 46 | "mocha": "^6.2.2", 47 | "prettier": "^2.0.5", 48 | "prettier-plugin-solidity": "^1.0.0-alpha.59", 49 | "solhint": "^3.2.1", 50 | "solhint-plugin-prettier": "^0.0.5", 51 | "solidity-coverage": "0.7.16", 52 | "ts-generator": "^0.1.1", 53 | "ts-node": "^8.5.4", 54 | "typechain": "^4.0.0", 55 | "typescript": "^3.7.3" 56 | }, 57 | "scripts": { 58 | "lint": "hardhat check", 59 | "compile": "hardhat compile", 60 | "coverage:batcher": "hardhat coverage --testfiles \"test/BatchRebalancer.spec.ts\"", 61 | "coverage:registry": "hardhat coverage --solcoverjs .solcover.registry.js --testfiles \"test/AdapterRegistry.spec.ts\"", 62 | "coverage:vault": "hardhat coverage --testfiles \"test/NirnVault.spec.ts\"", 63 | "coverage:factory": "hardhat coverage --testfiles \"test/NirnVaultFactory.spec.ts\"", 64 | "generate-tests": "yarn test test/TestMapper.spec.ts", 65 | "test": "hardhat test" 66 | }, 67 | "repository": { 68 | "type": "git", 69 | "url": "https://github.com/indexed-finance/apr-oracles" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/EtherAdapterBehavior.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ConvertHelper } from "../@types/augmentations"; 3 | import { IERC20, IErc20Adapter, IEtherAdapter } from "../typechain"; 4 | import { shouldBehaveLikeErc20Adapter } from "./Erc20AdapterBehavior.spec"; 5 | import { createBalanceCheckpoint, getContract, getTransactionCost } from "./shared"; 6 | 7 | export function shouldBehaveLikeEtherAdapterDepositETH() { 8 | beforeEach(function () {return this.resetTests();}) 9 | 10 | it('Should mint wrapper and transfer to caller', async function () { 11 | const getBalanceChange = await createBalanceCheckpoint(null, this.wallet.address) 12 | const amountMinted = await this.toWrapped(this.amountDeposited) 13 | const tx = (await getContract(this.adapter.address, 'IEtherAdapter')).depositETH({value: this.amountDeposited}) 14 | await expect(tx) 15 | .to.emit(this.wrapper, 'Transfer') 16 | .withArgs(this.depositSenderWrapped, this.depositReceiverWrapped, amountMinted) 17 | const cost = await getTransactionCost(tx) 18 | expect((await getBalanceChange()).add(cost)).to.eq(this.amountDeposited.mul(-1)) 19 | expect(await this.wrapper.balanceOf(this.depositReceiverWrapped)).to.eq(amountMinted) 20 | }) 21 | } 22 | 23 | export function shouldBehaveLikeEtherAdapterWithdrawAsETH() { 24 | beforeEach(function () {return this.resetTests(true);}) 25 | 26 | it('Should burn wrapper and redeem underlying', async function () { 27 | const balance = await this.wrapper.balanceOf(this.depositReceiverWrapped, { blockTag: 'pending' }) 28 | const getBalanceChange = await createBalanceCheckpoint(null, this.wallet.address) 29 | const amountUnderlying = await this.toUnderlying(balance) 30 | const tx = (await getContract(this.adapter.address, 'IEtherAdapter')).withdrawAsETH(balance) 31 | const cost = await getTransactionCost(tx) 32 | expect((await getBalanceChange()).add(cost)).to.eq(amountUnderlying) 33 | }) 34 | } 35 | 36 | export function shouldBehaveLikeEtherAdapterWithdrawAllAsEth() { 37 | beforeEach(function () {return this.resetTests(true);}) 38 | 39 | it('Should burn all caller wrapper token and redeem ETH', async function () { 40 | const wBalance = await this.wrapper.balanceOf(this.depositReceiverWrapped, { blockTag: 'pending' }) 41 | const getBalanceChange = await createBalanceCheckpoint(null, this.wallet.address) 42 | const expectedChange = await this.toUnderlying(wBalance) 43 | const tx = await (await getContract(this.adapter.address, 'IEtherAdapter')).withdrawAllAsETH() 44 | const balanceChange = await getBalanceChange() 45 | const cost = await getTransactionCost(tx) 46 | expect(balanceChange.add(cost)).to.eq(expectedChange) 47 | expect(await this.wrapper.balanceOf(this.wallet.address)).to.eq(0) 48 | }) 49 | } 50 | 51 | export function shouldBehaveLikeEtherAdapterWithdrawUnderlyingAsETH() { 52 | beforeEach(function () {return this.resetTests(true);}) 53 | 54 | it('Should burn wrapper and redeem ETH', async function () { 55 | const getBalanceChange = await createBalanceCheckpoint(null, this.wallet.address) 56 | const balanceUnderlying = await this.adapter.balanceUnderlying({ blockTag: 'pending' }) 57 | const tx = await (await getContract(this.adapter.address, 'IEtherAdapter')).withdrawUnderlyingAsETH(balanceUnderlying) 58 | const cost = await getTransactionCost(tx) 59 | expect((await getBalanceChange()).add(cost)).to.eq(balanceUnderlying) 60 | expect(await this.wrapper.balanceOf(this.adapter.address)).to.eq(0) 61 | }) 62 | } 63 | 64 | export function shouldBehaveLikeEtherAdapter( 65 | getImplementation: () => Promise, 66 | initialize: (adapter: IErc20Adapter, underlying: IERC20, token: IERC20) => Promise, 67 | converter: ConvertHelper, 68 | _underlying: string, 69 | _wrapper: string, 70 | symbol: string, 71 | transferAddressOverrides?: (adapter: IErc20Adapter, underlying: IERC20, token: IERC20) => Promise<{ 72 | depositSenderWrapped?: string 73 | depositReceiverWrapped?: string 74 | depositReceiverUnderlying?: string 75 | withdrawalSenderUnderlying?: string 76 | }> 77 | ) { 78 | shouldBehaveLikeErc20Adapter( 79 | getImplementation, 80 | initialize, 81 | converter, 82 | _underlying, 83 | _wrapper, 84 | symbol, 85 | transferAddressOverrides 86 | ) 87 | 88 | describe('depositETH()', function () { 89 | shouldBehaveLikeEtherAdapterDepositETH() 90 | }) 91 | 92 | describe('withdrawAsETH()', function () { 93 | shouldBehaveLikeEtherAdapterWithdrawAsETH() 94 | }) 95 | 96 | describe('withdrawAllAsEth()', function () { 97 | shouldBehaveLikeEtherAdapterWithdrawAllAsEth() 98 | }) 99 | 100 | describe('withdrawUnderlyingAsETH()', function () { 101 | shouldBehaveLikeEtherAdapterWithdrawUnderlyingAsETH() 102 | }) 103 | } -------------------------------------------------------------------------------- /test/TestMapper.ts: -------------------------------------------------------------------------------- 1 | import { TestMapper } from '../typechain' 2 | 3 | import { advanceBlock, deployContract, getBigNumber, sendTokenTo } from "./shared" 4 | 5 | 6 | /** 7 | * This test file is used to automatically generate tests for all the tokens 8 | * in each protocol. 9 | */ 10 | describe('Map Protocols', () => { 11 | let mapper: TestMapper 12 | 13 | before(async () => { 14 | mapper = await deployContract('TestMapper') 15 | }) 16 | 17 | it('AaveV1', async () => { 18 | await mapper.aaveV1() 19 | }) 20 | 21 | it('AaveV2', async () => { 22 | await mapper.aaveV1() 23 | }) 24 | 25 | it('Compound', async () => { 26 | await mapper.compound() 27 | }) 28 | 29 | it('Cream', async () => { 30 | await mapper.cream() 31 | }) 32 | 33 | it('Fulcrum', async () => { 34 | await mapper.fulcrum() 35 | }) 36 | 37 | it('Fuse', async () => { 38 | await mapper.fuse() 39 | }) 40 | 41 | it('Iron Bank', async () => { 42 | await mapper.iron() 43 | }) 44 | }) -------------------------------------------------------------------------------- /test/adapters/aave-v1/AaveV1Erc20Adapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { AaveV1Erc20Adapter } from "../../../typechain" 3 | import { shouldBehaveLikeErc20Adapter } from "../../Erc20AdapterBehavior.spec" 4 | import { deployContract, AaveV1Converter } from '../../shared' 5 | 6 | 7 | describe('AaveV1Erc20Adapter', () => { 8 | const testAdapter = (_underlying: string, _ctoken: string, symbol: string) => describe(`a${symbol}`, function () { 9 | shouldBehaveLikeErc20Adapter( 10 | async () => (await deployContract('AaveV1Erc20Adapter', '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8')) as AaveV1Erc20Adapter, 11 | async (adapter, underlying, token) => adapter.initialize(underlying.address, token.address), 12 | AaveV1Converter, 13 | _underlying, 14 | _ctoken, 15 | symbol, 16 | ) 17 | }) 18 | 19 | testAdapter(getAddress('0x6b175474e89094c44da98b954eedeac495271d0f'), getAddress('0xfc1e690f61efd961294b3e1ce3313fbd8aa4f85d'), 'DAI'); 20 | testAdapter(getAddress('0x0000000000085d4780b73119b644ae5ecd22b376'), getAddress('0x4da9b813057d04baef4e5800e36083717b4a0341'), 'TUSD'); 21 | testAdapter(getAddress('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'), getAddress('0x9ba00d6856a4edf4665bca2c2309936572473b7e'), 'USDC'); 22 | testAdapter(getAddress('0xdac17f958d2ee523a2206206994597c13d831ec7'), getAddress('0x71fc860f7d3a592a4a98740e39db31d25db65ae8'), 'USDT'); 23 | testAdapter(getAddress('0x57ab1ec28d129707052df4df418d58a2d46d5f51'), getAddress('0x625ae63000f46200499120b906716420bd059240'), 'sUSD'); 24 | testAdapter(getAddress('0x0d8775f648430679a709e98d2b0cb6250d2887ef'), getAddress('0xe1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00'), 'BAT'); 25 | testAdapter(getAddress('0x514910771af9ca656af840dff83e8264ecf986ca'), getAddress('0xa64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84'), 'LINK'); 26 | testAdapter(getAddress('0xdd974d5c2e2928dea5f71b9825b8b646686bd200'), getAddress('0x9d91be44c06d373a8a226e1f3b146956083803eb'), 'KNC'); 27 | testAdapter(getAddress('0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2'), getAddress('0x7deb5e830be29f91e298ba5ff1356bb7f8146998'), 'MKR'); 28 | testAdapter(getAddress('0x0f5d2fb29fb7d3cfee444a200298f468908cc942'), getAddress('0x6fce4a401b6b80ace52baaefe4421bd188e76f6f'), 'MANA'); 29 | testAdapter(getAddress('0xe41d2489571d322189246dafa5ebde1f4699f498'), getAddress('0x6fb0855c404e09c47c3fbca25f08d4e41f9f062f'), 'ZRX'); 30 | testAdapter(getAddress('0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f'), getAddress('0x328c4c80bc7aca0834db37e6600a6c49e12da4de'), 'SNX'); 31 | testAdapter(getAddress('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'), getAddress('0xfc4b8ed459e00e5400be803a9bb3954234fd50e3'), 'WBTC'); 32 | testAdapter(getAddress('0x4fabb145d64652a948d72533023f6e7a623c7c53'), getAddress('0x6ee0f7bb50a54ab5253da0667b0dc2ee526c30a8'), 'BUSD'); 33 | testAdapter(getAddress('0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c'), getAddress('0x712db54daa836b53ef1ecbb9c6ba3b9efb073f40'), 'ENJ'); 34 | testAdapter(getAddress('0x408e41876cccdc0f92210600ef50372656052a38'), getAddress('0x69948cc03f478b95283f7dbf1ce764d0fc7ec54c'), 'REN'); 35 | testAdapter(getAddress('0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e'), getAddress('0x12e51e77daaa58aa0e9247db7510ea4b46f9bead'), 'YFI'); 36 | testAdapter(getAddress('0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9'), getAddress('0xba3d9687cf50fe253cd2e1cfeede1d6787344ed5'), 'AAVE'); 37 | testAdapter(getAddress('0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'), getAddress('0xb124541127a0a657f056d9dd06188c4f1b0e5aab'), 'UNI'); 38 | }); -------------------------------------------------------------------------------- /test/adapters/aave-v1/AaveV1EtherAdapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { AaveV1Erc20Adapter } from "../../../typechain" 3 | import { shouldBehaveLikeEtherAdapter } from "../../EtherAdapterBehavior.spec" 4 | import { deployContract, AaveV1Converter } from '../../shared' 5 | 6 | 7 | describe('AaveV1EtherAdapter', () => { 8 | const testAdapter = (_underlying: string, _ctoken: string, symbol: string) => describe(`a${symbol}`, function () { 9 | shouldBehaveLikeEtherAdapter( 10 | async () => (await deployContract('AaveV1EtherAdapter', '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8')) as AaveV1Erc20Adapter, 11 | async (adapter, underlying, token) => adapter.initialize(underlying.address, token.address), 12 | AaveV1Converter, 13 | _underlying, 14 | _ctoken, 15 | symbol, 16 | ) 17 | }) 18 | 19 | testAdapter(getAddress('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), getAddress('0x3a3a65aab0dd2a17e3f1947ba16138cd37d08c04'), 'ETH'); 20 | }); -------------------------------------------------------------------------------- /test/adapters/cream/CrEtherAdapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { CrErc20Adapter } from "../../../typechain" 3 | import { shouldBehaveLikeEtherAdapter } from "../../EtherAdapterBehavior.spec" 4 | import { deployContract, CreamConverter } from '../../shared' 5 | 6 | 7 | describe('CrEtherAdapter', () => { 8 | const testAdapter = (_underlying: string, _ctoken: string, symbol: string) => describe(`cr${symbol}`, function () { 9 | shouldBehaveLikeEtherAdapter( 10 | async () => (await deployContract('CrEtherAdapter')) as CrErc20Adapter, 11 | async (adapter, underlying, token) => adapter.initialize(underlying.address, token.address), 12 | CreamConverter, 13 | _underlying, 14 | _ctoken, 15 | symbol, 16 | ) 17 | }) 18 | 19 | testAdapter(getAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), getAddress('0xD06527D5e56A3495252A528C4987003b712860eE'), 'ETH') 20 | }); -------------------------------------------------------------------------------- /test/adapters/fulcrum/FulcrumEtherAdapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { constants } from "ethers" 3 | import { FulcrumErc20Adapter } from "../../../typechain" 4 | import { shouldBehaveLikeEtherAdapter } from "../../EtherAdapterBehavior.spec" 5 | import { deployContract, FulcrumConverter } from '../../shared' 6 | 7 | describe('FulcrumEtherAdapter', () => { 8 | const testAdapter = (_underlying: string, _itoken: string, symbol: string) => describe(`i${symbol}`, function () { 9 | shouldBehaveLikeEtherAdapter( 10 | async () => (await deployContract('FulcrumEtherAdapter', _underlying, _itoken)) as FulcrumErc20Adapter, 11 | async (adapter, underlying, token) => {}, 12 | FulcrumConverter, 13 | _underlying, 14 | _itoken, 15 | symbol, 16 | async (_, __, wrapper) => ({ 17 | depositSenderWrapped: constants.AddressZero, 18 | withdrawalSenderUnderlying: wrapper.address 19 | }) 20 | ) 21 | }) 22 | 23 | testAdapter(getAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), getAddress('0xb983e01458529665007ff7e0cddecdb74b967eb6'), 'ETH'); 24 | }); -------------------------------------------------------------------------------- /test/adapters/ironbank/CyErc20Adapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { IErc20Adapter } from "../../../typechain" 3 | import { shouldBehaveLikeErc20Adapter } from "../../Erc20AdapterBehavior.spec" 4 | import { deployContract, IronBankConverter } from '../../shared' 5 | 6 | 7 | describe('CyErc20Adapter', () => { 8 | const testAdapter = (_underlying: string, _ctoken: string, symbol: string) => describe(`cy${symbol}`, function () { 9 | shouldBehaveLikeErc20Adapter( 10 | async () => (await deployContract('CyErc20Adapter')) as IErc20Adapter, 11 | (adapter, underlying, token) => adapter.initialize(underlying.address, token.address), 12 | IronBankConverter, 13 | _underlying, 14 | _ctoken, 15 | symbol 16 | ); 17 | }) 18 | 19 | testAdapter(getAddress('0x6b175474e89094c44da98b954eedeac495271d0f'), getAddress('0x8e595470ed749b85c6f7669de83eae304c2ec68f'), 'DAI'); 20 | testAdapter(getAddress('0x9ca85572e6a3ebf24dedd195623f188735a5179f'), getAddress('0x7589c9e17bcfce1ccaa1f921196fda177f0207fc'), 'y3Crv'); 21 | // Supply = 0 22 | // testAdapter(getAddress('0xd71ecff9342a5ced620049e616c5035f1db98620'), getAddress('0xca55f9c4e77f7b8524178583b0f7c798de17fd54'), 'sEUR'); 23 | testAdapter(getAddress('0x514910771af9ca656af840dff83e8264ecf986ca'), getAddress('0xe7bff2da8a2f619c2586fb83938fa56ce803aa16'), 'LINK'); 24 | testAdapter(getAddress('0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e'), getAddress('0xfa3472f7319477c9bfecdd66e4b948569e7621b9'), 'YFI'); 25 | testAdapter(getAddress('0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f'), getAddress('0x12a9cc33a980daa74e00cc2d1a0e74c57a93d12c'), 'SNX'); 26 | testAdapter(getAddress('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'), getAddress('0x8fc8bfd80d6a9f17fb98a373023d72531792b431'), 'WBTC'); 27 | testAdapter(getAddress('0xdac17f958d2ee523a2206206994597c13d831ec7'), getAddress('0x48759f220ed983db51fa7a8c0d2aab8f3ce4166a'), 'USDT'); 28 | testAdapter(getAddress('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'), getAddress('0x76eb2fe28b36b3ee97f3adae0c69606eedb2a37c'), 'USDC'); 29 | testAdapter(getAddress('0xe2f2a5c287993345a840db3b0845fbc70f5935a5'), getAddress('0xbe86e8918dfc7d3cb10d295fc220f941a1470c5c'), 'mUSD'); 30 | testAdapter(getAddress('0x5bc25f649fc4e26069ddf4cf4010f9f706c23831'), getAddress('0x297d4da727fbc629252845e96538fc46167e453a'), 'DUSD'); 31 | testAdapter(getAddress('0xdb25f211ab05b1c97d595516f45794528a807ad8'), getAddress('0xa8caea564811af0e92b1e044f3edd18fa9a73e4f'), 'EURS'); 32 | testAdapter(getAddress('0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b'), getAddress('0x7736ffb07104c0c400bb0cc9a7c228452a732992'), 'DPI'); 33 | testAdapter(getAddress('0x4fabb145d64652a948d72533023f6e7a623c7c53'), getAddress('0x09bdcce2593f0bef0991188c25fb744897b6572d'), 'BUSD'); 34 | testAdapter(getAddress('0x056fd409e1d7a124bd7017459dfea2f387b6d5cd'), getAddress('0xa0e5a19e091bbe241e655997e50da82da676b083'), 'GUSD'); 35 | testAdapter(getAddress('0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9'), getAddress('0xbb4b067cc612494914a902217cb6078ab4728e36'), 'cUSDT'); 36 | testAdapter(getAddress('0x1456688345527be1f37e9e627da0837d6f08c925'), getAddress('0xbddeb563e90f6cbf168a7cda4927806477e5b6c6'), 'USDP'); 37 | testAdapter(getAddress('0x5d3a536e4d6dbd6114cc1ead35777bab948e3643'), getAddress('0x4f12c9dabb5319a252463e6028ca833f1164d045'), 'cDAI'); 38 | testAdapter(getAddress('0x39aa39c021dfbae8fac545936693ac917d5e7563'), getAddress('0x950027632fbd6adadfe82644bfb64647642b6c09'), 'cUSDC'); 39 | testAdapter(getAddress('0x57ab1ec28d129707052df4df418d58a2d46d5f51'), getAddress('0xa7c4054afd3dbbbf5bfe80f41862b89ea05c9806'), 'sUSD'); 40 | testAdapter(getAddress('0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'), getAddress('0xfeeb92386a055e2ef7c2b598c872a4047a7db59f'), 'UNI'); 41 | testAdapter(getAddress('0x6b3595068778dd592e39a122f4f5a5cf09c90fe2'), getAddress('0x226f3738238932ba0db2319a8117d9555446102f'), 'SUSHI'); 42 | }); -------------------------------------------------------------------------------- /test/adapters/ironbank/CyEtherAdapter.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address" 2 | import { IErc20Adapter } from "../../../typechain" 3 | import { shouldBehaveLikeEtherAdapter } from "../../EtherAdapterBehavior.spec" 4 | import { deployContract, IronBankConverter } from '../../shared' 5 | 6 | 7 | describe('CyEtherAdapter', () => { 8 | const testAdapter = (_underlying: string, _ctoken: string, symbol: string) => describe(`cy${symbol}`, function () { 9 | shouldBehaveLikeEtherAdapter( 10 | async () => (await deployContract('CyEtherAdapter')) as IErc20Adapter, 11 | (adapter, underlying, token) => adapter.initialize(underlying.address, token.address), 12 | IronBankConverter, 13 | _underlying, 14 | _ctoken, 15 | symbol 16 | ); 17 | }) 18 | 19 | testAdapter(getAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), getAddress('0x41c84c0e2ee0b740cf0d31f63f3b6f627dc6b393'), 'ETH'); 20 | }); -------------------------------------------------------------------------------- /test/libraries/ArrayHelper.spec.ts: -------------------------------------------------------------------------------- 1 | import { keccak256 } from "@ethersproject/keccak256"; 2 | import { expect } from "chai"; 3 | import { BigNumber } from "ethers"; 4 | import { TestArrayHelper } from "../../typechain" 5 | import { deployContract } from "../shared"; 6 | 7 | const toAddress = (n: number) => `0x${n.toString().repeat(40)}`; 8 | const toHash = (n: number) => keccak256(Buffer.from(n.toString())); 9 | 10 | describe('DynamicArrays', () => { 11 | let test: TestArrayHelper; 12 | 13 | before(async () => { 14 | test = await deployContract('TestArrayHelper'); 15 | }) 16 | 17 | it('sum()', async () => { 18 | expect(await test.sum([11, 22, 33])).to.eq(66); 19 | }) 20 | 21 | describe('mremove(uint256[],index)', () => { 22 | it('Should remove an element before last', async () => { 23 | expect(await test["mremove(uint256[],uint256)"]( 24 | [5,4,3,2,1], 3 25 | )).to.deep.eq([5,4,3,1].map(BigNumber.from)) 26 | }) 27 | 28 | it('Should remove last element', async () => { 29 | expect(await test["mremove(uint256[],uint256)"]( 30 | [5,4,3,2,1], 4 31 | )).to.deep.eq([5,4,3,2].map(BigNumber.from)) 32 | }) 33 | }) 34 | 35 | describe('mremove(address[],index)', () => { 36 | it('Should remove an element before last', async () => { 37 | expect(await test["mremove(address[],uint256)"]( 38 | [5,4,3,2,1].map(toAddress), 3 39 | )).to.deep.eq( 40 | [5,4,3,1].map(toAddress) 41 | ) 42 | }) 43 | 44 | it('Should remove last element', async () => { 45 | expect(await test["mremove(address[],uint256)"]( 46 | [5,4,3,2,1].map(toAddress), 4 47 | )).to.deep.eq( 48 | [5,4,3,2].map(toAddress) 49 | ) 50 | }) 51 | }) 52 | 53 | describe('mremove(IErc20Adapter[],index)', () => { 54 | it('Should remove an element before last', async () => { 55 | expect(await test.mremoveAdapters( 56 | [5,4,3,2,1].map(toAddress), 3 57 | )).to.deep.eq( 58 | [5,4,3,1].map(toAddress) 59 | ) 60 | }) 61 | 62 | it('Should remove last element', async () => { 63 | expect(await test.mremoveAdapters( 64 | [5,4,3,2,1].map(toAddress), 4 65 | )).to.deep.eq( 66 | [5,4,3,2].map(toAddress) 67 | ) 68 | }) 69 | }) 70 | 71 | describe('remove(bytes32[] storage,index)', () => { 72 | it('Should remove an element before last', async () => { 73 | await test.setBytes32Array([5,4,3,2,1].map(toHash)) 74 | await test.removeBytes32(3) 75 | expect(await test.getBytes32Array()) 76 | .to.deep.eq([5,4,3,1].map(toHash)) 77 | }) 78 | 79 | it('Should remove last element', async () => { 80 | await test.setBytes32Array([5,4,3,2,1].map(toHash)) 81 | await test.removeBytes32(4) 82 | expect(await test.getBytes32Array()) 83 | .to.deep.eq([5,4,3,2].map(toHash)) 84 | }) 85 | }) 86 | 87 | describe('remove(address[] storage,index)', () => { 88 | it('Should remove an element before last', async () => { 89 | await test.setAddressArray([5,4,3,2,1].map(toAddress)) 90 | await test.removeAddress(3) 91 | expect(await test.getAddressArray()) 92 | .to.deep.eq([5,4,3,1].map(toAddress)) 93 | }) 94 | 95 | it('Should remove last element', async () => { 96 | await test.setAddressArray([5,4,3,2,1].map(toAddress)) 97 | await test.removeAddress(4) 98 | expect(await test.getAddressArray()) 99 | .to.deep.eq([5,4,3,2].map(toAddress)) 100 | }) 101 | }) 102 | 103 | describe('indexOf()', () => { 104 | it('Should revert if element not found', async () => { 105 | await expect(test.indexOf([5,4,3,2,1].map(toAddress), toAddress(6))) 106 | .to.be.revertedWith('element not found') 107 | }) 108 | 109 | it('Should return index of element', async () => { 110 | expect(await test.indexOf([5,4,3,2,1].map(toAddress), toAddress(3))) 111 | .to.eq(2) 112 | }) 113 | }) 114 | 115 | describe('sortByDescendingScore()', () => { 116 | it('Should sort the array of scores in descending order while maintaining relationship between adapters and scores', async () => { 117 | const { 118 | '0': addresses, 119 | '1': scores 120 | } = await test.sortByDescendingScore( 121 | [5,2,4,1].map(toAddress), 122 | [5,2,4,1] 123 | ) 124 | expect(addresses).to.deep.eq([5,4,2,1].map(toAddress)) 125 | expect(scores).to.deep.eq([5,4,2,1].map(BigNumber.from)) 126 | }) 127 | }) 128 | 129 | describe('toArray()', () => { 130 | it('Should convert a set to an array', async () => { 131 | await test.setAddressSet([5,4,3,2,1].map(toAddress)); 132 | expect(await test.toArray()).to.deep.eq([5,4,3,2,1].map(toAddress)) 133 | }) 134 | }) 135 | }) -------------------------------------------------------------------------------- /test/libraries/DynamicArrays.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { constants } from "ethers"; 3 | import { TestDynamicArrays } from "../../typechain" 4 | import { deployContract } from "../shared"; 5 | 6 | describe('DynamicArrays', () => { 7 | let test: TestDynamicArrays; 8 | 9 | before(async () => { 10 | test = await deployContract('TestDynamicArrays'); 11 | }) 12 | 13 | describe('dynamicPush(address)', () => { 14 | it('With no elements, array length is 0', async () => { 15 | expect(await test.buildDynamicAddressArray(10, [])).to.deep.eq([]); 16 | }) 17 | 18 | it('Pushes one element, gives array with length 1', async () => { 19 | expect( 20 | await test.buildDynamicAddressArray(10, [constants.AddressZero]) 21 | ).to.deep.eq([constants.AddressZero]); 22 | }) 23 | 24 | it('Pushes two elements, gives array with length 2', async () => { 25 | expect( 26 | await test.buildDynamicAddressArray(10, [constants.AddressZero, constants.AddressZero]) 27 | ).to.deep.eq([constants.AddressZero, constants.AddressZero]); 28 | }) 29 | 30 | it('Pushing past max size overflows memory', async () => { 31 | await test.testOverflowAddressArray(); 32 | }) 33 | }) 34 | 35 | describe('dynamicPush(uint256)', () => { 36 | it('With no elements, array length is 0', async () => { 37 | expect(await test.buildDynamicUint256Array(10, [])).to.deep.eq([]); 38 | }) 39 | 40 | it('Pushes one element, gives array with length 1', async () => { 41 | expect( 42 | await test.buildDynamicUint256Array(10, [constants.Zero]) 43 | ).to.deep.eq([constants.Zero]); 44 | }) 45 | 46 | it('Pushes two elements, gives array with length 2', async () => { 47 | expect( 48 | await test.buildDynamicUint256Array(10, [constants.Zero, constants.Zero]) 49 | ).to.deep.eq([constants.Zero, constants.Zero]); 50 | }) 51 | 52 | it('Pushing past max size overflows memory', async () => { 53 | await test.testOverflowUint256Array(); 54 | }) 55 | }) 56 | }) -------------------------------------------------------------------------------- /test/shared/coder.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | 3 | const padZero = (hex: string, bits: number) => hex.padStart(bits/4, '0'); 4 | 5 | export const packAdapterAndWeight = (address: string, weight: BigNumber) => 6 | address 7 | .concat(padZero(weight.toHexString().slice(2), 96)) 8 | .toLowerCase() -------------------------------------------------------------------------------- /test/shared/conversion.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | import { AaveV2Erc20Adapter, CErc20Adapter, ICToken, IERC20, IErc20Adapter, IToken } from "../../typechain"; 3 | import { getBigNumber, getContract } from "./utils"; 4 | import { ConvertHelper } from '../../@types/augmentations' 5 | 6 | export const CompoundConverter: ConvertHelper = { 7 | liquidityHolder: async (token) => token.address, 8 | toWrapped: async (cToken, amount) => { 9 | const c: ICToken = await getContract(cToken.address, 'ICToken'); 10 | const rate = await c.callStatic.exchangeRateCurrent({ blockTag: 'pending' }); 11 | return amount.mul(getBigNumber(1)).div(rate); 12 | }, 13 | toUnderlying: async (cToken, amount) => { 14 | const c: ICToken = await getContract(cToken.address, 'ICToken'); 15 | const rate = await c.callStatic.exchangeRateCurrent({ blockTag: 'pending' }); 16 | return amount.mul(rate).div(getBigNumber(1)); 17 | }, 18 | getRewardsTokenAndAPR: async (adapter): Promise<[string, BigNumber]> => { 19 | const cAdapter: CErc20Adapter = await getContract(adapter.address, 'CErc20Adapter') 20 | const rewardsAPR = await cAdapter.getRewardsAPR() 21 | if (rewardsAPR.eq(0)) return ['', rewardsAPR] 22 | return [ 23 | '0xc00e94Cb662C3520282E6f5717214004A7f26888', 24 | rewardsAPR 25 | ] 26 | }, 27 | protocolName: 'Compound', 28 | symbolPrefix: 'c' 29 | } 30 | 31 | export const CreamConverter = { 32 | liquidityHolder: CompoundConverter.liquidityHolder, 33 | toWrapped: CompoundConverter.toWrapped, 34 | toUnderlying: CompoundConverter.toUnderlying, 35 | protocolName: 'Cream', 36 | symbolPrefix: 'cr' 37 | }; 38 | 39 | export const IronBankConverter = { 40 | useWrappedEther: true, 41 | liquidityHolder: CompoundConverter.liquidityHolder, 42 | toWrapped: CompoundConverter.toWrapped, 43 | toUnderlying: CompoundConverter.toUnderlying, 44 | protocolName: 'IronBank', 45 | symbolPrefix: 'cy' 46 | }; 47 | 48 | export const AaveV1Converter: ConvertHelper = { 49 | liquidityHolder: async (_) => '0x3dfd23A6c5E8BbcFc9581d2E864a68feb6a076d3', 50 | toWrapped: async (_, amount) => { return amount; }, 51 | toUnderlying: async (_, amount) => { return amount; }, 52 | protocolName: 'Aave V1', 53 | symbolPrefix: 'a' 54 | } 55 | 56 | export const AaveV2Converter: ConvertHelper = { 57 | useWrappedEther: true, 58 | liquidityHolder: async (token) => token.address, 59 | toWrapped: async (_, amount) => { return amount; }, 60 | toUnderlying: async (_, amount) => { return amount; }, 61 | getRewardsTokenAndAPR: async (adapter): Promise<[string, BigNumber]> => { 62 | const aAdapter: AaveV2Erc20Adapter = await getContract(adapter.address, 'AaveV2Erc20Adapter') 63 | const rewardsAPR = await aAdapter.getRewardsAPR() 64 | if (rewardsAPR.eq(0)) return ['', rewardsAPR] 65 | return [ 66 | '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', 67 | rewardsAPR 68 | ] 69 | }, 70 | protocolName: 'Aave V2', 71 | symbolPrefix: 'a' 72 | } 73 | 74 | export const FulcrumConverter: ConvertHelper = { 75 | useWrappedEther: true, 76 | liquidityHolder: async (token) => token.address, 77 | toWrapped: async (token, amount, withdrawUnderlying?: boolean) => { 78 | const c: IToken = await getContract(token.address, 'IToken'); 79 | const rate = await c.tokenPrice({ blockTag: 'pending' }); 80 | let q = amount.mul(getBigNumber(1)).div(rate); 81 | if (withdrawUnderlying && !q.mul(rate).eq(amount)) { 82 | q = q.add(1); 83 | } 84 | return q; 85 | }, 86 | toUnderlying: async (token, amount) => { 87 | const c: IToken = await getContract(token.address, 'IToken'); 88 | const rate = await c.tokenPrice({ blockTag: 'pending' }); 89 | return amount.mul(rate).div(getBigNumber(1)); 90 | }, 91 | protocolName: 'Fulcrum', 92 | symbolPrefix: 'i', 93 | } -------------------------------------------------------------------------------- /test/shared/fixtures.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, constants } from "ethers"; 2 | import { waffle } from "hardhat"; 3 | import { AdapterRegistry, TestAdapter, TestERC20, TestNirnVault, TestVault } from "../../typechain"; 4 | import { deployClone, deployContract, getBigNumber, getContract } from "./utils"; 5 | 6 | export async function deployTestWrapperAndAdapter( 7 | underlying: string, 8 | apr: BigNumber = getBigNumber(1, 17) 9 | ) { 10 | const wrapper: TestVault = await deployContract('TestVault', underlying); 11 | const adapter: TestAdapter = await deployContract('TestAdapter', underlying, wrapper.address, apr) // 10% apr 12 | return { adapter, wrapper }; 13 | } 14 | 15 | export function deployTestERC20(name: string = 'Test Token', symbol: string = 'TOK', initBalance: BigNumber = getBigNumber(100)): Promise { 16 | return deployContract('TestERC20', name, symbol, initBalance); 17 | } 18 | 19 | export async function deployTestVaultStack( 20 | registry: AdapterRegistry, 21 | eoaSafeCaller = constants.AddressZero, 22 | name: string = 'Test Token 2', 23 | symbol: string = 'TOK2', 24 | initBalance: BigNumber = getBigNumber(150) 25 | ) { 26 | const [owner,,protocolAdapter, vaultFactory] = waffle.provider.getWallets() 27 | const underlying: TestERC20 = await deployContract('TestERC20', name, symbol, initBalance); 28 | const { adapter: adapter1 } = await deployTestWrapperAndAdapter(underlying.address, getBigNumber(1, 17)) 29 | const { adapter: adapter2 } = await deployTestWrapperAndAdapter(underlying.address, getBigNumber(5, 16)) 30 | await underlying.approve(adapter1.address, constants.MaxUint256) 31 | await underlying.approve(adapter2.address, constants.MaxUint256) 32 | await adapter1.deposit(initBalance.div(3)) 33 | await adapter2.deposit(initBalance.div(3)) 34 | if ((await registry.protocolAdapterIds(protocolAdapter.address)).eq(0)) { 35 | await registry.addProtocolAdapter(protocolAdapter.address) 36 | } 37 | await registry.connect(protocolAdapter).addTokenAdapters([ adapter1.address, adapter2.address ]) 38 | if (!(await registry.approvedVaultFactories(vaultFactory.address))) { 39 | await registry.addVaultFactory(vaultFactory.address) 40 | } 41 | const implementation = await deployContract('TestNirnVault', registry.address, eoaSafeCaller) 42 | const vault = await deployClone(implementation) 43 | await vault.initialize(underlying.address, owner.address, owner.address, owner.address) 44 | await registry.connect(vaultFactory).addVault(vault.address) 45 | await underlying.approve(vault.address, constants.MaxUint256) 46 | await underlying.mint(owner.address, getBigNumber(4)) 47 | return { 48 | adapter1, 49 | adapter2, 50 | underlying, 51 | vault 52 | } 53 | } 54 | 55 | export async function deployTestAdaptersAndRegistry(name: string = 'Test Token', symbol: string = 'TOK', initBalance: BigNumber = getBigNumber(150), approveAdapters = true) { 56 | const [,,protocolAdapter] = waffle.provider.getWallets() 57 | const underlying: TestERC20 = await deployContract('TestERC20', name, symbol, initBalance); 58 | const { adapter: adapter1, wrapper: wrapper1 } = await deployTestWrapperAndAdapter(underlying.address, getBigNumber(1, 17)) 59 | const { adapter: adapter2, wrapper: wrapper2 } = await deployTestWrapperAndAdapter(underlying.address, getBigNumber(5, 16)) 60 | const { adapter: adapter3, wrapper: wrapper3 } = await deployTestWrapperAndAdapter(underlying.address, getBigNumber(1, 16)) 61 | await underlying.approve(adapter1.address, constants.MaxUint256) 62 | await underlying.approve(adapter2.address, constants.MaxUint256) 63 | await underlying.approve(adapter3.address, constants.MaxUint256) 64 | await adapter1.deposit(initBalance.div(3)) 65 | await adapter2.deposit(initBalance.div(3)) 66 | await adapter3.deposit(initBalance.div(3)) 67 | const registry: AdapterRegistry = await deployContract('AdapterRegistry') 68 | await registry.addProtocolAdapter(protocolAdapter.address) 69 | if (approveAdapters) { 70 | await registry.connect(protocolAdapter).addTokenAdapters([ adapter1.address, adapter2.address, adapter3.address ]) 71 | } 72 | return { 73 | underlying, 74 | adapter1, 75 | adapter2, 76 | adapter3, 77 | wrapper1, 78 | wrapper2, 79 | wrapper3, 80 | registry 81 | } 82 | } -------------------------------------------------------------------------------- /test/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './time' 2 | export * from './utils' 3 | export * from './conversion' 4 | export * from './coder' 5 | export * from './tokens' 6 | export * from './fixtures' -------------------------------------------------------------------------------- /test/shared/time.ts: -------------------------------------------------------------------------------- 1 | const { waffle } = require("hardhat") 2 | 3 | export async function advanceBlock() { 4 | return waffle.provider.send("evm_mine", []) 5 | } 6 | 7 | export async function advanceBlockTo(blockNumber: number) { 8 | for (let i = await waffle.provider.getBlockNumber(); i < blockNumber; i++) { 9 | await advanceBlock() 10 | } 11 | } 12 | 13 | export async function latest() { 14 | const block = await waffle.provider.getBlock("latest") 15 | return block.timestamp 16 | } 17 | 18 | export async function advanceTimeAndBlock(time: number) { 19 | await advanceTime(time) 20 | await advanceBlock() 21 | } 22 | 23 | export async function setNextTimestamp(time: number) { 24 | await waffle.provider.send('evm_setNextBlockTimestamp', [time]) 25 | } 26 | 27 | export async function advanceTime(time: number) { 28 | await waffle.provider.send("evm_increaseTime", [time]) 29 | } 30 | 31 | export const duration = { 32 | seconds: function (val: number) { 33 | return val 34 | }, 35 | minutes: function (val: number) { 36 | return val * this.seconds(60) 37 | }, 38 | hours: function (val: number) { 39 | return val * this.minutes(60) 40 | }, 41 | days: function (val: number) { 42 | return val * this.hours(24) 43 | }, 44 | weeks: function (val: number) { 45 | return val * this.days(7) 46 | }, 47 | years: function (val: number) { 48 | return val * this.days(365) 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /test/shared/utils.ts: -------------------------------------------------------------------------------- 1 | import { getContractAddress } from '@ethersproject/address'; 2 | import { JsonRpcSigner } from '@ethersproject/providers'; 3 | import { BigNumber, Contract, ContractTransaction } from 'ethers'; 4 | import { ethers, network } from 'hardhat'; 5 | import { TestFactory, IProtocolAdapter } from '../../typechain'; 6 | 7 | export async function batchMap(adapter: IProtocolAdapter, batchSize: number) { 8 | const unmapped = await adapter.getUnmapped(); 9 | let remaining = unmapped.length; 10 | while (remaining > 0) { 11 | await adapter.map(batchSize); 12 | remaining -= batchSize; 13 | } 14 | } 15 | 16 | export function getBigNumber(n: number, decimals = 18) { 17 | return BigNumber.from(10).pow(decimals).mul(n); 18 | } 19 | 20 | export async function getContractBase(address: string, name: string): Promise { 21 | let contract = await ethers.getContractAt(name, address); 22 | return contract as C; 23 | } 24 | 25 | export async function getNextContractAddress(account: string): Promise { 26 | const nonce = await ethers.provider.getTransactionCount(account); 27 | return getContractAddress({ from: account, nonce }); 28 | } 29 | 30 | export async function getTransactionCost(tx: ContractTransaction | Promise) { 31 | const { wait, gasPrice } = await Promise.resolve(tx); 32 | const { gasUsed } = await wait() 33 | return gasUsed.mul(gasPrice); 34 | } 35 | 36 | //#region Chain utils 37 | 38 | export async function resetFork() { 39 | await network.provider.request({ 40 | method: 'hardhat_reset', 41 | params: [{ 42 | forking: { 43 | jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`, 44 | blockNumber: 12667185 45 | } 46 | }] 47 | }) 48 | } 49 | 50 | export async function createSnapshot() { 51 | let snapshotId = await network.provider.request({ 52 | method: 'evm_snapshot' 53 | }); 54 | return async () => { 55 | await network.provider.request({ 56 | method: 'evm_revert', 57 | params: [snapshotId] 58 | }); 59 | snapshotId = await network.provider.request({ 60 | method: 'evm_snapshot' 61 | }); 62 | } 63 | } 64 | //#endregion 65 | 66 | //#region Impersonation utils 67 | 68 | export async function impersonate(address: string) { 69 | await network.provider.request({ 70 | method: "hardhat_impersonateAccount", 71 | params: [address] 72 | }); 73 | return ethers.provider.getSigner(address); 74 | } 75 | 76 | export async function stopImpersonating(address: string) { 77 | await network.provider.request({ 78 | method: "hardhat_stopImpersonatingAccount", 79 | params: [address] 80 | }); 81 | } 82 | 83 | export async function withSigner(address: string, fn: (signer: JsonRpcSigner) => Promise) { 84 | const signer = await impersonate(address); 85 | await fn(signer); 86 | await stopImpersonating(address); 87 | } 88 | 89 | export async function getContract(address: string, name: string, signer?: string | JsonRpcSigner): Promise { 90 | let contract = await getContractBase(address, name); 91 | if (signer) { 92 | const _signer = typeof signer === 'string' ? await impersonate(signer) : signer; 93 | contract = contract.connect(_signer); 94 | } 95 | return contract as C; 96 | } 97 | //#endregion 98 | 99 | /* Other Utils */ 100 | export async function deploy(bytecode: string): Promise { 101 | const [signer] = await ethers.getSigners(); 102 | const tx = await signer.sendTransaction({ data: bytecode }); 103 | const { contractAddress } = await tx.wait(); 104 | return contractAddress; 105 | } 106 | 107 | export async function deployContract(name: string, ...args: any[]): Promise { 108 | const f = await ethers.getContractFactory(name); 109 | const c = await f.deploy(...args); 110 | return c as C; 111 | } 112 | 113 | export async function deployClone(implementation: C): Promise { 114 | const cloneFactory: TestFactory = await deployContract('TestFactory'); 115 | await cloneFactory.clone(implementation.address); 116 | const address = await cloneFactory.last(); 117 | return new Contract(address, implementation.interface, implementation.signer) as C; 118 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | "typeRoots": ["./@types", "./typechain", "node_modules/@types"], 9 | "types": ["@nomiclabs/hardhat-ethers", "@nomiclabs/hardhat-waffle"], 10 | "allowSyntheticDefaultImports": true 11 | }, 12 | "exclude": ["**/node_modules"], 13 | "include": ["test", "@types", "typechain"], 14 | "files": ["./hardhat.config.ts"], 15 | "ts-node": { 16 | "files": true 17 | } 18 | } 19 | --------------------------------------------------------------------------------