Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 4,139 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Extend Loan | 21332991 | 7 hrs ago | IN | 0 ETH | 0.00361217 | ||||
Extend Loan | 21325158 | 33 hrs ago | IN | 0 ETH | 0.00304933 | ||||
Extend Loan | 21325150 | 33 hrs ago | IN | 0 ETH | 0.00341466 | ||||
Extend Loan | 21325146 | 33 hrs ago | IN | 0 ETH | 0.00398834 | ||||
Extend Loan | 21321083 | 46 hrs ago | IN | 0 ETH | 0.00246118 | ||||
Extend Loan | 21321079 | 47 hrs ago | IN | 0 ETH | 0.00252097 | ||||
Extend Loan | 21314593 | 2 days ago | IN | 0 ETH | 0.00162808 | ||||
Extend Loan | 21314588 | 2 days ago | IN | 0 ETH | 0.00173445 | ||||
Extend Loan | 21313276 | 3 days ago | IN | 0 ETH | 0.00155055 | ||||
Extend Loan | 21313274 | 3 days ago | IN | 0 ETH | 0.00153957 | ||||
Extend Loan | 21312814 | 3 days ago | IN | 0 ETH | 0.0017565 | ||||
Extend Loan | 21311902 | 3 days ago | IN | 0 ETH | 0.0020155 | ||||
Extend Loan | 21311897 | 3 days ago | IN | 0 ETH | 0.0021231 | ||||
Extend Loan | 21311894 | 3 days ago | IN | 0 ETH | 0.00210445 | ||||
Extend Loan | 21311889 | 3 days ago | IN | 0 ETH | 0.00193831 | ||||
Extend Loan | 21311884 | 3 days ago | IN | 0 ETH | 0.00211964 | ||||
Extend Loan | 21310267 | 3 days ago | IN | 0 ETH | 0.00273864 | ||||
Extend Loan | 21310263 | 3 days ago | IN | 0 ETH | 0.00278973 | ||||
Extend Loan | 21310137 | 3 days ago | IN | 0 ETH | 0.00306469 | ||||
Extend Loan | 21304851 | 4 days ago | IN | 0 ETH | 0.00092119 | ||||
Extend Loan | 21304818 | 4 days ago | IN | 0 ETH | 0.00117704 | ||||
Extend Loan | 21304811 | 4 days ago | IN | 0 ETH | 0.00111003 | ||||
Extend Loan | 21304761 | 4 days ago | IN | 0 ETH | 0.00157575 | ||||
Lend To Cooler | 21302480 | 4 days ago | IN | 0 ETH | 0.00943871 | ||||
Lend To Cooler | 21299077 | 5 days ago | IN | 0 ETH | 0.00471003 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
Clearinghouse
Compiler Version
v0.8.15+commit.e14f2714
Optimization Enabled:
Yes with 10 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; import {IStaking} from "interfaces/IStaking.sol"; import {CoolerFactory, Cooler} from "cooler/CoolerFactory.sol"; import {CoolerCallback} from "cooler/CoolerCallback.sol"; import {ROLESv1, RolesConsumer} from "modules/ROLES/OlympusRoles.sol"; import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol"; import {MINTRv1} from "modules/MINTR/MINTR.v1.sol"; import "src/Kernel.sol"; /// @title Olympus Clearinghouse. /// @notice Olympus Clearinghouse (Policy) Contract. /// @dev The Olympus Clearinghouse is a lending facility built on top of Cooler Loans. The Clearinghouse /// ensures that OHM holders can take loans against their gOHM holdings according to the parameters /// approved by the community in OIP-144 and its subsequent RFCs. The Clearinghouse parameters are /// immutable, because of that, if backing was to increase substantially, a new governance process /// to fork this implementation with upgraded parameters should take place. /// Although the Cooler contracts allow lenders to transfer ownership of their repayment rights, the /// Clearinghouse doesn't implement any functions to use that feature. contract Clearinghouse is Policy, RolesConsumer, CoolerCallback { // --- ERRORS ---------------------------------------------------- error BadEscrow(); error DurationMaximum(); error OnlyBurnable(); error TooEarlyToFund(); error LengthDiscrepancy(); error OnlyBorrower(); error NotLender(); // --- EVENTS ---------------------------------------------------- /// @notice Logs whenever the Clearinghouse is deactivated. event Deactivate(); /// @notice Logs whenever the Clearinghouse is reactivated. event Reactivate(); /// @notice Logs whenever the treasury is defunded. event Defund(address token, uint256 amount); /// @notice Logs the balance change (in DAI terms) whenever a rebalance occurs. event Rebalance(bool defund, uint256 daiAmount); // --- RELEVANT CONTRACTS ---------------------------------------- ERC20 public immutable dai; // Debt token ERC4626 public immutable sdai; // Idle DAI will be wrapped into sDAI ERC20 public immutable gohm; // Collateral token ERC20 public immutable ohm; // Unwrapped gOHM IStaking public immutable staking; // Necessary to unstake (and burn) OHM from defaults // --- MODULES --------------------------------------------------- TRSRYv1 public TRSRY; // Olympus V3 Treasury Module MINTRv1 public MINTR; // Olympus V3 Minter Module // --- PARAMETER BOUNDS ------------------------------------------ uint256 public constant INTEREST_RATE = 5e15; // 0.5% anually uint256 public constant LOAN_TO_COLLATERAL = 289292e16; // 2,892.92 DAI/gOHM uint256 public constant DURATION = 121 days; // Four months uint256 public constant FUND_CADENCE = 7 days; // One week uint256 public constant FUND_AMOUNT = 18_000_000e18; // 18 million uint256 public constant MAX_REWARD = 1e17; // 0.1 gOHM // --- STATE VARIABLES ------------------------------------------- /// @notice determines whether the contract can be funded or not. bool public active; /// @notice timestamp at which the next rebalance can occur. uint256 public fundTime; /// @notice Outstanding receivables. /// Incremented when a loan is taken or rolled. /// Decremented when a loan is repaid or collateral is burned. uint256 public interestReceivables; uint256 public principalReceivables; // --- INITIALIZATION -------------------------------------------- constructor( address ohm_, address gohm_, address staking_, address sdai_, address coolerFactory_, address kernel_ ) Policy(Kernel(kernel_)) CoolerCallback(coolerFactory_) { // Store the relevant contracts. ohm = ERC20(ohm_); gohm = ERC20(gohm_); staking = IStaking(staking_); sdai = ERC4626(sdai_); dai = ERC20(sdai.asset()); // Initialize the contract status and its funding schedule. active = true; fundTime = block.timestamp; } /// @notice Default framework setup. Configure dependencies for olympus-v3 modules. /// @dev This function will be called when the `executor` installs the Clearinghouse /// policy in the olympus-v3 `Kernel`. function configureDependencies() external override returns (Keycode[] memory dependencies) { dependencies = new Keycode[](3); dependencies[0] = toKeycode("TRSRY"); dependencies[1] = toKeycode("MINTR"); dependencies[2] = toKeycode("ROLES"); TRSRY = TRSRYv1(getModuleAddress(toKeycode("TRSRY"))); MINTR = MINTRv1(getModuleAddress(toKeycode("MINTR"))); ROLES = ROLESv1(getModuleAddress(toKeycode("ROLES"))); // Approve MINTR for burning OHM (called here so that it is re-approved on updates) ohm.approve(address(MINTR), type(uint256).max); } /// @notice Default framework setup. Request permissions for interacting with olympus-v3 modules. /// @dev This function will be called when the `executor` installs the Clearinghouse /// policy in the olympus-v3 `Kernel`. function requestPermissions() external view override returns (Permissions[] memory requests) { Keycode TRSRY_KEYCODE = toKeycode("TRSRY"); requests = new Permissions[](4); requests[0] = Permissions(TRSRY_KEYCODE, TRSRY.setDebt.selector); requests[1] = Permissions(TRSRY_KEYCODE, TRSRY.increaseWithdrawApproval.selector); requests[2] = Permissions(TRSRY_KEYCODE, TRSRY.withdrawReserves.selector); requests[3] = Permissions(toKeycode("MINTR"), MINTR.burnOhm.selector); } // --- OPERATION ------------------------------------------------- /// @notice Lend to a cooler. /// @dev To simplify the UX and easily ensure that all holders get the same terms, /// this function requests a new loan and clears it in the same transaction. /// @param cooler_ to lend to. /// @param amount_ of DAI to lend. /// @return the id of the granted loan. function lendToCooler(Cooler cooler_, uint256 amount_) external returns (uint256) { // Attempt a Clearinghouse <> Treasury rebalance. rebalance(); // Validate that cooler was deployed by the trusted factory. if (!factory.created(address(cooler_))) revert OnlyFromFactory(); // Validate cooler collateral and debt tokens. if (cooler_.collateral() != gohm || cooler_.debt() != dai) revert BadEscrow(); // Transfer in collateral owed uint256 collateral = cooler_.collateralFor(amount_, LOAN_TO_COLLATERAL); gohm.transferFrom(msg.sender, address(this), collateral); // Increment interest to be expected (, uint256 interest) = getLoanForCollateral(collateral); interestReceivables += interest; principalReceivables += amount_; // Create a new loan request. gohm.approve(address(cooler_), collateral); uint256 reqID = cooler_.requestLoan(amount_, INTEREST_RATE, LOAN_TO_COLLATERAL, DURATION); // Clear the created loan request by providing enough DAI. sdai.withdraw(amount_, address(this), address(this)); dai.approve(address(cooler_), amount_); uint256 loanID = cooler_.clearRequest(reqID, address(this), true); return loanID; } /// @notice Extend the loan expiry by repaying the extension interest in advance. /// The extension cost is paid by the caller. If a third-party executes the /// extension, the loan period is extended, but the borrower debt does not increase. /// @param cooler_ holding the loan to be extended. /// @param loanID_ index of loan in loans[]. /// @param times_ Amount of times that the fixed-term loan duration is extended. function extendLoan(Cooler cooler_, uint256 loanID_, uint8 times_) external { Cooler.Loan memory loan = cooler_.getLoan(loanID_); // Validate that cooler was deployed by the trusted factory. if (!factory.created(address(cooler_))) revert OnlyFromFactory(); // Calculate extension interest based on the remaining principal. uint256 interestBase = interestForLoan(loan.principal, loan.request.duration); // Transfer in extension interest from the caller. dai.transferFrom(msg.sender, address(this), interestBase * times_); if (active) { _sweepIntoDSR(interestBase * times_); } else { _defund(dai, interestBase * times_); } // Signal to cooler that loan should be extended. cooler_.extendLoanTerms(loanID_, times_); } /// @notice Batch several default claims to save gas. /// The elements on both arrays must be paired based on their index. /// @dev Implements an auction style reward system that linearly increases up to a max reward. /// @param coolers_ Array of contracts where the default must be claimed. /// @param loans_ Array of defaulted loan ids. function claimDefaulted(address[] calldata coolers_, uint256[] calldata loans_) external { uint256 loans = loans_.length; if (loans != coolers_.length) revert LengthDiscrepancy(); uint256 totalPrincipal; uint256 totalInterest; uint256 totalCollateral; uint256 keeperRewards; for (uint256 i = 0; i < loans; ) { // Validate that cooler was deployed by the trusted factory. if (!factory.created(coolers_[i])) revert OnlyFromFactory(); // Validate that loan was written by clearinghouse. if (Cooler(coolers_[i]).getLoan(loans_[i]).lender != address(this)) revert NotLender(); // Claim defaults and update cached metrics. (uint256 principal, uint256 interest, uint256 collateral, uint256 elapsed) = Cooler( coolers_[i] ).claimDefaulted(loans_[i]); unchecked { // Cannot overflow due to max supply limits for both tokens totalPrincipal += principal; totalInterest += interest; totalCollateral += collateral; // There will not exist more than 2**256 loans ++i; } // Cap rewards to 5% of the collateral to avoid OHM holder's dillution. uint256 maxAuctionReward = (collateral * 5e16) / 1e18; // Cap rewards to avoid exorbitant amounts. uint256 maxReward = (maxAuctionReward < MAX_REWARD) ? maxAuctionReward : MAX_REWARD; // Calculate rewards based on the elapsed time since default. keeperRewards = (elapsed < 7 days) ? keeperRewards + (maxReward * elapsed) / 7 days : keeperRewards + maxReward; } // Decrement loan receivables. interestReceivables = (interestReceivables > totalInterest) ? interestReceivables - totalInterest : 0; principalReceivables = (principalReceivables > totalPrincipal) ? principalReceivables - totalPrincipal : 0; // Update outstanding debt owed to the Treasury upon default. uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this)); // debt owed to TRSRY = user debt - user interest TRSRY.setDebt({ debtor_: address(this), token_: dai, amount_: (outstandingDebt > totalPrincipal) ? outstandingDebt - totalPrincipal : 0 }); // Reward keeper. gohm.transfer(msg.sender, keeperRewards); // Burn the outstanding collateral of defaulted loans. burn(); } // --- CALLBACKS ----------------------------------------------------- /// @notice Overridden callback to decrement loan receivables. /// @param *unused loadID_ of the load. /// @param principalPaid_ in DAI. /// @param interestPaid_ in DAI. function _onRepay(uint256, uint256 principalPaid_, uint256 interestPaid_) internal override { if (active) { _sweepIntoDSR(principalPaid_ + interestPaid_); } else { _defund(dai, principalPaid_ + interestPaid_); } // Decrement loan receivables. interestReceivables = (interestReceivables > interestPaid_) ? interestReceivables - interestPaid_ : 0; principalReceivables = (principalReceivables > principalPaid_) ? principalReceivables - principalPaid_ : 0; } /// @notice Unused callback since defaults are handled by the clearinghouse. /// @dev Overriden and left empty to save gas. function _onDefault(uint256, uint256, uint256, uint256) internal override {} // --- FUNDING --------------------------------------------------- /// @notice Fund loan liquidity from treasury. /// @dev Exposure is always capped at FUND_AMOUNT and rebalanced at up to FUND_CADANCE. /// If several rebalances are available (because some were missed), calling this /// function several times won't impact the funds controlled by the contract. /// If the emergency shutdown is triggered, a rebalance will send funds back to /// the treasury. /// @return False if too early to rebalance. Otherwise, true. function rebalance() public returns (bool) { // If the contract is deactivated, defund. uint256 maxFundAmount = active ? FUND_AMOUNT : 0; // Update funding schedule if necessary. if (fundTime > block.timestamp) return false; fundTime += FUND_CADENCE; // Sweep DAI into DSR if necessary. uint256 idle = dai.balanceOf(address(this)); if (idle != 0) _sweepIntoDSR(idle); uint256 daiBalance = sdai.maxWithdraw(address(this)); uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this)); // Rebalance funds on hand with treasury's reserves. if (daiBalance < maxFundAmount) { // Since users loans are denominated in DAI, the clearinghouse // debt is set in DAI terms. It must be adjusted when funding. uint256 fundAmount = maxFundAmount - daiBalance; TRSRY.setDebt({ debtor_: address(this), token_: dai, amount_: outstandingDebt + fundAmount }); // Since TRSRY holds sDAI, a conversion must be done before // funding the clearinghouse. uint256 sdaiAmount = sdai.previewWithdraw(fundAmount); TRSRY.increaseWithdrawApproval(address(this), sdai, sdaiAmount); TRSRY.withdrawReserves(address(this), sdai, sdaiAmount); // Log the event. emit Rebalance(false, fundAmount); } else if (daiBalance > maxFundAmount) { // Since users loans are denominated in DAI, the clearinghouse // debt is set in DAI terms. It must be adjusted when defunding. uint256 defundAmount = daiBalance - maxFundAmount; TRSRY.setDebt({ debtor_: address(this), token_: dai, amount_: (outstandingDebt > defundAmount) ? outstandingDebt - defundAmount : 0 }); // Since TRSRY holds sDAI, a conversion must be done before // sending sDAI back. uint256 sdaiAmount = sdai.previewWithdraw(defundAmount); sdai.transfer(address(TRSRY), sdaiAmount); // Log the event. emit Rebalance(true, defundAmount); } return true; } /// @notice Sweep excess DAI into vault. function sweepIntoDSR() public { uint256 daiBalance = dai.balanceOf(address(this)); _sweepIntoDSR(daiBalance); } /// @notice Sweep excess DAI into vault. function _sweepIntoDSR(uint256 amount_) internal { dai.approve(address(sdai), amount_); sdai.deposit(amount_, address(this)); } /// @notice Return funds to treasury. /// @param token_ to transfer. /// @param amount_ to transfer. function defund(ERC20 token_, uint256 amount_) external onlyRole("cooler_overseer") { if (token_ == gohm) revert OnlyBurnable(); _defund(token_, amount_); } /// @notice Internal function to return funds to treasury. /// @param token_ to transfer. /// @param amount_ to transfer. function _defund(ERC20 token_, uint256 amount_) internal { if (token_ == sdai || token_ == dai) { // Since users loans are denominated in DAI, the clearinghouse // debt is set in DAI terms. It must be adjusted when defunding. uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this)); uint256 daiAmount = (token_ == sdai) ? sdai.previewRedeem(amount_) : amount_; TRSRY.setDebt({ debtor_: address(this), token_: dai, amount_: (outstandingDebt > daiAmount) ? outstandingDebt - daiAmount : 0 }); } // Defund and log the event token_.transfer(address(TRSRY), amount_); emit Defund(address(token_), amount_); } /// @notice Public function to burn gOHM. /// @dev Can be used to burn any gOHM defaulted using the Cooler instead of the Clearinghouse. function burn() public { uint256 gohmBalance = gohm.balanceOf(address(this)); // Unstake and burn gOHM holdings. gohm.approve(address(staking), gohmBalance); MINTR.burnOhm(address(this), staking.unstake(address(this), gohmBalance, false, false)); } /// @notice Deactivate the contract and return funds to treasury. function emergencyShutdown() external onlyRole("emergency_shutdown") { active = false; // If necessary, defund sDAI. uint256 sdaiBalance = sdai.balanceOf(address(this)); if (sdaiBalance != 0) _defund(sdai, sdaiBalance); // If necessary, defund DAI. uint256 daiBalance = dai.balanceOf(address(this)); if (daiBalance != 0) _defund(dai, daiBalance); emit Deactivate(); } /// @notice Reactivate the contract. function reactivate() external onlyRole("cooler_overseer") { active = true; fundTime = block.timestamp; emit Reactivate(); } // --- AUX FUNCTIONS --------------------------------------------- /// @notice view function computing collateral for a loan amount. function getCollateralForLoan(uint256 principal_) external pure returns (uint256) { return (principal_ * 1e18) / LOAN_TO_COLLATERAL; } /// @notice view function computing loan for a collateral amount. /// @param collateral_ amount of gOHM. /// @return debt (amount to be lent + interest) for a given collateral amount. function getLoanForCollateral(uint256 collateral_) public pure returns (uint256, uint256) { uint256 principal = (collateral_ * LOAN_TO_COLLATERAL) / 1e18; uint256 interest = interestForLoan(principal, DURATION); return (principal, interest); } /// @notice view function to compute the interest for given principal amount. /// @param principal_ amount of DAI being lent. /// @param duration_ elapsed time in seconds. function interestForLoan(uint256 principal_, uint256 duration_) public pure returns (uint256) { uint256 interestPercent = (INTEREST_RATE * duration_) / 365 days; return (principal_ * interestPercent) / 1e18; } /// @notice Get total receivable DAI for the treasury. /// Includes both principal and interest. function getTotalReceivables() external view returns (uint256) { return principalReceivables + interestReceivables; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IStaking { function unstake( address to, uint256 amount, bool trigger, bool rebasing ) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol"; import {Cooler} from "./Cooler.sol"; /// @title Cooler Loans Factory. /// @notice The Cooler Factory creates new Cooler escrow contracts. /// @dev This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args) /// to save gas on deployment. contract CoolerFactory { using ClonesWithImmutableArgs for address; // --- ERRORS ---------------------------------------------------- error NotFromFactory(); error DecimalsNot18(); // --- EVENTS ---------------------------------------------------- /// @notice A global event when a new loan request is created. event RequestLoan(address indexed cooler, address collateral, address debt, uint256 reqID); /// @notice A global event when a loan request is rescinded. event RescindRequest(address indexed cooler, uint256 reqID); /// @notice A global event when a loan request is fulfilled. event ClearRequest(address indexed cooler, uint256 reqID, uint256 loanID); /// @notice A global event when a loan is repaid. event RepayLoan(address indexed cooler, uint256 loanID, uint256 amount); /// @notice A global event when a loan is extended. event ExtendLoan(address indexed cooler, uint256 loanID, uint8 times); /// @notice A global event when the collateral of defaulted loan is claimed. event DefaultLoan(address indexed cooler, uint256 loanID, uint256 amount); // -- STATE VARIABLES -------------------------------------------- /// @notice Cooler reference implementation (deployed on creation to clone from). Cooler public immutable coolerImplementation; /// @notice Mapping to validate deployed coolers. mapping(address => bool) public created; /// @notice Mapping to prevent duplicate coolers. mapping(address => mapping(ERC20 => mapping(ERC20 => address))) private coolerFor; /// @notice Mapping to query Coolers for Collateral-Debt pair. mapping(ERC20 => mapping(ERC20 => address[])) public coolersFor; // --- INITIALIZATION -------------------------------------------- constructor() { coolerImplementation = new Cooler(); } // --- DEPLOY NEW COOLERS ---------------------------------------- /// @notice creates a new Escrow contract for collateral and debt tokens. /// @param collateral_ the token given as collateral. /// @param debt_ the token to be lent. Interest is denominated in debt tokens. /// @return cooler address of the contract. function generateCooler(ERC20 collateral_, ERC20 debt_) external returns (address cooler) { // Return address if cooler exists. cooler = coolerFor[msg.sender][collateral_][debt_]; // Otherwise generate new cooler. if (cooler == address(0)) { if (collateral_.decimals() != 18 || debt_.decimals() != 18) revert DecimalsNot18(); // Clone the cooler implementation. bytes memory coolerData = abi.encodePacked( msg.sender, // owner address(collateral_), // collateral address(debt_), // debt address(this) // factory ); cooler = address(coolerImplementation).clone(coolerData); // Update storage accordingly. coolerFor[msg.sender][collateral_][debt_] = cooler; coolersFor[collateral_][debt_].push(cooler); created[cooler] = true; } } // --- EMIT EVENTS ----------------------------------------------- /// @notice Ensure that the called is a Cooler. modifier onlyFromFactory { if (!created[msg.sender]) revert NotFromFactory(); _; } /// @notice Emit a global event when a new loan request is created. function logRequestLoan(uint256 reqID_) external onlyFromFactory { emit RequestLoan(msg.sender, address(Cooler(msg.sender).collateral()), address(Cooler(msg.sender).debt()), reqID_); } /// @notice Emit a global event when a loan request is rescinded. function logRescindRequest(uint256 reqID_) external onlyFromFactory { emit RescindRequest(msg.sender, reqID_); } /// @notice Emit a global event when a loan request is fulfilled. function logClearRequest(uint256 reqID_, uint256 loanID_) external onlyFromFactory { emit ClearRequest(msg.sender, reqID_, loanID_); } /// @notice Emit a global event when a loan is repaid. function logRepayLoan(uint256 loanID_, uint256 repayment_) external onlyFromFactory { emit RepayLoan(msg.sender, loanID_, repayment_); } /// @notice Emit a global event when a loan is extended. function logExtendLoan(uint256 loanID_, uint8 times_) external onlyFromFactory { emit ExtendLoan(msg.sender, loanID_, times_); } /// @notice Emit a global event when the collateral of defaulted loan is claimed. function logDefaultLoan(uint256 loanID_, uint256 collateral_) external onlyFromFactory { emit DefaultLoan(msg.sender, loanID_, collateral_); } // --- AUX FUNCTIONS --------------------------------------------- /// @notice Getter function to get an existing cooler for a given user <> collateral <> debt combination. function getCoolerFor(address user_, address collateral_, address debt_) public view returns (address) { return coolerFor[user_][ERC20(collateral_)][ERC20(debt_)]; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import {CoolerFactory} from "./CoolerFactory.sol"; /// @notice Allows for debt issuers to execute logic when a loan is repaid, rolled, or defaulted. /// @dev The three callback functions must be implemented if `isCoolerCallback()` is set to true. abstract contract CoolerCallback { // --- ERRORS ---------------------------------------------------- error OnlyFromFactory(); // --- INITIALIZATION -------------------------------------------- CoolerFactory public immutable factory; constructor(address coolerFactory_) { factory = CoolerFactory(coolerFactory_); } // --- EXTERNAL FUNCTIONS ------------------------------------------------ /// @notice Informs to Cooler that this contract can handle its callbacks. function isCoolerCallback() external pure returns (bool) { return true; } /// @notice Callback function that handles repayments. function onRepay(uint256 loanID_, uint256 principlePaid_, uint256 interestPaid_) external { if (!factory.created(msg.sender)) revert OnlyFromFactory(); _onRepay(loanID_, principlePaid_, interestPaid_); } /// @notice Callback function that handles defaults. function onDefault( uint256 loanID_, uint256 principle, uint256 interest, uint256 collateral ) external { if (!factory.created(msg.sender)) revert OnlyFromFactory(); _onDefault(loanID_, principle, interest, collateral); } // --- INTERNAL FUNCTIONS ------------------------------------------------ /// @notice Callback function that handles repayments. Override for custom logic. function _onRepay( uint256 loanID_, uint256 principlePaid_, uint256 interestPaid_ ) internal virtual; /// @notice Callback function that handles defaults. function _onDefault( uint256 loanID_, uint256 principle_, uint256 interestDue_, uint256 collateral ) internal virtual; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol"; import "src/Kernel.sol"; /// @notice Abstract contract to have the `onlyRole` modifier /// @dev Inheriting this automatically makes ROLES module a dependency abstract contract RolesConsumer { ROLESv1 public ROLES; modifier onlyRole(bytes32 role_) { ROLES.requireRole(role_, msg.sender); _; } } /// @notice Module that holds multisig roles needed by various policies. contract OlympusRoles is ROLESv1 { //============================================================================================// // MODULE SETUP // //============================================================================================// constructor(Kernel kernel_) Module(kernel_) {} /// @inheritdoc Module function KEYCODE() public pure override returns (Keycode) { return toKeycode("ROLES"); } /// @inheritdoc Module function VERSION() external pure override returns (uint8 major, uint8 minor) { major = 1; minor = 0; } //============================================================================================// // CORE FUNCTIONS // //============================================================================================// /// @inheritdoc ROLESv1 function saveRole(bytes32 role_, address addr_) external override permissioned { if (hasRole[addr_][role_]) revert ROLES_AddressAlreadyHasRole(addr_, role_); ensureValidRole(role_); // Grant role to the address hasRole[addr_][role_] = true; emit RoleGranted(role_, addr_); } /// @inheritdoc ROLESv1 function removeRole(bytes32 role_, address addr_) external override permissioned { if (!hasRole[addr_][role_]) revert ROLES_AddressDoesNotHaveRole(addr_, role_); hasRole[addr_][role_] = false; emit RoleRevoked(role_, addr_); } //============================================================================================// // VIEW FUNCTIONS // //============================================================================================// /// @inheritdoc ROLESv1 function requireRole(bytes32 role_, address caller_) external view override { if (!hasRole[caller_][role_]) revert ROLES_RequireRole(role_); } /// @inheritdoc ROLESv1 function ensureValidRole(bytes32 role_) public pure override { for (uint256 i = 0; i < 32; ) { bytes1 char = role_[i]; if ((char < 0x61 || char > 0x7A) && char != 0x5f && char != 0x00) { revert ROLES_InvalidRole(role_); // a-z only } unchecked { i++; } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; import {ERC20} from "solmate/tokens/ERC20.sol"; import "src/Kernel.sol"; /// @notice Treasury holds all other assets under the control of the protocol. abstract contract TRSRYv1 is Module { // ========= EVENTS ========= // event IncreaseWithdrawApproval( address indexed withdrawer_, ERC20 indexed token_, uint256 newAmount_ ); event DecreaseWithdrawApproval( address indexed withdrawer_, ERC20 indexed token_, uint256 newAmount_ ); event Withdrawal( address indexed policy_, address indexed withdrawer_, ERC20 indexed token_, uint256 amount_ ); event IncreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_); event DecreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_); event DebtIncurred(ERC20 indexed token_, address indexed policy_, uint256 amount_); event DebtRepaid(ERC20 indexed token_, address indexed policy_, uint256 amount_); event DebtSet(ERC20 indexed token_, address indexed policy_, uint256 amount_); // ========= ERRORS ========= // error TRSRY_NoDebtOutstanding(); error TRSRY_NotActive(); // ========= STATE ========= // /// @notice Status of the treasury. If false, no withdrawals or debt can be incurred. bool public active; /// @notice Mapping of who is approved for withdrawal. /// @dev withdrawer -> token -> amount. Infinite approval is max(uint256). mapping(address => mapping(ERC20 => uint256)) public withdrawApproval; /// @notice Mapping of who is approved to incur debt. /// @dev debtor -> token -> amount. Infinite approval is max(uint256). mapping(address => mapping(ERC20 => uint256)) public debtApproval; /// @notice Total debt for token across all withdrawals. mapping(ERC20 => uint256) public totalDebt; /// @notice Debt for particular token and debtor address mapping(ERC20 => mapping(address => uint256)) public reserveDebt; // ========= FUNCTIONS ========= // modifier onlyWhileActive() { if (!active) revert TRSRY_NotActive(); _; } /// @notice Increase approval for specific withdrawer addresses function increaseWithdrawApproval( address withdrawer_, ERC20 token_, uint256 amount_ ) external virtual; /// @notice Decrease approval for specific withdrawer addresses function decreaseWithdrawApproval( address withdrawer_, ERC20 token_, uint256 amount_ ) external virtual; /// @notice Allow withdrawal of reserve funds from pre-approved addresses. function withdrawReserves(address to_, ERC20 token_, uint256 amount_) external virtual; /// @notice Increase approval for someone to accrue debt in order to withdraw reserves. /// @dev Debt will generally be taken by contracts to allocate treasury funds in yield sources. function increaseDebtorApproval( address debtor_, ERC20 token_, uint256 amount_ ) external virtual; /// @notice Decrease approval for someone to withdraw reserves as debt. function decreaseDebtorApproval( address debtor_, ERC20 token_, uint256 amount_ ) external virtual; /// @notice Pre-approved policies can get a loan to perform operations with treasury assets. function incurDebt(ERC20 token_, uint256 amount_) external virtual; /// @notice Repay a debtor debt. /// @dev Only confirmed to safely handle standard and non-standard ERC20s. /// @dev Can have unforeseen consequences with ERC777. Be careful with ERC777 as reserve. function repayDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual; /// @notice An escape hatch for setting debt in special cases, like swapping reserves to another token. function setDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual; /// @notice Get total balance of assets inside the treasury + any debt taken out against those assets. function getReserveBalance(ERC20 token_) external view virtual returns (uint256); /// @notice Emergency shutdown of withdrawals. function deactivate() external virtual; /// @notice Re-activate withdrawals after shutdown. function activate() external virtual; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; import {OlympusERC20Token as OHM} from "src/external/OlympusERC20.sol"; import "src/Kernel.sol"; /// @notice Wrapper for minting and burning functions of OHM token. abstract contract MINTRv1 is Module { // ========= EVENTS ========= // event IncreaseMintApproval(address indexed policy_, uint256 newAmount_); event DecreaseMintApproval(address indexed policy_, uint256 newAmount_); event Mint(address indexed policy_, address indexed to_, uint256 amount_); event Burn(address indexed policy_, address indexed from_, uint256 amount_); // ========= ERRORS ========= // error MINTR_NotApproved(); error MINTR_ZeroAmount(); error MINTR_NotActive(); // ========= STATE ========= // OHM public ohm; /// @notice Status of the minter. If false, minting and burning OHM is disabled. bool public active; /// @notice Mapping of who is approved for minting. /// @dev minter -> amount. Infinite approval is max(uint256). mapping(address => uint256) public mintApproval; // ========= FUNCTIONS ========= // modifier onlyWhileActive() { if (!active) revert MINTR_NotActive(); _; } /// @notice Mint OHM to an address. function mintOhm(address to_, uint256 amount_) external virtual; /// @notice Burn OHM from an address. Must have approval. function burnOhm(address from_, uint256 amount_) external virtual; /// @notice Increase approval for specific withdrawer addresses /// @dev Policies must explicity request how much they want approved before withdrawing. function increaseMintApproval(address policy_, uint256 amount_) external virtual; /// @notice Decrease approval for specific withdrawer addresses function decreaseMintApproval(address policy_, uint256 amount_) external virtual; /// @notice Emergency shutdown of minting and burning. function deactivate() external virtual; /// @notice Re-activate minting and burning after shutdown. function activate() external virtual; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; // ███████ █████ █████ █████ ██████ ██████ ███████████ █████ █████ █████████ // ███░░░░░███ ░░███ ░░███ ░░███ ░░██████ ██████ ░░███░░░░░███░░███ ░░███ ███░░░░░███ // ███ ░░███ ░███ ░░███ ███ ░███░█████░███ ░███ ░███ ░███ ░███ ░███ ░░░ // ░███ ░███ ░███ ░░█████ ░███░░███ ░███ ░██████████ ░███ ░███ ░░█████████ // ░███ ░███ ░███ ░░███ ░███ ░░░ ░███ ░███░░░░░░ ░███ ░███ ░░░░░░░░███ // ░░███ ███ ░███ █ ░███ ░███ ░███ ░███ ░███ ░███ ███ ░███ // ░░░███████░ ███████████ █████ █████ █████ █████ ░░████████ ░░█████████ // ░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░░░░ //============================================================================================// // GLOBAL TYPES // //============================================================================================// /// @notice Actions to trigger state changes in the kernel. Passed by the executor enum Actions { InstallModule, UpgradeModule, ActivatePolicy, DeactivatePolicy, ChangeExecutor, MigrateKernel } /// @notice Used by executor to select an action and a target contract for a kernel action struct Instruction { Actions action; address target; } /// @notice Used to define which module functions a policy needs access to struct Permissions { Keycode keycode; bytes4 funcSelector; } type Keycode is bytes5; //============================================================================================// // UTIL FUNCTIONS // //============================================================================================// error TargetNotAContract(address target_); error InvalidKeycode(Keycode keycode_); // solhint-disable-next-line func-visibility function toKeycode(bytes5 keycode_) pure returns (Keycode) { return Keycode.wrap(keycode_); } // solhint-disable-next-line func-visibility function fromKeycode(Keycode keycode_) pure returns (bytes5) { return Keycode.unwrap(keycode_); } // solhint-disable-next-line func-visibility function ensureContract(address target_) view { if (target_.code.length == 0) revert TargetNotAContract(target_); } // solhint-disable-next-line func-visibility function ensureValidKeycode(Keycode keycode_) pure { bytes5 unwrapped = Keycode.unwrap(keycode_); for (uint256 i = 0; i < 5; ) { bytes1 char = unwrapped[i]; if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only unchecked { i++; } } } //============================================================================================// // COMPONENTS // //============================================================================================// /// @notice Generic adapter interface for kernel access in modules and policies. abstract contract KernelAdapter { error KernelAdapter_OnlyKernel(address caller_); Kernel public kernel; constructor(Kernel kernel_) { kernel = kernel_; } /// @notice Modifier to restrict functions to be called only by kernel. modifier onlyKernel() { if (msg.sender != address(kernel)) revert KernelAdapter_OnlyKernel(msg.sender); _; } /// @notice Function used by kernel when migrating to a new kernel. function changeKernel(Kernel newKernel_) external onlyKernel { kernel = newKernel_; } } /// @notice Base level extension of the kernel. Modules act as independent state components to be /// interacted with and mutated through policies. /// @dev Modules are installed and uninstalled via the executor. abstract contract Module is KernelAdapter { error Module_PolicyNotPermitted(address policy_); constructor(Kernel kernel_) KernelAdapter(kernel_) {} /// @notice Modifier to restrict which policies have access to module functions. modifier permissioned() { if ( msg.sender == address(kernel) || !kernel.modulePermissions(KEYCODE(), Policy(msg.sender), msg.sig) ) revert Module_PolicyNotPermitted(msg.sender); _; } /// @notice 5 byte identifier for a module. function KEYCODE() public pure virtual returns (Keycode) {} /// @notice Returns which semantic version of a module is being implemented. /// @return major - Major version upgrade indicates breaking change to the interface. /// @return minor - Minor version change retains backward-compatible interface. function VERSION() external pure virtual returns (uint8 major, uint8 minor) {} /// @notice Initialization function for the module /// @dev This function is called when the module is installed or upgraded by the kernel. /// @dev MUST BE GATED BY onlyKernel. Used to encompass any initialization or upgrade logic. function INIT() external virtual onlyKernel {} } /// @notice Policies are application logic and external interface for the kernel and installed modules. /// @dev Policies are activated and deactivated in the kernel by the executor. /// @dev Module dependencies and function permissions must be defined in appropriate functions. abstract contract Policy is KernelAdapter { error Policy_ModuleDoesNotExist(Keycode keycode_); constructor(Kernel kernel_) KernelAdapter(kernel_) {} /// @notice Easily accessible indicator for if a policy is activated or not. function isActive() external view returns (bool) { return kernel.isPolicyActive(this); } /// @notice Function to grab module address from a given keycode. function getModuleAddress(Keycode keycode_) internal view returns (address) { address moduleForKeycode = address(kernel.getModuleForKeycode(keycode_)); if (moduleForKeycode == address(0)) revert Policy_ModuleDoesNotExist(keycode_); return moduleForKeycode; } /// @notice Define module dependencies for this policy. /// @return dependencies - Keycode array of module dependencies. function configureDependencies() external virtual returns (Keycode[] memory dependencies) {} /// @notice Function called by kernel to set module function permissions. /// @return requests - Array of keycodes and function selectors for requested permissions. function requestPermissions() external view virtual returns (Permissions[] memory requests) {} } /// @notice Main contract that acts as a central component registry for the protocol. /// @dev The kernel manages modules and policies. The kernel is mutated via predefined Actions, /// @dev which are input from any address assigned as the executor. The executor can be changed as needed. contract Kernel { // ========= EVENTS ========= // event PermissionsUpdated( Keycode indexed keycode_, Policy indexed policy_, bytes4 funcSelector_, bool granted_ ); event ActionExecuted(Actions indexed action_, address indexed target_); // ========= ERRORS ========= // error Kernel_OnlyExecutor(address caller_); error Kernel_ModuleAlreadyInstalled(Keycode module_); error Kernel_InvalidModuleUpgrade(Keycode module_); error Kernel_PolicyAlreadyActivated(address policy_); error Kernel_PolicyNotActivated(address policy_); // ========= PRIVILEGED ADDRESSES ========= // /// @notice Address that is able to initiate Actions in the kernel. Can be assigned to a multisig or governance contract. address public executor; // ========= MODULE MANAGEMENT ========= // /// @notice Array of all modules currently installed. Keycode[] public allKeycodes; /// @notice Mapping of module address to keycode. mapping(Keycode => Module) public getModuleForKeycode; /// @notice Mapping of keycode to module address. mapping(Module => Keycode) public getKeycodeForModule; /// @notice Mapping of a keycode to all of its policy dependents. Used to efficiently reconfigure policy dependencies. mapping(Keycode => Policy[]) public moduleDependents; /// @notice Helper for module dependent arrays. Prevents the need to loop through array. mapping(Keycode => mapping(Policy => uint256)) public getDependentIndex; /// @notice Module <> Policy Permissions. /// @dev Keycode -> Policy -> Function Selector -> bool for permission mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions; // ========= POLICY MANAGEMENT ========= // /// @notice List of all active policies Policy[] public activePolicies; /// @notice Helper to get active policy quickly. Prevents need to loop through array. mapping(Policy => uint256) public getPolicyIndex; //============================================================================================// // CORE FUNCTIONS // //============================================================================================// constructor() { executor = msg.sender; } /// @notice Modifier to check if caller is the executor. modifier onlyExecutor() { if (msg.sender != executor) revert Kernel_OnlyExecutor(msg.sender); _; } function isPolicyActive(Policy policy_) public view returns (bool) { return activePolicies.length > 0 && activePolicies[getPolicyIndex[policy_]] == policy_; } /// @notice Main kernel function. Initiates state changes to kernel depending on Action passed in. function executeAction(Actions action_, address target_) external onlyExecutor { if (action_ == Actions.InstallModule) { ensureContract(target_); ensureValidKeycode(Module(target_).KEYCODE()); _installModule(Module(target_)); } else if (action_ == Actions.UpgradeModule) { ensureContract(target_); ensureValidKeycode(Module(target_).KEYCODE()); _upgradeModule(Module(target_)); } else if (action_ == Actions.ActivatePolicy) { ensureContract(target_); _activatePolicy(Policy(target_)); } else if (action_ == Actions.DeactivatePolicy) { ensureContract(target_); _deactivatePolicy(Policy(target_)); } else if (action_ == Actions.ChangeExecutor) { executor = target_; } else if (action_ == Actions.MigrateKernel) { ensureContract(target_); _migrateKernel(Kernel(target_)); } emit ActionExecuted(action_, target_); } function _installModule(Module newModule_) internal { Keycode keycode = newModule_.KEYCODE(); if (address(getModuleForKeycode[keycode]) != address(0)) revert Kernel_ModuleAlreadyInstalled(keycode); getModuleForKeycode[keycode] = newModule_; getKeycodeForModule[newModule_] = keycode; allKeycodes.push(keycode); newModule_.INIT(); } function _upgradeModule(Module newModule_) internal { Keycode keycode = newModule_.KEYCODE(); Module oldModule = getModuleForKeycode[keycode]; if (address(oldModule) == address(0) || oldModule == newModule_) revert Kernel_InvalidModuleUpgrade(keycode); getKeycodeForModule[oldModule] = Keycode.wrap(bytes5(0)); getKeycodeForModule[newModule_] = keycode; getModuleForKeycode[keycode] = newModule_; newModule_.INIT(); _reconfigurePolicies(keycode); } function _activatePolicy(Policy policy_) internal { if (isPolicyActive(policy_)) revert Kernel_PolicyAlreadyActivated(address(policy_)); // Add policy to list of active policies activePolicies.push(policy_); getPolicyIndex[policy_] = activePolicies.length - 1; // Record module dependencies Keycode[] memory dependencies = policy_.configureDependencies(); uint256 depLength = dependencies.length; for (uint256 i; i < depLength; ) { Keycode keycode = dependencies[i]; moduleDependents[keycode].push(policy_); getDependentIndex[keycode][policy_] = moduleDependents[keycode].length - 1; unchecked { ++i; } } // Grant permissions for policy to access restricted module functions Permissions[] memory requests = policy_.requestPermissions(); _setPolicyPermissions(policy_, requests, true); } function _deactivatePolicy(Policy policy_) internal { if (!isPolicyActive(policy_)) revert Kernel_PolicyNotActivated(address(policy_)); // Revoke permissions Permissions[] memory requests = policy_.requestPermissions(); _setPolicyPermissions(policy_, requests, false); // Remove policy from all policy data structures uint256 idx = getPolicyIndex[policy_]; Policy lastPolicy = activePolicies[activePolicies.length - 1]; activePolicies[idx] = lastPolicy; activePolicies.pop(); getPolicyIndex[lastPolicy] = idx; delete getPolicyIndex[policy_]; // Remove policy from module dependents _pruneFromDependents(policy_); } /// @notice All functionality will move to the new kernel. WARNING: ACTION WILL BRICK THIS KERNEL. /// @dev New kernel must add in all of the modules and policies via executeAction. /// @dev NOTE: Data does not get cleared from this kernel. function _migrateKernel(Kernel newKernel_) internal { uint256 keycodeLen = allKeycodes.length; for (uint256 i; i < keycodeLen; ) { Module module = Module(getModuleForKeycode[allKeycodes[i]]); module.changeKernel(newKernel_); unchecked { ++i; } } uint256 policiesLen = activePolicies.length; for (uint256 j; j < policiesLen; ) { Policy policy = activePolicies[j]; // Deactivate before changing kernel policy.changeKernel(newKernel_); unchecked { ++j; } } } function _reconfigurePolicies(Keycode keycode_) internal { Policy[] memory dependents = moduleDependents[keycode_]; uint256 depLength = dependents.length; for (uint256 i; i < depLength; ) { dependents[i].configureDependencies(); unchecked { ++i; } } } function _setPolicyPermissions( Policy policy_, Permissions[] memory requests_, bool grant_ ) internal { uint256 reqLength = requests_.length; for (uint256 i = 0; i < reqLength; ) { Permissions memory request = requests_[i]; modulePermissions[request.keycode][policy_][request.funcSelector] = grant_; emit PermissionsUpdated(request.keycode, policy_, request.funcSelector, grant_); unchecked { ++i; } } } function _pruneFromDependents(Policy policy_) internal { Keycode[] memory dependencies = policy_.configureDependencies(); uint256 depcLength = dependencies.length; for (uint256 i; i < depcLength; ) { Keycode keycode = dependencies[i]; Policy[] storage dependents = moduleDependents[keycode]; uint256 origIndex = getDependentIndex[keycode][policy_]; Policy lastPolicy = dependents[dependents.length - 1]; // Swap with last and pop dependents[origIndex] = lastPolicy; dependents.pop(); // Record new index and delete deactivated policy index getDependentIndex[keycode][lastPolicy] = origIndex; delete getDependentIndex[keycode][policy_]; unchecked { ++i; } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: BSD pragma solidity ^0.8.4; /// @title ClonesWithImmutableArgs /// @author wighawag, zefram.eth /// @notice Enables creating clone contracts with immutable args library ClonesWithImmutableArgs { error CreateFail(); /// @notice Creates a clone proxy of the implementation contract, with immutable args /// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length /// @param implementation The implementation contract to clone /// @param data Encoded immutable args /// @return instance The address of the created clone function clone(address implementation, bytes memory data) internal returns (address instance) { // unrealistic for memory ptr or data length to exceed 256 bits unchecked { uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call uint256 creationSize = 0x43 + extraLength; uint256 runSize = creationSize - 11; uint256 dataPtr; uint256 ptr; // solhint-disable-next-line no-inline-assembly assembly { ptr := mload(0x40) // ------------------------------------------------------------------------------------------------------------- // CREATION (11 bytes) // ------------------------------------------------------------------------------------------------------------- // 3d | RETURNDATASIZE | 0 | – // 61 runtime | PUSH2 runtime (r) | r 0 | – mstore( ptr, 0x3d61000000000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x02), shl(240, runSize)) // size of the contract running bytecode (16 bits) // creation size = 0b // 80 | DUP1 | r r 0 | – // 60 creation | PUSH1 creation (c) | c r r 0 | – // 3d | RETURNDATASIZE | 0 c r r 0 | – // 39 | CODECOPY | r 0 | [0-2d]: runtime code // 81 | DUP2 | 0 c 0 | [0-2d]: runtime code // f3 | RETURN | 0 | [0-2d]: runtime code mstore( add(ptr, 0x04), 0x80600b3d3981f300000000000000000000000000000000000000000000000000 ) // ------------------------------------------------------------------------------------------------------------- // RUNTIME // ------------------------------------------------------------------------------------------------------------- // 36 | CALLDATASIZE | cds | – // 3d | RETURNDATASIZE | 0 cds | – // 3d | RETURNDATASIZE | 0 0 cds | – // 37 | CALLDATACOPY | – | [0, cds] = calldata // 61 | PUSH2 extra | extra | [0, cds] = calldata mstore( add(ptr, 0x0b), 0x363d3d3761000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x10), shl(240, extraLength)) // 60 0x38 | PUSH1 0x38 | 0x38 extra | [0, cds] = calldata // 0x38 (56) is runtime size - data // 36 | CALLDATASIZE | cds 0x38 extra | [0, cds] = calldata // 39 | CODECOPY | _ | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 0 0 | [0, cds] = calldata // 36 | CALLDATASIZE | cds 0 0 0 | [0, cds] = calldata // 61 extra | PUSH2 extra | extra cds 0 0 0 | [0, cds] = calldata mstore( add(ptr, 0x12), 0x603836393d3d3d36610000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x1b), shl(240, extraLength)) // 01 | ADD | cds+extra 0 0 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | 0 cds 0 0 0 | [0, cds] = calldata // 73 addr | PUSH20 0x123… | addr 0 cds 0 0 0 | [0, cds] = calldata mstore( add(ptr, 0x1d), 0x013d730000000000000000000000000000000000000000000000000000000000 ) mstore(add(ptr, 0x20), shl(0x60, implementation)) // 5a | GAS | gas addr 0 cds 0 0 0 | [0, cds] = calldata // f4 | DELEGATECALL | success 0 | [0, cds] = calldata // 3d | RETURNDATASIZE | rds success 0 | [0, cds] = calldata // 82 | DUP3 | 0 rds success 0 | [0, cds] = calldata // 80 | DUP1 | 0 0 rds success 0 | [0, cds] = calldata // 3e | RETURNDATACOPY | success 0 | [0, rds] = return data (there might be some irrelevant leftovers in memory [rds, cds] when rds < cds) // 90 | SWAP1 | 0 success | [0, rds] = return data // 3d | RETURNDATASIZE | rds 0 success | [0, rds] = return data // 91 | SWAP2 | success 0 rds | [0, rds] = return data // 60 0x36 | PUSH1 0x36 | 0x36 sucess 0 rds | [0, rds] = return data // 57 | JUMPI | 0 rds | [0, rds] = return data // fd | REVERT | – | [0, rds] = return data // 5b | JUMPDEST | 0 rds | [0, rds] = return data // f3 | RETURN | – | [0, rds] = return data mstore( add(ptr, 0x34), 0x5af43d82803e903d91603657fd5bf30000000000000000000000000000000000 ) } // ------------------------------------------------------------------------------------------------------------- // APPENDED DATA (Accessible from extcodecopy) // (but also send as appended data to the delegatecall) // ------------------------------------------------------------------------------------------------------------- extraLength -= 2; uint256 counter = extraLength; uint256 copyPtr = ptr + 0x43; // solhint-disable-next-line no-inline-assembly assembly { dataPtr := add(data, 32) } for (; counter >= 32; counter -= 32) { // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, mload(dataPtr)) } copyPtr += 32; dataPtr += 32; } uint256 mask = ~(256**(32 - counter) - 1); // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, and(mload(dataPtr), mask)) } copyPtr += counter; // solhint-disable-next-line no-inline-assembly assembly { mstore(copyPtr, shl(240, extraLength)) } // solhint-disable-next-line no-inline-assembly assembly { instance := create(0, ptr, creationSize) } if (instance == address(0)) { revert CreateFail(); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {Clone} from "clones/Clone.sol"; import {CoolerFactory} from "./CoolerFactory.sol"; import {CoolerCallback} from "./CoolerCallback.sol"; // Function sig taken from gOHM contract interface IDelegate { function delegate(address to_) external; } /// @title Cooler Loans. /// @notice A Cooler is a smart contract escrow that facilitates fixed-duration, peer-to-peer /// loans for a user-defined debt-collateral pair. /// @dev This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args) /// to save gas on deployment. contract Cooler is Clone { using SafeTransferLib for ERC20; // --- ERRORS ---------------------------------------------------- error OnlyApproved(); error Deactivated(); error Default(); error NotExpired(); error NotCoolerCallback(); // --- DATA STRUCTURES ------------------------------------------- /// @notice A loan begins with a borrow request. struct Request { uint256 amount; // Amount to be borrowed. uint256 interest; // Annualized percentage to be paid as interest. uint256 loanToCollateral; // Requested loan-to-collateral ratio. uint256 duration; // Time to repay the loan before it defaults. bool active; // Any lender can clear an active loan request. address requester; // The address that created the request. } /// @notice A request is converted to a loan when a lender clears it. struct Loan { Request request; // Loan terms specified in the request. uint256 principal; // Amount of principal debt owed to the lender. uint256 interestDue; // Interest owed to the lender. uint256 collateral; // Amount of collateral pledged. uint256 expiry; // Time when the loan defaults. address lender; // Lender's address. address recipient; // Recipient of repayments. bool callback; // If this is true, the lender must inherit CoolerCallback. } // --- IMMUTABLES ------------------------------------------------ // This makes the code look prettier. uint256 private constant DECIMALS_INTEREST = 1e18; /// @notice This address owns the collateral in escrow. function owner() public pure returns (address _owner) { return _getArgAddress(0x0); } /// @notice This token is borrowed against. function collateral() public pure returns (ERC20 _collateral) { return ERC20(_getArgAddress(0x14)); } /// @notice This token is lent. function debt() public pure returns (ERC20 _debt) { return ERC20(_getArgAddress(0x28)); } /// @notice This contract created the Cooler function factory() public pure returns (CoolerFactory _factory) { return CoolerFactory(_getArgAddress(0x3c)); } // --- STATE VARIABLES ------------------------------------------- /// @notice Arrays stores all the loan requests. Request[] public requests; /// @notice Arrays stores all the granted loans. Loan[] public loans; /// @notice Facilitates transfer of lender ownership to new addresses mapping(uint256 => address) public approvals; // --- BORROWER -------------------------------------------------- /// @notice Request a loan with given parameters. /// Collateral is taken at time of request. /// @param amount_ of debt tokens to borrow. /// @param interest_ to pay (annualized % of 'amount_'). Expressed in DECIMALS_INTEREST. /// @param loanToCollateral_ debt tokens per collateral token pledged. Expressed in 10**collateral().decimals(). /// @param duration_ of loan tenure in seconds. /// @return reqID of the created request. Equivalent to the index of request in requests[]. function requestLoan( uint256 amount_, uint256 interest_, uint256 loanToCollateral_, uint256 duration_ ) external returns (uint256 reqID) { reqID = requests.length; requests.push( Request({ amount: amount_, interest: interest_, loanToCollateral: loanToCollateral_, duration: duration_, active: true, requester: msg.sender }) ); // The collateral is taken upfront. Will be escrowed // until the loan is repaid or defaulted. collateral().safeTransferFrom( msg.sender, address(this), collateralFor(amount_, loanToCollateral_) ); // Log the event. factory().logRequestLoan(reqID); } /// @notice Cancel a loan request and get the collateral back. /// @param reqID_ index of request in requests[]. function rescindRequest(uint256 reqID_) external { if (msg.sender != owner()) revert OnlyApproved(); Request storage req = requests[reqID_]; if (!req.active) revert Deactivated(); // Update storage and send collateral back to the owner. req.active = false; collateral().safeTransfer(owner(), collateralFor(req.amount, req.loanToCollateral)); // Log the event. factory().logRescindRequest(reqID_); } /// @notice Repay a loan to get the collateral back. /// @dev Despite a malicious lender could reenter with the callback, the /// usage of `msg.sender` prevents any economical benefit to the /// attacker, since they would be repaying the loan themselves. /// @param loanID_ index of loan in loans[]. /// @param repayment_ debt tokens to be repaid. /// @return collateral given back to the borrower. function repayLoan(uint256 loanID_, uint256 repayment_) external returns (uint256) { Loan memory loan = loans[loanID_]; if (block.timestamp > loan.expiry) revert Default(); // Cap the repayment to the total debt of the loan uint256 totalDebt = loan.principal + loan.interestDue; if (repayment_ > totalDebt) repayment_ = totalDebt; // Need to repay interest first, then any extra goes to paying down principal. uint256 interestPaid; uint256 remainder; if (repayment_ >= loan.interestDue) { remainder = repayment_ - loan.interestDue; interestPaid = loan.interestDue; loan.interestDue = 0; } else { loan.interestDue -= repayment_; interestPaid = repayment_; } // We pay back only if user has paid back principal. This can be 0. uint256 decollateralized; if (remainder > 0) { decollateralized = (loan.collateral * remainder) / loan.principal; loan.principal -= remainder; loan.collateral -= decollateralized; } // Save updated loan info in storage. loans[loanID_] = loan; // Transfer repaid debt back to the lender and collateral back to the owner if applicable debt().safeTransferFrom(msg.sender, loan.recipient, repayment_); if (decollateralized > 0) collateral().safeTransfer(owner(), decollateralized); // Log the event. factory().logRepayLoan(loanID_, repayment_); // If necessary, trigger lender callback. if (loan.callback) { CoolerCallback(loan.lender).onRepay(loanID_, remainder, interestPaid); } return decollateralized; } /// @notice Delegate voting power on collateral. /// @param to_ address to delegate. function delegateVoting(address to_) external { if (msg.sender != owner()) revert OnlyApproved(); IDelegate(address(collateral())).delegate(to_); } // --- LENDER ---------------------------------------------------- /// @notice Fill a requested loan as a lender. /// @param reqID_ index of request in requests[]. /// @param recipient_ address to repay the loan to. /// @param isCallback_ true if the lender implements the CoolerCallback abstract. False otherwise. /// @return loanID of the granted loan. Equivalent to the index of loan in loans[]. function clearRequest( uint256 reqID_, address recipient_, bool isCallback_ ) external returns (uint256 loanID) { Request memory req = requests[reqID_]; // Loan callbacks are only allowed if: // 1. The loan request has been created via a trusted lender. // 2. The lender signals that it implements the CoolerCallback Abstract. bool callback = (isCallback_ && msg.sender == req.requester); // If necessary, ensure lender implements the CoolerCallback abstract. if (callback && !CoolerCallback(msg.sender).isCoolerCallback()) revert NotCoolerCallback(); // Ensure loan request is active. if (!req.active) revert Deactivated(); // Clear the loan request in memory. req.active = false; // Calculate and store loan terms. uint256 interest = interestFor(req.amount, req.interest, req.duration); uint256 collat = collateralFor(req.amount, req.loanToCollateral); loanID = loans.length; loans.push( Loan({ request: req, principal: req.amount, interestDue: interest, collateral: collat, expiry: block.timestamp + req.duration, lender: msg.sender, recipient: recipient_, callback: callback }) ); // Clear the loan request storage. requests[reqID_].active = false; // Transfer debt tokens to the owner of the request. debt().safeTransferFrom(msg.sender, owner(), req.amount); // Log the event. factory().logClearRequest(reqID_, loanID); } /// @notice Allow lender to extend a loan for the borrower. Doesn't require /// borrower permission because it doesn't have a negative impact for them. /// @dev Since this function solely impacts the expiration day, the lender /// should ensure that extension interest payments are done beforehand. /// @param loanID_ index of loan in loans[]. /// @param times_ that the fixed-term loan duration is extended. function extendLoanTerms(uint256 loanID_, uint8 times_) external { Loan memory loan = loans[loanID_]; if (msg.sender != loan.lender) revert OnlyApproved(); if (block.timestamp > loan.expiry) revert Default(); // Update loan terms to reflect the extension. loan.expiry += loan.request.duration * times_; // Save updated loan info in storage. loans[loanID_] = loan; // Log the event. factory().logExtendLoan(loanID_, times_); } /// @notice Claim collateral upon loan default. /// @param loanID_ index of loan in loans[]. /// @return defaulted debt by the borrower, collateral kept by the lender, elapsed time since expiry. function claimDefaulted(uint256 loanID_) external returns (uint256, uint256, uint256, uint256) { Loan memory loan = loans[loanID_]; if (block.timestamp <= loan.expiry) revert NotExpired(); loans[loanID_].principal = 0; loans[loanID_].interestDue = 0; loans[loanID_].collateral = 0; // Transfer defaulted collateral to the lender. collateral().safeTransfer(loan.lender, loan.collateral); // Log the event. factory().logDefaultLoan(loanID_, loan.collateral); // If necessary, trigger lender callback. if (loan.callback) { CoolerCallback(loan.lender).onDefault(loanID_, loan.principal, loan.interestDue, loan.collateral); } return (loan.principal, loan.interestDue, loan.collateral, block.timestamp - loan.expiry); } /// @notice Approve transfer of loan ownership rights to a new address. /// @param to_ address to be approved. /// @param loanID_ index of loan in loans[]. function approveTransfer(address to_, uint256 loanID_) external { if (msg.sender != loans[loanID_].lender) revert OnlyApproved(); // Update transfer approvals. approvals[loanID_] = to_; } /// @notice Execute loan ownership transfer. Must be previously approved by the lender. /// @param loanID_ index of loan in loans[]. function transferOwnership(uint256 loanID_) external { if (msg.sender != approvals[loanID_]) revert OnlyApproved(); // Update the load lender and the recipient. loans[loanID_].lender = msg.sender; loans[loanID_].recipient = msg.sender; // Callbacks are disabled when transferring ownership. loans[loanID_].callback = false; // Clear transfer approvals. approvals[loanID_] = address(0); } /// @notice Allow lender to set repayment recipient of a given loan. /// @param loanID_ of lender's loan. /// @param recipient_ reciever of repayments function setRepaymentAddress(uint256 loanID_, address recipient_) external { if (msg.sender != loans[loanID_].lender) revert OnlyApproved(); // Update the repayment method. loans[loanID_].recipient = recipient_; } // --- AUX FUNCTIONS --------------------------------------------- /// @notice Compute collateral needed for a desired loan amount at given loan to collateral ratio. /// @param principal_ amount of debt tokens. /// @param loanToCollateral_ ratio for loan. Expressed in 10**collateral().decimals(). function collateralFor(uint256 principal_, uint256 loanToCollateral_) public view returns (uint256) { return (principal_ * (10 ** collateral().decimals())) / loanToCollateral_; } /// @notice Compute interest cost on amount for duration at given annualized rate. /// @param principal_ amount of debt tokens. /// @param rate_ of interest (annualized). /// @param duration_ of the loan in seconds. /// @return Interest in debt token terms. function interestFor(uint256 principal_, uint256 rate_, uint256 duration_) public pure returns (uint256) { uint256 interest = (rate_ * duration_) / 365 days; return (principal_ * interest) / DECIMALS_INTEREST; } /// @notice Check if given loan has expired. /// @param loanID_ index of loan in loans[]. /// @return Expiration status. function hasExpired(uint256 loanID_) external view returns (bool) { return block.timestamp > loans[loanID_].expiry; } /// @notice Check if a given request is active. /// @param reqID_ index of request in requests[]. /// @return Active status. function isActive(uint256 reqID_) external view returns (bool) { return requests[reqID_].active; } /// @notice Getter for Request data as a struct. /// @param reqID_ index of request in requests[]. /// @return Request struct. function getRequest(uint256 reqID_) external view returns (Request memory) { return requests[reqID_]; } /// @notice Getter for Loan data as a struct. /// @param loanID_ index of loan in loans[]. /// @return Loan struct. function getLoan(uint256 loanID_) external view returns (Loan memory) { return loans[loanID_]; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; import "src/Kernel.sol"; abstract contract ROLESv1 is Module { // ========= EVENTS ========= // event RoleGranted(bytes32 indexed role_, address indexed addr_); event RoleRevoked(bytes32 indexed role_, address indexed addr_); // ========= ERRORS ========= // error ROLES_InvalidRole(bytes32 role_); error ROLES_RequireRole(bytes32 role_); error ROLES_AddressAlreadyHasRole(address addr_, bytes32 role_); error ROLES_AddressDoesNotHaveRole(address addr_, bytes32 role_); error ROLES_RoleDoesNotExist(bytes32 role_); // ========= STATE ========= // /// @notice Mapping for if an address has a policy-defined role. mapping(address => mapping(bytes32 => bool)) public hasRole; // ========= FUNCTIONS ========= // /// @notice Function to grant policy-defined roles to some address. Can only be called by admin. function saveRole(bytes32 role_, address addr_) external virtual; /// @notice Function to revoke policy-defined roles from some address. Can only be called by admin. function removeRole(bytes32 role_, address addr_) external virtual; /// @notice "Modifier" to restrict policy function access to certain addresses with a role. /// @dev Roles are defined in the policy and granted by the ROLES admin. function requireRole(bytes32 role_, address caller_) external virtual; /// @notice Function that checks if role is valid (all lower case) function ensureValidRole(bytes32 role_) external pure virtual; }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity >=0.7.5; /// @notice Olympus OHM token /// @dev This contract is the legacy v2 OHM token. Included in the repo for completeness, /// since it is not being changed and is imported in some contracts. interface IOlympusAuthority { // ========= EVENTS ========= // event GovernorPushed(address indexed from, address indexed to, bool _effectiveImmediately); event GuardianPushed(address indexed from, address indexed to, bool _effectiveImmediately); event PolicyPushed(address indexed from, address indexed to, bool _effectiveImmediately); event VaultPushed(address indexed from, address indexed to, bool _effectiveImmediately); event GovernorPulled(address indexed from, address indexed to); event GuardianPulled(address indexed from, address indexed to); event PolicyPulled(address indexed from, address indexed to); event VaultPulled(address indexed from, address indexed to); // ========= VIEW ========= // function governor() external view returns (address); function guardian() external view returns (address); function policy() external view returns (address); function vault() external view returns (address); } // File: types/OlympusAccessControlled.sol abstract contract OlympusAccessControlled { // ========= EVENTS ========= // event AuthorityUpdated(IOlympusAuthority indexed authority); string internal UNAUTHORIZED = "UNAUTHORIZED"; // save gas // ========= STATE VARIABLES ========= // IOlympusAuthority public authority; // ========= Constructor ========= // constructor(IOlympusAuthority _authority) { authority = _authority; emit AuthorityUpdated(_authority); } // ========= MODIFIERS ========= // modifier onlyGovernor() { require(msg.sender == authority.governor(), UNAUTHORIZED); _; } modifier onlyGuardian() { require(msg.sender == authority.guardian(), UNAUTHORIZED); _; } modifier onlyPermitted() { require(msg.sender == authority.policy(), UNAUTHORIZED); _; } modifier onlyVault() { require(msg.sender == authority.vault(), UNAUTHORIZED); _; } // ========= GOV ONLY ========= // function setAuthority(IOlympusAuthority _newAuthority) external onlyGovernor { authority = _newAuthority; emit AuthorityUpdated(_newAuthority); } } // File: cryptography/ECDSA.sol /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes memory signature ) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s; uint8 v; assembly { s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash( bytes32 domainSeparator, bytes32 structHash ) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } } // File: cryptography/EIP712.sol /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * _Available since v3.4._ */ abstract contract EIP712 { /* solhint-disable var-name-mixedcase */ // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; bytes32 private immutable _HASHED_NAME; bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; /* solhint-enable var-name-mixedcase */ /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { uint256 chainID; assembly { chainID := chainid() } bytes32 hashedName = keccak256(bytes(name)); bytes32 hashedVersion = keccak256(bytes(version)); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = chainID; _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); _TYPE_HASH = typeHash; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { uint256 chainID; assembly { chainID := chainid() } if (chainID == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { uint256 chainID; assembly { chainID := chainid() } return keccak256(abi.encode(typeHash, nameHash, versionHash, chainID, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } } // File: interfaces/IERC20Permit.sol /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as th xe allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); } // File: interfaces/IERC20.sol interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File: interfaces/IOHM.sol interface IOHM is IERC20 { function mint(address account_, uint256 amount_) external; function burn(uint256 amount) external; function burnFrom(address account_, uint256 amount_) external; } // File: libraries/SafeMath.sol library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; assert(a == b * c + (a % b)); // There is no case in which this doesn't hold return c; } // Only used in the BondingCalculator.sol function sqrrt(uint256 a) internal pure returns (uint256 c) { if (a > 3) { c = a; uint256 b = add(div(a, 2), 1); while (b < c) { c = b; b = div(add(div(a, b), b), 2); } } else if (a != 0) { c = 1; } } } // File: libraries/Counters.sol library Counters { using SafeMath for uint256; struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } // File: types/ERC20.sol abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 private constant ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256("ERC20Token"); mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) internal _allowances; uint256 internal _totalSupply; string internal _name; string internal _symbol; uint8 internal immutable _decimals; constructor(string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view virtual returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance( address owner, address spender ) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve( sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance") ); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } function decreaseAllowance( address spender, uint256 subtractedValue ) public virtual returns (bool) { _approve( msg.sender, spender, _allowances[msg.sender][spender].sub( subtractedValue, "ERC20: decreased allowance below zero" ) ); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer(address from_, address to_, uint256 amount_) internal virtual {} } // File: types/ERC20Permit.sol /** * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * _Available since v3.4._ */ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // solhint-disable-next-line var-name-mixedcase bytes32 private immutable _PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); /** * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. * * It's a good idea to use the same `name` that is defined as the ERC20 token name. */ constructor(string memory name) EIP712(name, "1") {} /** * @dev See {IERC20Permit-permit}. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); bytes32 structHash = keccak256( abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline) ); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSA.recover(hash, v, r, s); require(signer == owner, "ERC20Permit: invalid signature"); _approve(owner, spender, value); } /** * @dev See {IERC20Permit-nonces}. */ function nonces(address owner) public view virtual override returns (uint256) { return _nonces[owner].current(); } /** * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view override returns (bytes32) { return _domainSeparatorV4(); } /** * @dev "Consume a nonce": return the current value and increment. * * _Available since v4.1._ */ function _useNonce(address owner) internal virtual returns (uint256 current) { Counters.Counter storage nonce = _nonces[owner]; current = nonce.current(); nonce.increment(); } } // File: OlympusERC20.sol contract OlympusERC20Token is ERC20Permit, IOHM, OlympusAccessControlled { using SafeMath for uint256; constructor( address _authority ) ERC20("Olympus", "OHM", 9) ERC20Permit("Olympus") OlympusAccessControlled(IOlympusAuthority(_authority)) {} function mint(address account_, uint256 amount_) external override onlyVault { _mint(account_, amount_); } function burn(uint256 amount) external override { _burn(msg.sender, amount); } function burnFrom(address account_, uint256 amount_) external override { _burnFrom(account_, amount_); } function _burnFrom(address account_, uint256 amount_) internal { uint256 decreasedAllowance_ = allowance(account_, msg.sender).sub( amount_, "ERC20: burn amount exceeds allowance" ); _approve(account_, msg.sender, decreasedAllowance_); _burn(account_, amount_); } }
// SPDX-License-Identifier: BSD pragma solidity ^0.8.4; /// @title Clone /// @author zefram.eth /// @notice Provides helper functions for reading immutable args from calldata contract Clone { /// @notice Reads an immutable arg with type address /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgAddress(uint256 argOffset) internal pure returns (address arg) { uint256 offset = _getImmutableArgsOffset(); assembly { arg := shr(0x60, calldataload(add(offset, argOffset))) } } /// @notice Reads an immutable arg with type uint256 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := calldataload(add(offset, argOffset)) } } /// @notice Reads an immutable arg with type uint64 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := shr(0xc0, calldataload(add(offset, argOffset))) } } /// @notice Reads an immutable arg with type uint8 /// @param argOffset The offset of the arg in the packed data /// @return arg The arg value function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) { uint256 offset = _getImmutableArgsOffset(); // solhint-disable-next-line no-inline-assembly assembly { arg := shr(0xf8, calldataload(add(offset, argOffset))) } } /// @return offset The offset of the packed immutable args in calldata function _getImmutableArgsOffset() internal pure returns (uint256 offset) { // solhint-disable-next-line no-inline-assembly assembly { offset := sub( calldatasize(), add(shr(240, calldataload(sub(calldatasize(), 2))), 2) ) } } }
{ "remappings": [ "ds-test/=lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "interfaces/=src/interfaces/", "modules/=src/modules/", "policies/=src/policies/", "libraries/=src/libraries/", "solmate/=lib/solmate/src/", "layer-zero/=lib/solidity-examples/contracts/", "bonds/=lib/bonds/src/", "test/=src/test/", "clones/=lib/clones-with-immutable-args/src/", "cooler/=lib/Cooler/src/", "balancer-v2/=lib/balancer-v2/", "@openzeppelin/=lib/openzeppelin-contracts/", "Cooler/=lib/Cooler/src/", "clones-with-immutable-args/=lib/clones-with-immutable-args/src/", "olympus-v3/=lib/Cooler/lib/olympus-v3/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solidity-examples/=lib/solidity-examples/contracts/" ], "optimizer": { "enabled": true, "runs": 10 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"ohm_","type":"address"},{"internalType":"address","name":"gohm_","type":"address"},{"internalType":"address","name":"staking_","type":"address"},{"internalType":"address","name":"sdai_","type":"address"},{"internalType":"address","name":"coolerFactory_","type":"address"},{"internalType":"address","name":"kernel_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadEscrow","type":"error"},{"inputs":[],"name":"DurationMaximum","type":"error"},{"inputs":[{"internalType":"address","name":"caller_","type":"address"}],"name":"KernelAdapter_OnlyKernel","type":"error"},{"inputs":[],"name":"LengthDiscrepancy","type":"error"},{"inputs":[],"name":"NotLender","type":"error"},{"inputs":[],"name":"OnlyBorrower","type":"error"},{"inputs":[],"name":"OnlyBurnable","type":"error"},{"inputs":[],"name":"OnlyFromFactory","type":"error"},{"inputs":[{"internalType":"Keycode","name":"keycode_","type":"bytes5"}],"name":"Policy_ModuleDoesNotExist","type":"error"},{"inputs":[],"name":"TooEarlyToFund","type":"error"},{"anonymous":false,"inputs":[],"name":"Deactivate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Defund","type":"event"},{"anonymous":false,"inputs":[],"name":"Reactivate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"defund","type":"bool"},{"indexed":false,"internalType":"uint256","name":"daiAmount","type":"uint256"}],"name":"Rebalance","type":"event"},{"inputs":[],"name":"DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUND_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUND_CADENCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INTEREST_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOAN_TO_COLLATERAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTR","outputs":[{"internalType":"contract MINTRv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLES","outputs":[{"internalType":"contract ROLESv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRSRY","outputs":[{"internalType":"contract TRSRYv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"active","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Kernel","name":"newKernel_","type":"address"}],"name":"changeKernel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"coolers_","type":"address[]"},{"internalType":"uint256[]","name":"loans_","type":"uint256[]"}],"name":"claimDefaulted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"configureDependencies","outputs":[{"internalType":"Keycode[]","name":"dependencies","type":"bytes5[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"defund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Cooler","name":"cooler_","type":"address"},{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint8","name":"times_","type":"uint8"}],"name":"extendLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract CoolerFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"principal_","type":"uint256"}],"name":"getCollateralForLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"name":"getLoanForCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTotalReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gohm","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"duration_","type":"uint256"}],"name":"interestForLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"interestReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCoolerCallback","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"kernel","outputs":[{"internalType":"contract Kernel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Cooler","name":"cooler_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"lendToCooler","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ohm","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint256","name":"principle","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"onDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint256","name":"principlePaid_","type":"uint256"},{"internalType":"uint256","name":"interestPaid_","type":"uint256"}],"name":"onRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"principalReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebalance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestPermissions","outputs":[{"components":[{"internalType":"Keycode","name":"keycode","type":"bytes5"},{"internalType":"bytes4","name":"funcSelector","type":"bytes4"}],"internalType":"struct Permissions[]","name":"requests","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sdai","outputs":[{"internalType":"contract ERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"staking","outputs":[{"internalType":"contract IStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sweepIntoDSR","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b506040516200365038038062003650833981016040819052620000359162000121565b600080546001600160a01b0319166001600160a01b03838116919091179091558281166080528681166101005285811660e05284811661012052831660c0819052604080516338d52e0f60e01b815290516338d52e0f916004808201926020929091908290030181865afa158015620000b2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d89190620001b5565b6001600160a01b031660a05250506003805460ff60a01b1916600160a01b17905550504260045550620001dc9050565b6001600160a01b03811681146200011e57600080fd5b50565b60008060008060008060c087890312156200013b57600080fd5b8651620001488162000108565b60208801519096506200015b8162000108565b60408801519095506200016e8162000108565b6060880151909450620001818162000108565b6080880151909350620001948162000108565b60a0880151909250620001a78162000108565b809150509295509295509295565b600060208284031215620001c857600080fd5b8151620001d58162000108565b9392505050565b60805160a05160c05160e05161010051610120516132db6200037560003960008181610346015281816111a101526112500152600081816101fe0152612337015260008181610256015281816105ba015281816107a001528181610872015281816110e40152818161117401528181611a6601526124430152600081816103a5015281816109a601528181610f3201528181610fb501528181611c3801528181611e0501528181611ea401528181611f2a01528181612080015281816121110152818161273c015281816127c1015281816128410152818161294f01526129a20152600081816104e10152818161065701528181610a3301528181610ce901528181610db001528181610ff40152818161107701528181611948015281816119ca01528181611b9701528181611cdc01528181611d7401528181611fe1015281816125710152818161270f0152818161287c015281816128de01528181612a2c0152612c3101526000818161047b0152818161052501528181610c3201528181611380015281816115d001526124b901526132db6000f3fe608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6040516101f09190612ca1565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e660065481565b6101e661028f366004612cca565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612cf6565b610bab565b005b6001610241565b6102f66102f1366004612d3e565b610e4b565b604080519283526020830191909152016101f0565b6102da610e92565b6102da6110ca565b6102da610329366004612d57565b611311565b6102da61033c366004612d7b565b611369565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611419565b6040516101f09190612dad565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e66611c37937e0800081565b6102da6103e3366004612e5b565b61159c565b6101e66103f6366004612d3e565b611af7565b6101e662093a8081565b610241611b25565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6121c3565b6040516101f09190612ec6565b6101e66123b9565b6102da61045e366004612cca565b6123cb565b6102da610471366004612f14565b6124a2565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6102da612557565b6102da6125f5565b600054610220906001600160a01b031681565b6101e66104ce366004612f40565b6126ae565b6101e660045481565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b600061050d611b25565b5060405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd839061055a908690600401612ca1565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612f77565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612f92565b6001600160a01b03161415806106ed57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612f92565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612faf565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906107d990339030908690600401612fc8565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612f77565b50600061082882610e4b565b915050806005600082825461083d9190613002565b9250508190555083600660008282546108569190613002565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906108a9908890869060040161301a565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612f77565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612faf565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612faf565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390610a6a908990899060040161301a565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612f77565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612faf565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612ca1565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612f77565b905090565b604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c18919061310d565b60405163d42efd8360e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd8390610c67908790600401612ca1565b602060405180830381865afa158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca89190612f77565b610cc557604051636f2c0c0160e01b815260040160405180910390fd5b6000610cdd82602001518360000151606001516126ae565b90506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166323b872dd3330610d1d60ff88168661319b565b6040518463ffffffff1660e01b8152600401610d3b93929190612fc8565b6020604051808303816000875af1158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190612f77565b50600354600160a01b900460ff1615610dab57610da6610da160ff85168361319b565b6126f8565b610de1565b610de17f0000000000000000000000000000000000000000000000000000000000000000610ddc60ff86168461319b565b61283f565b6040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610e2c57600080fd5b505af1158015610e40573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610e6b689cd35474a7270c00008661319b565b610e7591906131ba565b90506000610e8682629f85806126ae565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610ed990849033906004016131dc565b600060405180830381600087803b158015610ef357600080fd5b505af1158015610f07573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190610f67903090600401612ca1565b602060405180830381865afa158015610f84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa89190612faf565b90508015610fda57610fda7f00000000000000000000000000000000000000000000000000000000000000008261283f565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611029903090600401612ca1565b602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190612faf565b9050801561109c5761109c7f00000000000000000000000000000000000000000000000000000000000000008261283f565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611119903090600401612ca1565b602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190612faf565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906111cb907f000000000000000000000000000000000000000000000000000000000000000090859060040161301a565b6020604051808303816000875af11580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190612f77565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f00000000000000000000000000000000000000000000000000000000000000009091169063990966d5906084016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf9190612faf565b6040518363ffffffff1660e01b81526004016112dc92919061301a565b600060405180830381600087803b1580156112f657600080fd5b505af115801561130a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611347573360405163053e900f60e21b815260040161133e9190612ca1565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd83906113b5903390600401612ca1565b602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f69190612f77565b61141357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161143c575050604080518082019091526001600160d81b031983168152634aae164b60e01b6020820152815191935090839060009061149d5761149d6131f3565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b031916815250826001815181106114eb576114eb6131f3565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b03191681525082600281518110611539576115396131f3565b6020026020010181905250604051806040016040528061155e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b60209091015282518390600390811061158d5761158d6131f3565b60200260200101819052505090565b808381146115bd57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b858110156118dc577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d42efd838b8b8481811061160f5761160f6131f3565b90506020020160208101906116249190612d57565b6040518263ffffffff1660e01b81526004016116409190612ca1565b602060405180830381865afa15801561165d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116819190612f77565b61169e57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106116b1576116b16131f3565b90506020020160208101906116c69190612d57565b6001600160a01b031663504006ca8a8a858181106116e6576116e66131f3565b905060200201356040518263ffffffff1660e01b815260040161170b91815260200190565b6101a060405180830381865afa158015611729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174d919061310d565b60a001516001600160a01b03161461177857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d86818110611790576117906131f3565b90506020020160208101906117a59190612d57565b6001600160a01b03166357e8a2f08d8d888181106117c5576117c56131f3565b905060200201356040518263ffffffff1660e01b81526004016117ea91815260200190565b6080604051808303816000875af1158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d9190613209565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006118658466b1a2bc2ec5000061319b565b61186f91906131ba565b9050600067016345785d8a000082106118905767016345785d8a0000611892565b815b905062093a8083106118ad576118a88189613002565b6118cf565b62093a806118bb848361319b565b6118c591906131ba565b6118cf9089613002565b97505050505050506115c6565b5082600554116118ed5760006118fb565b826005546118fb919061323f565b600555600654841061190e57600061191c565b8360065461191c919061323f565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590611972907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190612faf565b6002549091506001600160a01b0316634aae164b307f00000000000000000000000000000000000000000000000000000000000000008885116119f7576000611a01565b611a01898661323f565b6040518463ffffffff1660e01b8152600401611a1f93929190612fc8565b600060405180830381600087803b158015611a3957600080fd5b505af1158015611a4d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb9150611a9f903390869060040161301a565b6020604051808303816000875af1158015611abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae29190612f77565b50611aeb6110ca565b50505050505050505050565b6000689cd35474a7270c0000611b1583670de0b6b3a764000061319b565b611b1f91906131ba565b92915050565b6003546000908190600160a01b900460ff16611b42576000611b4f565b6a0ee3a5f48a68b5520000005b9050426004541115611b6357600091505090565b62093a8060046000828254611b789190613002565b90915550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611bcc903090600401612ca1565b602060405180830381865afa158015611be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0d9190612faf565b90508015611c1e57611c1e816126f8565b60405163ce96cb7760e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ce96cb7790611c6d903090600401612ca1565b602060405180830381865afa158015611c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cae9190612faf565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611d06907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa158015611d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d479190612faf565b905083821015611fb6576000611d5d838661323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000611d9d8587613002565b6040518463ffffffff1660e01b8152600401611dbb93929190612fc8565b600060405180830381600087803b158015611dd557600080fd5b505af1158015611de9573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa158015611e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e799190612faf565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611ece9030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612fc8565b600060405180830381600087803b158015611ee857600080fd5b505af1158015611efc573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611f549030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612fc8565b600060405180830381600087803b158015611f6e57600080fd5b505af1158015611f82573d6000803e3d6000fd5b5050604080516000815260208101869052600080516020613286833981519152935001905060405180910390a150506121b8565b838211156121b8576000611fca858461323f565b6002549091506001600160a01b0316634aae164b307f000000000000000000000000000000000000000000000000000000000000000084861161200e576000612018565b612018858761323f565b6040518463ffffffff1660e01b815260040161203693929190612fc8565b600060405180830381600087803b15801561205057600080fd5b505af1158015612064573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa1580156120d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f49190612faf565b60025460405163a9059cbb60e01b81529192506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263a9059cbb92612149921690859060040161301a565b6020604051808303816000875af1158015612168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218c9190612f77565b50604080516001815260208101849052600080516020613286833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b81600081518110612201576122016131f3565b6001600160d81b0319909216602092830291909101909101526122296426a4a72a2960d91b90565b8160018151811061223c5761223c6131f3565b6001600160d81b03199092166020928302919091019091015261226464524f4c455360d81b90565b81600281518110612277576122776131f3565b6001600160d81b0319909216602092830291909101909101526122a76122a264545253525960d81b90565b612b6b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122d86426a4a72a2960d91b612b6b565b600380546001600160a01b0319166001600160a01b039290921691909117905561230964524f4c455360d81b612b6b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f000000000000000000000000000000000000000000000000000000000000000083169263095ea7b392612372929116906000199060040161301a565b6020604051808303816000875af1158015612391573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b59190612f77565b5090565b6000600554600654610ba69190613002565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061240f90849033906004016131dc565b600060405180830381600087803b15801561242957600080fd5b505af115801561243d573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603612493576040516323286f0960e01b815260040160405180910390fd5b61249d838361283f565b505050565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd83906124ee903390600401612ca1565b602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190612f77565b61254c57604051636f2c0c0160e01b815260040160405180910390fd5b61249d838383612c08565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a08231906125a6903090600401612ca1565b602060405180830381865afa1580156125c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e79190612faf565b90506125f2816126f8565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061263990849033906004016131dc565b600060405180830381600087803b15801561265357600080fd5b505af1158015612667573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806126c7846611c37937e0800061319b565b6126d191906131ba565b9050670de0b6b3a76400006126e6828661319b565b6126f091906131ba565b949350505050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390612766907f000000000000000000000000000000000000000000000000000000000000000090859060040161301a565b6020604051808303816000875af1158015612785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a99190612f77565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636e553f65906127f890849030906004016131dc565b6020604051808303816000875af1158015612817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283b9190612faf565b5050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614806128b057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316145b15612ab6576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590612908907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa158015612925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129499190612faf565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161461298c5782612a15565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634cdad50690602401602060405180830381865afa1580156129f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a159190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000848611612a59576000612a63565b612a63858761323f565b6040518463ffffffff1660e01b8152600401612a8193929190612fc8565b600060405180830381600087803b158015612a9b57600080fd5b505af1158015612aaf573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb92612aea9290911690859060040161301a565b6020604051808303816000875af1158015612b09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2d9190612f77565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612b5f92919061301a565b60405180910390a15050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612b9c908690600401613270565b602060405180830381865afa158015612bb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bdd9190612f92565b90506001600160a01b038116611b1f5782604051635c3fa9cd60e01b815260040161133e9190613270565b600354600160a01b900460ff1615612c2c57612c27610da18284613002565b612c5a565b612c5a7f0000000000000000000000000000000000000000000000000000000000000000610ddc8385613002565b8060055411612c6a576000612c78565b80600554612c78919061323f565b6005556006548210612c8b576000612c99565b81600654612c99919061323f565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146125f257600080fd5b60008060408385031215612cdd57600080fd5b8235612ce881612cb5565b946020939093013593505050565b600080600060608486031215612d0b57600080fd5b8335612d1681612cb5565b925060208401359150604084013560ff81168114612d3357600080fd5b809150509250925092565b600060208284031215612d5057600080fd5b5035919050565b600060208284031215612d6957600080fd5b8135612d7481612cb5565b9392505050565b60008060008060808587031215612d9157600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612e0357815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612dca565b5091979650505050505050565b60008083601f840112612e2257600080fd5b5081356001600160401b03811115612e3957600080fd5b6020830191508360208260051b8501011115612e5457600080fd5b9250929050565b60008060008060408587031215612e7157600080fd5b84356001600160401b0380821115612e8857600080fd5b612e9488838901612e10565b90965094506020870135915080821115612ead57600080fd5b50612eba87828801612e10565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612f085783516001600160d81b03191683529284019291840191600101612ee2565b50909695505050505050565b600080600060608486031215612f2957600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612f5357600080fd5b50508035926020909101359150565b80518015158114612f7257600080fd5b919050565b600060208284031215612f8957600080fd5b612d7482612f62565b600060208284031215612fa457600080fd5b8151612d7481612cb5565b600060208284031215612fc157600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b6000821982111561301557613015612fec565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b038111828210171561306457634e487b7160e01b600052604160045260246000fd5b60405290565b8051612f7281612cb5565b600060c0828403121561308757600080fd5b60405160c081016001600160401b03811182821017156130b757634e487b7160e01b600052604160045260246000fd5b8060405250809150825181526020830151602082015260408301516040820152606083015160608201526130ed60808401612f62565b608082015260a083015161310081612cb5565b60a0919091015292915050565b60006101a0828403121561312057600080fd5b613128613033565b6131328484613075565b815260c0830151602082015260e083015160408201526101008301516060820152610120830151608082015261316b610140840161306a565b60a082015261317d610160840161306a565b60c082015261318f6101808401612f62565b60e08201529392505050565b60008160001904831182151516156131b5576131b5612fec565b500290565b6000826131d757634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561321f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561325157613251612fec565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220d205e3efb2fe0a5ca57840cf78a349373f74cd12b83978ac9eaa6aa0a2a22e4b64736f6c634300080f003300000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42160000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d581565b6040516101f09190612ca1565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f81565b6101e660065481565b6101e661028f366004612cca565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612cf6565b610bab565b005b6001610241565b6102f66102f1366004612d3e565b610e4b565b604080519283526020830191909152016101f0565b6102da610e92565b6102da6110ca565b6102da610329366004612d57565b611311565b6102da61033c366004612d7b565b611369565b6102207f000000000000000000000000b63cac384247597756545b500253ff8e607a802081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611419565b6040516101f09190612dad565b6102207f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81565b6101e66611c37937e0800081565b6102da6103e3366004612e5b565b61159c565b6101e66103f6366004612d3e565b611af7565b6101e662093a8081565b610241611b25565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6121c3565b6040516101f09190612ec6565b6101e66123b9565b6102da61045e366004612cca565b6123cb565b6102da610471366004612f14565b6124a2565b6102207f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db421681565b6102da612557565b6102da6125f5565b600054610220906001600160a01b031681565b6101e66104ce366004612f40565b6126ae565b6101e660045481565b6102207f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b600061050d611b25565b5060405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd839061055a908690600401612ca1565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612f77565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612f92565b6001600160a01b03161415806106ed57507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612f92565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612faf565b6040516323b872dd60e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906323b872dd906107d990339030908690600401612fc8565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612f77565b50600061082882610e4b565b915050806005600082825461083d9190613002565b9250508190555083600660008282546108569190613002565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906108a9908890869060040161301a565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612f77565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612faf565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612faf565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b390610a6a908990899060040161301a565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612f77565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612faf565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612ca1565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612f77565b905090565b604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c18919061310d565b60405163d42efd8360e01b81529091506001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd8390610c67908790600401612ca1565b602060405180830381865afa158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca89190612f77565b610cc557604051636f2c0c0160e01b815260040160405180910390fd5b6000610cdd82602001518360000151606001516126ae565b90506001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f166323b872dd3330610d1d60ff88168661319b565b6040518463ffffffff1660e01b8152600401610d3b93929190612fc8565b6020604051808303816000875af1158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190612f77565b50600354600160a01b900460ff1615610dab57610da6610da160ff85168361319b565b6126f8565b610de1565b610de17f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f610ddc60ff86168461319b565b61283f565b6040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610e2c57600080fd5b505af1158015610e40573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610e6b689cd35474a7270c00008661319b565b610e7591906131ba565b90506000610e8682629f85806126ae565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610ed990849033906004016131dc565b600060405180830381600087803b158015610ef357600080fd5b505af1158015610f07573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea16906370a0823190610f67903090600401612ca1565b602060405180830381865afa158015610f84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa89190612faf565b90508015610fda57610fda7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea8261283f565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190611029903090600401612ca1565b602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190612faf565b9050801561109c5761109c7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f8261283f565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906370a0823190611119903090600401612ca1565b602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190612faf565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906111cb907f000000000000000000000000b63cac384247597756545b500253ff8e607a802090859060040161301a565b6020604051808303816000875af11580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190612f77565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f000000000000000000000000b63cac384247597756545b500253ff8e607a80209091169063990966d5906084016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf9190612faf565b6040518363ffffffff1660e01b81526004016112dc92919061301a565b600060405180830381600087803b1580156112f657600080fd5b505af115801561130a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611347573360405163053e900f60e21b815260040161133e9190612ca1565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd83906113b5903390600401612ca1565b602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f69190612f77565b61141357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161143c575050604080518082019091526001600160d81b031983168152634aae164b60e01b6020820152815191935090839060009061149d5761149d6131f3565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b031916815250826001815181106114eb576114eb6131f3565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b03191681525082600281518110611539576115396131f3565b6020026020010181905250604051806040016040528061155e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b60209091015282518390600390811061158d5761158d6131f3565b60200260200101819052505090565b808381146115bd57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b858110156118dc577f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42166001600160a01b031663d42efd838b8b8481811061160f5761160f6131f3565b90506020020160208101906116249190612d57565b6040518263ffffffff1660e01b81526004016116409190612ca1565b602060405180830381865afa15801561165d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116819190612f77565b61169e57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106116b1576116b16131f3565b90506020020160208101906116c69190612d57565b6001600160a01b031663504006ca8a8a858181106116e6576116e66131f3565b905060200201356040518263ffffffff1660e01b815260040161170b91815260200190565b6101a060405180830381865afa158015611729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174d919061310d565b60a001516001600160a01b03161461177857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d86818110611790576117906131f3565b90506020020160208101906117a59190612d57565b6001600160a01b03166357e8a2f08d8d888181106117c5576117c56131f3565b905060200201356040518263ffffffff1660e01b81526004016117ea91815260200190565b6080604051808303816000875af1158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d9190613209565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006118658466b1a2bc2ec5000061319b565b61186f91906131ba565b9050600067016345785d8a000082106118905767016345785d8a0000611892565b815b905062093a8083106118ad576118a88189613002565b6118cf565b62093a806118bb848361319b565b6118c591906131ba565b6118cf9089613002565b97505050505050506115c6565b5082600554116118ed5760006118fb565b826005546118fb919061323f565b600555600654841061190e57600061191c565b8360065461191c919061323f565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590611972907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f8885116119f7576000611a01565b611a01898661323f565b6040518463ffffffff1660e01b8152600401611a1f93929190612fc8565b600060405180830381600087803b158015611a3957600080fd5b505af1158015611a4d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16925063a9059cbb9150611a9f903390869060040161301a565b6020604051808303816000875af1158015611abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae29190612f77565b50611aeb6110ca565b50505050505050505050565b6000689cd35474a7270c0000611b1583670de0b6b3a764000061319b565b611b1f91906131ba565b92915050565b6003546000908190600160a01b900460ff16611b42576000611b4f565b6a0ee3a5f48a68b5520000005b9050426004541115611b6357600091505090565b62093a8060046000828254611b789190613002565b90915550506040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190611bcc903090600401612ca1565b602060405180830381865afa158015611be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0d9190612faf565b90508015611c1e57611c1e816126f8565b60405163ce96cb7760e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea169063ce96cb7790611c6d903090600401612ca1565b602060405180830381865afa158015611c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cae9190612faf565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611d06907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa158015611d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d479190612faf565b905083821015611fb6576000611d5d838661323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f611d9d8587613002565b6040518463ffffffff1660e01b8152600401611dbb93929190612fc8565b600060405180830381600087803b158015611dd557600080fd5b505af1158015611de9573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa158015611e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e799190612faf565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611ece9030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612fc8565b600060405180830381600087803b158015611ee857600080fd5b505af1158015611efc573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611f549030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612fc8565b600060405180830381600087803b158015611f6e57600080fd5b505af1158015611f82573d6000803e3d6000fd5b5050604080516000815260208101869052600080516020613286833981519152935001905060405180910390a150506121b8565b838211156121b8576000611fca858461323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f84861161200e576000612018565b612018858761323f565b6040518463ffffffff1660e01b815260040161203693929190612fc8565b600060405180830381600087803b15801561205057600080fd5b505af1158015612064573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa1580156120d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f49190612faf565b60025460405163a9059cbb60e01b81529192506001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81169263a9059cbb92612149921690859060040161301a565b6020604051808303816000875af1158015612168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218c9190612f77565b50604080516001815260208101849052600080516020613286833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b81600081518110612201576122016131f3565b6001600160d81b0319909216602092830291909101909101526122296426a4a72a2960d91b90565b8160018151811061223c5761223c6131f3565b6001600160d81b03199092166020928302919091019091015261226464524f4c455360d81b90565b81600281518110612277576122776131f3565b6001600160d81b0319909216602092830291909101909101526122a76122a264545253525960d81b90565b612b6b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122d86426a4a72a2960d91b612b6b565b600380546001600160a01b0319166001600160a01b039290921691909117905561230964524f4c455360d81b612b6b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d583169263095ea7b392612372929116906000199060040161301a565b6020604051808303816000875af1158015612391573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b59190612f77565b5090565b6000600554600654610ba69190613002565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061240f90849033906004016131dc565b600060405180830381600087803b15801561242957600080fd5b505af115801561243d573d6000803e3d6000fd5b505050507f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b031603612493576040516323286f0960e01b815260040160405180910390fd5b61249d838361283f565b505050565b60405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd83906124ee903390600401612ca1565b602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190612f77565b61254c57604051636f2c0c0160e01b815260040160405180910390fd5b61249d838383612c08565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a08231906125a6903090600401612ca1565b602060405180830381865afa1580156125c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e79190612faf565b90506125f2816126f8565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061263990849033906004016131dc565b600060405180830381600087803b15801561265357600080fd5b505af1158015612667573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806126c7846611c37937e0800061319b565b6126d191906131ba565b9050670de0b6b3a76400006126e6828661319b565b6126f091906131ba565b949350505050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b390612766907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea90859060040161301a565b6020604051808303816000875af1158015612785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a99190612f77565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea1690636e553f65906127f890849030906004016131dc565b6020604051808303816000875af1158015612817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283b9190612faf565b5050565b7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316826001600160a01b031614806128b057507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316826001600160a01b0316145b15612ab6576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590612908907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa158015612925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129499190612faf565b905060007f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316846001600160a01b03161461298c5782612a15565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b031690634cdad50690602401602060405180830381865afa1580156129f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a159190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f848611612a59576000612a63565b612a63858761323f565b6040518463ffffffff1660e01b8152600401612a8193929190612fc8565b600060405180830381600087803b158015612a9b57600080fd5b505af1158015612aaf573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb92612aea9290911690859060040161301a565b6020604051808303816000875af1158015612b09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2d9190612f77565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612b5f92919061301a565b60405180910390a15050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612b9c908690600401613270565b602060405180830381865afa158015612bb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bdd9190612f92565b90506001600160a01b038116611b1f5782604051635c3fa9cd60e01b815260040161133e9190613270565b600354600160a01b900460ff1615612c2c57612c27610da18284613002565b612c5a565b612c5a7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f610ddc8385613002565b8060055411612c6a576000612c78565b80600554612c78919061323f565b6005556006548210612c8b576000612c99565b81600654612c99919061323f565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146125f257600080fd5b60008060408385031215612cdd57600080fd5b8235612ce881612cb5565b946020939093013593505050565b600080600060608486031215612d0b57600080fd5b8335612d1681612cb5565b925060208401359150604084013560ff81168114612d3357600080fd5b809150509250925092565b600060208284031215612d5057600080fd5b5035919050565b600060208284031215612d6957600080fd5b8135612d7481612cb5565b9392505050565b60008060008060808587031215612d9157600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612e0357815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612dca565b5091979650505050505050565b60008083601f840112612e2257600080fd5b5081356001600160401b03811115612e3957600080fd5b6020830191508360208260051b8501011115612e5457600080fd5b9250929050565b60008060008060408587031215612e7157600080fd5b84356001600160401b0380821115612e8857600080fd5b612e9488838901612e10565b90965094506020870135915080821115612ead57600080fd5b50612eba87828801612e10565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612f085783516001600160d81b03191683529284019291840191600101612ee2565b50909695505050505050565b600080600060608486031215612f2957600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612f5357600080fd5b50508035926020909101359150565b80518015158114612f7257600080fd5b919050565b600060208284031215612f8957600080fd5b612d7482612f62565b600060208284031215612fa457600080fd5b8151612d7481612cb5565b600060208284031215612fc157600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b6000821982111561301557613015612fec565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b038111828210171561306457634e487b7160e01b600052604160045260246000fd5b60405290565b8051612f7281612cb5565b600060c0828403121561308757600080fd5b60405160c081016001600160401b03811182821017156130b757634e487b7160e01b600052604160045260246000fd5b8060405250809150825181526020830151602082015260408301516040820152606083015160608201526130ed60808401612f62565b608082015260a083015161310081612cb5565b60a0919091015292915050565b60006101a0828403121561312057600080fd5b613128613033565b6131328484613075565b815260c0830151602082015260e083015160408201526101008301516060820152610120830151608082015261316b610140840161306a565b60a082015261317d610160840161306a565b60c082015261318f6101808401612f62565b60e08201529392505050565b60008160001904831182151516156131b5576131b5612fec565b500290565b6000826131d757634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561321f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561325157613251612fec565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220d205e3efb2fe0a5ca57840cf78a349373f74cd12b83978ac9eaa6aa0a2a22e4b64736f6c634300080f0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42160000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
-----Decoded View---------------
Arg [0] : ohm_ (address): 0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5
Arg [1] : gohm_ (address): 0x0ab87046fBb341D058F17CBC4c1133F25a20a52f
Arg [2] : staking_ (address): 0xB63cac384247597756545b500253ff8E607a8020
Arg [3] : sdai_ (address): 0x83F20F44975D03b1b09e64809B757c47f942BEeA
Arg [4] : coolerFactory_ (address): 0x30Ce56e80aA96EbbA1E1a74bC5c0FEB5B0dB4216
Arg [5] : kernel_ (address): 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5
Arg [1] : 0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f
Arg [2] : 000000000000000000000000b63cac384247597756545b500253ff8e607a8020
Arg [3] : 00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea
Arg [4] : 00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216
Arg [5] : 0000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.