Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
Latest 25 from a total of 297 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Update Exchange ... | 19413606 | 239 days ago | IN | 0 ETH | 0.01231983 | ||||
Cook | 19394029 | 241 days ago | IN | 0 ETH | 0.01334641 | ||||
Update Exchange ... | 19369241 | 245 days ago | IN | 0 ETH | 0.01324222 | ||||
Update Exchange ... | 19360672 | 246 days ago | IN | 0 ETH | 0.00996429 | ||||
Cook | 19337817 | 249 days ago | IN | 0 ETH | 0.01410042 | ||||
Update Exchange ... | 19327446 | 251 days ago | IN | 0 ETH | 0.02090519 | ||||
Update Exchange ... | 19324983 | 251 days ago | IN | 0 ETH | 0.00899278 | ||||
Update Exchange ... | 19315723 | 252 days ago | IN | 0 ETH | 0.00774744 | ||||
Update Exchange ... | 19306824 | 254 days ago | IN | 0 ETH | 0.00525999 | ||||
Cook | 19252474 | 261 days ago | IN | 0 ETH | 0.00418052 | ||||
Cook | 19240476 | 263 days ago | IN | 0 ETH | 0.00860619 | ||||
Update Exchange ... | 19225369 | 265 days ago | IN | 0 ETH | 0.00478292 | ||||
Update Exchange ... | 19212911 | 267 days ago | IN | 0 ETH | 0.00669423 | ||||
Update Exchange ... | 19189353 | 270 days ago | IN | 0 ETH | 0.00771877 | ||||
Cook | 19121263 | 280 days ago | IN | 0 ETH | 0.00804669 | ||||
Cook | 19121074 | 280 days ago | IN | 0 ETH | 0.00832103 | ||||
Cook | 19119109 | 280 days ago | IN | 0 ETH | 0.00968313 | ||||
Cook | 19119068 | 280 days ago | IN | 0 ETH | 0.01216024 | ||||
Cook | 19119030 | 280 days ago | IN | 0 ETH | 0.00940772 | ||||
Cook | 19118840 | 280 days ago | IN | 0 ETH | 0.0075047 | ||||
Cook | 19118773 | 280 days ago | IN | 0 ETH | 0.00900514 | ||||
Cook | 19117645 | 280 days ago | IN | 0 ETH | 0.0094149 | ||||
Update Exchange ... | 19115188 | 281 days ago | IN | 0 ETH | 0.00278051 | ||||
Cook | 19076424 | 286 days ago | IN | 0 ETH | 0.00305152 | ||||
Cook | 19073240 | 286 days ago | IN | 0 ETH | 0.00265062 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
16520538 | 645 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0xc4113ae18e0d3213c6a06947a2ffc70ad3517c77
Contract Name:
CauldronV4
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED // Cauldron // ( ( ( // )\ ) ( )\ )\ ) ( // (((_) ( /( ))\ ((_)(()/( )( ( ( // )\___ )(_)) /((_) _ ((_))(()\ )\ )\ ) // ((/ __|((_)_ (_))( | | _| | ((_) ((_) _(_/( // | (__ / _` || || || |/ _` | | '_|/ _ \| ' \)) // \___|\__,_| \_,_||_|\__,_| |_| \___/|_||_| // Copyright (c) 2021 BoringCrypto - All rights reserved // Twitter: @Boring_Crypto // Special thanks to: // @0xKeno - for all his invaluable contributions // @burger_crypto - for the idea of trying to let the LPs benefit from liquidations pragma solidity >=0.8.0; import "BoringSolidity/BoringOwnable.sol"; import "BoringSolidity/ERC20.sol"; import "BoringSolidity/interfaces/IMasterContract.sol"; import "BoringSolidity/libraries/BoringRebase.sol"; import "BoringSolidity/libraries/BoringERC20.sol"; import "libraries/compat/BoringMath.sol"; import "interfaces/IOracle.sol"; import "interfaces/ISwapperV2.sol"; import "interfaces/IBentoBoxV1.sol"; import "interfaces/IBentoBoxOwner.sol"; // solhint-disable avoid-low-level-calls // solhint-disable no-inline-assembly /// @title Cauldron /// @dev This contract allows contract calls to any contract (except BentoBox) /// from arbitrary callers thus, don't trust calls from this contract in any circumstances. contract CauldronV4 is BoringOwnable, IMasterContract { using BoringMath for uint256; using BoringMath128 for uint128; using RebaseLibrary for Rebase; using BoringERC20 for IERC20; event LogExchangeRate(uint256 rate); event LogAccrue(uint128 accruedAmount); event LogAddCollateral(address indexed from, address indexed to, uint256 share); event LogRemoveCollateral(address indexed from, address indexed to, uint256 share); event LogBorrow(address indexed from, address indexed to, uint256 amount, uint256 part); event LogRepay(address indexed from, address indexed to, uint256 amount, uint256 part); event LogFeeTo(address indexed newFeeTo); event LogWithdrawFees(address indexed feeTo, uint256 feesEarnedFraction); event LogInterestChange(uint64 oldInterestRate, uint64 newInterestRate); event LogChangeBorrowLimit(uint128 newLimit, uint128 perAddressPart); event LogChangeBlacklistedCallee(address indexed account, bool blacklisted); event LogRepayForAll(uint256 amount, uint128 previousElastic, uint128 newElastic); event LogLiquidation( address indexed from, address indexed user, address indexed to, uint256 collateralShare, uint256 borrowAmount, uint256 borrowPart ); // Immutables (for MasterContract and all clones) IBentoBoxV1 public immutable bentoBox; CauldronV4 public immutable masterContract; IERC20 public immutable magicInternetMoney; // MasterContract variables address public feeTo; // Per clone variables // Clone init settings IERC20 public collateral; IOracle public oracle; bytes public oracleData; struct BorrowCap { uint128 total; uint128 borrowPartPerAddress; } BorrowCap public borrowLimit; // Total amounts uint256 public totalCollateralShare; // Total collateral supplied Rebase public totalBorrow; // elastic = Total token amount to be repayed by borrowers, base = Total parts of the debt held by borrowers // User balances mapping(address => uint256) public userCollateralShare; mapping(address => uint256) public userBorrowPart; // Callee restrictions mapping(address => bool) public blacklistedCallees; /// @notice Exchange and interest rate tracking. /// This is 'cached' here because calls to Oracles can be very expensive. uint256 public exchangeRate; struct AccrueInfo { uint64 lastAccrued; uint128 feesEarned; uint64 INTEREST_PER_SECOND; } AccrueInfo public accrueInfo; uint64 internal constant ONE_PERCENT_RATE = 317097920; /// @notice tracking of last interest update uint256 internal lastInterestUpdate; // Settings uint256 public COLLATERIZATION_RATE; uint256 internal constant COLLATERIZATION_RATE_PRECISION = 1e5; // Must be less than EXCHANGE_RATE_PRECISION (due to optimization in math) uint256 internal constant EXCHANGE_RATE_PRECISION = 1e18; uint256 public LIQUIDATION_MULTIPLIER; uint256 internal constant LIQUIDATION_MULTIPLIER_PRECISION = 1e5; uint256 public BORROW_OPENING_FEE; uint256 internal constant BORROW_OPENING_FEE_PRECISION = 1e5; uint256 internal constant DISTRIBUTION_PART = 10; uint256 internal constant DISTRIBUTION_PRECISION = 100; modifier onlyMasterContractOwner() { require(msg.sender == masterContract.owner(), "Caller is not the owner"); _; } /// @notice The constructor is only used for the initial master contract. Subsequent clones are initialised via `init`. constructor(IBentoBoxV1 bentoBox_, IERC20 magicInternetMoney_) { bentoBox = bentoBox_; magicInternetMoney = magicInternetMoney_; masterContract = this; blacklistedCallees[address(bentoBox)] = true; blacklistedCallees[address(this)] = true; blacklistedCallees[BoringOwnable(address(bentoBox)).owner()] = true; } /// @notice Serves as the constructor for clones, as clones can't have a regular constructor /// @dev `data` is abi encoded in the format: (IERC20 collateral, IERC20 asset, IOracle oracle, bytes oracleData) function init(bytes calldata data) public virtual payable override { require(address(collateral) == address(0), "Cauldron: already initialized"); (collateral, oracle, oracleData, accrueInfo.INTEREST_PER_SECOND, LIQUIDATION_MULTIPLIER, COLLATERIZATION_RATE, BORROW_OPENING_FEE) = abi.decode(data, (IERC20, IOracle, bytes, uint64, uint256, uint256, uint256)); borrowLimit = BorrowCap(type(uint128).max, type(uint128).max); require(address(collateral) != address(0), "Cauldron: bad pair"); magicInternetMoney.approve(address(bentoBox), type(uint256).max); blacklistedCallees[address(bentoBox)] = true; blacklistedCallees[address(this)] = true; blacklistedCallees[BoringOwnable(address(bentoBox)).owner()] = true; (, exchangeRate) = oracle.get(oracleData); accrue(); } /// @notice Accrues the interest on the borrowed tokens and handles the accumulation of fees. function accrue() public { AccrueInfo memory _accrueInfo = accrueInfo; // Number of seconds since accrue was called uint256 elapsedTime = block.timestamp - _accrueInfo.lastAccrued; if (elapsedTime == 0) { return; } _accrueInfo.lastAccrued = uint64(block.timestamp); Rebase memory _totalBorrow = totalBorrow; if (_totalBorrow.base == 0) { accrueInfo = _accrueInfo; return; } // Accrue interest uint128 extraAmount = (uint256(_totalBorrow.elastic).mul(_accrueInfo.INTEREST_PER_SECOND).mul(elapsedTime) / 1e18).to128(); _totalBorrow.elastic = _totalBorrow.elastic.add(extraAmount); _accrueInfo.feesEarned = _accrueInfo.feesEarned.add(extraAmount); totalBorrow = _totalBorrow; accrueInfo = _accrueInfo; emit LogAccrue(extraAmount); } /// @notice Concrete implementation of `isSolvent`. Includes a third parameter to allow caching `exchangeRate`. /// @param _exchangeRate The exchange rate. Used to cache the `exchangeRate` between calls. function _isSolvent(address user, uint256 _exchangeRate) internal view returns (bool) { // accrue must have already been called! uint256 borrowPart = userBorrowPart[user]; if (borrowPart == 0) return true; uint256 collateralShare = userCollateralShare[user]; if (collateralShare == 0) return false; Rebase memory _totalBorrow = totalBorrow; return bentoBox.toAmount( collateral, collateralShare.mul(EXCHANGE_RATE_PRECISION / COLLATERIZATION_RATE_PRECISION).mul(COLLATERIZATION_RATE), false ) >= // Moved exchangeRate here instead of dividing the other side to preserve more precision borrowPart.mul(_totalBorrow.elastic).mul(_exchangeRate) / _totalBorrow.base; } /// @dev Checks if the user is solvent in the closed liquidation case at the end of the function body. modifier solvent() { _; (, uint256 _exchangeRate) = updateExchangeRate(); require(_isSolvent(msg.sender, _exchangeRate), "Cauldron: user insolvent"); } /// @notice Gets the exchange rate. I.e how much collateral to buy 1e18 asset. /// This function is supposed to be invoked if needed because Oracle queries can be expensive. /// @return updated True if `exchangeRate` was updated. /// @return rate The new exchange rate. function updateExchangeRate() public returns (bool updated, uint256 rate) { (updated, rate) = oracle.get(oracleData); if (updated) { exchangeRate = rate; emit LogExchangeRate(rate); } else { // Return the old rate if fetching wasn't successful rate = exchangeRate; } } /// @dev Helper function to move tokens. /// @param token The ERC-20 token. /// @param share The amount in shares to add. /// @param total Grand total amount to deduct from this contract's balance. Only applicable if `skim` is True. /// Only used for accounting checks. /// @param skim If True, only does a balance check on this contract. /// False if tokens from msg.sender in `bentoBox` should be transferred. function _addTokens( IERC20 token, uint256 share, uint256 total, bool skim ) internal { if (skim) { require(share <= bentoBox.balanceOf(token, address(this)).sub(total), "Cauldron: Skim too much"); } else { bentoBox.transfer(token, msg.sender, address(this), share); } } function _afterAddCollateral(address user, uint256 collateralShare) internal virtual {} /// @notice Adds `collateral` from msg.sender to the account `to`. /// @param to The receiver of the tokens. /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.x /// False if tokens from msg.sender in `bentoBox` should be transferred. /// @param share The amount of shares to add for `to`. function addCollateral( address to, bool skim, uint256 share ) public virtual { userCollateralShare[to] = userCollateralShare[to].add(share); uint256 oldTotalCollateralShare = totalCollateralShare; totalCollateralShare = oldTotalCollateralShare.add(share); _addTokens(collateral, share, oldTotalCollateralShare, skim); _afterAddCollateral(to, share); emit LogAddCollateral(skim ? address(bentoBox) : msg.sender, to, share); } function _afterRemoveCollateral(address from, address to, uint256 collateralShare) internal virtual {} /// @dev Concrete implementation of `removeCollateral`. function _removeCollateral(address to, uint256 share) internal virtual { userCollateralShare[msg.sender] = userCollateralShare[msg.sender].sub(share); totalCollateralShare = totalCollateralShare.sub(share); _afterRemoveCollateral(msg.sender, to, share); emit LogRemoveCollateral(msg.sender, to, share); bentoBox.transfer(collateral, address(this), to, share); } /// @notice Removes `share` amount of collateral and transfers it to `to`. /// @param to The receiver of the shares. /// @param share Amount of shares to remove. function removeCollateral(address to, uint256 share) public solvent { // accrue must be called because we check solvency accrue(); _removeCollateral(to, share); } function _preBorrowAction(address to, uint256 amount, uint256 newBorrowPart, uint256 part) internal virtual { } /// @dev Concrete implementation of `borrow`. function _borrow(address to, uint256 amount) internal returns (uint256 part, uint256 share) { uint256 feeAmount = amount.mul(BORROW_OPENING_FEE) / BORROW_OPENING_FEE_PRECISION; // A flat % fee is charged for any borrow (totalBorrow, part) = totalBorrow.add(amount.add(feeAmount), true); BorrowCap memory cap = borrowLimit; require(totalBorrow.elastic <= cap.total, "Borrow Limit reached"); accrueInfo.feesEarned = accrueInfo.feesEarned.add(uint128(feeAmount)); uint256 newBorrowPart = userBorrowPart[msg.sender].add(part); require(newBorrowPart <= cap.borrowPartPerAddress, "Borrow Limit reached"); _preBorrowAction(to, amount, newBorrowPart, part); userBorrowPart[msg.sender] = newBorrowPart; // As long as there are tokens on this contract you can 'mint'... this enables limiting borrows share = bentoBox.toShare(magicInternetMoney, amount, false); bentoBox.transfer(magicInternetMoney, address(this), to, share); emit LogBorrow(msg.sender, to, amount.add(feeAmount), part); } /// @notice Sender borrows `amount` and transfers it to `to`. /// @return part Total part of the debt held by borrowers. /// @return share Total amount in shares borrowed. function borrow(address to, uint256 amount) public solvent returns (uint256 part, uint256 share) { accrue(); (part, share) = _borrow(to, amount); } /// @dev Concrete implementation of `repay`. function _repay( address to, bool skim, uint256 part ) internal returns (uint256 amount) { (totalBorrow, amount) = totalBorrow.sub(part, true); userBorrowPart[to] = userBorrowPart[to].sub(part); uint256 share = bentoBox.toShare(magicInternetMoney, amount, true); bentoBox.transfer(magicInternetMoney, skim ? address(bentoBox) : msg.sender, address(this), share); emit LogRepay(skim ? address(bentoBox) : msg.sender, to, amount, part); } /// @notice Repays a loan. /// @param to Address of the user this payment should go. /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender. /// False if tokens from msg.sender in `bentoBox` should be transferred. /// @param part The amount to repay. See `userBorrowPart`. /// @return amount The total amount repayed. function repay( address to, bool skim, uint256 part ) public returns (uint256 amount) { accrue(); amount = _repay(to, skim, part); } // Functions that need accrue to be called uint8 internal constant ACTION_REPAY = 2; uint8 internal constant ACTION_REMOVE_COLLATERAL = 4; uint8 internal constant ACTION_BORROW = 5; uint8 internal constant ACTION_GET_REPAY_SHARE = 6; uint8 internal constant ACTION_GET_REPAY_PART = 7; uint8 internal constant ACTION_ACCRUE = 8; // Functions that don't need accrue to be called uint8 internal constant ACTION_ADD_COLLATERAL = 10; uint8 internal constant ACTION_UPDATE_EXCHANGE_RATE = 11; // Function on BentoBox uint8 internal constant ACTION_BENTO_DEPOSIT = 20; uint8 internal constant ACTION_BENTO_WITHDRAW = 21; uint8 internal constant ACTION_BENTO_TRANSFER = 22; uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23; uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24; // Any external call (except to BentoBox) uint8 internal constant ACTION_CALL = 30; uint8 internal constant ACTION_LIQUIDATE = 31; uint8 internal constant ACTION_RELEASE_COLLATERAL_FROM_STRATEGY = 33; int256 internal constant USE_VALUE1 = -1; int256 internal constant USE_VALUE2 = -2; /// @dev Helper function for choosing the correct value (`value1` or `value2`) depending on `inNum`. function _num( int256 inNum, uint256 value1, uint256 value2 ) internal pure returns (uint256 outNum) { outNum = inNum >= 0 ? uint256(inNum) : (inNum == USE_VALUE1 ? value1 : value2); } /// @dev Helper function for depositing into `bentoBox`. function _bentoDeposit( bytes memory data, uint256 value, uint256 value1, uint256 value2 ) internal returns (uint256, uint256) { (IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256)); amount = int256(_num(amount, value1, value2)); // Done this way to avoid stack too deep errors share = int256(_num(share, value1, value2)); return bentoBox.deposit{value: value}(token, msg.sender, to, uint256(amount), uint256(share)); } /// @dev Helper function to withdraw from the `bentoBox`. function _bentoWithdraw( bytes memory data, uint256 value1, uint256 value2 ) internal returns (uint256, uint256) { (IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256)); return bentoBox.withdraw(token, msg.sender, to, _num(amount, value1, value2), _num(share, value1, value2)); } /// @dev Helper function to perform a contract call and eventually extracting revert messages on failure. /// Calls to `bentoBox` are not allowed for obvious security reasons. /// This also means that calls made from this contract shall *not* be trusted. function _call( uint256 value, bytes memory data, uint256 value1, uint256 value2 ) internal returns (bytes memory, uint8) { (address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) = abi.decode(data, (address, bytes, bool, bool, uint8)); if (useValue1 && !useValue2) { callData = abi.encodePacked(callData, value1); } else if (!useValue1 && useValue2) { callData = abi.encodePacked(callData, value2); } else if (useValue1 && useValue2) { callData = abi.encodePacked(callData, value1, value2); } require(!blacklistedCallees[callee], "Cauldron: can't call"); (bool success, bytes memory returnData) = callee.call{value: value}(callData); require(success, "Cauldron: call failed"); return (returnData, returnValues); } function _additionalCookAction(uint8 action, uint256 value, bytes memory data, uint256 value1, uint256 value2) internal virtual returns (bytes memory, uint8) {} struct CookStatus { bool needsSolvencyCheck; bool hasAccrued; } /// @notice Executes a set of actions and allows composability (contract calls) to other contracts. /// @param actions An array with a sequence of actions to execute (see ACTION_ declarations). /// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions. /// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`. /// @param datas A one-to-one mapped array to `actions`. Contains abi encoded data of function arguments. /// @return value1 May contain the first positioned return value of the last executed action (if applicable). /// @return value2 May contain the second positioned return value of the last executed action which returns 2 values (if applicable). function cook( uint8[] calldata actions, uint256[] calldata values, bytes[] calldata datas ) external payable returns (uint256 value1, uint256 value2) { CookStatus memory status; uint64 previousStrategyTargetPercentage = type(uint64).max; for (uint256 i = 0; i < actions.length; i++) { uint8 action = actions[i]; if (!status.hasAccrued && action < 10) { accrue(); status.hasAccrued = true; } if (action == ACTION_ADD_COLLATERAL) { (int256 share, address to, bool skim) = abi.decode(datas[i], (int256, address, bool)); addCollateral(to, skim, _num(share, value1, value2)); } else if (action == ACTION_REPAY) { (int256 part, address to, bool skim) = abi.decode(datas[i], (int256, address, bool)); _repay(to, skim, _num(part, value1, value2)); } else if (action == ACTION_REMOVE_COLLATERAL) { (int256 share, address to) = abi.decode(datas[i], (int256, address)); _removeCollateral(to, _num(share, value1, value2)); status.needsSolvencyCheck = true; } else if (action == ACTION_BORROW) { (int256 amount, address to) = abi.decode(datas[i], (int256, address)); (value1, value2) = _borrow(to, _num(amount, value1, value2)); status.needsSolvencyCheck = true; } else if (action == ACTION_UPDATE_EXCHANGE_RATE) { (bool must_update, uint256 minRate, uint256 maxRate) = abi.decode(datas[i], (bool, uint256, uint256)); (bool updated, uint256 rate) = updateExchangeRate(); require((!must_update || updated) && rate > minRate && (maxRate == 0 || rate > maxRate), "Cauldron: rate not ok"); } else if (action == ACTION_BENTO_SETAPPROVAL) { (address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) = abi.decode(datas[i], (address, address, bool, uint8, bytes32, bytes32)); bentoBox.setMasterContractApproval(user, _masterContract, approved, v, r, s); } else if (action == ACTION_BENTO_DEPOSIT) { (value1, value2) = _bentoDeposit(datas[i], values[i], value1, value2); } else if (action == ACTION_BENTO_WITHDRAW) { (value1, value2) = _bentoWithdraw(datas[i], value1, value2); } else if (action == ACTION_BENTO_TRANSFER) { (IERC20 token, address to, int256 share) = abi.decode(datas[i], (IERC20, address, int256)); bentoBox.transfer(token, msg.sender, to, _num(share, value1, value2)); } else if (action == ACTION_BENTO_TRANSFER_MULTIPLE) { (IERC20 token, address[] memory tos, uint256[] memory shares) = abi.decode(datas[i], (IERC20, address[], uint256[])); bentoBox.transferMultiple(token, msg.sender, tos, shares); } else if (action == ACTION_CALL) { (bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2); if (returnValues == 1) { (value1) = abi.decode(returnData, (uint256)); } else if (returnValues == 2) { (value1, value2) = abi.decode(returnData, (uint256, uint256)); } } else if (action == ACTION_GET_REPAY_SHARE) { int256 part = abi.decode(datas[i], (int256)); value1 = bentoBox.toShare(magicInternetMoney, totalBorrow.toElastic(_num(part, value1, value2), true), true); } else if (action == ACTION_GET_REPAY_PART) { int256 amount = abi.decode(datas[i], (int256)); value1 = totalBorrow.toBase(_num(amount, value1, value2), false); } else if (action == ACTION_LIQUIDATE) { _cookActionLiquidate(datas[i]); } else if (action == ACTION_RELEASE_COLLATERAL_FROM_STRATEGY) { require(previousStrategyTargetPercentage == type(uint64).max, "Cauldron: strategy already released"); (, previousStrategyTargetPercentage,) = bentoBox.strategyData(collateral); IBentoBoxOwner(bentoBox.owner()).setStrategyTargetPercentageAndRebalance(collateral, 0); } else { (bytes memory returnData, uint8 returnValues) = _additionalCookAction(action, values[i], datas[i], value1, value2); if (returnValues == 1) { (value1) = abi.decode(returnData, (uint256)); } else if (returnValues == 2) { (value1, value2) = abi.decode(returnData, (uint256, uint256)); } } } if (previousStrategyTargetPercentage != type(uint64).max) { IBentoBoxOwner(bentoBox.owner()).setStrategyTargetPercentageAndRebalance(collateral, previousStrategyTargetPercentage); } if (status.needsSolvencyCheck) { (, uint256 _exchangeRate) = updateExchangeRate(); require(_isSolvent(msg.sender, _exchangeRate), "Cauldron: user insolvent"); } } function _cookActionLiquidate(bytes calldata data) internal { (address[] memory users, uint256[] memory maxBorrowParts, address to, ISwapperV2 swapper, bytes memory swapperData) = abi.decode(data, (address[], uint256[], address, ISwapperV2, bytes)); liquidate(users, maxBorrowParts, to, swapper, swapperData); } function _beforeUsersLiquidated(address[] memory users, uint256[] memory maxBorrowPart) internal virtual {} function _afterUserLiquidated(address user, uint256 collateralShare) internal virtual {} /// @notice Handles the liquidation of users' balances, once the users' amount of collateral is too low. /// @param users An array of user addresses. /// @param maxBorrowParts A one-to-one mapping to `users`, contains maximum (partial) borrow amounts (to liquidate) of the respective user. /// @param to Address of the receiver in open liquidations if `swapper` is zero. function liquidate( address[] memory users, uint256[] memory maxBorrowParts, address to, ISwapperV2 swapper, bytes memory swapperData ) public virtual { // Oracle can fail but we still need to allow liquidations (, uint256 _exchangeRate) = updateExchangeRate(); accrue(); uint256 allCollateralShare; uint256 allBorrowAmount; uint256 allBorrowPart; Rebase memory bentoBoxTotals = bentoBox.totals(collateral); _beforeUsersLiquidated(users, maxBorrowParts); for (uint256 i = 0; i < users.length; i++) { address user = users[i]; if (!_isSolvent(user, _exchangeRate)) { uint256 borrowPart; { uint256 availableBorrowPart = userBorrowPart[user]; borrowPart = maxBorrowParts[i] > availableBorrowPart ? availableBorrowPart : maxBorrowParts[i]; userBorrowPart[user] = availableBorrowPart.sub(borrowPart); } uint256 borrowAmount = totalBorrow.toElastic(borrowPart, false); uint256 collateralShare = bentoBoxTotals.toBase( borrowAmount.mul(LIQUIDATION_MULTIPLIER).mul(_exchangeRate) / (LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION), false ); userCollateralShare[user] = userCollateralShare[user].sub(collateralShare); _afterUserLiquidated(user, collateralShare); emit LogRemoveCollateral(user, to, collateralShare); emit LogRepay(msg.sender, user, borrowAmount, borrowPart); emit LogLiquidation(msg.sender, user, to, collateralShare, borrowAmount, borrowPart); // Keep totals allCollateralShare = allCollateralShare.add(collateralShare); allBorrowAmount = allBorrowAmount.add(borrowAmount); allBorrowPart = allBorrowPart.add(borrowPart); } } require(allBorrowAmount != 0, "Cauldron: all are solvent"); totalBorrow.elastic = totalBorrow.elastic.sub(allBorrowAmount.to128()); totalBorrow.base = totalBorrow.base.sub(allBorrowPart.to128()); totalCollateralShare = totalCollateralShare.sub(allCollateralShare); // Apply a percentual fee share to sSpell holders { uint256 distributionAmount = (allBorrowAmount.mul(LIQUIDATION_MULTIPLIER) / LIQUIDATION_MULTIPLIER_PRECISION).sub(allBorrowAmount).mul(DISTRIBUTION_PART) / DISTRIBUTION_PRECISION; // Distribution Amount allBorrowAmount = allBorrowAmount.add(distributionAmount); accrueInfo.feesEarned = accrueInfo.feesEarned.add(distributionAmount.to128()); } uint256 allBorrowShare = bentoBox.toShare(magicInternetMoney, allBorrowAmount, true); // Swap using a swapper freely chosen by the caller // Open (flash) liquidation: get proceeds first and provide the borrow after bentoBox.transfer(collateral, address(this), to, allCollateralShare); if (swapper != ISwapperV2(address(0))) { swapper.swap(address(collateral), address(magicInternetMoney), msg.sender, allBorrowShare, allCollateralShare, swapperData); } allBorrowShare = bentoBox.toShare(magicInternetMoney, allBorrowAmount, true); bentoBox.transfer(magicInternetMoney, msg.sender, address(this), allBorrowShare); } /// @notice Withdraws the fees accumulated. function withdrawFees() public { accrue(); address _feeTo = masterContract.feeTo(); uint256 _feesEarned = accrueInfo.feesEarned; uint256 share = bentoBox.toShare(magicInternetMoney, _feesEarned, false); bentoBox.transfer(magicInternetMoney, address(this), _feeTo, share); accrueInfo.feesEarned = 0; emit LogWithdrawFees(_feeTo, _feesEarned); } /// @notice Sets the beneficiary of interest accrued. /// MasterContract Only Admin function. /// @param newFeeTo The address of the receiver. function setFeeTo(address newFeeTo) public onlyOwner { feeTo = newFeeTo; emit LogFeeTo(newFeeTo); } /// @notice reduces the supply of MIM /// @param amount amount to reduce supply by function reduceSupply(uint256 amount) public onlyMasterContractOwner { uint256 maxAmount = bentoBox.toAmount(magicInternetMoney, bentoBox.balanceOf(magicInternetMoney, address(this)), false); amount = maxAmount > amount ? amount : maxAmount; bentoBox.withdraw(magicInternetMoney, address(this), msg.sender, amount, 0); } /// @notice allows to change the interest rate /// @param newInterestRate new interest rate function changeInterestRate(uint64 newInterestRate) public onlyMasterContractOwner { uint64 oldInterestRate = accrueInfo.INTEREST_PER_SECOND; require(newInterestRate < oldInterestRate + oldInterestRate * 3 / 4 || newInterestRate <= ONE_PERCENT_RATE, "Interest rate increase > 75%"); require(lastInterestUpdate + 3 days < block.timestamp, "Update only every 3 days"); lastInterestUpdate = block.timestamp; accrueInfo.INTEREST_PER_SECOND = newInterestRate; emit LogInterestChange(oldInterestRate, newInterestRate); } /// @notice allows to change the borrow limit /// @param newBorrowLimit new borrow limit /// @param perAddressPart new borrow limit per address function changeBorrowLimit(uint128 newBorrowLimit, uint128 perAddressPart) public onlyMasterContractOwner { borrowLimit = BorrowCap(newBorrowLimit, perAddressPart); emit LogChangeBorrowLimit(newBorrowLimit, perAddressPart); } /// @notice allows to change blacklisted callees /// @param callee callee to blacklist or not /// @param blacklisted true when the callee cannot be used in call cook action function setBlacklistedCallee(address callee, bool blacklisted) public onlyMasterContractOwner { require(callee != address(bentoBox) && callee != address(this), "invalid callee"); blacklistedCallees[callee] = blacklisted; emit LogChangeBlacklistedCallee(callee, blacklisted); } /// @notice Used to auto repay everyone liabilities'. /// Transfer MIM deposit to DegenBox for this Cauldron and increase the totalBorrow base or skim /// all mim inside this contract function repayForAll(uint128 amount, bool skim) public returns(uint128) { accrue(); if(skim) { // ignore amount and take every mim in this contract since it could be taken by anyone, the next block. amount = uint128(magicInternetMoney.balanceOf(address(this))); bentoBox.deposit(magicInternetMoney, address(this), address(this), amount, 0); } else { bentoBox.transfer(magicInternetMoney, msg.sender, address(this), bentoBox.toShare(magicInternetMoney, amount, true)); } uint128 previousElastic = totalBorrow.elastic; require(previousElastic - amount > 1000 * 1e18, "Total Elastic too small"); totalBorrow.elastic = previousElastic - amount; emit LogRepayForAll(amount, previousElastic, totalBorrow.elastic); return amount; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol // Simplified by BoringCrypto contract BoringOwnableData { address public owner; address public pendingOwner; } contract BoringOwnable is BoringOwnableData { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /// @notice `owner` defaults to msg.sender on construction. constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner. /// Can only be invoked by the current `owner`. /// @param newOwner Address of the new owner. /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`. /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise. function transferOwnership( address newOwner, bool direct, bool renounce ) public onlyOwner { if (direct) { // Checks require(newOwner != address(0) || renounce, "Ownable: zero address"); // Effects emit OwnershipTransferred(owner, newOwner); owner = newOwner; pendingOwner = address(0); } else { // Effects pendingOwner = newOwner; } } /// @notice Needs to be called by `pendingOwner` to claim ownership. function claimOwnership() public { address _pendingOwner = pendingOwner; // Checks require(msg.sender == _pendingOwner, "Ownable: caller != pending owner"); // Effects emit OwnershipTransferred(owner, _pendingOwner); owner = _pendingOwner; pendingOwner = address(0); } /// @notice Only allows the `owner` to execute the function. modifier onlyOwner() { require(msg.sender == owner, "Ownable: caller is not the owner"); _; } }
// SPDX-License-Identifier: MIT // Based on code and smartness by Ross Campbell and Keno // Uses immutable to store the domain separator to reduce gas usage // If the chain id changes due to a fork, the forked chain will calculate on the fly. pragma solidity ^0.8.0; // solhint-disable no-inline-assembly contract Domain { bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); // See https://eips.ethereum.org/EIPS/eip-191 string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01"; // solhint-disable var-name-mixedcase bytes32 private immutable _DOMAIN_SEPARATOR; uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID; /// @dev Calculate the DOMAIN_SEPARATOR function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) { return keccak256(abi.encode(DOMAIN_SEPARATOR_SIGNATURE_HASH, chainId, address(this))); } constructor() { _DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = block.chainid); } /// @dev Return the DOMAIN_SEPARATOR // It's named internal to allow making it public from the contract that uses it by creating a simple view function // with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator. // solhint-disable-next-line func-name-mixedcase function _domainSeparator() internal view returns (bytes32) { return block.chainid == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid); } function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) { digest = keccak256(abi.encodePacked(EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA, _domainSeparator(), dataHash)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IERC20.sol"; import "./Domain.sol"; // solhint-disable no-inline-assembly // solhint-disable not-rely-on-time // Data part taken out for building of contracts that receive delegate calls contract ERC20Data { /// @notice owner > balance mapping. mapping(address => uint256) public balanceOf; /// @notice owner > spender > allowance mapping. mapping(address => mapping(address => uint256)) public allowance; /// @notice owner > nonce mapping. Used in `permit`. mapping(address => uint256) public nonces; } abstract contract ERC20 is IERC20, Domain { /// @notice owner > balance mapping. mapping(address => uint256) public override balanceOf; /// @notice owner > spender > allowance mapping. mapping(address => mapping(address => uint256)) public override allowance; /// @notice owner > nonce mapping. Used in `permit`. mapping(address => uint256) public nonces; /// @notice Transfers `amount` tokens from `msg.sender` to `to`. /// @param to The address to move the tokens. /// @param amount of the tokens to move. /// @return (bool) Returns True if succeeded. function transfer(address to, uint256 amount) public returns (bool) { // If `amount` is 0, or `msg.sender` is `to` nothing happens if (amount != 0 || msg.sender == to) { uint256 srcBalance = balanceOf[msg.sender]; require(srcBalance >= amount, "ERC20: balance too low"); if (msg.sender != to) { require(to != address(0), "ERC20: no zero address"); // Moved down so low balance calls safe some gas balanceOf[msg.sender] = srcBalance - amount; // Underflow is checked balanceOf[to] += amount; } } emit Transfer(msg.sender, to, amount); return true; } /// @notice Transfers `amount` tokens from `from` to `to`. Caller needs approval for `from`. /// @param from Address to draw tokens from. /// @param to The address to move the tokens. /// @param amount The token amount to move. /// @return (bool) Returns True if succeeded. function transferFrom( address from, address to, uint256 amount ) public returns (bool) { // If `amount` is 0, or `from` is `to` nothing happens if (amount != 0) { uint256 srcBalance = balanceOf[from]; require(srcBalance >= amount, "ERC20: balance too low"); if (from != to) { uint256 spenderAllowance = allowance[from][msg.sender]; // If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20). if (spenderAllowance != type(uint256).max) { require(spenderAllowance >= amount, "ERC20: allowance too low"); allowance[from][msg.sender] = spenderAllowance - amount; // Underflow is checked } require(to != address(0), "ERC20: no zero address"); // Moved down so other failed calls safe some gas balanceOf[from] = srcBalance - amount; // Underflow is checked balanceOf[to] += amount; } } emit Transfer(from, to, amount); return true; } /// @notice Approves `amount` from sender to be spend by `spender`. /// @param spender Address of the party that can draw from msg.sender's account. /// @param amount The maximum collective amount that `spender` can draw. /// @return (bool) Returns True if approved. function approve(address spender, uint256 amount) public override returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32) { return _domainSeparator(); } // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /// @notice Approves `value` from `owner_` to be spend by `spender`. /// @param owner_ Address of the owner. /// @param spender The address of the spender that gets approved to draw from `owner_`. /// @param value The maximum collective amount that `spender` can draw. /// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds). function permit( address owner_, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override { require(owner_ != address(0), "ERC20: Owner cannot be 0"); require(block.timestamp < deadline, "ERC20: Expired"); require( ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) == owner_, "ERC20: Invalid Signature" ); allowance[owner_][spender] = value; emit Approval(owner_, spender, value); } } contract ERC20WithSupply is IERC20, ERC20 { uint256 public override totalSupply; function _mint(address user, uint256 amount) internal { uint256 newTotalSupply = totalSupply + amount; require(newTotalSupply >= totalSupply, "Mint overflow"); totalSupply = newTotalSupply; balanceOf[user] += amount; emit Transfer(address(0), user, amount); } function _burn(address user, uint256 amount) internal { require(balanceOf[user] >= amount, "Burn too much"); totalSupply -= amount; balanceOf[user] -= amount; emit Transfer(user, address(0), amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IERC20 { // transfer and tranferFrom have been removed, because they don't work on all tokens (some aren't ERC20 complaint). // By removing them you can't accidentally use them. // name, symbol and decimals have been removed, because they are optional and sometimes wrongly implemented (MKR). // Use BoringERC20 with `using BoringERC20 for IERC20` and call `safeTransfer`, `safeTransferFrom`, etc instead. function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); /// @notice EIP 2612 function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; } interface IStrictERC20 { // This is the strict ERC20 interface. Don't use this, certainly not if you don't control the ERC20 token you're calling. function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256 balance); function transfer(address _to, uint256 _value) external returns (bool success); function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); function approve(address _spender, uint256 _value) external returns (bool success); function allowance(address _owner, address _spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); /// @notice EIP 2612 function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IMasterContract { /// @notice Init function that gets called from `BoringFactory.deploy`. /// Also kown as the constructor for cloned contracts. /// Any ETH send to `BoringFactory.deploy` ends up here. /// @param data Can be abi encoded arguments or anything else. function init(bytes calldata data) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/IERC20.sol"; // solhint-disable avoid-low-level-calls library BoringERC20 { bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol() bytes4 private constant SIG_NAME = 0x06fdde03; // name() bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals() bytes4 private constant SIG_BALANCE_OF = 0x70a08231; // balanceOf(address) bytes4 private constant SIG_TOTALSUPPLY = 0x18160ddd; // balanceOf(address) bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256) bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256) function returnDataToString(bytes memory data) internal pure returns (string memory) { if (data.length >= 64) { return abi.decode(data, (string)); } else if (data.length == 32) { uint8 i = 0; while (i < 32 && data[i] != 0) { i++; } bytes memory bytesArray = new bytes(i); for (i = 0; i < 32 && data[i] != 0; i++) { bytesArray[i] = data[i]; } return string(bytesArray); } else { return "???"; } } /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string. /// @param token The address of the ERC-20 token contract. /// @return (string) Token symbol. function safeSymbol(IERC20 token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL)); return success ? returnDataToString(data) : "???"; } /// @notice Provides a safe ERC20.name version which returns '???' as fallback string. /// @param token The address of the ERC-20 token contract. /// @return (string) Token name. function safeName(IERC20 token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME)); return success ? returnDataToString(data) : "???"; } /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value. /// @param token The address of the ERC-20 token contract. /// @return (uint8) Token decimals. function safeDecimals(IERC20 token) internal view returns (uint8) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS)); return success && data.length == 32 ? abi.decode(data, (uint8)) : 18; } /// @notice Provides a gas-optimized balance check to avoid a redundant extcodesize check in addition to the returndatasize check. /// @param token The address of the ERC-20 token. /// @param to The address of the user to check. /// @return amount The token amount. function safeBalanceOf(IERC20 token, address to) internal view returns (uint256 amount) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_BALANCE_OF, to)); require(success && data.length >= 32, "BoringERC20: BalanceOf failed"); amount = abi.decode(data, (uint256)); } /// @notice Provides a gas-optimized totalSupply to avoid a redundant extcodesize check in addition to the returndatasize check. /// @param token The address of the ERC-20 token. /// @return totalSupply The token totalSupply. function safeTotalSupply(IERC20 token) internal view returns (uint256 totalSupply) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_TOTALSUPPLY)); require(success && data.length >= 32, "BoringERC20: totalSupply failed"); totalSupply = abi.decode(data, (uint256)); } /// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations. /// Reverts on a failed transfer. /// @param token The address of the ERC-20 token. /// @param to Transfer tokens to. /// @param amount The token amount. function safeTransfer( IERC20 token, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount)); require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed"); } /// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations. /// Reverts on a failed transfer. /// @param token The address of the ERC-20 token. /// @param from Transfer tokens from. /// @param to Transfer tokens to. /// @param amount The token amount. function safeTransferFrom( IERC20 token, address from, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount)); require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; struct Rebase { uint128 elastic; uint128 base; } /// @notice A rebasing library using overflow-/underflow-safe math. library RebaseLibrary { /// @notice Calculates the base value in relationship to `elastic` and `total`. function toBase( Rebase memory total, uint256 elastic, bool roundUp ) internal pure returns (uint256 base) { if (total.elastic == 0) { base = elastic; } else { base = (elastic * total.base) / total.elastic; if (roundUp && (base * total.elastic) / total.base < elastic) { base++; } } } /// @notice Calculates the elastic value in relationship to `base` and `total`. function toElastic( Rebase memory total, uint256 base, bool roundUp ) internal pure returns (uint256 elastic) { if (total.base == 0) { elastic = base; } else { elastic = (base * total.elastic) / total.base; if (roundUp && (elastic * total.base) / total.elastic < base) { elastic++; } } } /// @notice Add `elastic` to `total` and doubles `total.base`. /// @return (Rebase) The new total. /// @return base in relationship to `elastic`. function add( Rebase memory total, uint256 elastic, bool roundUp ) internal pure returns (Rebase memory, uint256 base) { base = toBase(total, elastic, roundUp); total.elastic += uint128(elastic); total.base += uint128(base); return (total, base); } /// @notice Sub `base` from `total` and update `total.elastic`. /// @return (Rebase) The new total. /// @return elastic in relationship to `base`. function sub( Rebase memory total, uint256 base, bool roundUp ) internal pure returns (Rebase memory, uint256 elastic) { elastic = toElastic(total, base, roundUp); total.elastic -= uint128(elastic); total.base -= uint128(base); return (total, elastic); } /// @notice Add `elastic` and `base` to `total`. function add( Rebase memory total, uint256 elastic, uint256 base ) internal pure returns (Rebase memory) { total.elastic += uint128(elastic); total.base += uint128(base); return total; } /// @notice Subtract `elastic` and `base` to `total`. function sub( Rebase memory total, uint256 elastic, uint256 base ) internal pure returns (Rebase memory) { total.elastic -= uint128(elastic); total.base -= uint128(base); return total; } /// @notice Add `elastic` to `total` and update storage. /// @return newElastic Returns updated `elastic`. function addElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) { newElastic = total.elastic += uint128(elastic); } /// @notice Subtract `elastic` from `total` and update storage. /// @return newElastic Returns updated `elastic`. function subElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) { newElastic = total.elastic -= uint128(elastic); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import "BoringSolidity/interfaces/IERC20.sol"; interface IBentoBoxOwner { function setStrategyTargetPercentageAndRebalance(IERC20 token, uint64 targetPercentage) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import "BoringSolidity/interfaces/IERC20.sol"; import "BoringSolidity/libraries/BoringRebase.sol"; import "interfaces/IStrategy.sol"; interface IFlashBorrower { /// @notice The flashloan callback. `amount` + `fee` needs to repayed to msg.sender before this call returns. /// @param sender The address of the invoker of this flashloan. /// @param token The address of the token that is loaned. /// @param amount of the `token` that is loaned. /// @param fee The fee that needs to be paid on top for this loan. Needs to be the same as `token`. /// @param data Additional data that was passed to the flashloan function. function onFlashLoan( address sender, IERC20 token, uint256 amount, uint256 fee, bytes calldata data ) external; } interface IBatchFlashBorrower { /// @notice The callback for batched flashloans. Every amount + fee needs to repayed to msg.sender before this call returns. /// @param sender The address of the invoker of this flashloan. /// @param tokens Array of addresses for ERC-20 tokens that is loaned. /// @param amounts A one-to-one map to `tokens` that is loaned. /// @param fees A one-to-one map to `tokens` that needs to be paid on top for each loan. Needs to be the same token. /// @param data Additional data that was passed to the flashloan function. function onBatchFlashLoan( address sender, IERC20[] calldata tokens, uint256[] calldata amounts, uint256[] calldata fees, bytes calldata data ) external; } interface IBentoBoxV1 { function balanceOf(IERC20, address) external view returns (uint256); function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results); function batchFlashLoan( IBatchFlashBorrower borrower, address[] calldata receivers, IERC20[] calldata tokens, uint256[] calldata amounts, bytes calldata data ) external; function claimOwnership() external; function flashLoan( IFlashBorrower borrower, address receiver, IERC20 token, uint256 amount, bytes calldata data ) external; function deploy( address masterContract, bytes calldata data, bool useCreate2 ) external payable returns (address); function deposit( IERC20 token_, address from, address to, uint256 amount, uint256 share ) external payable returns (uint256 amountOut, uint256 shareOut); function harvest( IERC20 token, bool balance, uint256 maxChangeAmount ) external; function masterContractApproved(address, address) external view returns (bool); function masterContractOf(address) external view returns (address); function nonces(address) external view returns (uint256); function owner() external view returns (address); function pendingOwner() external view returns (address); function pendingStrategy(IERC20) external view returns (IStrategy); function permitToken( IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function registerProtocol() external; function setMasterContractApproval( address user, address masterContract, bool approved, uint8 v, bytes32 r, bytes32 s ) external; function setStrategy(IERC20 token, IStrategy newStrategy) external; function setStrategyTargetPercentage(IERC20 token, uint64 targetPercentage_) external; function strategy(IERC20) external view returns (IStrategy); function strategyData(IERC20) external view returns ( uint64 strategyStartDate, uint64 targetPercentage, uint128 balance ); function toAmount( IERC20 token, uint256 share, bool roundUp ) external view returns (uint256 amount); function toShare( IERC20 token, uint256 amount, bool roundUp ) external view returns (uint256 share); function totals(IERC20) external view returns (Rebase memory totals_); function transfer( IERC20 token, address from, address to, uint256 share ) external; function transferMultiple( IERC20 token, address from, address[] calldata tos, uint256[] calldata shares ) external; function transferOwnership( address newOwner, bool direct, bool renounce ) external; function whitelistMasterContract(address masterContract, bool approved) external; function whitelistedMasterContracts(address) external view returns (bool); function withdraw( IERC20 token_, address from, address to, uint256 amount, uint256 share ) external returns (uint256 amountOut, uint256 shareOut); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IOracle { /// @notice Get the latest exchange rate. /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle. /// For example: /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256)); /// @return success if no valid (recent) rate is available, return false else true. /// @return rate The rate of the requested asset / pair / pool. function get(bytes calldata data) external returns (bool success, uint256 rate); /// @notice Check the last exchange rate without any state changes. /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle. /// For example: /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256)); /// @return success if no valid (recent) rate is available, return false else true. /// @return rate The rate of the requested asset / pair / pool. function peek(bytes calldata data) external view returns (bool success, uint256 rate); /// @notice Check the current spot exchange rate without any state changes. For oracles like TWAP this will be different from peek(). /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle. /// For example: /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256)); /// @return rate The rate of the requested asset / pair / pool. function peekSpot(bytes calldata data) external view returns (uint256 rate); /// @notice Returns a human readable (short) name about this oracle. /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle. /// For example: /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256)); /// @return (string) A human readable symbol name about this oracle. function symbol(bytes calldata data) external view returns (string memory); /// @notice Returns a human readable name about this oracle. /// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle. /// For example: /// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256)); /// @return (string) A human readable name about this oracle. function name(bytes calldata data) external view returns (string memory); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.0; interface IStrategy { /// @notice Send the assets to the Strategy and call skim to invest them. /// @param amount The amount of tokens to invest. function skim(uint256 amount) external; /// @notice Harvest any profits made converted to the asset and pass them to the caller. /// @param balance The amount of tokens the caller thinks it has invested. /// @param sender The address of the initiator of this transaction. Can be used for reimbursements, etc. /// @return amountAdded The delta (+profit or -loss) that occured in contrast to `balance`. function harvest(uint256 balance, address sender) external returns (int256 amountAdded); /// @notice Withdraw assets. The returned amount can differ from the requested amount due to rounding. /// @dev The `actualAmount` should be very close to the amount. /// The difference should NOT be used to report a loss. That's what harvest is for. /// @param amount The requested amount the caller wants to withdraw. /// @return actualAmount The real amount that is withdrawn. function withdraw(uint256 amount) external returns (uint256 actualAmount); /// @notice Withdraw all assets in the safest way possible. This shouldn't fail. /// @param balance The amount of tokens the caller thinks it has invested. /// @return amountAdded The delta (+profit or -loss) that occured in contrast to `balance`. function exit(uint256 balance) external returns (int256 amountAdded); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ISwapperV2 { /// @notice Withdraws 'amountFrom' of token 'from' from the BentoBox account for this swapper. /// Swaps it for at least 'amountToMin' of token 'to'. /// Transfers the swapped tokens of 'to' into the BentoBox using a plain IERC20 transfer. /// Returns the amount of tokens 'to' transferred to BentoBox. /// (The BentoBox skim function will be used by the caller to get the swapped funds). function swap( address fromToken, address toToken, address recipient, uint256 shareToMin, uint256 shareFrom, bytes calldata data ) external returns (uint256 extraShare, uint256 shareReturned); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; library BoringMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } function to128(uint256 a) internal pure returns (uint128 c) { c = uint128(a); } function to64(uint256 a) internal pure returns (uint64 c) { c = uint64(a); } function to32(uint256 a) internal pure returns (uint32 c) { c = uint32(a); } } library BoringMath128 { function add(uint128 a, uint128 b) internal pure returns (uint128 c) { c = a + b; } function sub(uint128 a, uint128 b) internal pure returns (uint128 c) { c = a - b; } }
{ "remappings": [ "/=src/", "BoringSolidity/=lib/BoringSolidity/contracts/", "OpenZeppelin/=lib/openzeppelin-contracts/contracts/", "cauldrons/=src/cauldrons/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "interfaces/=src/interfaces/", "libraries/=src/libraries/", "mocks/=src/mocks/", "oracles/=src/oracles/", "periphery/=src/periphery/", "solmate/=lib/solmate/src/", "strategies/=src/strategies/", "swappers/=src/swappers/", "tokens/=src/tokens/", "utils/=utils/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
[{"inputs":[{"internalType":"contract IBentoBoxV1","name":"bentoBox_","type":"address"},{"internalType":"contract IERC20","name":"magicInternetMoney_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"accruedAmount","type":"uint128"}],"name":"LogAccrue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"}],"name":"LogAddCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"}],"name":"LogBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"LogChangeBlacklistedCallee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newLimit","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"perAddressPart","type":"uint128"}],"name":"LogChangeBorrowLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"LogExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeTo","type":"address"}],"name":"LogFeeTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"oldInterestRate","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newInterestRate","type":"uint64"}],"name":"LogInterestChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralShare","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowPart","type":"uint256"}],"name":"LogLiquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"}],"name":"LogRemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"}],"name":"LogRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"previousElastic","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"newElastic","type":"uint128"}],"name":"LogRepayForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"feesEarnedFraction","type":"uint256"}],"name":"LogWithdrawFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"BORROW_OPENING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERIZATION_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATION_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrueInfo","outputs":[{"internalType":"uint64","name":"lastAccrued","type":"uint64"},{"internalType":"uint128","name":"feesEarned","type":"uint128"},{"internalType":"uint64","name":"INTEREST_PER_SECOND","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bentoBox","outputs":[{"internalType":"contract IBentoBoxV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blacklistedCallees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"part","type":"uint256"},{"internalType":"uint256","name":"share","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowLimit","outputs":[{"internalType":"uint128","name":"total","type":"uint128"},{"internalType":"uint128","name":"borrowPartPerAddress","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"newBorrowLimit","type":"uint128"},{"internalType":"uint128","name":"perAddressPart","type":"uint128"}],"name":"changeBorrowLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newInterestRate","type":"uint64"}],"name":"changeInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"actions","type":"uint8[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"cook","outputs":[{"internalType":"uint256","name":"value1","type":"uint256"},{"internalType":"uint256","name":"value2","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"exchangeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256[]","name":"maxBorrowParts","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"contract ISwapperV2","name":"swapper","type":"address"},{"internalType":"bytes","name":"swapperData","type":"bytes"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"magicInternetMoney","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"masterContract","outputs":[{"internalType":"contract CauldronV4","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"part","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bool","name":"skim","type":"bool"}],"name":"repayForAll","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"callee","type":"address"},{"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"setBlacklistedCallee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeTo","type":"address"}],"name":"setFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalBorrow","outputs":[{"internalType":"uint128","name":"elastic","type":"uint128"},{"internalType":"uint128","name":"base","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"direct","type":"bool"},{"internalType":"bool","name":"renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"bool","name":"updated","type":"bool"},{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userBorrowPart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.