Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 145 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Extend Loan | 21570363 | 8 days ago | IN | 0 ETH | 0.00187608 | ||||
Extend Loan | 21558092 | 10 days ago | IN | 0 ETH | 0.00297964 | ||||
Extend Loan | 21555200 | 11 days ago | IN | 0 ETH | 0.0017407 | ||||
Extend Loan | 21533102 | 14 days ago | IN | 0 ETH | 0.0091922 | ||||
Extend Loan | 21521849 | 15 days ago | IN | 0 ETH | 0.00595103 | ||||
Extend Loan | 21503382 | 18 days ago | IN | 0 ETH | 0.00151696 | ||||
Extend Loan | 21503375 | 18 days ago | IN | 0 ETH | 0.00161753 | ||||
Extend Loan | 21466600 | 23 days ago | IN | 0 ETH | 0.00385656 | ||||
Extend Loan | 21354177 | 39 days ago | IN | 0 ETH | 0.0030798 | ||||
Extend Loan | 21314585 | 44 days ago | IN | 0 ETH | 0.00386333 | ||||
Extend Loan | 21216277 | 58 days ago | IN | 0 ETH | 0.00666448 | ||||
Extend Loan | 21025300 | 84 days ago | IN | 0 ETH | 0.00165322 | ||||
Claim Defaulted | 20802593 | 116 days ago | IN | 0 ETH | 0.00181458 | ||||
Extend Loan | 20779433 | 119 days ago | IN | 0 ETH | 0.00518281 | ||||
Extend Loan | 20758669 | 122 days ago | IN | 0 ETH | 0.00106403 | ||||
Extend Loan | 20724826 | 126 days ago | IN | 0 ETH | 0.00063509 | ||||
Extend Loan | 20718520 | 127 days ago | IN | 0 ETH | 0.0004555 | ||||
Extend Loan | 20711019 | 128 days ago | IN | 0 ETH | 0.00059962 | ||||
Extend Loan | 20680761 | 133 days ago | IN | 0 ETH | 0.00067441 | ||||
Extend Loan | 20675417 | 133 days ago | IN | 0 ETH | 0.00082306 | ||||
Extend Loan | 20643181 | 138 days ago | IN | 0 ETH | 0.00067415 | ||||
Extend Loan | 20643149 | 138 days ago | IN | 0 ETH | 0.00057655 | ||||
Extend Loan | 20640669 | 138 days ago | IN | 0 ETH | 0.0005708 | ||||
Extend Loan | 20594141 | 145 days ago | IN | 0 ETH | 0.00065203 | ||||
Extend Loan | 20446506 | 165 days ago | IN | 0 ETH | 0.00048 |
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 { // Attempt a clearinghouse <> treasury rebalance. rebalance(); Cooler.Loan memory loan = cooler_.getLoan(loanID_); // Ensure Clearinghouse is the lender. if (loan.lender != address(this)) revert NotLender(); // 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, loan.recipient, interestBase * times_); // Signal to cooler that loan can 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_); } }
// 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"; // Function sig taken from gOHM contract interface IDelegate { function delegate(address to_) external; } import {CoolerFactory} from "./CoolerFactory.sol"; import {CoolerCallback} from "./CoolerCallback.sol"; /// @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
6101406040523480156200001257600080fd5b506040516200357c3803806200357c833981016040819052620000359162000121565b600080546001600160a01b0319166001600160a01b03838116919091179091558281166080528681166101005285811660e05284811661012052831660c0819052604080516338d52e0f60e01b815290516338d52e0f916004808201926020929091908290030181865afa158015620000b2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d89190620001b5565b6001600160a01b031660a05250506003805460ff60a01b1916600160a01b17905550504260045550620001dc9050565b6001600160a01b03811681146200011e57600080fd5b50565b60008060008060008060c087890312156200013b57600080fd5b8651620001488162000108565b60208801519096506200015b8162000108565b60408801519095506200016e8162000108565b6060880151909450620001818162000108565b6080880151909350620001948162000108565b60a0880151909250620001a78162000108565b809150509295509295509295565b600060208284031215620001c857600080fd5b8151620001d58162000108565b9392505050565b60805160a05160c05160e05161010051610120516132156200036760003960008181610346015281816110d101526111800152600081816101fe0152612267015260008181610256015281816105ba015281816107a00152818161087201528181611014015281816110a40152818161199601526123730152600081816103a5015281816109a601528181610e6201528181610ee501528181611b6801528181611d3501528181611dd401528181611e5a01528181611fb0015281816120410152818161262a015281816127380152818161278b015281816129980152612a1d0152600081816104e10152818161065701528181610a3301528181610c6d01528181610f2401528181610fa701528181611878015281816118fa01528181611ac701528181611c0c01528181611ca401528181611f11015281816124a101528181612665015281816126c7015281816128150152818161296b0152612b6601526000818161047b01528181610525015281816112b00152818161150001526123e901526132156000f3fe608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6040516101f09190612bdb565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e660065481565b6101e661028f366004612c04565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612c30565b610bab565b005b6001610241565b6102f66102f1366004612c78565b610d7b565b604080519283526020830191909152016101f0565b6102da610dc2565b6102da610ffa565b6102da610329366004612c91565b611241565b6102da61033c366004612cb5565b611299565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611349565b6040516101f09190612ce7565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e66611c37937e0800081565b6102da6103e3366004612d95565b6114cc565b6101e66103f6366004612c78565b611a27565b6101e662093a8081565b610241611a55565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6120f3565b6040516101f09190612e00565b6101e66122e9565b6102da61045e366004612c04565b6122fb565b6102da610471366004612e4e565b6123d2565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6102da612487565b6102da612525565b600054610220906001600160a01b031681565b6101e66104ce366004612e7a565b6125de565b6101e660045481565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b600061050d611a55565b5060405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd839061055a908690600401612bdb565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612eb1565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612ecc565b6001600160a01b03161415806106ed57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612ecc565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612ee9565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906107d990339030908690600401612f02565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612eb1565b50600061082882610d7b565b915050806005600082825461083d9190612f3c565b9250508190555083600660008282546108569190612f3c565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906108a99088908690600401612f54565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612eb1565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612ee9565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612ee9565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390610a6a9089908990600401612f54565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612eb1565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612ee9565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612bdb565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612eb1565b905090565b610bb3611a55565b50604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c219190613047565b60a08101519091506001600160a01b03163014610c5157604051638c38000360e01b815260040160405180910390fd5b6000610c6982602001518360000151606001516125de565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd338460c001518660ff1685610caf91906130d5565b6040518463ffffffff1660e01b8152600401610ccd93929190612f02565b6020604051808303816000875af1158015610cec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d109190612eb1565b506040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610d5c57600080fd5b505af1158015610d70573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610d9b689cd35474a7270c0000866130d5565b610da591906130f4565b90506000610db682629f85806125de565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610e099084903390600401613116565b600060405180830381600087803b158015610e2357600080fd5b505af1158015610e37573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190610e97903090600401612bdb565b602060405180830381865afa158015610eb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed89190612ee9565b90508015610f0a57610f0a7f000000000000000000000000000000000000000000000000000000000000000082612628565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190610f59903090600401612bdb565b602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190612ee9565b90508015610fcc57610fcc7f000000000000000000000000000000000000000000000000000000000000000082612628565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611049903090600401612bdb565b602060405180830381865afa158015611066573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108a9190612ee9565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906110fb907f0000000000000000000000000000000000000000000000000000000000000000908590600401612f54565b6020604051808303816000875af115801561111a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113e9190612eb1565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f00000000000000000000000000000000000000000000000000000000000000009091169063990966d5906084016020604051808303816000875af11580156111cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ef9190612ee9565b6040518363ffffffff1660e01b815260040161120c929190612f54565b600060405180830381600087803b15801561122657600080fd5b505af115801561123a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611277573360405163053e900f60e21b815260040161126e9190612bdb565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd83906112e5903390600401612bdb565b602060405180830381865afa158015611302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113269190612eb1565b61134357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161136c575050604080518082019091526001600160d81b031983168152634aae164b60e01b602082015281519193509083906000906113cd576113cd61312d565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b0319168152508260018151811061141b5761141b61312d565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b031916815250826002815181106114695761146961312d565b6020026020010181905250604051806040016040528061148e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b6020909101528251839060039081106114bd576114bd61312d565b60200260200101819052505090565b808381146114ed57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b8581101561180c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d42efd838b8b8481811061153f5761153f61312d565b90506020020160208101906115549190612c91565b6040518263ffffffff1660e01b81526004016115709190612bdb565b602060405180830381865afa15801561158d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b19190612eb1565b6115ce57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106115e1576115e161312d565b90506020020160208101906115f69190612c91565b6001600160a01b031663504006ca8a8a858181106116165761161661312d565b905060200201356040518263ffffffff1660e01b815260040161163b91815260200190565b6101a060405180830381865afa158015611659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167d9190613047565b60a001516001600160a01b0316146116a857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d868181106116c0576116c061312d565b90506020020160208101906116d59190612c91565b6001600160a01b03166357e8a2f08d8d888181106116f5576116f561312d565b905060200201356040518263ffffffff1660e01b815260040161171a91815260200190565b6080604051808303816000875af1158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d9190613143565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006117958466b1a2bc2ec500006130d5565b61179f91906130f4565b9050600067016345785d8a000082106117c05767016345785d8a00006117c2565b815b905062093a8083106117dd576117d88189612f3c565b6117ff565b62093a806117eb84836130d5565b6117f591906130f4565b6117ff9089612f3c565b97505050505050506114f6565b50826005541161181d57600061182b565b8260055461182b9190613179565b600555600654841061183e57600061184c565b8360065461184c9190613179565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace5906118a2907f0000000000000000000000000000000000000000000000000000000000000000903090600401613190565b602060405180830381865afa1580156118bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e39190612ee9565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000888511611927576000611931565b6119318986613179565b6040518463ffffffff1660e01b815260040161194f93929190612f02565b600060405180830381600087803b15801561196957600080fd5b505af115801561197d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb91506119cf9033908690600401612f54565b6020604051808303816000875af11580156119ee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a129190612eb1565b50611a1b610ffa565b50505050505050505050565b6000689cd35474a7270c0000611a4583670de0b6b3a76400006130d5565b611a4f91906130f4565b92915050565b6003546000908190600160a01b900460ff16611a72576000611a7f565b6a0ee3a5f48a68b5520000005b9050426004541115611a9357600091505090565b62093a8060046000828254611aa89190612f3c565b90915550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611afc903090600401612bdb565b602060405180830381865afa158015611b19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3d9190612ee9565b90508015611b4e57611b4e81612954565b60405163ce96cb7760e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ce96cb7790611b9d903090600401612bdb565b602060405180830381865afa158015611bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bde9190612ee9565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611c36907f0000000000000000000000000000000000000000000000000000000000000000903090600401613190565b602060405180830381865afa158015611c53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c779190612ee9565b905083821015611ee6576000611c8d8386613179565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000611ccd8587612f3c565b6040518463ffffffff1660e01b8152600401611ceb93929190612f02565b600060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa158015611d85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da99190612ee9565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611dfe9030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612f02565b600060405180830381600087803b158015611e1857600080fd5b505af1158015611e2c573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611e849030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612f02565b600060405180830381600087803b158015611e9e57600080fd5b505af1158015611eb2573d6000803e3d6000fd5b50506040805160008152602081018690526000805160206131c0833981519152935001905060405180910390a150506120e8565b838211156120e8576000611efa8584613179565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000848611611f3e576000611f48565b611f488587613179565b6040518463ffffffff1660e01b8152600401611f6693929190612f02565b600060405180830381600087803b158015611f8057600080fd5b505af1158015611f94573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa158015612000573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120249190612ee9565b60025460405163a9059cbb60e01b81529192506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263a9059cbb926120799216908590600401612f54565b6020604051808303816000875af1158015612098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bc9190612eb1565b506040805160018152602081018490526000805160206131c0833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b816000815181106121315761213161312d565b6001600160d81b0319909216602092830291909101909101526121596426a4a72a2960d91b90565b8160018151811061216c5761216c61312d565b6001600160d81b03199092166020928302919091019091015261219464524f4c455360d81b90565b816002815181106121a7576121a761312d565b6001600160d81b0319909216602092830291909101909101526121d76121d264545253525960d81b90565b612a9b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122086426a4a72a2960d91b612a9b565b600380546001600160a01b0319166001600160a01b039290921691909117905561223964524f4c455360d81b612a9b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f000000000000000000000000000000000000000000000000000000000000000083169263095ea7b3926122a29291169060001990600401612f54565b6020604051808303816000875af11580156122c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e59190612eb1565b5090565b6000600554600654610ba69190612f3c565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061233f9084903390600401613116565b600060405180830381600087803b15801561235957600080fd5b505af115801561236d573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036123c3576040516323286f0960e01b815260040160405180910390fd5b6123cd8383612628565b505050565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd839061241e903390600401612bdb565b602060405180830381865afa15801561243b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245f9190612eb1565b61247c57604051636f2c0c0160e01b815260040160405180910390fd5b6123cd838383612b38565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a08231906124d6903090600401612bdb565b602060405180830381865afa1580156124f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125179190612ee9565b905061252281612954565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c5906125699084903390600401613116565b600060405180830381600087803b15801561258357600080fd5b505af1158015612597573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806125f7846611c37937e080006130d5565b61260191906130f4565b9050670de0b6b3a764000061261682866130d5565b61262091906130f4565b949350505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316148061269957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316145b1561289f576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace5906126f1907f0000000000000000000000000000000000000000000000000000000000000000903090600401613190565b602060405180830381865afa15801561270e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127329190612ee9565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161461277557826127fe565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634cdad50690602401602060405180830381865afa1580156127da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127fe9190612ee9565b6002549091506001600160a01b0316634aae164b307f000000000000000000000000000000000000000000000000000000000000000084861161284257600061284c565b61284c8587613179565b6040518463ffffffff1660e01b815260040161286a93929190612f02565b600060405180830381600087803b15801561288457600080fd5b505af1158015612898573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb926128d392909116908590600401612f54565b6020604051808303816000875af11580156128f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129169190612eb1565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612948929190612f54565b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906129c2907f0000000000000000000000000000000000000000000000000000000000000000908590600401612f54565b6020604051808303816000875af11580156129e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a059190612eb1565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636e553f6590612a549084903090600401613116565b6020604051808303816000875af1158015612a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a979190612ee9565b5050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612acc9086906004016131aa565b602060405180830381865afa158015612ae9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0d9190612ecc565b90506001600160a01b038116611a4f5782604051635c3fa9cd60e01b815260040161126e91906131aa565b600354600160a01b900460ff1615612b6157612b5c612b578284612f3c565b612954565b612b94565b612b947f0000000000000000000000000000000000000000000000000000000000000000612b8f8385612f3c565b612628565b8060055411612ba4576000612bb2565b80600554612bb29190613179565b6005556006548210612bc5576000612bd3565b81600654612bd39190613179565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b038116811461252257600080fd5b60008060408385031215612c1757600080fd5b8235612c2281612bef565b946020939093013593505050565b600080600060608486031215612c4557600080fd5b8335612c5081612bef565b925060208401359150604084013560ff81168114612c6d57600080fd5b809150509250925092565b600060208284031215612c8a57600080fd5b5035919050565b600060208284031215612ca357600080fd5b8135612cae81612bef565b9392505050565b60008060008060808587031215612ccb57600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612d3d57815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612d04565b5091979650505050505050565b60008083601f840112612d5c57600080fd5b5081356001600160401b03811115612d7357600080fd5b6020830191508360208260051b8501011115612d8e57600080fd5b9250929050565b60008060008060408587031215612dab57600080fd5b84356001600160401b0380821115612dc257600080fd5b612dce88838901612d4a565b90965094506020870135915080821115612de757600080fd5b50612df487828801612d4a565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612e425783516001600160d81b03191683529284019291840191600101612e1c565b50909695505050505050565b600080600060608486031215612e6357600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612e8d57600080fd5b50508035926020909101359150565b80518015158114612eac57600080fd5b919050565b600060208284031215612ec357600080fd5b612cae82612e9c565b600060208284031215612ede57600080fd5b8151612cae81612bef565b600060208284031215612efb57600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b60008219821115612f4f57612f4f612f26565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b0381118282101715612f9e57634e487b7160e01b600052604160045260246000fd5b60405290565b8051612eac81612bef565b600060c08284031215612fc157600080fd5b60405160c081016001600160401b0381118282101715612ff157634e487b7160e01b600052604160045260246000fd5b80604052508091508251815260208301516020820152604083015160408201526060830151606082015261302760808401612e9c565b608082015260a083015161303a81612bef565b60a0919091015292915050565b60006101a0828403121561305a57600080fd5b613062612f6d565b61306c8484612faf565b815260c0830151602082015260e08301516040820152610100830151606082015261012083015160808201526130a56101408401612fa4565b60a08201526130b76101608401612fa4565b60c08201526130c96101808401612e9c565b60e08201529392505050565b60008160001904831182151516156130ef576130ef612f26565b500290565b60008261311157634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561315957600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561318b5761318b612f26565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220e87ac8ebbd1bd58f4f830b3317030820a71105f2a771ab356ec82a0dd0d3674764736f6c634300080f003300000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f7720000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d581565b6040516101f09190612bdb565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f81565b6101e660065481565b6101e661028f366004612c04565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612c30565b610bab565b005b6001610241565b6102f66102f1366004612c78565b610d7b565b604080519283526020830191909152016101f0565b6102da610dc2565b6102da610ffa565b6102da610329366004612c91565b611241565b6102da61033c366004612cb5565b611299565b6102207f000000000000000000000000b63cac384247597756545b500253ff8e607a802081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611349565b6040516101f09190612ce7565b6102207f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81565b6101e66611c37937e0800081565b6102da6103e3366004612d95565b6114cc565b6101e66103f6366004612c78565b611a27565b6101e662093a8081565b610241611a55565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6120f3565b6040516101f09190612e00565b6101e66122e9565b6102da61045e366004612c04565b6122fb565b6102da610471366004612e4e565b6123d2565b6102207f000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f77281565b6102da612487565b6102da612525565b600054610220906001600160a01b031681565b6101e66104ce366004612e7a565b6125de565b6101e660045481565b6102207f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b600061050d611a55565b5060405163d42efd8360e01b81526001600160a01b037f000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f772169063d42efd839061055a908690600401612bdb565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612eb1565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612ecc565b6001600160a01b03161415806106ed57507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612ecc565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612ee9565b6040516323b872dd60e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906323b872dd906107d990339030908690600401612f02565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612eb1565b50600061082882610d7b565b915050806005600082825461083d9190612f3c565b9250508190555083600660008282546108569190612f3c565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906108a99088908690600401612f54565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612eb1565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612ee9565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612ee9565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b390610a6a9089908990600401612f54565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612eb1565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612ee9565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612bdb565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612eb1565b905090565b610bb3611a55565b50604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c219190613047565b60a08101519091506001600160a01b03163014610c5157604051638c38000360e01b815260040160405180910390fd5b6000610c6982602001518360000151606001516125de565b90507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b03166323b872dd338460c001518660ff1685610caf91906130d5565b6040518463ffffffff1660e01b8152600401610ccd93929190612f02565b6020604051808303816000875af1158015610cec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d109190612eb1565b506040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610d5c57600080fd5b505af1158015610d70573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610d9b689cd35474a7270c0000866130d5565b610da591906130f4565b90506000610db682629f85806125de565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610e099084903390600401613116565b600060405180830381600087803b158015610e2357600080fd5b505af1158015610e37573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea16906370a0823190610e97903090600401612bdb565b602060405180830381865afa158015610eb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed89190612ee9565b90508015610f0a57610f0a7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea82612628565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190610f59903090600401612bdb565b602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190612ee9565b90508015610fcc57610fcc7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f82612628565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906370a0823190611049903090600401612bdb565b602060405180830381865afa158015611066573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108a9190612ee9565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906110fb907f000000000000000000000000b63cac384247597756545b500253ff8e607a8020908590600401612f54565b6020604051808303816000875af115801561111a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113e9190612eb1565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f000000000000000000000000b63cac384247597756545b500253ff8e607a80209091169063990966d5906084016020604051808303816000875af11580156111cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ef9190612ee9565b6040518363ffffffff1660e01b815260040161120c929190612f54565b600060405180830381600087803b15801561122657600080fd5b505af115801561123a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611277573360405163053e900f60e21b815260040161126e9190612bdb565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f772169063d42efd83906112e5903390600401612bdb565b602060405180830381865afa158015611302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113269190612eb1565b61134357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161136c575050604080518082019091526001600160d81b031983168152634aae164b60e01b602082015281519193509083906000906113cd576113cd61312d565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b0319168152508260018151811061141b5761141b61312d565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b031916815250826002815181106114695761146961312d565b6020026020010181905250604051806040016040528061148e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b6020909101528251839060039081106114bd576114bd61312d565b60200260200101819052505090565b808381146114ed57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b8581101561180c577f000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f7726001600160a01b031663d42efd838b8b8481811061153f5761153f61312d565b90506020020160208101906115549190612c91565b6040518263ffffffff1660e01b81526004016115709190612bdb565b602060405180830381865afa15801561158d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b19190612eb1565b6115ce57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106115e1576115e161312d565b90506020020160208101906115f69190612c91565b6001600160a01b031663504006ca8a8a858181106116165761161661312d565b905060200201356040518263ffffffff1660e01b815260040161163b91815260200190565b6101a060405180830381865afa158015611659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167d9190613047565b60a001516001600160a01b0316146116a857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d868181106116c0576116c061312d565b90506020020160208101906116d59190612c91565b6001600160a01b03166357e8a2f08d8d888181106116f5576116f561312d565b905060200201356040518263ffffffff1660e01b815260040161171a91815260200190565b6080604051808303816000875af1158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d9190613143565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006117958466b1a2bc2ec500006130d5565b61179f91906130f4565b9050600067016345785d8a000082106117c05767016345785d8a00006117c2565b815b905062093a8083106117dd576117d88189612f3c565b6117ff565b62093a806117eb84836130d5565b6117f591906130f4565b6117ff9089612f3c565b97505050505050506114f6565b50826005541161181d57600061182b565b8260055461182b9190613179565b600555600654841061183e57600061184c565b8360065461184c9190613179565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace5906118a2907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613190565b602060405180830381865afa1580156118bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e39190612ee9565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f888511611927576000611931565b6119318986613179565b6040518463ffffffff1660e01b815260040161194f93929190612f02565b600060405180830381600087803b15801561196957600080fd5b505af115801561197d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16925063a9059cbb91506119cf9033908690600401612f54565b6020604051808303816000875af11580156119ee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a129190612eb1565b50611a1b610ffa565b50505050505050505050565b6000689cd35474a7270c0000611a4583670de0b6b3a76400006130d5565b611a4f91906130f4565b92915050565b6003546000908190600160a01b900460ff16611a72576000611a7f565b6a0ee3a5f48a68b5520000005b9050426004541115611a9357600091505090565b62093a8060046000828254611aa89190612f3c565b90915550506040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190611afc903090600401612bdb565b602060405180830381865afa158015611b19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3d9190612ee9565b90508015611b4e57611b4e81612954565b60405163ce96cb7760e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea169063ce96cb7790611b9d903090600401612bdb565b602060405180830381865afa158015611bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bde9190612ee9565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611c36907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613190565b602060405180830381865afa158015611c53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c779190612ee9565b905083821015611ee6576000611c8d8386613179565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f611ccd8587612f3c565b6040518463ffffffff1660e01b8152600401611ceb93929190612f02565b600060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa158015611d85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da99190612ee9565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611dfe9030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612f02565b600060405180830381600087803b158015611e1857600080fd5b505af1158015611e2c573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611e849030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612f02565b600060405180830381600087803b158015611e9e57600080fd5b505af1158015611eb2573d6000803e3d6000fd5b50506040805160008152602081018690526000805160206131c0833981519152935001905060405180910390a150506120e8565b838211156120e8576000611efa8584613179565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f848611611f3e576000611f48565b611f488587613179565b6040518463ffffffff1660e01b8152600401611f6693929190612f02565b600060405180830381600087803b158015611f8057600080fd5b505af1158015611f94573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa158015612000573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120249190612ee9565b60025460405163a9059cbb60e01b81529192506001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81169263a9059cbb926120799216908590600401612f54565b6020604051808303816000875af1158015612098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bc9190612eb1565b506040805160018152602081018490526000805160206131c0833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b816000815181106121315761213161312d565b6001600160d81b0319909216602092830291909101909101526121596426a4a72a2960d91b90565b8160018151811061216c5761216c61312d565b6001600160d81b03199092166020928302919091019091015261219464524f4c455360d81b90565b816002815181106121a7576121a761312d565b6001600160d81b0319909216602092830291909101909101526121d76121d264545253525960d81b90565b612a9b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122086426a4a72a2960d91b612a9b565b600380546001600160a01b0319166001600160a01b039290921691909117905561223964524f4c455360d81b612a9b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d583169263095ea7b3926122a29291169060001990600401612f54565b6020604051808303816000875af11580156122c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e59190612eb1565b5090565b6000600554600654610ba69190612f3c565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061233f9084903390600401613116565b600060405180830381600087803b15801561235957600080fd5b505af115801561236d573d6000803e3d6000fd5b505050507f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b0316036123c3576040516323286f0960e01b815260040160405180910390fd5b6123cd8383612628565b505050565b60405163d42efd8360e01b81526001600160a01b037f000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f772169063d42efd839061241e903390600401612bdb565b602060405180830381865afa15801561243b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061245f9190612eb1565b61247c57604051636f2c0c0160e01b815260040160405180910390fd5b6123cd838383612b38565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a08231906124d6903090600401612bdb565b602060405180830381865afa1580156124f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125179190612ee9565b905061252281612954565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c5906125699084903390600401613116565b600060405180830381600087803b15801561258357600080fd5b505af1158015612597573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806125f7846611c37937e080006130d5565b61260191906130f4565b9050670de0b6b3a764000061261682866130d5565b61262091906130f4565b949350505050565b7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316826001600160a01b0316148061269957507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316826001600160a01b0316145b1561289f576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace5906126f1907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613190565b602060405180830381865afa15801561270e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127329190612ee9565b905060007f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316846001600160a01b03161461277557826127fe565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b031690634cdad50690602401602060405180830381865afa1580156127da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127fe9190612ee9565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f84861161284257600061284c565b61284c8587613179565b6040518463ffffffff1660e01b815260040161286a93929190612f02565b600060405180830381600087803b15801561288457600080fd5b505af1158015612898573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb926128d392909116908590600401612f54565b6020604051808303816000875af11580156128f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129169190612eb1565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612948929190612f54565b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b3906129c2907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908590600401612f54565b6020604051808303816000875af11580156129e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a059190612eb1565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea1690636e553f6590612a549084903090600401613116565b6020604051808303816000875af1158015612a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a979190612ee9565b5050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612acc9086906004016131aa565b602060405180830381865afa158015612ae9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0d9190612ecc565b90506001600160a01b038116611a4f5782604051635c3fa9cd60e01b815260040161126e91906131aa565b600354600160a01b900460ff1615612b6157612b5c612b578284612f3c565b612954565b612b94565b612b947f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f612b8f8385612f3c565b612628565b8060055411612ba4576000612bb2565b80600554612bb29190613179565b6005556006548210612bc5576000612bd3565b81600654612bd39190613179565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b038116811461252257600080fd5b60008060408385031215612c1757600080fd5b8235612c2281612bef565b946020939093013593505050565b600080600060608486031215612c4557600080fd5b8335612c5081612bef565b925060208401359150604084013560ff81168114612c6d57600080fd5b809150509250925092565b600060208284031215612c8a57600080fd5b5035919050565b600060208284031215612ca357600080fd5b8135612cae81612bef565b9392505050565b60008060008060808587031215612ccb57600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612d3d57815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612d04565b5091979650505050505050565b60008083601f840112612d5c57600080fd5b5081356001600160401b03811115612d7357600080fd5b6020830191508360208260051b8501011115612d8e57600080fd5b9250929050565b60008060008060408587031215612dab57600080fd5b84356001600160401b0380821115612dc257600080fd5b612dce88838901612d4a565b90965094506020870135915080821115612de757600080fd5b50612df487828801612d4a565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612e425783516001600160d81b03191683529284019291840191600101612e1c565b50909695505050505050565b600080600060608486031215612e6357600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612e8d57600080fd5b50508035926020909101359150565b80518015158114612eac57600080fd5b919050565b600060208284031215612ec357600080fd5b612cae82612e9c565b600060208284031215612ede57600080fd5b8151612cae81612bef565b600060208284031215612efb57600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b60008219821115612f4f57612f4f612f26565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b0381118282101715612f9e57634e487b7160e01b600052604160045260246000fd5b60405290565b8051612eac81612bef565b600060c08284031215612fc157600080fd5b60405160c081016001600160401b0381118282101715612ff157634e487b7160e01b600052604160045260246000fd5b80604052508091508251815260208301516020820152604083015160408201526060830151606082015261302760808401612e9c565b608082015260a083015161303a81612bef565b60a0919091015292915050565b60006101a0828403121561305a57600080fd5b613062612f6d565b61306c8484612faf565b815260c0830151602082015260e08301516040820152610100830151606082015261012083015160808201526130a56101408401612fa4565b60a08201526130b76101608401612fa4565b60c08201526130c96101808401612e9c565b60e08201529392505050565b60008160001904831182151516156130ef576130ef612f26565b500290565b60008261311157634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561315957600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561318b5761318b612f26565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220e87ac8ebbd1bd58f4f830b3317030820a71105f2a771ab356ec82a0dd0d3674764736f6c634300080f0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f7720000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
-----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): 0xDE3e735d37A8498AD2F141F603A6d0F976A6F772
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] : 000000000000000000000000de3e735d37a8498ad2f141f603a6d0f976a6f772
Arg [5] : 0000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $1 | 503.487 | $503.49 |
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.