Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61014060 | 15652049 | 668 days ago | IN | 0 ETH | 0.03810915 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RibbonTreasuryVault
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {Vault} from "../../libraries/Vault.sol"; import { VaultLifecycleTreasury } from "../../libraries/VaultLifecycleTreasury.sol"; import { RibbonTreasuryVaultStorage } from "../../storage/RibbonTreasuryVaultStorage.sol"; import {ShareMath} from "../../libraries/ShareMath.sol"; import {IWETH} from "../../interfaces/IWETH.sol"; import {GnosisAuction} from "../../libraries/GnosisAuction.sol"; import {IERC20Detailed} from "../../interfaces/IERC20Detailed.sol"; contract RibbonTreasuryVault is ReentrancyGuardUpgradeable, OwnableUpgradeable, ERC20Upgradeable, RibbonTreasuryVaultStorage { using SafeERC20 for IERC20; using SafeMath for uint256; using ShareMath for Vault.DepositReceipt; /************************************************ * IMMUTABLES & CONSTANTS ***********************************************/ /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 address public immutable WETH; /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 address public immutable USDC; /// @notice 15 minute timelock between commitAndClose and rollToNexOption. uint256 public constant DELAY = 0; // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857 // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR) uint256 private constant WEEKS_PER_YEAR = 52142857; // GAMMA_CONTROLLER is the top-level contract in Gamma protocol // which allows users to perform multiple actions on their vaults // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol address public immutable GAMMA_CONTROLLER; // MARGIN_POOL is Gamma protocol's collateral pool. // Needed to approve collateral.safeTransferFrom for minting otokens. // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol address public immutable MARGIN_POOL; // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol address public immutable GNOSIS_EASY_AUCTION; /// OTOKEN_FACTORY is the factory contract used to spawn otokens. Used to lookup otokens. address public immutable OTOKEN_FACTORY; // The minimum duration for an option auction. uint256 private constant MIN_AUCTION_DURATION = 5 minutes; // The minimum amount above which premium distribution will occur during commitAndClose uint256 private constant MIN_DUST_AMOUNT = 10000000; /************************************************ * EVENTS ***********************************************/ event Deposit(address indexed account, uint256 amount, uint256 round); event InitiateWithdraw( address indexed account, uint256 shares, uint256 round ); event Redeem(address indexed account, uint256 share, uint256 round); event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee); event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee); event CapSet(uint256 oldCap, uint256 newCap); event Withdraw(address indexed account, uint256 amount, uint256 shares); event CollectManagementFee( uint256 managementFee, uint256 round, address indexed feeRecipient ); event CollectPerformanceFee( uint256 performanceFee, uint256 round, address indexed feeRecipient ); event DistributePremium( uint256 amount, uint256[] amounts, address[] recipients, uint256 round ); event OpenShort( address indexed options, uint256 depositAmount, address indexed manager ); event CloseShort( address indexed options, uint256 withdrawAmount, address indexed manager ); event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta); event PremiumDiscountSet( uint256 premiumDiscount, uint256 newPremiumDiscount ); event AuctionDurationSet( uint256 auctionDuration, uint256 newAuctionDuration ); event InstantWithdraw( address indexed account, uint256 amount, uint256 round ); event InitiateGnosisAuction( address indexed auctioningToken, address indexed biddingToken, uint256 auctionCounter, address indexed manager ); /************************************************ * CONSTRUCTOR & INITIALIZATION ***********************************************/ /** * @notice Initializes the contract with immutable variables * @param _weth is the Wrapped Ether contract * @param _usdc is the USDC contract * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry) * @param _gammaController is the contract address for opyn actions * @param _marginPool is the contract address for providing collateral to opyn * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions */ constructor( address _weth, address _usdc, address _oTokenFactory, address _gammaController, address _marginPool, address _gnosisEasyAuction ) { require(_weth != address(0), "!_weth"); require(_usdc != address(0), "!_usdc"); require(_oTokenFactory != address(0), "!_oTokenFactory"); require(_gammaController != address(0), "!_gammaController"); require(_marginPool != address(0), "!_marginPool"); require(_gnosisEasyAuction != address(0), "!_gnosisEasyAuction"); WETH = _weth; USDC = _usdc; OTOKEN_FACTORY = _oTokenFactory; GAMMA_CONTROLLER = _gammaController; MARGIN_POOL = _marginPool; GNOSIS_EASY_AUCTION = _gnosisEasyAuction; } /** * @notice Initializes the OptionVault contract with storage variables. */ function initialize( VaultLifecycleTreasury.InitParams calldata _initParams, Vault.VaultParams calldata _vaultParams ) external initializer { VaultLifecycleTreasury.verifyInitializerParams( _initParams, _vaultParams, MIN_AUCTION_DURATION ); __ReentrancyGuard_init(); __ERC20_init(_initParams._tokenName, _initParams._tokenSymbol); __Ownable_init(); transferOwnership(_initParams._owner); keeper = _initParams._keeper; period = _initParams._period; optionsPremiumPricer = _initParams._optionsPremiumPricer; strikeSelection = _initParams._strikeSelection; premiumDiscount = _initParams._premiumDiscount; auctionDuration = _initParams._auctionDuration; feeRecipient = _initParams._feeRecipient; performanceFee = _initParams._performanceFee; managementFee = _perRoundManagementFee(_initParams._managementFee); maxDepositors = _initParams._maxDepositors; minDeposit = _initParams._minDeposit; vaultParams = _vaultParams; vaultState.round = 1; uint256 assetBalance = IERC20(vaultParams.asset).balanceOf(address(this)); ShareMath.assertUint104(assetBalance); vaultState.lastLockedAmount = uint104(assetBalance); } /** * @dev Throws if called by any account other than the keeper. */ modifier onlyKeeper() { require(msg.sender == keeper, "!keeper"); _; } /************************************************ * SETTERS ***********************************************/ /** * @notice Sets the new keeper * @param newKeeper is the address of the new keeper */ function setNewKeeper(address newKeeper) external onlyOwner { require(newKeeper != address(0), "!newKeeper"); keeper = newKeeper; } /** * @notice Sets the new fee recipient * @param newFeeRecipient is the address of the new fee recipient */ function setFeeRecipient(address newFeeRecipient) external onlyOwner { require(newFeeRecipient != address(0), "!newFeeRecipient"); require(newFeeRecipient != feeRecipient, "Must be new feeRecipient"); feeRecipient = newFeeRecipient; } /** * @notice Sets the management fee for the vault * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2% */ function setManagementFee(uint256 newManagementFee) external onlyOwner { require( newManagementFee < 100 * Vault.FEE_MULTIPLIER, "Invalid management fee" ); managementFee = _perRoundManagementFee(newManagementFee); emit ManagementFeeSet(managementFee, newManagementFee); } /** * @notice Internal function to set the management fee for the vault * @param managementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2 * @return perRoundManagementFee is the management divided by the number of rounds per year */ function _perRoundManagementFee(uint256 managementFee) internal view returns (uint256) { uint256 _period = period; uint256 feeDivider = _period % 30 == 0 ? Vault.FEE_MULTIPLIER * (12 / (_period / 30)) : WEEKS_PER_YEAR / (_period / 7); // We are dividing annualized management fee by num weeks in a year return managementFee.mul(Vault.FEE_MULTIPLIER).div(feeDivider); } /** * @notice Sets the performance fee for the vault * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20% */ function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner { require( newPerformanceFee < 100 * Vault.FEE_MULTIPLIER, "Invalid performance fee" ); emit PerformanceFeeSet(performanceFee, newPerformanceFee); performanceFee = newPerformanceFee; } /** * @notice Sets a new cap for deposits * @param newCap is the new cap for deposits */ function setCap(uint256 newCap) external onlyOwner { require(newCap > 0, "!newCap"); ShareMath.assertUint104(newCap); emit CapSet(vaultParams.cap, newCap); vaultParams.cap = uint104(newCap); } /** * @notice Sets the new discount on premiums for options we are selling * @param newPremiumDiscount is the premium discount */ function setPremiumDiscount(uint256 newPremiumDiscount) external onlyOwner { require( newPremiumDiscount > 0 && newPremiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER, "Invalid discount" ); emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount); premiumDiscount = newPremiumDiscount; } /** * @notice Sets the new auction duration * @param newAuctionDuration is the auction duration */ function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner { require( newAuctionDuration >= MIN_AUCTION_DURATION, "Invalid auction duration" ); emit AuctionDurationSet(auctionDuration, newAuctionDuration); auctionDuration = newAuctionDuration; } /** * @notice Sets the new strike selection contract * @param newStrikeSelection is the address of the new strike selection contract */ function setStrikeSelection(address newStrikeSelection) external onlyOwner { require(newStrikeSelection != address(0), "!newStrikeSelection"); strikeSelection = newStrikeSelection; } /** * @notice Sets the new options premium pricer contract * @param newOptionsPremiumPricer is the address of the new strike selection contract */ function setOptionsPremiumPricer(address newOptionsPremiumPricer) external onlyOwner { require( newOptionsPremiumPricer != address(0), "!newOptionsPremiumPricer" ); optionsPremiumPricer = newOptionsPremiumPricer; } /** * @notice Optionality to set strike price manually * @param strikePrice is the strike price of the new oTokens (decimals = 8) */ function setStrikePrice(uint128 strikePrice) external onlyOwner nonReentrant { require(strikePrice > 0, "!strikePrice"); overriddenStrikePrice = strikePrice; lastStrikeOverrideRound = vaultState.round; } /** * @notice Set the maximum number of depositors * @param newMaxDepositors is the new cap for number of depositors */ function setMaxDepositors(uint256 newMaxDepositors) external onlyOwner nonReentrant { require(newMaxDepositors > 0, "!newMaxDepositors"); maxDepositors = newMaxDepositors; } /** * @notice Set the minimum deposit amount * @param newMinDeposit is the new minimum amount for deposit */ function setMinDeposit(uint256 newMinDeposit) external onlyOwner nonReentrant { require(newMinDeposit > 0, "!newMinDeposit"); minDeposit = newMinDeposit; } /************************************************ * DEPOSIT & WITHDRAWALS ***********************************************/ /** * @notice Internal function to add new depositor address * @param newDepositor is the address to include in the depositors list */ function _addDepositor(address newDepositor) internal { if (!depositorsMap[newDepositor]) { require(newDepositor != address(0), "Depositor address null"); require( (depositorsArray.length + 1) <= maxDepositors, "Number of depositors exceeds limit" ); depositorsMap[newDepositor] = true; depositorsArray.push(newDepositor); } } /** * @notice Remove addresses from depositors list * @param excludeDepositor is the address to exclude from the depositors list */ function _removeDepositor(address excludeDepositor) internal { address[] storage array = depositorsArray; uint256 arrayLength = array.length; require(depositorsMap[excludeDepositor], "Depositor does not exist"); depositorsMap[excludeDepositor] = false; for (uint256 i = 0; i < arrayLength - 1; i++) { if (excludeDepositor == array[i]) { (array[i], array[arrayLength - 1]) = ( array[arrayLength - 1], array[i] ); } } array.pop(); } /** * @notice Deposits the `asset` from msg.sender. * @param amount is the amount of `asset` to deposit */ function deposit(uint256 amount) external nonReentrant { require(amount > 0, "!amount"); _addDepositor(msg.sender); _depositFor(amount, msg.sender); // An approve() by the msg.sender is required beforehand IERC20(vaultParams.asset).safeTransferFrom( msg.sender, address(this), amount ); } /** * @notice Mints the vault shares to the creditor * @param amount is the amount of `asset` deposited * @param creditor is the address to receieve the deposit */ function _depositFor(uint256 amount, address creditor) private { uint256 currentRound = vaultState.round; uint256 totalWithDepositedAmount = totalBalance().add(amount); Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor]; uint256 totalUserDeposit = accountVaultBalance(msg.sender).add(depositReceipt.amount).add( amount ); require(totalWithDepositedAmount <= vaultParams.cap, "Exceed cap"); require( totalWithDepositedAmount >= vaultParams.minimumSupply, "Insufficient balance" ); require(totalUserDeposit >= minDeposit, "Minimum deposit not reached"); emit Deposit(creditor, amount, currentRound); // If we have an unprocessed pending deposit from the previous rounds, we have to process it. uint256 unredeemedShares = depositReceipt.getSharesFromReceipt( currentRound, roundPricePerShare[depositReceipt.round], vaultParams.decimals ); uint256 depositAmount = amount; // If we have a pending deposit in the current round, we add on to the pending deposit if (currentRound == depositReceipt.round) { uint256 newAmount = uint256(depositReceipt.amount).add(amount); depositAmount = newAmount; } ShareMath.assertUint104(depositAmount); depositReceipts[creditor] = Vault.DepositReceipt({ round: uint16(currentRound), amount: uint104(depositAmount), unredeemedShares: uint128(unredeemedShares) }); uint256 newTotalPending = uint256(vaultState.totalPending).add(amount); ShareMath.assertUint128(newTotalPending); vaultState.totalPending = uint128(newTotalPending); } /** * @notice Initiates a withdrawal that can be processed once the round completes * @param numShares is the number of shares to withdraw */ function initiateWithdraw(uint256 numShares) external nonReentrant { require(numShares > 0, "!numShares"); // We do a max redeem before initiating a withdrawal // But we check if they must first have unredeemed shares Vault.DepositReceipt storage depositReceipt = depositReceipts[msg.sender]; if (depositReceipt.amount > 0 || depositReceipt.unredeemedShares > 0) { _redeem(0, true); } // This caches the `round` variable used in shareBalances uint256 currentRound = vaultState.round; Vault.Withdrawal storage withdrawal = withdrawals[msg.sender]; uint256 withdrawalRound = withdrawal.round; bool withdrawalIsSameRound = withdrawalRound == currentRound; emit InitiateWithdraw(msg.sender, numShares, currentRound); uint256 existingShares = uint256(withdrawal.shares); uint256 withdrawalShares; if (withdrawalIsSameRound) { withdrawalShares = existingShares.add(numShares); } else { require(existingShares == 0, "Existing withdraw"); withdrawalShares = numShares; withdrawals[msg.sender].round = uint16(currentRound); } // Ensure withdrawal does not reduce user deposit below the minimum amount uint256 vaultDecimals = vaultParams.decimals; uint256 userBalance = accountVaultBalance(msg.sender); uint256 withdrawAmount = ShareMath.sharesToAsset( numShares, currentRound != 1 ? roundPricePerShare[currentRound - 1] : 10**vaultDecimals, vaultDecimals ); if (userBalance > withdrawAmount) { uint256 totalDeposit = userBalance.sub(withdrawAmount); require(totalDeposit >= minDeposit, "Minimum deposit not reached"); } ShareMath.assertUint128(withdrawalShares); withdrawals[msg.sender].shares = uint128(withdrawalShares); uint256 newQueuedWithdrawShares = uint256(vaultState.queuedWithdrawShares).add(numShares); ShareMath.assertUint128(newQueuedWithdrawShares); vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares); if (depositReceipt.amount == 0 && balanceOf(msg.sender) == numShares) { _removeDepositor(msg.sender); } _transfer(msg.sender, address(this), numShares); } /** * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round * @return withdrawAmount the current withdrawal amount */ function _completeWithdraw() internal returns (uint256) { Vault.Withdrawal storage withdrawal = withdrawals[msg.sender]; uint256 withdrawalShares = withdrawal.shares; uint256 withdrawalRound = withdrawal.round; // This checks if there is a withdrawal require(withdrawalShares > 0, "Not initiated"); require(withdrawalRound < vaultState.round, "Round not closed"); // We leave the round number as non-zero to save on gas for subsequent writes withdrawals[msg.sender].shares = 0; vaultState.queuedWithdrawShares = uint128( uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares) ); uint256 withdrawAmount = ShareMath.sharesToAsset( withdrawalShares, roundPricePerShare[withdrawalRound], vaultParams.decimals ); emit Withdraw(msg.sender, withdrawAmount, withdrawalShares); _burn(address(this), withdrawalShares); require(withdrawAmount > 0, "!withdrawAmount"); transferAsset(msg.sender, withdrawAmount); return withdrawAmount; } /** * @notice Redeems shares that are owed to the account * @param numShares is the number of shares to redeem */ function redeem(uint256 numShares) external nonReentrant { require(numShares > 0, "!numShares"); _redeem(numShares, false); } /** * @notice Redeems the entire unredeemedShares balance that is owed to the account */ function maxRedeem() external nonReentrant { _redeem(0, true); } /** * @notice Redeems shares that are owed to the account * @param numShares is the number of shares to redeem, could be 0 when isMax=true * @param isMax is flag for when callers do a max redemption */ function _redeem(uint256 numShares, bool isMax) internal { Vault.DepositReceipt memory depositReceipt = depositReceipts[msg.sender]; // This handles the null case when depositReceipt.round = 0 // Because we start with round = 1 at `initialize` uint256 currentRound = vaultState.round; uint256 unredeemedShares = depositReceipt.getSharesFromReceipt( currentRound, roundPricePerShare[depositReceipt.round], vaultParams.decimals ); numShares = isMax ? unredeemedShares : numShares; if (numShares == 0) { return; } require(numShares <= unredeemedShares, "Exceeds available"); // If we have a depositReceipt on the same round, BUT we have some unredeemed shares // we debit from the unredeemedShares, but leave the amount field intact // If the round has past, with no new deposits, we just zero it out for new deposits. if (depositReceipt.round < currentRound) { depositReceipts[msg.sender].amount = 0; } ShareMath.assertUint128(numShares); depositReceipts[msg.sender].unredeemedShares = uint128( unredeemedShares.sub(numShares) ); emit Redeem(msg.sender, numShares, depositReceipt.round); _transfer(address(this), msg.sender, numShares); } /** * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount` * @param amount is the amount to withdraw */ function withdrawInstantly(uint256 amount) external nonReentrant { Vault.DepositReceipt storage depositReceipt = depositReceipts[msg.sender]; uint256 currentRound = vaultState.round; require(amount > 0, "!amount"); require(depositReceipt.round == currentRound, "Invalid round"); uint256 receiptAmount = depositReceipt.amount; require(receiptAmount >= amount, "Exceed amount"); uint256 userBalance = accountVaultBalance(msg.sender).add(receiptAmount); if (userBalance > amount) { uint256 totalUserDeposit = userBalance.sub(amount); require( totalUserDeposit >= minDeposit, "Minimum deposit not reached" ); } // Subtraction underflow checks already ensure it is smaller than uint104 depositReceipt.amount = uint104(receiptAmount.sub(amount)); vaultState.totalPending = uint128( uint256(vaultState.totalPending).sub(amount) ); emit InstantWithdraw(msg.sender, amount, currentRound); if (depositReceipt.amount == 0 && shares(msg.sender) == 0) { _removeDepositor(msg.sender); } transferAsset(msg.sender, amount); } /** * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round */ function completeWithdraw() external nonReentrant { uint256 withdrawAmount = _completeWithdraw(); lastQueuedWithdrawAmount = uint128( uint256(lastQueuedWithdrawAmount).sub(withdrawAmount) ); } /************************************************ * VAULT OPERATIONS ***********************************************/ /* * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map. * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k. * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0. * @param numRounds is the number of rounds to initialize in the map */ function initRounds(uint256 numRounds) external nonReentrant { require(numRounds > 0, "!numRounds"); uint256 _round = vaultState.round; for (uint256 i = 0; i < numRounds; i++) { uint256 index = _round + i; require(roundPricePerShare[index] == 0, "Initialized"); // AVOID OVERWRITING ACTUAL VALUES roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT; } } /* * @notice Helper function that performs most administrative tasks * such as setting next option, minting new shares, getting vault fees, etc. * @param lastQueuedWithdrawAmount is old queued withdraw amount * @return newOption is the new option address * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size * @return queuedWithdrawAmount is the new queued withdraw amount for this round */ function _rollToNextOption(uint256 lastQueuedWithdrawAmount) internal returns ( address newOption, uint256 lockedBalance, uint256 queuedWithdrawAmount ) { require(block.timestamp >= optionState.nextOptionReadyAt, "!ready"); newOption = optionState.nextOption; require(newOption != address(0), "!nextOption"); uint256 currentRound = vaultState.round; address recipient = feeRecipient; uint256 mintShares; uint256 managementFeeInAsset; { uint256 newPricePerShare; ( lockedBalance, queuedWithdrawAmount, newPricePerShare, mintShares, managementFeeInAsset ) = VaultLifecycleTreasury.rollover( vaultState, VaultLifecycleTreasury.RolloverParams( vaultParams.decimals, IERC20(vaultParams.asset).balanceOf(address(this)), totalSupply(), lastQueuedWithdrawAmount, currentRound != 1 ? managementFee : 0 ) ); optionState.currentOption = newOption; optionState.nextOption = address(0); // Finalize the pricePerShare at the end of the round roundPricePerShare[currentRound] = newPricePerShare; emit CollectManagementFee( managementFeeInAsset, currentRound, recipient ); vaultState.totalPending = 0; vaultState.round = uint16(currentRound + 1); } _mint(address(this), mintShares); if (managementFeeInAsset > 0) { transferAsset(payable(recipient), managementFeeInAsset); } return (newOption, lockedBalance, queuedWithdrawAmount); } /** * @notice Helper function to make an ERC20 transfer * @param recipient is the receiving address * @param amount is the transfer amount */ function transferAsset(address recipient, uint256 amount) internal { address asset = vaultParams.asset; IERC20(asset).safeTransfer(recipient, amount); } /** * @notice Sets the next option the vault will be shorting, and closes the existing short. * This allows all the users to withdraw if the next option is malicious. */ function commitAndClose() external nonReentrant { address oldOption = optionState.currentOption; VaultLifecycleTreasury.CloseParams memory closeParams = VaultLifecycleTreasury.CloseParams({ OTOKEN_FACTORY: OTOKEN_FACTORY, USDC: USDC, currentOption: oldOption, delay: DELAY, lastStrikeOverrideRound: lastStrikeOverrideRound, overriddenStrikePrice: overriddenStrikePrice, period: period }); ( address otokenAddress, uint256 premium, uint256 strikePrice, uint256 delta ) = VaultLifecycleTreasury.commitAndClose( strikeSelection, optionsPremiumPricer, premiumDiscount, closeParams, vaultParams, vaultState ); emit NewOptionStrikeSelected(strikePrice, delta); ShareMath.assertUint104(premium); currentOtokenPremium = uint104(premium); optionState.nextOption = otokenAddress; uint256 nextOptionReady = block.timestamp.add(DELAY); require( nextOptionReady <= type(uint32).max, "Overflow nextOptionReady" ); optionState.nextOptionReadyAt = uint32(nextOptionReady); _closeShort(oldOption); // In case chargeAndDistribute was not called last round, call // the function to conclude last round's performance fee and distribution if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) { _chargeAndDistribute(); } } /** * @notice Closes the existing short position for the vault. */ function _closeShort(address oldOption) private { uint256 lockedAmount = vaultState.lockedAmount; if (oldOption != address(0)) { vaultState.lastLockedAmount = uint104(lockedAmount); } vaultState.lockedAmount = 0; optionState.currentOption = address(0); if (oldOption != address(0)) { uint256 withdrawAmount = VaultLifecycleTreasury.settleShort(GAMMA_CONTROLLER); emit CloseShort(oldOption, withdrawAmount, msg.sender); } } /** * @notice Rolls the vault's funds into a new short position. */ function rollToNextOption() external onlyKeeper nonReentrant { ( address newOption, uint256 lockedBalance, uint256 queuedWithdrawAmount ) = _rollToNextOption(uint256(lastQueuedWithdrawAmount)); lastQueuedWithdrawAmount = queuedWithdrawAmount; ShareMath.assertUint104(lockedBalance); vaultState.lockedAmount = uint104(lockedBalance); emit OpenShort(newOption, lockedBalance, msg.sender); VaultLifecycleTreasury.createShort( GAMMA_CONTROLLER, MARGIN_POOL, newOption, lockedBalance ); _startAuction(); } /** * @notice Initiate the gnosis auction. */ function startAuction() external onlyKeeper nonReentrant { _startAuction(); } function _startAuction() private { GnosisAuction.AuctionDetails memory auctionDetails; uint256 currOtokenPremium = currentOtokenPremium; require(currOtokenPremium > 0, "!currentOtokenPremium"); uint256 stableDecimals = IERC20Detailed(USDC).decimals(); auctionDetails.oTokenAddress = optionState.currentOption; auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION; auctionDetails.asset = USDC; auctionDetails.assetDecimals = stableDecimals; auctionDetails.oTokenPremium = currOtokenPremium; auctionDetails.duration = auctionDuration; optionAuctionID = VaultLifecycleTreasury.startAuction(auctionDetails); } /** * @notice Burn the remaining oTokens left over from gnosis auction. */ function burnRemainingOTokens() external onlyKeeper nonReentrant { uint256 unlockedAssetAmount = VaultLifecycleTreasury.burnOtokens( GAMMA_CONTROLLER, optionState.currentOption ); vaultState.lockedAmount = uint104( uint256(vaultState.lockedAmount).sub(unlockedAssetAmount) ); } /** * @notice Settles the round's Gnosis auction and distribute the premiums earned */ function concludeOptionsSale() external onlyKeeper nonReentrant { VaultLifecycleTreasury.settleAuction( GNOSIS_EASY_AUCTION, optionAuctionID ); if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) { _chargeAndDistribute(); } } /** * @notice Charge performance fee and distribute remaining to depositors addresses */ function chargeAndDistribute() external onlyKeeper nonReentrant { _chargeAndDistribute(); } /** * @notice Calculate performance fee and transfer to fee recipient */ function _chargeAndDistribute() internal { IERC20 stableAsset = IERC20(USDC); uint256 stableBalance = stableAsset.balanceOf(address(this)); require(stableBalance > 0, "no premium to distribute"); _chargePerformanceFee(stableAsset, stableBalance); _distributePremium( stableAsset, stableAsset.balanceOf(address(this)) // Get the new balance ); } /** * @notice Charge performance fee */ function _chargePerformanceFee(IERC20 token, uint256 amount) internal { address recipient = feeRecipient; uint256 transferAmount = amount.mul(performanceFee).div(100 * Vault.FEE_MULTIPLIER); token.safeTransfer(recipient, transferAmount); // Performance fee for the round is charged after rollover // hence we need to adjust the round to the previous emit CollectPerformanceFee( transferAmount, vaultState.round - 1, recipient ); } /** * @notice Distribute the premium to depositor addresses */ function _distributePremium(IERC20 token, uint256 amount) internal { // Distribute to depositor address address[] storage _depositors = depositorsArray; uint256[] memory _amounts = new uint256[](_depositors.length); uint256 totalSupply = totalSupply() - lastQueuedWithdrawAmount; for (uint256 i = 0; i < _depositors.length; i++) { // Distribute to depositors proportional to the amount of // shares they own address depositorAddress = _depositors[i]; _amounts[i] = shares(depositorAddress).mul(amount).div(totalSupply); token.safeTransfer(depositorAddress, _amounts[i]); } emit DistributePremium( amount, _amounts, _depositors, vaultState.round - 1 ); } function setCurrentOtokenPremium(uint newOtokenPremium) public onlyOwner { currentOtokenPremium = newOtokenPremium; } /************************************************ * GETTERS ***********************************************/ /** * @notice Returns the asset balance held on the vault for the account * @param account is the address to lookup balance for * @return the amount of `asset` custodied by the vault for the user */ function accountVaultBalance(address account) public view returns (uint256) { uint256 _decimals = vaultParams.decimals; uint256 assetPerShare = ShareMath.pricePerShare( totalSupply(), totalBalance(), vaultState.totalPending, _decimals ); return ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals); } /** * @notice Getter for returning the account's share balance including unredeemed shares * @param account is the account to lookup share balance for * @return the share balance */ function shares(address account) public view returns (uint256) { (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account); return heldByAccount.add(heldByVault); } /** * @notice Getter for returning the account's share balance split between account and vault holdings * @param account is the account to lookup share balance for * @return heldByAccount is the shares held by account * @return heldByVault is the shares held on the vault (unredeemedShares) */ function shareBalances(address account) public view returns (uint256 heldByAccount, uint256 heldByVault) { Vault.DepositReceipt memory depositReceipt = depositReceipts[account]; if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) { return (balanceOf(account), 0); } uint256 unredeemedShares = depositReceipt.getSharesFromReceipt( vaultState.round, roundPricePerShare[depositReceipt.round], vaultParams.decimals ); return (balanceOf(account), unredeemedShares); } /** * @notice The price of a unit of share denominated in the `asset` */ function pricePerShare() external view returns (uint256) { return ShareMath.pricePerShare( totalSupply(), totalBalance(), vaultState.totalPending, vaultParams.decimals ); } /** * @notice Returns the vault's total balance, including the amounts locked into a short position * @return total balance of the vault, including the amounts locked in third party protocols */ function totalBalance() public view returns (uint256) { return uint256(vaultState.lockedAmount).add( IERC20(vaultParams.asset).balanceOf(address(this)) ); } /** * @notice Returns the token decimals */ function decimals() public view override returns (uint8) { return vaultParams.decimals; } /** * @notice Returns the maximum capacity of the vault in terms of the vault's asset */ function cap() external view returns (uint256) { return vaultParams.cap; } /** * @notice Returns the date and time for the next options sale */ function nextOptionReadyAt() external view returns (uint256) { return optionState.nextOptionReadyAt; } /** * @notice Returns the options specification for the current round */ function currentOption() external view returns (address) { return optionState.currentOption; } /** * @notice Returns the options specification for the next round */ function nextOption() external view returns (address) { return optionState.nextOption; } /** * @notice Returns total pending deposit for the current round */ function totalPending() external view returns (uint256) { return vaultState.totalPending; } /** * @notice ERC20 _transfer override function */ function _transfer( address sender, address recipient, uint256 amount ) internal override { require( recipient == address(this) || sender == address(this), "Treasury rToken is not transferrable" ); return ERC20Upgradeable._transfer(sender, recipient, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IERC20Upgradeable.sol"; import "./extensions/IERC20MetadataUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal initializer { __Context_init_unchained(); __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { uint256 currentAllowance = _allowances[_msgSender()][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(_msgSender(), spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ 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); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; } _balances[recipient] += amount; emit Transfer(sender, recipient, amount); _afterTokenTransfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ 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); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} uint256[45] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; library Vault { /************************************************ * IMMUTABLES & CONSTANTS ***********************************************/ // Fees are 6-decimal places. For example: 20 * 10**6 = 20% uint256 internal constant FEE_MULTIPLIER = 10**6; // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount. uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10; // Otokens have 8 decimal places. uint256 internal constant OTOKEN_DECIMALS = 8; // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10% uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2; // Placeholder uint value to prevent cold writes uint256 internal constant PLACEHOLDER_UINT = 1; struct VaultParams { // Option type the vault is selling bool isPut; // Token decimals for vault shares uint8 decimals; // Asset used in Theta / Delta Vault address asset; // Underlying asset of the options sold by vault address underlying; // Minimum supply of the vault shares issued, for ETH it's 10**10 uint56 minimumSupply; // Vault cap uint104 cap; } struct OptionState { // Option that the vault is shorting / longing in the next cycle address nextOption; // Option that the vault is currently shorting / longing address currentOption; // The timestamp when the `nextOption` can be used by the vault uint32 nextOptionReadyAt; } struct VaultState { // 32 byte slot 1 // Current round number. `round` represents the number of `period`s elapsed. uint16 round; // Amount that is currently locked for selling options uint104 lockedAmount; // Amount that was locked for selling options in the previous round // used for calculating performance fee deduction uint104 lastLockedAmount; // 32 byte slot 2 // Stores the total tally of how much of `asset` there is // to be used to mint rTHETA tokens uint128 totalPending; // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round) uint128 queuedWithdrawShares; } struct DepositReceipt { // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years. uint16 round; // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit uint104 amount; // Unredeemed shares balance uint128 unredeemedShares; } struct Withdrawal { // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years. uint16 round; // Number of shares withdrawn uint128 shares; } struct AuctionSellOrder { // Amount of `asset` token offered in auction uint96 sellAmount; // Amount of oToken requested in auction uint96 buyAmount; // User Id of delta vault in latest gnosis auction uint64 userId; } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Vault} from "./Vault.sol"; import {ShareMath} from "./ShareMath.sol"; import {IStrikeSelection} from "../interfaces/IRibbon.sol"; import {GnosisAuction} from "./GnosisAuction.sol"; import {DateTime} from "./DateTime.sol"; import { IOtokenFactory, IOtoken, IController, GammaTypes } from "../interfaces/GammaInterface.sol"; import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol"; import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol"; import {SupportsNonCompliantERC20} from "./SupportsNonCompliantERC20.sol"; library VaultLifecycleTreasury { using SafeMath for uint256; using SupportsNonCompliantERC20 for IERC20; struct CloseParams { address OTOKEN_FACTORY; address USDC; address currentOption; uint256 delay; uint16 lastStrikeOverrideRound; uint256 overriddenStrikePrice; uint256 period; } /** * @notice Initialization parameters for the vault. * @param _owner is the owner of the vault with critical permissions * @param _feeRecipient is the address to recieve vault performance and management fees * @param _managementFee is the management fee pct. * @param _performanceFee is the perfomance fee pct. * @param _tokenName is the name of the token * @param _tokenSymbol is the symbol of the token * @param _optionsPremiumPricer is the address of the contract with the black-scholes premium calculation logic * @param _strikeSelection is the address of the contract with strike selection logic * @param _premiumDiscount is the vault's discount applied to the premium * @param _auctionDuration is the duration of the gnosis auction * @param _period is the period between each option sales */ struct InitParams { address _owner; address _keeper; address _feeRecipient; uint256 _managementFee; uint256 _performanceFee; string _tokenName; string _tokenSymbol; address _optionsPremiumPricer; address _strikeSelection; uint32 _premiumDiscount; uint256 _auctionDuration; uint256 _period; uint256 _maxDepositors; uint256 _minDeposit; } /** * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction * @param strikeSelection is the address of the contract with strike selection logic * @param optionsPremiumPricer is the address of the contract with the black-scholes premium calculation logic * @param premiumDiscount is the vault's discount applied to the premium * @param closeParams is the struct with details on previous option and strike selection details * @param vaultParams is the struct with vault general data * @param vaultState is the struct with vault accounting state * @return otokenAddress is the address of the new option * @return premium is the premium of the new option * @return strikePrice is the strike price of the new option * @return delta is the delta of the new option */ function commitAndClose( address strikeSelection, address optionsPremiumPricer, uint256 premiumDiscount, CloseParams calldata closeParams, Vault.VaultParams storage vaultParams, Vault.VaultState storage vaultState ) external returns ( address otokenAddress, uint256 premium, uint256 strikePrice, uint256 delta ) { uint256 expiry; // uninitialized state if (closeParams.currentOption == address(0)) { expiry = getNextExpiry(block.timestamp, closeParams.period); } else { expiry = getNextExpiry( IOtoken(closeParams.currentOption).expiryTimestamp(), closeParams.period ); } IStrikeSelection selection = IStrikeSelection(strikeSelection); bool isPut = vaultParams.isPut; address underlying = vaultParams.underlying; address asset = vaultParams.asset; (strikePrice, delta) = closeParams.lastStrikeOverrideRound == vaultState.round ? (closeParams.overriddenStrikePrice, 0) : selection.getStrikePrice(expiry, isPut); require(strikePrice != 0, "!strikePrice"); // retrieve address if option already exists, or deploy it otokenAddress = getOrDeployOtoken( closeParams, vaultParams, underlying, asset, strikePrice, expiry, isPut ); // get the black scholes premium of the option premium = GnosisAuction.getOTokenPremiumInStables( otokenAddress, optionsPremiumPricer, premiumDiscount ); require(premium > 0, "!premium"); return (otokenAddress, premium, strikePrice, delta); } /** * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes * @param otokenAddress is the address of the otoken * @param vaultParams is the struct with vault general data * @param collateralAsset is the address of the collateral asset * @param USDC is the address of usdc * @param delay is the delay between commitAndClose and rollToNextOption */ function verifyOtoken( address otokenAddress, Vault.VaultParams storage vaultParams, address collateralAsset, address USDC, uint256 delay ) private view { require(otokenAddress != address(0), "!otokenAddress"); IOtoken otoken = IOtoken(otokenAddress); require(otoken.isPut() == vaultParams.isPut, "Type mismatch"); require( otoken.underlyingAsset() == vaultParams.underlying, "Wrong underlyingAsset" ); require( otoken.collateralAsset() == collateralAsset, "Wrong collateralAsset" ); // we just assume all options use USDC as the strike require(otoken.strikeAsset() == USDC, "strikeAsset != USDC"); uint256 readyAt = block.timestamp.add(delay); require(otoken.expiryTimestamp() >= readyAt, "Expiry before delay"); } /** * @param currentShareSupply is the supply of the shares invoked with totalSupply() * @param asset is the address of the vault's asset * @param decimals is the decimals of the asset * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round * @param managementFee is the management fee percent to charge on the AUM */ struct RolloverParams { uint256 decimals; uint256 totalBalance; uint256 currentShareSupply; uint256 lastQueuedWithdrawAmount; uint256 managementFee; } /** * @notice Calculate the shares to mint, new price per share, and amount of funds to re-allocate as collateral for the new round * @param vaultState is the storage variable vaultState passed from RibbonVault * @param params is the rollover parameters passed to compute the next state * @return newLockedAmount is the amount of funds to allocate for the new round * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal * @return newPricePerShare is the price per share of the new round * @return mintShares is the amount of shares to mint from deposits * @return managementFeeInAsset is the amount of management fee charged by vault */ function rollover( Vault.VaultState storage vaultState, RolloverParams calldata params ) external view returns ( uint256 newLockedAmount, uint256 queuedWithdrawAmount, uint256 newPricePerShare, uint256 mintShares, uint256 managementFeeInAsset ) { uint256 currentBalance = params.totalBalance; uint256 pendingAmount = vaultState.totalPending; uint256 queuedWithdrawShares = vaultState.queuedWithdrawShares; uint256 balanceForVaultFees; { uint256 pricePerShareBeforeFee = ShareMath.pricePerShare( params.currentShareSupply, currentBalance, pendingAmount, params.decimals ); uint256 queuedWithdrawBeforeFee = params.currentShareSupply > 0 ? ShareMath.sharesToAsset( queuedWithdrawShares, pricePerShareBeforeFee, params.decimals ) : 0; // Deduct the difference between the newly scheduled withdrawals // and the older withdrawals // so we can charge them fees before they leave uint256 withdrawAmountDiff = queuedWithdrawBeforeFee > params.lastQueuedWithdrawAmount ? queuedWithdrawBeforeFee.sub( params.lastQueuedWithdrawAmount ) : 0; balanceForVaultFees = currentBalance .sub(queuedWithdrawBeforeFee) .add(withdrawAmountDiff); } managementFeeInAsset = getManagementFee( balanceForVaultFees, vaultState.totalPending, params.managementFee ); // Take into account the fee // so we can calculate the newPricePerShare currentBalance = currentBalance.sub(managementFeeInAsset); { newPricePerShare = ShareMath.pricePerShare( params.currentShareSupply, currentBalance, pendingAmount, params.decimals ); // After closing the short, if the options expire in-the-money // vault pricePerShare would go down because vault's asset balance decreased. // This ensures that the newly-minted shares do not take on the loss. mintShares = ShareMath.assetToShares( pendingAmount, newPricePerShare, params.decimals ); uint256 newSupply = params.currentShareSupply.add(mintShares); queuedWithdrawAmount = newSupply > 0 ? ShareMath.sharesToAsset( queuedWithdrawShares, newPricePerShare, params.decimals ) : 0; } return ( currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals queuedWithdrawAmount, newPricePerShare, mintShares, managementFeeInAsset ); } /** * @notice Creates the actual Opyn short position by depositing collateral and minting otokens * @param gammaController is the address of the opyn controller contract * @param marginPool is the address of the opyn margin contract which holds the collateral * @param oTokenAddress is the address of the otoken to mint * @param depositAmount is the amount of collateral to deposit * @return the otoken mint amount */ function createShort( address gammaController, address marginPool, address oTokenAddress, uint256 depositAmount ) external returns (uint256) { IController controller = IController(gammaController); uint256 newVaultID = (controller.getAccountVaultCounter(address(this))).add(1); // An otoken's collateralAsset is the vault's `asset` // So in the context of performing Opyn short operations we call them collateralAsset IOtoken oToken = IOtoken(oTokenAddress); address collateralAsset = oToken.collateralAsset(); uint256 collateralDecimals = uint256(IERC20Detailed(collateralAsset).decimals()); uint256 mintAmount; if (oToken.isPut()) { // For minting puts, there will be instances where the full depositAmount will not be used for minting. // This is because of an issue with precision. // // For ETH put options, we are calculating the mintAmount (10**8 decimals) using // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down. // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens. // // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens. // We retain the dust in the vault so the calling contract can withdraw the // actual locked amount + dust at settlement. // // To test this behavior, we can console.log // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault) // to see how much dust (or excess collateral) is left behind. mintAmount = depositAmount .mul(10**Vault.OTOKEN_DECIMALS) .mul(10**18) // we use 10**18 to give extra precision .div(oToken.strikePrice().mul(10**(10 + collateralDecimals))); } else { mintAmount = depositAmount; if (collateralDecimals > 8) { uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals if (mintAmount > scaleBy) { mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8 } } } // double approve to fix non-compliant ERC20s IERC20 collateralToken = IERC20(collateralAsset); collateralToken.safeApproveNonCompliant(marginPool, depositAmount); IController.ActionArgs[] memory actions = new IController.ActionArgs[](3); actions[0] = IController.ActionArgs( IController.ActionType.OpenVault, address(this), // owner address(this), // receiver address(0), // asset, otoken newVaultID, // vaultId 0, // amount 0, //index "" //data ); actions[1] = IController.ActionArgs( IController.ActionType.DepositCollateral, address(this), // owner address(this), // address to transfer from collateralAsset, // deposited asset newVaultID, // vaultId depositAmount, // amount 0, //index "" //data ); actions[2] = IController.ActionArgs( IController.ActionType.MintShortOption, address(this), // owner address(this), // address to transfer to oTokenAddress, // option address newVaultID, // vaultId mintAmount, // amount 0, //index "" //data ); controller.operate(actions); return mintAmount; } /** * @notice Close the existing short otoken position. Currently this implementation is simple. * It closes the most recent vault opened by the contract. This assumes that the contract will * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by calling SettleVault action, this assumption should hold. * @param gammaController is the address of the opyn controller contract * @return amount of collateral redeemed from the vault */ function settleShort(address gammaController) external returns (uint256) { IController controller = IController(gammaController); // gets the currently active vault ID uint256 vaultID = controller.getAccountVaultCounter(address(this)); GammaTypes.Vault memory vault = controller.getVault(address(this), vaultID); require(vault.shortOtokens.length > 0, "No short"); // An otoken's collateralAsset is the vault's `asset` // So in the context of performing Opyn short operations we call them collateralAsset IERC20 collateralToken = IERC20(vault.collateralAssets[0]); // The short position has been previously closed, or all the otokens have been burned. // So we return early. if (address(collateralToken) == address(0)) { return 0; } // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this)) uint256 startCollateralBalance = collateralToken.balanceOf(address(this)); // If it is after expiry, we need to settle the short position using the normal way // Delete the vault and withdraw all remaining collateral from the vault IController.ActionArgs[] memory actions = new IController.ActionArgs[](1); actions[0] = IController.ActionArgs( IController.ActionType.SettleVault, address(this), // owner address(this), // address to transfer to address(0), // not used vaultID, // vaultId 0, // not used 0, // not used "" // not used ); controller.operate(actions); uint256 endCollateralBalance = collateralToken.balanceOf(address(this)); return endCollateralBalance.sub(startCollateralBalance); } /** * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple. * It calls the `Redeem` action to claim the payout. * @param gammaController is the address of the opyn controller contract * @param oldOption is the address of the old option * @param asset is the address of the vault's asset * @return amount of asset received by exercising the option */ function settleLong( address gammaController, address oldOption, address asset ) external returns (uint256) { IController controller = IController(gammaController); uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this)); if (controller.getPayout(oldOption, oldOptionBalance) == 0) { return 0; } uint256 startAssetBalance = IERC20(asset).balanceOf(address(this)); // If it is after expiry, we need to redeem the profits IController.ActionArgs[] memory actions = new IController.ActionArgs[](1); actions[0] = IController.ActionArgs( IController.ActionType.Redeem, address(0), // not used address(this), // address to send profits to oldOption, // address of otoken 0, // not used oldOptionBalance, // otoken balance 0, // not used "" // not used ); controller.operate(actions); uint256 endAssetBalance = IERC20(asset).balanceOf(address(this)); return endAssetBalance.sub(startAssetBalance); } /** * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple. * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will * only have a single vault open at any given time. * @param gammaController is the address of the opyn controller contract * @param currentOption is the address of the current option * @return amount of collateral redeemed by burning otokens */ function burnOtokens(address gammaController, address currentOption) external returns (uint256) { uint256 numOTokensToBurn = IERC20(currentOption).balanceOf(address(this)); require(numOTokensToBurn > 0, "No oTokens to burn"); IController controller = IController(gammaController); // gets the currently active vault ID uint256 vaultID = controller.getAccountVaultCounter(address(this)); GammaTypes.Vault memory vault = controller.getVault(address(this), vaultID); require(vault.shortOtokens.length > 0, "No short"); IERC20 collateralToken = IERC20(vault.collateralAssets[0]); uint256 startCollateralBalance = collateralToken.balanceOf(address(this)); // Burning `amount` of oTokens from the ribbon vault, // then withdrawing the corresponding collateral amount from the vault IController.ActionArgs[] memory actions = new IController.ActionArgs[](2); actions[0] = IController.ActionArgs( IController.ActionType.BurnShortOption, address(this), // owner address(this), // address to transfer from address(vault.shortOtokens[0]), // otoken address vaultID, // vaultId numOTokensToBurn, // amount 0, //index "" //data ); actions[1] = IController.ActionArgs( IController.ActionType.WithdrawCollateral, address(this), // owner address(this), // address to transfer to address(collateralToken), // withdrawn asset vaultID, // vaultId vault.collateralAmounts[0].mul(numOTokensToBurn).div( vault.shortAmounts[0] ), // amount 0, //index "" //data ); controller.operate(actions); uint256 endCollateralBalance = collateralToken.balanceOf(address(this)); return endCollateralBalance.sub(startCollateralBalance); } /** * @notice Calculates the management fee for this week's round * @param currentBalance is the balance of funds held on the vault after closing short * @param pendingAmount is the pending deposit amount * @param managementFeePercent is the management fee pct. * @return managementFeeInAsset is the management fee */ function getManagementFee( uint256 currentBalance, uint256 pendingAmount, uint256 managementFeePercent ) internal pure returns (uint256 managementFeeInAsset) { // At the first round, currentBalance=0, pendingAmount>0 // so we just do not charge anything on the first round uint256 lockedBalanceSansPending = currentBalance > pendingAmount ? currentBalance.sub(pendingAmount) : 0; uint256 _managementFeeInAsset; // Always charge management fee regardless of whether the vault is // making a profit from the previous options sale _managementFeeInAsset = managementFeePercent > 0 ? lockedBalanceSansPending.mul(managementFeePercent).div( 100 * Vault.FEE_MULTIPLIER ) : 0; return _managementFeeInAsset; } /** * @notice Either retrieves the option token if it already exists, or deploy it * @param closeParams is the struct with details on previous option and strike selection details * @param vaultParams is the struct with vault general data * @param underlying is the address of the underlying asset of the option * @param collateralAsset is the address of the collateral asset of the option * @param strikePrice is the strike price of the option * @param expiry is the expiry timestamp of the option * @param isPut is whether the option is a put * @return the address of the option */ function getOrDeployOtoken( CloseParams calldata closeParams, Vault.VaultParams storage vaultParams, address underlying, address collateralAsset, uint256 strikePrice, uint256 expiry, bool isPut ) internal returns (address) { IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY); address otokenFromFactory = factory.getOtoken( underlying, closeParams.USDC, collateralAsset, strikePrice, expiry, isPut ); if (otokenFromFactory != address(0)) { return otokenFromFactory; } address otoken = factory.createOtoken( underlying, closeParams.USDC, collateralAsset, strikePrice, expiry, isPut ); verifyOtoken( otoken, vaultParams, collateralAsset, closeParams.USDC, closeParams.delay ); return otoken; } /** * @notice Starts the gnosis auction * @param auctionDetails is the struct with all the custom parameters of the auction * @return the auction id of the newly created auction */ function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails) external returns (uint256) { return GnosisAuction.startAuction(auctionDetails); } /** * @notice Settles the gnosis auction * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol * @param auctionID is the auction ID of the gnosis easy auction */ function settleAuction(address gnosisEasyAuction, uint256 auctionID) internal { IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID); } /** * @notice Places a bid in an auction * @param bidDetails is the struct with all the details of the bid including the auction's id and how much to bid */ function placeBid(GnosisAuction.BidDetails calldata bidDetails) external returns ( uint256 sellAmount, uint256 buyAmount, uint64 userId ) { return GnosisAuction.placeBid(bidDetails); } /** * @notice Claims the oTokens belonging to the vault * @param auctionSellOrder is the sell order of the bid * @param gnosisEasyAuction is the address of the gnosis auction contract holding custody to the funds * @param counterpartyThetaVault is the address of the counterparty theta vault of this delta vault */ function claimAuctionOtokens( Vault.AuctionSellOrder calldata auctionSellOrder, address gnosisEasyAuction, address counterpartyThetaVault ) external { GnosisAuction.claimAuctionOtokens( auctionSellOrder, gnosisEasyAuction, counterpartyThetaVault ); } /** * @notice Verify the constructor params satisfy requirements * @param _initParams is the initialization parameter including owner, keeper, etc. * @param _vaultParams is the struct with vault general data */ function verifyInitializerParams( InitParams calldata _initParams, Vault.VaultParams calldata _vaultParams, uint256 _min_auction_duration ) external pure { require(_initParams._owner != address(0), "!_owner"); require(_initParams._keeper != address(0), "!_keeper"); require(_initParams._feeRecipient != address(0), "!_feeRecipient"); require( _initParams._performanceFee < 100 * Vault.FEE_MULTIPLIER, "performanceFee >= 100%" ); require( _initParams._managementFee < 100 * Vault.FEE_MULTIPLIER, "managementFee >= 100%" ); require(bytes(_initParams._tokenName).length > 0, "!_tokenName"); require(bytes(_initParams._tokenSymbol).length > 0, "!_tokenSymbol"); require( (_initParams._period == 7) || (_initParams._period == 14) || (_initParams._period == 30) || (_initParams._period == 90) || (_initParams._period == 180), "!_period" ); require( _initParams._optionsPremiumPricer != address(0), "!_optionsPremiumPricer" ); require( _initParams._strikeSelection != address(0), "!_strikeSelection" ); require( _initParams._premiumDiscount > 0 && _initParams._premiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER, "!_premiumDiscount" ); require( _initParams._auctionDuration >= _min_auction_duration, "!_auctionDuration" ); require(_initParams._maxDepositors > 0, "!_maxDepositors"); require(_initParams._minDeposit > 0, "!_minDeposit"); require(_vaultParams.asset != address(0), "!asset"); require(_vaultParams.underlying != address(0), "!underlying"); require(_vaultParams.minimumSupply > 0, "!minimumSupply"); require(_vaultParams.cap > 0, "!cap"); require( _vaultParams.cap > _vaultParams.minimumSupply, "cap has to be higher than minimumSupply" ); } /** * @notice Gets the next options expiry timestamp, this function should be called when there is sufficient guard to ensure valid period * @param timestamp is the expiry timestamp of the current option * @param period is no. of days in between option sales. Available periods are: * 7(1w), 14(2w), 30(1m), 90(3m), 180(6m) */ function getNextExpiry(uint256 timestamp, uint256 period) internal pure returns (uint256 nextExpiry) { if (period == 7) { nextExpiry = DateTime.getNextFriday(timestamp); nextExpiry = nextExpiry <= timestamp ? nextExpiry + 1 weeks : nextExpiry; } else if (period == 14) { nextExpiry = DateTime.getNextFriday(timestamp); nextExpiry = nextExpiry <= timestamp ? nextExpiry + 2 weeks : nextExpiry; } else if (period == 30) { nextExpiry = DateTime.getMonthLastFriday(timestamp); nextExpiry = nextExpiry <= timestamp ? DateTime.getMonthLastFriday(nextExpiry + 1 weeks) : nextExpiry; } else if (period == 90) { nextExpiry = DateTime.getQuarterLastFriday(timestamp); nextExpiry = nextExpiry <= timestamp ? DateTime.getQuarterLastFriday(nextExpiry + 1 weeks) : nextExpiry; } else if (period == 180) { nextExpiry = DateTime.getBiannualLastFriday(timestamp); nextExpiry = nextExpiry <= timestamp ? DateTime.getBiannualLastFriday(nextExpiry + 1 weeks) : nextExpiry; } nextExpiry = nextExpiry - (nextExpiry % (24 hours)) + (8 hours); } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {Vault} from "../libraries/Vault.sol"; abstract contract RibbonTreasuryVaultStorageV1 { /// @notice Vault's parameters like cap, decimals Vault.VaultParams public vaultParams; /// @notice Vault's lifecycle state like round and locked amounts Vault.VaultState public vaultState; /// @notice Vault's state of the options sold and the timelocked option Vault.OptionState public optionState; /// @notice Stores the user's pending deposit for the round mapping(address => Vault.DepositReceipt) public depositReceipts; /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored /// This is used to determine the number of shares to be returned /// to a user with their DepositReceipt.depositAmount mapping(uint256 => uint256) public roundPricePerShare; /// @notice Stores pending user withdrawals mapping(address => Vault.Withdrawal) public withdrawals; /// @notice Mapping of depositors in the vault mapping(address => bool) public depositorsMap; /// @notice Array of depositors in the vault address[] public depositorsArray; /// @notice Fee recipient for the performance and management fees address public feeRecipient; /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens // no access to critical vault changes address public keeper; /// @notice Logic contract used to price options address public optionsPremiumPricer; /// @notice Logic contract used to select strike prices address public strikeSelection; /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss. uint256 public performanceFee; /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss. uint256 public managementFee; /// @notice Premium discount on options we are selling (thousandths place: 000 - 999) uint256 public premiumDiscount; /// @notice Current oToken premium uint256 public currentOtokenPremium; /// @notice Price last overridden strike set to uint256 public overriddenStrikePrice; /// @notice Auction duration uint256 public auctionDuration; /// @notice Auction id of current option uint256 public optionAuctionID; /// @notice Amount locked for scheduled withdrawals last week; uint256 public lastQueuedWithdrawAmount; /// @notice Period between each options sale. /// Available options 7 (weekly), 14 (biweekly), 30 (monthly), 90 (quarterly), 180 (biannually) uint256 public period; /// @notice Maximum number of depositors uint256 public maxDepositors; /// @notice Minimum amount to deposit uint256 public minDeposit; /// @notice Last round id at which the strike was manually overridden uint16 public lastStrikeOverrideRound; } // We are following Compound's method of upgrading new contract implementations // When we need to add new storage variables, we create a new version of RibbonTreasuryVaultStorage // e.g. RibbonTreasuryVaultStorage<versionNumber>, so finally it would look like // contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1, RibbonTreasuryVaultStorageV2 abstract contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1 { }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {Vault} from "./Vault.sol"; library ShareMath { using SafeMath for uint256; uint256 internal constant PLACEHOLDER_UINT = 1; function assetToShares( uint256 assetAmount, uint256 assetPerShare, uint256 decimals ) internal pure returns (uint256) { // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet // which should never happen. // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes. require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare"); return assetAmount.mul(10**decimals).div(assetPerShare); } function sharesToAsset( uint256 shares, uint256 assetPerShare, uint256 decimals ) internal pure returns (uint256) { // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet // which should never happen. // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes. require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare"); return shares.mul(assetPerShare).div(10**decimals); } /** * @notice Returns the shares unredeemed by the user given their DepositReceipt * @param depositReceipt is the user's deposit receipt * @param currentRound is the `round` stored on the vault * @param assetPerShare is the price in asset per share * @param decimals is the number of decimals the asset/shares use * @return unredeemedShares is the user's virtual balance of shares that are owed */ function getSharesFromReceipt( Vault.DepositReceipt memory depositReceipt, uint256 currentRound, uint256 assetPerShare, uint256 decimals ) internal pure returns (uint256 unredeemedShares) { if (depositReceipt.round > 0 && depositReceipt.round < currentRound) { uint256 sharesFromRound = assetToShares(depositReceipt.amount, assetPerShare, decimals); return uint256(depositReceipt.unredeemedShares).add(sharesFromRound); } return depositReceipt.unredeemedShares; } function pricePerShare( uint256 totalSupply, uint256 totalBalance, uint256 pendingAmount, uint256 decimals ) internal pure returns (uint256) { uint256 singleShare = 10**decimals; return totalSupply > 0 ? singleShare.mul(totalBalance.sub(pendingAmount)).div( totalSupply ) : singleShare; } /************************************************ * HELPERS ***********************************************/ function assertUint104(uint256 num) internal pure { require(num <= type(uint104).max, "Overflow uint104"); } function assertUint128(uint256 num) internal pure { require(num <= type(uint128).max, "Overflow uint128"); } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; interface IWETH { function deposit() external payable; function withdraw(uint256) external; function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); function decimals() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {DSMath} from "../vendor/DSMath.sol"; import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol"; import {IOtoken} from "../interfaces/GammaInterface.sol"; import {IOptionsPremiumPricer} from "../interfaces/IRibbon.sol"; import {Vault} from "./Vault.sol"; import {IRibbonThetaVault} from "../interfaces/IRibbonThetaVault.sol"; library GnosisAuction { using SafeMath for uint256; using SafeERC20 for IERC20; event InitiateGnosisAuction( address indexed auctioningToken, address indexed biddingToken, uint256 auctionCounter, address indexed manager ); event PlaceAuctionBid( uint256 auctionId, address indexed auctioningToken, uint256 sellAmount, uint256 buyAmount, address indexed bidder ); struct AuctionDetails { address oTokenAddress; address gnosisEasyAuction; address asset; uint256 assetDecimals; uint256 oTokenPremium; uint256 duration; } struct BidDetails { address oTokenAddress; address gnosisEasyAuction; address asset; uint256 assetDecimals; uint256 auctionId; uint256 lockedBalance; uint256 optionAllocation; uint256 optionPremium; address bidder; } function startAuction(AuctionDetails calldata auctionDetails) internal returns (uint256 auctionID) { uint256 oTokenSellAmount = getOTokenSellAmount(auctionDetails.oTokenAddress); require(oTokenSellAmount > 0, "No otokens to sell"); IERC20(auctionDetails.oTokenAddress).safeApprove( auctionDetails.gnosisEasyAuction, IERC20(auctionDetails.oTokenAddress).balanceOf(address(this)) ); // minBidAmount is total oTokens to sell * premium per oToken // shift decimals to correspond to decimals of USDC for puts // and underlying for calls uint256 minBidAmount = DSMath.wmul( oTokenSellAmount.mul(10**10), auctionDetails.oTokenPremium ); minBidAmount = auctionDetails.assetDecimals > 18 ? minBidAmount.mul(10**(auctionDetails.assetDecimals.sub(18))) : minBidAmount.div( 10**(uint256(18).sub(auctionDetails.assetDecimals)) ); require( minBidAmount <= type(uint96).max, "optionPremium * oTokenSellAmount > type(uint96) max value!" ); uint256 auctionEnd = block.timestamp.add(auctionDetails.duration); auctionID = IGnosisAuction(auctionDetails.gnosisEasyAuction) .initiateAuction( // address of oToken we minted and are selling auctionDetails.oTokenAddress, // address of asset we want in exchange for oTokens. Should match vault `asset` auctionDetails.asset, // orders can be cancelled at any time during the auction auctionEnd, // order will last for `duration` auctionEnd, // we are selling all of the otokens minus a fee taken by gnosis uint96(oTokenSellAmount), // the minimum we are willing to sell all the oTokens for. A discount is applied on black-scholes price uint96(minBidAmount), // the minimum bidding amount must be 1 * 10 ** -assetDecimals 1, // the min funding threshold 0, // no atomic closure false, // access manager contract address(0), // bytes for storing info like a whitelist for who can bid bytes("") ); emit InitiateGnosisAuction( auctionDetails.oTokenAddress, auctionDetails.asset, auctionID, msg.sender ); } function placeBid(BidDetails calldata bidDetails) internal returns ( uint256 sellAmount, uint256 buyAmount, uint64 userId ) { // calculate how much to allocate sellAmount = bidDetails .lockedBalance .mul(bidDetails.optionAllocation) .div(100 * Vault.OPTION_ALLOCATION_MULTIPLIER); // divide the `asset` sellAmount by the target premium per oToken to // get the number of oTokens to buy (8 decimals) buyAmount = sellAmount .mul(10**(bidDetails.assetDecimals.add(Vault.OTOKEN_DECIMALS))) .div(bidDetails.optionPremium) .div(10**bidDetails.assetDecimals); require( sellAmount <= type(uint96).max, "sellAmount > type(uint96) max value!" ); require( buyAmount <= type(uint96).max, "buyAmount > type(uint96) max value!" ); // approve that amount IERC20(bidDetails.asset).safeApprove( bidDetails.gnosisEasyAuction, sellAmount ); uint96[] memory _minBuyAmounts = new uint96[](1); uint96[] memory _sellAmounts = new uint96[](1); bytes32[] memory _prevSellOrders = new bytes32[](1); _minBuyAmounts[0] = uint96(buyAmount); _sellAmounts[0] = uint96(sellAmount); _prevSellOrders[ 0 ] = 0x0000000000000000000000000000000000000000000000000000000000000001; // place sell order with that amount userId = IGnosisAuction(bidDetails.gnosisEasyAuction).placeSellOrders( bidDetails.auctionId, _minBuyAmounts, _sellAmounts, _prevSellOrders, "0x" ); emit PlaceAuctionBid( bidDetails.auctionId, bidDetails.oTokenAddress, sellAmount, buyAmount, bidDetails.bidder ); return (sellAmount, buyAmount, userId); } function claimAuctionOtokens( Vault.AuctionSellOrder calldata auctionSellOrder, address gnosisEasyAuction, address counterpartyThetaVault ) internal { bytes32 order = encodeOrder( auctionSellOrder.userId, auctionSellOrder.buyAmount, auctionSellOrder.sellAmount ); bytes32[] memory orders = new bytes32[](1); orders[0] = order; IGnosisAuction(gnosisEasyAuction).claimFromParticipantOrder( IRibbonThetaVault(counterpartyThetaVault).optionAuctionID(), orders ); } function getOTokenSellAmount(address oTokenAddress) internal view returns (uint256) { // We take our current oToken balance. That will be our sell amount // but otokens will be transferred to gnosis. uint256 oTokenSellAmount = IERC20(oTokenAddress).balanceOf(address(this)); require( oTokenSellAmount <= type(uint96).max, "oTokenSellAmount > type(uint96) max value!" ); return oTokenSellAmount; } function getOTokenPremiumInStables( address oTokenAddress, address optionsPremiumPricer, uint256 premiumDiscount ) internal view returns (uint256) { IOtoken newOToken = IOtoken(oTokenAddress); IOptionsPremiumPricer premiumPricer = IOptionsPremiumPricer(optionsPremiumPricer); // Apply black-scholes formula (from rvol library) to option given its features // and get price for 100 contracts denominated USDC for both call and put options uint256 optionPremium = premiumPricer.getPremiumInStables( newOToken.strikePrice(), newOToken.expiryTimestamp(), newOToken.isPut() ); // Apply a discount to incentivize arbitraguers optionPremium = optionPremium.mul(premiumDiscount).div( 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER ); require( optionPremium <= type(uint96).max, "optionPremium > type(uint96) max value!" ); return optionPremium; } function encodeOrder( uint64 userId, uint96 buyAmount, uint96 sellAmount ) internal pure returns (bytes32) { return bytes32( (uint256(userId) << 192) + (uint256(buyAmount) << 96) + uint256(sellAmount) ); } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IERC20Detailed is IERC20 { function decimals() external view returns (uint8); function symbol() external view returns (string calldata); function name() external view returns (string calldata); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {Vault} from "../libraries/Vault.sol"; interface IRibbonVault { function deposit(uint256 amount) external; function depositETH() external payable; function cap() external view returns (uint256); function depositFor(uint256 amount, address creditor) external; function vaultParams() external view returns (Vault.VaultParams memory); } interface IStrikeSelection { function getStrikePrice(uint256 expiryTimestamp, bool isPut) external view returns (uint256, uint256); function delta() external view returns (uint256); } interface IOptionsPremiumPricer { function getPremium( uint256 strikePrice, uint256 timeToExpiry, bool isPut ) external view returns (uint256); function getPremiumInStables( uint256 strikePrice, uint256 timeToExpiry, bool isPut ) external view returns (uint256); function getOptionDelta( uint256 spotPrice, uint256 strikePrice, uint256 volatility, uint256 expiryTimestamp ) external view returns (uint256 delta); function getUnderlyingPrice() external view returns (uint256); function priceOracle() external view returns (address); function volatilityOracle() external view returns (address); function optionId() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary // ---------------------------------------------------------------------------- // BokkyPooBah's DateTime Library v1.01 // ---------------------------------------------------------------------------- pragma solidity =0.8.4; library DateTime { uint256 constant SECONDS_PER_DAY = 24 * 60 * 60; uint256 constant SECONDS_PER_HOUR = 60 * 60; uint256 constant SECONDS_PER_MINUTE = 60; int256 constant OFFSET19700101 = 2440588; uint256 constant DOW_MON = 1; uint256 constant DOW_TUE = 2; uint256 constant DOW_WED = 3; uint256 constant DOW_THU = 4; uint256 constant DOW_FRI = 5; uint256 constant DOW_SAT = 6; uint256 constant DOW_SUN = 7; // ------------------------------------------------------------------------ // Calculate the number of days from 1970/01/01 to year/month/day using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and subtracting the offset 2440588 so that 1970/01/01 is day 0 // // days = day // - 32075 // + 1461 * (year + 4800 + (month - 14) / 12) / 4 // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 // - offset // ------------------------------------------------------------------------ function _daysFromDate( uint256 year, uint256 month, uint256 day ) internal pure returns (uint256 _days) { require(year >= 1970); int256 _year = int256(year); int256 _month = int256(month); int256 _day = int256(day); int256 __days = _day - 32075 + (1461 * (_year + 4800 + (_month - 14) / 12)) / 4 + (367 * (_month - 2 - ((_month - 14) / 12) * 12)) / 12 - (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) / 4 - OFFSET19700101; _days = uint256(__days); } // ------------------------------------------------------------------------ // Calculate year/month/day from the number of days since 1970/01/01 using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and adding the offset 2440588 so that 1970/01/01 is day 0 // // int L = days + 68569 + offset // int N = 4 * L / 146097 // L = L - (146097 * N + 3) / 4 // year = 4000 * (L + 1) / 1461001 // L = L - 1461 * year / 4 + 31 // month = 80 * L / 2447 // dd = L - 2447 * month / 80 // L = month / 11 // month = month + 2 - 12 * L // year = 100 * (N - 49) + year + L // ------------------------------------------------------------------------ function _daysToDate(uint256 _days) internal pure returns ( uint256 year, uint256 month, uint256 day ) { int256 __days = int256(_days); int256 L = __days + 68569 + OFFSET19700101; int256 N = (4 * L) / 146097; L = L - (146097 * N + 3) / 4; int256 _year = (4000 * (L + 1)) / 1461001; L = L - (1461 * _year) / 4 + 31; int256 _month = (80 * L) / 2447; int256 _day = L - (2447 * _month) / 80; L = _month / 11; _month = _month + 2 - 12 * L; _year = 100 * (N - 49) + _year + L; year = uint256(_year); month = uint256(_month); day = uint256(_day); } function isLeapYear(uint256 timestamp) internal pure returns (bool leapYear) { (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY); leapYear = _isLeapYear(year); } function _isLeapYear(uint256 year) internal pure returns (bool leapYear) { leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } function getDaysInMonth(uint256 timestamp) internal pure returns (uint256 daysInMonth) { (uint256 year, uint256 month, ) = _daysToDate(timestamp / SECONDS_PER_DAY); daysInMonth = _getDaysInMonth(year, month); } function _getDaysInMonth(uint256 year, uint256 month) internal pure returns (uint256 daysInMonth) { if ( month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ) { daysInMonth = 31; } else if (month != 2) { daysInMonth = 30; } else { daysInMonth = _isLeapYear(year) ? 29 : 28; } } // 1 = Monday, 7 = Sunday function getDayOfWeek(uint256 timestamp) internal pure returns (uint256 dayOfWeek) { uint256 _days = timestamp / SECONDS_PER_DAY; dayOfWeek = ((_days + 3) % 7) + 1; } function getYear(uint256 timestamp) internal pure returns (uint256 year) { (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY); } function getMonth(uint256 timestamp) internal pure returns (uint256 month) { (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY); } function getDay(uint256 timestamp) internal pure returns (uint256 day) { (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY); } function timestampFromDate( uint256 year, uint256 month, uint256 day ) internal pure returns (uint256 timestamp) { timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; } /** * @notice Gets the Friday of the same week * @param timestamp is the given date and time * @return the Friday of the same week in unix time */ function getThisWeekFriday(uint256 timestamp) internal pure returns (uint256) { return timestamp + 5 days - getDayOfWeek(timestamp) * 1 days; } /** * @notice Gets the next friday after the given date and time * @param timestamp is the given date and time * @return the next friday after the given date and time */ function getNextFriday(uint256 timestamp) internal pure returns (uint256) { uint256 friday = getThisWeekFriday(timestamp); return friday >= timestamp ? friday : friday + 1 weeks; } /** * @notice Gets the last day of the month * @param timestamp is the given date and time * @return the last day of the same month in unix time */ function getLastDayOfMonth(uint256 timestamp) internal pure returns (uint256) { return timestampFromDate(getYear(timestamp), getMonth(timestamp) + 1, 1) - 1 days; } /** * @notice Gets the last Friday of the month * @param timestamp is the given date and time * @return the last Friday of the same month in unix time */ function getMonthLastFriday(uint256 timestamp) internal pure returns (uint256) { uint256 lastDay = getLastDayOfMonth(timestamp); uint256 friday = getThisWeekFriday(lastDay); return friday > lastDay ? friday - 1 weeks : friday; } /** * @notice Gets the last Friday of the quarter * @param timestamp is the given date and time * @return the last Friday of the quarter in unix time */ function getQuarterLastFriday(uint256 timestamp) internal pure returns (uint256) { uint256 month = getMonth(timestamp); uint256 quarterMonth = (month <= 3) ? 3 : (month <= 6) ? 6 : (month <= 9) ? 9 : 12; uint256 quarterDate = timestampFromDate(getYear(timestamp), quarterMonth, 1); return getMonthLastFriday(quarterDate); } /** * @notice Gets the last Friday of the half-year * @param timestamp is the given date and time * @return the last friday of the half-year */ function getBiannualLastFriday(uint256 timestamp) internal pure returns (uint256) { uint256 month = getMonth(timestamp); uint256 biannualMonth = (month <= 6) ? 6 : 12; uint256 biannualDate = timestampFromDate(getYear(timestamp), biannualMonth, 1); return getMonthLastFriday(biannualDate); } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; library GammaTypes { // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults. struct Vault { // addresses of oTokens a user has shorted (i.e. written) against this vault address[] shortOtokens; // addresses of oTokens a user has bought and deposited in this vault // user can be long oTokens without opening a vault (e.g. by buying on a DEX) // generally, long oTokens will be 'deposited' in vaults to act as collateral // in order to write oTokens against (i.e. in spreads) address[] longOtokens; // addresses of other ERC-20s a user has deposited as collateral in this vault address[] collateralAssets; // quantity of oTokens minted/written for each oToken address in shortOtokens uint256[] shortAmounts; // quantity of oTokens owned and held in the vault for each oToken address in longOtokens uint256[] longAmounts; // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets uint256[] collateralAmounts; } } interface IOtoken { function underlyingAsset() external view returns (address); function strikeAsset() external view returns (address); function collateralAsset() external view returns (address); function strikePrice() external view returns (uint256); function expiryTimestamp() external view returns (uint256); function isPut() external view returns (bool); } interface IOtokenFactory { function getOtoken( address _underlyingAsset, address _strikeAsset, address _collateralAsset, uint256 _strikePrice, uint256 _expiry, bool _isPut ) external view returns (address); function createOtoken( address _underlyingAsset, address _strikeAsset, address _collateralAsset, uint256 _strikePrice, uint256 _expiry, bool _isPut ) external returns (address); function getTargetOtokenAddress( address _underlyingAsset, address _strikeAsset, address _collateralAsset, uint256 _strikePrice, uint256 _expiry, bool _isPut ) external view returns (address); event OtokenCreated( address tokenAddress, address creator, address indexed underlying, address indexed strike, address indexed collateral, uint256 strikePrice, uint256 expiry, bool isPut ); } interface IController { // possible actions that can be performed enum ActionType { OpenVault, MintShortOption, BurnShortOption, DepositLongOption, WithdrawLongOption, DepositCollateral, WithdrawCollateral, SettleVault, Redeem, Call, Liquidate } struct ActionArgs { // type of action that is being performed on the system ActionType actionType; // address of the account owner address owner; // address which we move assets from or to (depending on the action type) address secondAddress; // asset that is to be transfered address asset; // index of the vault that is to be modified (if any) uint256 vaultId; // amount of asset that is to be transfered uint256 amount; // each vault can hold multiple short / long / collateral assets // but we are restricting the scope to only 1 of each in this version // in future versions this would be the index of the short / long / collateral asset that needs to be modified uint256 index; // any other data that needs to be passed in for arbitrary function calls bytes data; } struct RedeemArgs { // address to which we pay out the oToken proceeds address receiver; // oToken that is to be redeemed address otoken; // amount of oTokens that is to be redeemed uint256 amount; } function getPayout(address _otoken, uint256 _amount) external view returns (uint256); function operate(ActionArgs[] calldata _actions) external; function getAccountVaultCounter(address owner) external view returns (uint256); function oracle() external view returns (address); function getVault(address _owner, uint256 _vaultId) external view returns (GammaTypes.Vault memory); function getProceed(address _owner, uint256 _vaultId) external view returns (uint256); function isSettlementAllowed( address _underlying, address _strike, address _collateral, uint256 _expiry ) external view returns (bool); } interface IOracle { function setAssetPricer(address _asset, address _pricer) external; function updateAssetPricer(address _asset, address _pricer) external; function getPrice(address _asset) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; library AuctionType { struct AuctionData { IERC20 auctioningToken; IERC20 biddingToken; uint256 orderCancellationEndDate; uint256 auctionEndDate; bytes32 initialAuctionOrder; uint256 minimumBiddingAmountPerOrder; uint256 interimSumBidAmount; bytes32 interimOrder; bytes32 clearingPriceOrder; uint96 volumeClearingPriceOrder; bool minFundingThresholdNotReached; bool isAtomicClosureAllowed; uint256 feeNumerator; uint256 minFundingThreshold; } } interface IGnosisAuction { function initiateAuction( address _auctioningToken, address _biddingToken, uint256 orderCancellationEndDate, uint256 auctionEndDate, uint96 _auctionedSellAmount, uint96 _minBuyAmount, uint256 minimumBiddingAmountPerOrder, uint256 minFundingThreshold, bool isAtomicClosureAllowed, address accessManagerContract, bytes memory accessManagerContractData ) external returns (uint256); function auctionCounter() external view returns (uint256); function auctionData(uint256 auctionId) external view returns (AuctionType.AuctionData memory); function auctionAccessManager(uint256 auctionId) external view returns (address); function auctionAccessData(uint256 auctionId) external view returns (bytes memory); function FEE_DENOMINATOR() external view returns (uint256); function feeNumerator() external view returns (uint256); function settleAuction(uint256 auctionId) external returns (bytes32); function placeSellOrders( uint256 auctionId, uint96[] memory _minBuyAmounts, uint96[] memory _sellAmounts, bytes32[] memory _prevSellOrders, bytes calldata allowListCallData ) external returns (uint64); function claimFromParticipantOrder( uint256 auctionId, bytes32[] memory orders ) external returns (uint256, uint256); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /** * This library supports ERC20s that have quirks in their behavior. * One such ERC20 is USDT, which requires allowance to be 0 before calling approve. * We plan to update this library with ERC20s that display such idiosyncratic behavior. */ library SupportsNonCompliantERC20 { address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; function safeApproveNonCompliant( IERC20 token, address spender, uint256 amount ) internal { if (address(token) == USDT) { SafeERC20.safeApprove(token, spender, 0); } SafeERC20.safeApprove(token, spender, amount); } }
// SPDX-License-Identifier: MIT /// math.sol -- mixin for inline numerical wizardry // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >0.4.13; library DSMath { function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x, "ds-math-add-overflow"); } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x - y) <= x, "ds-math-sub-underflow"); } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } function min(uint256 x, uint256 y) internal pure returns (uint256 z) { return x <= y ? x : y; } function max(uint256 x, uint256 y) internal pure returns (uint256 z) { return x >= y ? x : y; } function imin(int256 x, int256 y) internal pure returns (int256 z) { return x <= y ? x : y; } function imax(int256 x, int256 y) internal pure returns (int256 z) { return x >= y ? x : y; } uint256 constant WAD = 10**18; uint256 constant RAY = 10**27; //rounds to zero if x*y < WAD / 2 function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, y), WAD / 2) / WAD; } //rounds to zero if x*y < WAD / 2 function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, y), RAY / 2) / RAY; } //rounds to zero if x*y < WAD / 2 function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, WAD), y / 2) / y; } //rounds to zero if x*y < RAY / 2 function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, RAY), y / 2) / y; } // This famous algorithm is called "exponentiation by squaring" // and calculates x^n with x as fixed-point and n as regular unsigned. // // It's O(log n), instead of O(n) for naive repeated multiplication. // // These facts are why it works: // // If n is even, then x^n = (x^2)^(n/2). // If n is odd, then x^n = x * x^(n-1), // and applying the equation for even x gives // x^n = x * (x^2)^((n-1) / 2). // // Also, EVM division is flooring and // floor[(n-1) / 2] = floor[n / 2]. // function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) { z = n % 2 != 0 ? x : RAY; for (n /= 2; n != 0; n /= 2) { x = rmul(x, x); if (n % 2 != 0) { z = rmul(z, x); } } } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; import {Vault} from "../libraries/Vault.sol"; interface IRibbonThetaVault { function currentOption() external view returns (address); function nextOption() external view returns (address); function vaultParams() external view returns (Vault.VaultParams memory); function vaultState() external view returns (Vault.VaultState memory); function optionState() external view returns (Vault.OptionState memory); function optionAuctionID() external view returns (uint256); function pricePerShare() external view returns (uint256); function roundPricePerShare(uint256) external view returns (uint256); function depositFor(uint256 amount, address creditor) external; function initiateWithdraw(uint256 numShares) external; function completeWithdraw() external; function maxRedeem() external; function depositYieldTokenFor(uint256 amount, address creditor) external; function symbol() external view returns (string calldata); }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "contracts/libraries/VaultLifecycleTreasury.sol": { "VaultLifecycleTreasury": "0xe1d00f9bafea5aa40a2192af12b68af3d390afe2" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_usdc","type":"address"},{"internalType":"address","name":"_oTokenFactory","type":"address"},{"internalType":"address","name":"_gammaController","type":"address"},{"internalType":"address","name":"_marginPool","type":"address"},{"internalType":"address","name":"_gnosisEasyAuction","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAuctionDuration","type":"uint256"}],"name":"AuctionDurationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"CapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"options","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"CloseShort","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"managementFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"CollectManagementFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"CollectPerformanceFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"recipients","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"DistributePremium","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auctioningToken","type":"address"},{"indexed":true,"internalType":"address","name":"biddingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"auctionCounter","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"InitiateGnosisAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"InitiateWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"InstantWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"managementFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"ManagementFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"strikePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delta","type":"uint256"}],"name":"NewOptionStrikeSelected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"options","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"OpenShort","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"premiumDiscount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPremiumDiscount","type":"uint256"}],"name":"PremiumDiscountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAMMA_CONTROLLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GNOSIS_EASY_AUCTION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MARGIN_POOL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OTOKEN_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountVaultBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"auctionDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnRemainingOTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chargeAndDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"commitAndClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completeWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"concludeOptionsSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentOption","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentOtokenPremium","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositReceipts","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint104","name":"amount","type":"uint104"},{"internalType":"uint128","name":"unredeemedShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositorsArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositorsMap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numRounds","type":"uint256"}],"name":"initRounds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint256","name":"_managementFee","type":"uint256"},{"internalType":"uint256","name":"_performanceFee","type":"uint256"},{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"},{"internalType":"address","name":"_optionsPremiumPricer","type":"address"},{"internalType":"address","name":"_strikeSelection","type":"address"},{"internalType":"uint32","name":"_premiumDiscount","type":"uint32"},{"internalType":"uint256","name":"_auctionDuration","type":"uint256"},{"internalType":"uint256","name":"_period","type":"uint256"},{"internalType":"uint256","name":"_maxDepositors","type":"uint256"},{"internalType":"uint256","name":"_minDeposit","type":"uint256"}],"internalType":"struct VaultLifecycleTreasury.InitParams","name":"_initParams","type":"tuple"},{"components":[{"internalType":"bool","name":"isPut","type":"bool"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint56","name":"minimumSupply","type":"uint56"},{"internalType":"uint104","name":"cap","type":"uint104"}],"internalType":"struct Vault.VaultParams","name":"_vaultParams","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"initiateWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastQueuedWithdrawAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastStrikeOverrideRound","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxDepositors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextOption","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextOptionReadyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionAuctionID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionState","outputs":[{"internalType":"address","name":"nextOption","type":"address"},{"internalType":"address","name":"currentOption","type":"address"},{"internalType":"uint32","name":"nextOptionReadyAt","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionsPremiumPricer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"overriddenStrikePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"period","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"premiumDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollToNextOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"roundPricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAuctionDuration","type":"uint256"}],"name":"setAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newOtokenPremium","type":"uint256"}],"name":"setCurrentOtokenPremium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"setManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxDepositors","type":"uint256"}],"name":"setMaxDepositors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinDeposit","type":"uint256"}],"name":"setMinDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newKeeper","type":"address"}],"name":"setNewKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOptionsPremiumPricer","type":"address"}],"name":"setOptionsPremiumPricer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPremiumDiscount","type":"uint256"}],"name":"setPremiumDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"strikePrice","type":"uint128"}],"name":"setStrikePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newStrikeSelection","type":"address"}],"name":"setStrikeSelection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"shareBalances","outputs":[{"internalType":"uint256","name":"heldByAccount","type":"uint256"},{"internalType":"uint256","name":"heldByVault","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strikeSelection","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPending","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultParams","outputs":[{"internalType":"bool","name":"isPut","type":"bool"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint56","name":"minimumSupply","type":"uint56"},{"internalType":"uint104","name":"cap","type":"uint104"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultState","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint104","name":"lockedAmount","type":"uint104"},{"internalType":"uint104","name":"lastLockedAmount","type":"uint104"},{"internalType":"uint128","name":"totalPending","type":"uint128"},{"internalType":"uint128","name":"queuedWithdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawInstantly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawals","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b50604051620061873803806200618783398101604081905262000035916200024e565b6001600160a01b0386166200007a5760405162461bcd60e51b8152602060048201526006602482015265042beeecae8d60d31b60448201526064015b60405180910390fd5b6001600160a01b038516620000bb5760405162461bcd60e51b8152602060048201526006602482015265215f7573646360d01b604482015260640162000071565b6001600160a01b038416620001055760405162461bcd60e51b815260206004820152600f60248201526e215f6f546f6b656e466163746f727960881b604482015260640162000071565b6001600160a01b038316620001515760405162461bcd60e51b815260206004820152601160248201527010afb3b0b6b6b0a1b7b73a3937b63632b960791b604482015260640162000071565b6001600160a01b038216620001985760405162461bcd60e51b815260206004820152600c60248201526b0857db585c99da5b941bdbdb60a21b604482015260640162000071565b6001600160a01b038116620001f05760405162461bcd60e51b815260206004820152601360248201527f215f676e6f7369734561737941756374696f6e00000000000000000000000000604482015260640162000071565b6001600160601b0319606096871b811660805294861b851660a05292851b84166101205290841b831660c052831b821660e05290911b1661010052620002ce565b80516001600160a01b03811681146200024957600080fd5b919050565b60008060008060008060c0878903121562000267578182fd5b620002728762000231565b9550620002826020880162000231565b9450620002926040880162000231565b9350620002a26060880162000231565b9250620002b26080880162000231565b9150620002c260a0880162000231565b90509295509295509295565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c615dfc6200038b60003960008181610ba5015261285f0152600081816107c901528181611ebe01526136c801526000818161071b01526124a4015260008181610500015281816117ae0152818161247c01526144950152600081816108d801528181611efd0152818161288401528181612af101528181613627015281816136f20152613a4e01526000610ae00152615dfc6000f3fe608060405234801561001057600080fd5b50600436106104965760003560e01c80638da5cb5b11610262578063b4befff211610151578063e73c63d5116100ce578063f656ba5111610092578063f656ba5114610c40578063f756fa2114610c68578063f79d8d1214610c70578063f957a06714610c93578063fc29738014610ca6578063fe56e23214610cb957600080fd5b8063e73c63d514610c00578063e74b981b14610c09578063ea00b1ca14610c1c578063ef78d4fd14610c24578063f2fde38b14610c2d57600080fd5b8063d164cc1511610115578063d164cc1514610b71578063d5f2638214610b84578063db006a7514610b8d578063db43e86214610ba0578063dd62ed3e14610bc757600080fd5b8063b4befff214610b1d578063b6b55f2514610b30578063b9f8092b14610b43578063ce7c2ac214610b4b578063d13f1b3e14610b5e57600080fd5b8063a285c9e8116101df578063a9059cbb116101a3578063a9059cbb14610ab5578063aced166114610ac8578063ad5c464814610adb578063ad7a672f14610b02578063afa6626414610b0a57600080fd5b8063a285c9e814610a60578063a2db9d8314610a75578063a457c2d714610a86578063a497e67414610a99578063a6f7f5d614610aac57600080fd5b806399530b061161022657806399530b06146109475780639b2e601b1461094f5780639be43daa146109585780639fcc2d751461096b578063a083ff1714610a0a57600080fd5b80638da5cb5b1461090b5780638fcc9cfb1461091c57806395d89b411461092f57806396d2a76b14610937578063990c56101461093f57600080fd5b80634af96d29116103895780636f31ab34116103065780637e108d52116102ca5780637e108d521461088e57806383536ff3146108a157806387153eb1146108aa57806387788782146108ca57806389a30271146108d35780638b10cc7c146108fa57600080fd5b80636f31ab34146107eb57806370897b23146107f357806370a0823114610806578063715018a61461082f5780637a9262a21461083757600080fd5b8063650cce8a1161034d578063650cce8a146107165780636719b2ee1461073d57806369b41170146107b45780636b64c769146107bc5780636c10f66c146107c457600080fd5b80634af96d29146106cc578063503c70aa146106df57806355489bb2146106e8578063600a2cfb146106fb578063601d6ba91461070357600080fd5b8063355274ea1161041757806341b3d185116103db57806341b3d18514610681578063432833a61461068a5780634603c0aa1461069357806346904840146106a657806347786d37146106b957600080fd5b8063355274ea1461061857806339509351146106295780633ec143d31461063c5780633f23bb731461065d5780633f90916a1461067057600080fd5b806323b872dd1161045e57806323b872dd1461053a5780632728f3331461054d5780632775d01c146105d357806330630da4146105e8578063313ce567146105fb57600080fd5b806306fdde031461049b578063095ea7b3146104b95780630cbf54c8146104dc57806318160ddd146104f35780631a92f6c2146104fb575b600080fd5b6104a3610ccc565b6040516104b0919061561f565b60405180910390f35b6104cc6104c7366004615397565b610d5e565b60405190151581526020016104b0565b6104e560de5481565b6040519081526020016104b0565b6099546104e5565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016104b0565b6104cc610548366004615357565b610d75565b60cc5460cd5461058e9161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a0016104b0565b6105e66105e13660046154b7565b610e26565b005b6105e66105f6366004615303565b611067565b60c954610100900460ff1660405160ff90911681526020016104b0565b60cb546001600160681b03166104e5565b6104cc610637366004615397565b6110ff565b60e45461064a9061ffff1681565b60405161ffff90911681526020016104b0565b6104e561066b366004615303565b61113b565b60cd546001600160801b03166104e5565b6104e560e35481565b6104e560df5481565b6105e66106a1366004615303565b61118e565b60d554610522906001600160a01b031681565b6105e66106c73660046154b7565b611230565b6105e66106da366004615434565b61130b565b6104e560e05481565b6105e66106f6366004615490565b611681565b6105e6611742565b6105226107113660046154b7565b61189d565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b61078361074b366004615303565b60d06020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b0316908201526060016104b0565b6104e5600081565b6105e66118c7565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b6105e6611927565b6105e66108013660046154b7565b61195e565b6104e5610814366004615303565b6001600160a01b031660009081526097602052604090205490565b6105e6611a25565b61086c610845366004615303565b60d26020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b039091166020830152016104b0565b6105e661089c3660046154b7565b611a5b565b6104e560db5481565b6104e56108b83660046154b7565b60d16020526000908152604090205481565b6104e560d95481565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b60cf546001600160a01b0316610522565b6065546001600160a01b0316610522565b6105e661092a3660046154b7565b611d61565b6104a3611dfa565b6105e6611e09565b6105e6611e63565b6104e5611f8d565b6104e560e25481565b6105e66109663660046154b7565b611fc7565b60c95460ca5460cb546109ba9260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c0016104b0565b60ce5460cf54610a34916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff16908201526060016104b0565b60cf54600160a01b900463ffffffff166104e5565b60ce546001600160a01b0316610522565b6104cc610a94366004615397565b6120c1565b6105e6610aa73660046154b7565b61215a565b6104e560da5481565b6104cc610ac3366004615397565b612217565b60d654610522906001600160a01b031681565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b6104e5612224565b60d754610522906001600160a01b031681565b6105e6610b2b3660046154b7565b6122c3565b6105e6610b3e3660046154b7565b6122f2565b6105e6612389565b6104e5610b59366004615303565b612550565b6105e6610b6c3660046154b7565b61256d565b6105e6610b7f366004615303565b612631565b6104e560dc5481565b6105e6610b9b3660046154b7565b6126c0565b6105227f000000000000000000000000000000000000000000000000000000000000000081565b6104e5610bd536600461531f565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b6104e560dd5481565b6105e6610c17366004615303565b61272d565b6105e6612820565b6104e560e15481565b6105e6610c3b366004615303565b612b8e565b610c53610c4e366004615303565b612c29565b604080519283526020830191909152016104b0565b6105e6612d0c565b6104cc610c7e366004615303565b60d36020526000908152604090205460ff1681565b60d854610522906001600160a01b031681565b6105e6610cb43660046154b7565b612d61565b6105e6610cc73660046154b7565b612dfd565b6060609a8054610cdb90615b9c565b80601f0160208091040260200160405190810160405280929190818152602001828054610d0790615b9c565b8015610d545780601f10610d2957610100808354040283529160200191610d54565b820191906000526020600020905b815481529060010190602001808311610d3757829003601f168201915b5050505050905090565b6000610d6b338484612ec6565b5060015b92915050565b6000610d82848484612feb565b6001600160a01b038416600090815260986020908152604080832033845290915290205482811015610e0c5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610e198533858403612ec6565b60019150505b9392505050565b60026001541415610e495760405162461bcd60e51b8152600401610e039061572d565b600260015533600090815260d06020526040902060cc5461ffff1682610e9b5760405162461bcd60e51b815260206004820152600760248201526608585b5bdd5b9d60ca1b6044820152606401610e03565b815461ffff168114610edf5760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b6044820152606401610e03565b81546201000090046001600160681b031683811015610f305760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b6044820152606401610e03565b6000610f4582610f3f3361113b565b9061306d565b905084811115610f81576000610f5b8287613079565b905060e354811015610f7f5760405162461bcd60e51b8152600401610e03906156f6565b505b610f8b8286613079565b84546001600160681b0391909116620100000262010000600160781b031990911617845560cd54610fc5906001600160801b031686613079565b60cd80546001600160801b0319166001600160801b0392909216919091179055604080518681526020810185905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a283546201000090046001600160681b0316158015611044575061104233612550565b155b156110525761105233613085565b61105c33866132d6565b505060018055505050565b6065546001600160a01b031633146110915760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b0381166110dd5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b6044820152606401610e03565b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b3360008181526098602090815260408083206001600160a01b03871684529091528120549091610d6b918590611136908690615a00565b612ec6565b60c95460009060ff610100909104168161117161115760995490565b61115f612224565b60cd546001600160801b0316856132f3565b905061118661117f85612550565b8284613339565b949350505050565b6065546001600160a01b031633146111b85760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b03811661120e5760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d50726963657200000000000000006044820152606401610e03565b60d780546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b0316331461125a5760405162461bcd60e51b8152600401610e03906156c1565b600081116112945760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b6044820152606401610e03565b61129d8161339b565b60cb54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160cb80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b600054610100900460ff1680611324575060005460ff16155b6113405760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015611362576000805461ffff19166101011790555b6040516384d831a960e01b815273e1d00f9bafea5aa40a2192af12b68af3d390afe2906384d831a99061139f908690869061012c90600401615764565b60006040518083038186803b1580156113b757600080fd5b505af41580156113cb573d6000803e3d6000fd5b505050506113d76133e5565b6114636113e760a085018561596f565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506114299250505060c086018661596f565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061345892505050565b61146b6134d7565b61147b610c3b6020850185615303565b61148b6040840160208501615303565b60d680546001600160a01b0319166001600160a01b039290921691909117905561016083013560e1556114c5610100840160e08501615303565b60d780546001600160a01b0319166001600160a01b03929092169190911790556114f761012084016101008501615303565b60d880546001600160a01b0319166001600160a01b03929092169190911790556115296101408401610120850161550e565b63ffffffff1660db5561014083013560de5561154b6060840160408501615303565b60d580546001600160a01b0319166001600160a01b0392909216919091179055608083013560d955611580606084013561353e565b60da5561018083013560e2556101a083013560e3558160c96115a28282615c59565b505060cc805461ffff1916600117905560c9546040516370a0823160e01b81523060048201526000916001600160a01b036201000090910416906370a082319060240160206040518083038186803b1580156115fd57600080fd5b505afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611635919061541c565b90506116408161339b565b60cc80546001600160681b03909216600160781b02600160781b600160e01b0319909216919091179055801561167c576000805461ff00191690555b505050565b6065546001600160a01b031633146116ab5760405162461bcd60e51b8152600401610e03906156c1565b600260015414156116ce5760405162461bcd60e51b8152600401610e039061572d565b60026001556001600160801b0381166117185760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b6044820152606401610e03565b6001600160801b031660dd5560cc5460e4805461ffff191661ffff90921691909117905560018055565b60d6546001600160a01b0316331461176c5760405162461bcd60e51b8152600401610e0390615652565b6002600154141561178f5760405162461bcd60e51b8152600401610e039061572d565b600260015560cf546040516358ffbb3d60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152909116602482015260009073e1d00f9bafea5aa40a2192af12b68af3d390afe2906358ffbb3d9060440160206040518083038186803b15801561181657600080fd5b505af415801561182a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184e919061541c565b60cc5490915061186d906201000090046001600160681b031682613079565b60cc80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d481815481106118ad57600080fd5b6000918252602090912001546001600160a01b0316905081565b60d6546001600160a01b031633146118f15760405162461bcd60e51b8152600401610e0390615652565b600260015414156119145760405162461bcd60e51b8152600401610e039061572d565b60026001556119216135a9565b60018055565b6002600154141561194a5760405162461bcd60e51b8152600401610e039061572d565b6002600181905550611921600060016137ee565b6065546001600160a01b031633146119885760405162461bcd60e51b8152600401610e03906156c1565b611996620f42406064615b17565b81106119e45760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e6365206665650000000000000000006044820152606401610e03565b60d95460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d955565b6065546001600160a01b03163314611a4f5760405162461bcd60e51b8152600401610e03906156c1565b611a59600061399d565b565b60026001541415611a7e5760405162461bcd60e51b8152600401610e039061572d565b600260015580611abd5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b6044820152606401610e03565b33600090815260d06020526040902080546201000090046001600160681b0316151580611afa57508054600160781b90046001600160801b031615155b15611b0b57611b0b600060016137ee565b60cc5433600081815260d260209081526040918290208054835188815261ffff96871693810184905292959194911692838614927f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a282546201000090046001600160801b031660008215611b9457611b8d828961306d565b9050611bf7565b8115611bd65760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b6044820152606401610e03565b5033600090815260d260205260409020805461ffff191661ffff8716179055865b60c954610100900460ff166000611c0d3361113b565b90506000611c538b8a60011415611c2e57611c2985600a615a6f565b611c4d565b60d16000611c3d60018e615b59565b8152602001908152602001600020545b85613339565b905080821115611c8f576000611c698383613079565b905060e354811015611c8d5760405162461bcd60e51b8152600401610e03906156f6565b505b611c98846139ef565b33600090815260d26020526040812080546001600160801b03808816620100000262010000600160901b03199092169190911790915560cd54611ce491600160801b909104168d61306d565b9050611cef816139ef565b60cd80546001600160801b03808416600160801b0291161790558a546001600160681b036201000090910416158015611d365750336000908152609760205260409020548c145b15611d4457611d4433613085565b611d4f33308e612feb565b50506001805550505050505050505050565b6065546001600160a01b03163314611d8b5760405162461bcd60e51b8152600401610e03906156c1565b60026001541415611dae5760405162461bcd60e51b8152600401610e039061572d565b600260015580611df15760405162461bcd60e51b815260206004820152600e60248201526d085b995dd35a5b91195c1bdcda5d60921b6044820152606401610e03565b60e35560018055565b6060609b8054610cdb90615b9c565b60d6546001600160a01b03163314611e335760405162461bcd60e51b8152600401610e0390615652565b60026001541415611e565760405162461bcd60e51b8152600401610e039061572d565b6002600155611921613a39565b60d6546001600160a01b03163314611e8d5760405162461bcd60e51b8152600401610e0390615652565b60026001541415611eb05760405162461bcd60e51b8152600401610e039061572d565b600260015560df54611ee3907f000000000000000000000000000000000000000000000000000000000000000090613bb7565b6040516370a0823160e01b815230600482015262989680907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015611f4757600080fd5b505afa158015611f5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f7f919061541c565b111561192157611921613a39565b6000611fc2611f9b60995490565b611fa3612224565b60cd5460c9546001600160801b0390911690610100900460ff166132f3565b905090565b60026001541415611fea5760405162461bcd60e51b8152600401610e039061572d565b6002600155806120295760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b6044820152606401610e03565b60cc5461ffff1660005b828110156120b85760006120478284615a00565b600081815260d16020526040902054909150156120945760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b6044820152606401610e03565b600090815260d16020526040902060019055806120b081615bd7565b915050612033565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156121435760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610e03565b6121503385858403612ec6565b5060019392505050565b6065546001600160a01b031633146121845760405162461bcd60e51b8152600401610e03906156c1565b61012c8110156121d65760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e00000000000000006044820152606401610e03565b60de5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160de55565b6000610d6b338484612feb565b60c9546040516370a0823160e01b8152306004820152600091611fc291620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561227357600080fd5b505afa158015612287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ab919061541c565b60cc546201000090046001600160681b03169061306d565b6065546001600160a01b031633146122ed5760405162461bcd60e51b8152600401610e03906156c1565b60dc55565b600260015414156123155760405162461bcd60e51b8152600401610e039061572d565b6002600155806123515760405162461bcd60e51b815260206004820152600760248201526608585b5bdd5b9d60ca1b6044820152606401610e03565b61235a33613c31565b6123648133613d70565b60c954612382906201000090046001600160a01b031633308461403e565b5060018055565b60d6546001600160a01b031633146123b35760405162461bcd60e51b8152600401610e0390615652565b600260015414156123d65760405162461bcd60e51b8152600401610e039061572d565b600260018190555060008060006123ee60e0546140af565b60e0819055919450925090506124038261339b565b60cc805462010000600160781b031916620100006001600160681b0385160217905560405182815233906001600160a01b038516907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f000000000000000000000000000000000000000000000000000000000000000081166024830152841660448201526064810183905273e1d00f9bafea5aa40a2192af12b68af3d390afe290632904c2399060840160206040518083038186803b15801561250f57600080fd5b505af4158015612523573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612547919061541c565b506120b86135a9565b600080600061255e84612c29565b9092509050611186828261306d565b6065546001600160a01b031633146125975760405162461bcd60e51b8152600401610e03906156c1565b6000811180156125b157506125ae600a6064615b17565b81105b6125f05760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b6044820152606401610e03565b60db5460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160db55565b6065546001600160a01b0316331461265b5760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b03811661269e5760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b6044820152606401610e03565b60d680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156126e35760405162461bcd60e51b8152600401610e039061572d565b6002600155806127225760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b6044820152606401610e03565b6123828160006137ee565b6065546001600160a01b031633146127575760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b0381166127a05760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b6044820152606401610e03565b60d5546001600160a01b03828116911614156127fe5760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e7400000000000000006044820152606401610e03565b60d580546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156128435760405162461bcd60e51b8152600401610e039061572d565b600260015560cf546040805160e0810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f000000000000000000000000000000000000000000000000000000000000000081166020830190815293811682840181815260006060850181815260e4805461ffff9081166080890190815260dd5460a08a0190815260e15460c08b0190815260d85460d75460db549d51635a90235d60e01b8152918c1660048301528b166024820152604481019c909c528a518a1660648d01529b51891660848c0152955190971660a48a0152915160c48901529451169386019390935251610104850152935161012484015260c961014484015260cc610164840152909181908190819073e1d00f9bafea5aa40a2192af12b68af3d390afe290635a90235d906101840160806040518083038186803b15801561299b57600080fd5b505af41580156129af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d391906153c2565b93509350935093507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb08282604051612a15929190918252602082015260400190565b60405180910390a1612a268361339b565b6001600160681b03831660dc5560ce80546001600160a01b0319166001600160a01b0386161790556000612a5a428261306d565b905063ffffffff811115612ab05760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e526561647900000000000000006044820152606401610e03565b60cf805463ffffffff60a01b1916600160a01b63ffffffff841602179055612ad787614407565b6040516370a0823160e01b815230600482015262989680907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015612b3b57600080fd5b505afa158015612b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b73919061541c565b1115612b8157612b81613a39565b5050600180555050505050565b6065546001600160a01b03163314612bb85760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b038116612c1d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610e03565b612c268161399d565b50565b6001600160a01b038116600090815260d0602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b031691810191909152829160011115612cab575050506001600160a01b031660009081526097602052604081205491565b60cc54815161ffff908116600090815260d1602052604081205460c9549193612ce193869391169190610100900460ff16614573565b9050612d02856001600160a01b031660009081526097602052604090205490565b9590945092505050565b60026001541415612d2f5760405162461bcd60e51b8152600401610e039061572d565b60026001556000612d3e6145e4565b60e054909150612d4e9082613079565b6001600160801b031660e0555060018055565b6065546001600160a01b03163314612d8b5760405162461bcd60e51b8152600401610e03906156c1565b60026001541415612dae5760405162461bcd60e51b8152600401610e039061572d565b600260015580612df45760405162461bcd60e51b8152602060048201526011602482015270216e65774d61784465706f7369746f727360781b6044820152606401610e03565b60e25560018055565b6065546001600160a01b03163314612e275760405162461bcd60e51b8152600401610e03906156c1565b612e35620f42406064615b17565b8110612e7c5760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b6044820152606401610e03565b612e858161353e565b60da81905560408051918252602082018390527f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a150565b6001600160a01b038316612f285760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610e03565b6001600160a01b038216612f895760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610e03565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b03821630148061300a57506001600160a01b03831630145b6130625760405162461bcd60e51b8152602060048201526024808201527f54726561737572792072546f6b656e206973206e6f74207472616e736665727260448201526361626c6560e01b6064820152608401610e03565b61167c8383836147a1565b6000610e1f8284615a00565b6000610e1f8284615b59565b60d480546001600160a01b038316600090815260d3602052604090205460ff166130f15760405162461bcd60e51b815260206004820152601860248201527f4465706f7369746f7220646f6573206e6f7420657869737400000000000000006044820152606401610e03565b6001600160a01b038316600090815260d360205260408120805460ff191690555b61311d600183615b59565b8110156132905782818154811061314457634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b038581169116141561327e578261316f600184615b59565b8154811061318d57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b03168382815481106131cb57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b031684838154811061320957634e487b7160e01b600052603260045260246000fd5b600091825260208220019086613220600188615b59565b8154811061323e57634e487b7160e01b600052603260045260246000fd5b600091825260209091200180546001600160a01b039485166001600160a01b031990911617905581549383166101009190910a9081029202199092161790555b8061328881615bd7565b915050613112565b50818054806132af57634e487b7160e01b600052603160045260246000fd5b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b60c9546201000090046001600160a01b031661167c81848461496f565b60008061330183600a615a6f565b905060008611613311578061332f565b61332f866133296133228888613079565b849061499f565b906149ab565b9695505050505050565b6000600183116133835760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610e03565b61118661339183600a615a6f565b613329868661499f565b6001600160681b03811115612c265760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b6044820152606401610e03565b600054610100900460ff16806133fe575060005460ff16155b61341a5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff1615801561343c576000805461ffff19166101011790555b6134446149b7565b8015612c26576000805461ff001916905550565b600054610100900460ff1680613471575060005460ff16155b61348d5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff161580156134af576000805461ffff19166101011790555b6134b7614a26565b6134c18383614a90565b801561167c576000805461ff0019169055505050565b600054610100900460ff16806134f0575060005460ff16155b61350c5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff1615801561352e576000805461ffff19166101011790555b613536614a26565b613444614b25565b60e15460009081613550601e83615bf2565b1561357357613560600783615a18565b61356e9063031ba309615a18565b613596565b61357e601e83615a18565b61358990600c615a18565b61359690620f4240615b17565b90506111868161332986620f424061499f565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915260dc54806136235760405162461bcd60e51b81526020600482015260156024820152742163757272656e744f746f6b656e5072656d69756d60581b6044820152606401610e03565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561367e57600080fd5b505afa158015613692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136b69190615528565b60cf546001600160a01b0390811685527f00000000000000000000000000000000000000000000000000000000000000008116602086019081527f00000000000000000000000000000000000000000000000000000000000000008216604080880191825260ff94909416606088018181526080890188815260de5460a08b019081529651633baf17fd60e21b81528a51871660048201529451861660248601529251909416604484015292516064830152516084820152915160a4830152915073e1d00f9bafea5aa40a2192af12b68af3d390afe29063eebc5ff49060c40160206040518083038186803b1580156137ae57600080fd5b505af41580156137c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e6919061541c565b60df55505050565b33600090815260d0602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cc5491865260d19094529184205460c954919492909316926138699185918591610100900460ff16614573565b9050836138765784613878565b805b945084613886575050505050565b808511156138ca5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b6044820152606401610e03565b825161ffff168211156138f75733600090815260d060205260409020805462010000600160781b03191690555b613900856139ef565b61390a8186613079565b33600081815260d060205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916139839189825261ffff16602082015260400190565b60405180910390a2613996303387612feb565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160801b03811115612c265760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b6044820152606401610e03565b6040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038316906370a082319060240160206040518083038186803b158015613a9d57600080fd5b505afa158015613ab1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ad5919061541c565b905060008111613b275760405162461bcd60e51b815260206004820152601860248201527f6e6f207072656d69756d20746f206469737472696275746500000000000000006044820152606401610e03565b613b318282614b85565b6040516370a0823160e01b8152306004820152613bb39083906001600160a01b038216906370a082319060240160206040518083038186803b158015613b7657600080fd5b505afa158015613b8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bae919061541c565b614c2b565b5050565b604051632e99361160e01b8152600481018290526001600160a01b03831690632e99361190602401602060405180830381600087803b158015613bf957600080fd5b505af1158015613c0d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167c919061541c565b6001600160a01b038116600090815260d3602052604090205460ff16612c26576001600160a01b038116613ca05760405162461bcd60e51b815260206004820152601660248201527511195c1bdcda5d1bdc881859191c995cdcc81b9d5b1b60521b6044820152606401610e03565b60e25460d454613cb1906001615a00565b1115613d0a5760405162461bcd60e51b815260206004820152602260248201527f4e756d626572206f66206465706f7369746f72732065786365656473206c696d6044820152611a5d60f21b6064820152608401610e03565b6001600160a01b0316600081815260d360205260408120805460ff1916600190811790915560d4805491820181559091527f9780e26d96b1f2a9a18ef8fc72d589dbf03ef788137b64f43897e83a91e7feec0180546001600160a01b0319169091179055565b60cc5461ffff166000613d8584610f3f612224565b6001600160a01b038416600090815260d0602090815260408083208151606081018352905461ffff811682526201000081046001600160681b0316938201849052600160781b90046001600160801b031691810191909152929350613df1908790610f3f90813361113b565b60cb549091506001600160681b0316831115613e3c5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b6044820152606401610e03565b60ca54600160a01b900466ffffffffffffff16831015613e955760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610e03565b60e354811015613eb75760405162461bcd60e51b8152600401610e03906156f6565b60408051878152602081018690526001600160a01b038716917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2815161ffff16600090815260d1602052604081205460c954613f29918591889190610100900460ff16614573565b8351909150879061ffff16861415613f5a576020840151600090613f56906001600160681b03168a61306d565b9150505b613f638161339b565b6040805160608101825261ffff80891682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038f16600090815260d090935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560cd5461400991168a61306d565b9050614014816139ef565b60cd80546001600160801b0319166001600160801b03929092169190911790555050505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526140a99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614dd9565b50505050565b60cf5460009081908190600160a01b900463ffffffff164210156140fe5760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b6044820152606401610e03565b60ce546001600160a01b03169250826141475760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b6044820152606401610e03565b60cc805460d5546040805160a08101825260c954610100810460ff16825291516370a0823160e01b815230600482015261ffff909416946001600160a01b03938416946000948594859473e1d00f9bafea5aa40a2192af12b68af3d390afe29463686fb2f2949093919260208401926201000090910416906370a082319060240160206040518083038186803b1580156141e057600080fd5b505afa1580156141f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614218919061541c565b815260200161422660995490565b81526020018d81526020018960011415614241576000614245565b60da545b9052604080516001600160e01b031960e086901b168152600481019390935281516024840152602082015160448401528101516064830152606081015160848301526080015160a482015260c40160a06040518083038186803b1580156142ab57600080fd5b505af41580156142bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e391906154cf565b809650819750829550839a50849b5050505050508760ce60010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060ce60000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508060d1600087815260200190815260200160002081905550836001600160a01b03167fd42b93f751df91c40b94860a26f5e43b9fc78089c1e3df5d4602eee07009e5c683876040516143a8929190918252602082015260400190565b60405180910390a260cd80546001600160801b03191690556143cb856001615a00565b60cc805461ffff191661ffff92909216919091179055506143ec3083614eab565b80156143fc576143fc83826132d6565b505050509193909250565b60cc546201000090046001600160681b03166001600160a01b0382161561444d5760cc8054600160781b600160e01b031916600160781b6001600160681b038416021790555b60cc805462010000600160781b031916905560cf80546001600160a01b03191690556001600160a01b03821615613bb357604051636c6fe87f60e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015260009073e1d00f9bafea5aa40a2192af12b68af3d390afe29063d8dfd0fe9060240160206040518083038186803b1580156144f457600080fd5b505af4158015614508573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061452c919061541c565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f783604051612fde91815260200190565b835160009061ffff161580159061458e5750845161ffff1684115b156145d05760006145ad86602001516001600160681b03168585614f8a565b60408701519091506145c8906001600160801b03168261306d565b915050611186565b50505050604001516001600160801b031690565b33600090815260d26020526040812080546001600160801b03620100008204169061ffff16816146465760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b6044820152606401610e03565b60cc5461ffff16811061468e5760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b6044820152606401610e03565b33600090815260d260205260409020805462010000600160901b031916905560cd546146ca90600160801b90046001600160801b031683613079565b60cd80546001600160801b03928316600160801b029216919091179055600081815260d1602052604081205460c95461470d91859160ff61010090910416613339565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a26147553084614fed565b600081116147975760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b6044820152606401610e03565b61118633826132d6565b6001600160a01b0383166148055760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610e03565b6001600160a01b0382166148675760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610e03565b6001600160a01b038316600090815260976020526040902054818110156148df5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610e03565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290614916908490615a00565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161496291815260200190565b60405180910390a36140a9565b6040516001600160a01b03831660248201526044810182905261167c90849063a9059cbb60e01b90606401614072565b6000610e1f8284615b17565b6000610e1f8284615a18565b600054610100900460ff16806149d0575060005460ff16155b6149ec5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614a0e576000805461ffff19166101011790555b600180558015612c26576000805461ff001916905550565b600054610100900460ff1680614a3f575060005460ff16155b614a5b5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015613444576000805461ffff19166101011790558015612c26576000805461ff001916905550565b600054610100900460ff1680614aa9575060005460ff16155b614ac55760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614ae7576000805461ffff19166101011790555b8251614afa90609a906020860190615246565b508151614b0e90609b906020850190615246565b50801561167c576000805461ff0019169055505050565b600054610100900460ff1680614b3e575060005460ff16155b614b5a5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614b7c576000805461ffff19166101011790555b6134443361399d565b60d5546001600160a01b03166000614bb2614ba4620f42406064615b17565b60d95461332990869061499f565b9050614bc86001600160a01b038516838361496f565b60cc546001600160a01b038316907f8df5f80a19898219351b3c8e70c8cffe1adc78e2394d21dc2da56a39e5dabfe8908390614c0a9060019061ffff16615b36565b6040805192835261ffff90911660208301520160405180910390a250505050565b60d4805460009067ffffffffffffffff811115614c5857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614c81578160200160208202803683370190505b509050600060e054614c9260995490565b614c9c9190615b59565b905060005b8354811015614d7e576000848281548110614ccc57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03169050614cfa8361332988614cf485612550565b9061499f565b848381518110614d1a57634e487b7160e01b600052603260045260246000fd5b602002602001018181525050614d6b81858481518110614d4a57634e487b7160e01b600052603260045260246000fd5b6020026020010151896001600160a01b031661496f9092919063ffffffff16565b5080614d7681615bd7565b915050614ca1565b5060cc547fe55fc5ddbb2bfc597b0e1d510ece6ff2733bd909a81db6e2b6e092aeda7af1c390859084908690614dba9060019061ffff16615b36565b604051614dca94939291906158d5565b60405180910390a15050505050565b6000614e2e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661513b9092919063ffffffff16565b80519091501561167c5780806020019051810190614e4c9190615400565b61167c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610e03565b6001600160a01b038216614f015760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610e03565b8060996000828254614f139190615a00565b90915550506001600160a01b03821660009081526097602052604081208054839290614f40908490615a00565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614fd45760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610e03565b61118683613329614fe685600a615a6f565b879061499f565b6001600160a01b03821661504d5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610e03565b6001600160a01b038216600090815260976020526040902054818110156150c15760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610e03565b6001600160a01b03831660009081526097602052604081208383039055609980548492906150f0908490615b59565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6060611186848460008585843b6151945760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e03565b600080866001600160a01b031685876040516151b09190615603565b60006040518083038185875af1925050503d80600081146151ed576040519150601f19603f3d011682016040523d82523d6000602084013e6151f2565b606091505b509150915061520282828661520d565b979650505050505050565b6060831561521c575081610e1f565b82511561522c5782518084602001fd5b8160405162461bcd60e51b8152600401610e03919061561f565b82805461525290615b9c565b90600052602060002090601f01602090048101928261527457600085556152ba565b82601f1061528d57805160ff19168380011785556152ba565b828001600101855582156152ba579182015b828111156152ba57825182559160200191906001019061529f565b506152c69291506152ca565b5090565b5b808211156152c657600081556001016152cb565b80356152ea81615d6a565b919050565b803563ffffffff811681146152ea57600080fd5b600060208284031215615314578081fd5b8135610e1f81615d6a565b60008060408385031215615331578081fd5b823561533c81615d6a565b9150602083013561534c81615d6a565b809150509250929050565b60008060006060848603121561536b578081fd5b833561537681615d6a565b9250602084013561538681615d6a565b929592945050506040919091013590565b600080604083850312156153a9578182fd5b82356153b481615d6a565b946020939093013593505050565b600080600080608085870312156153d7578081fd5b84516153e281615d6a565b60208601516040870151606090970151919890975090945092505050565b600060208284031215615411578081fd5b8151610e1f81615d7f565b60006020828403121561542d578081fd5b5051919050565b60008082840360e0811215615447578283fd5b833567ffffffffffffffff81111561545d578384fd5b84016101c0818703121561546f578384fd5b925060c0601f1982011215615482578182fd5b506020830190509250929050565b6000602082840312156154a1578081fd5b81356001600160801b0381168114610e1f578182fd5b6000602082840312156154c8578081fd5b5035919050565b600080600080600060a086880312156154e6578283fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b60006020828403121561551f578081fd5b610e1f826152ef565b600060208284031215615539578081fd5b8151610e1f81615db7565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b803561557881615d7f565b15158252602081013561558a81615db7565b60ff16602083015260408101356155a081615d6a565b6001600160a01b0390811660408401526060820135906155bf82615d6a565b16606083015260808101356155d381615da2565b66ffffffffffffff16608083015260a08101356155ef81615d8d565b6001600160681b03811660a0840152505050565b60008251615615818460208701615b70565b9190910192915050565b602081526000825180602084015261563e816040850160208701615b70565b601f01601f19169190910160400192915050565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601b908201527f4d696e696d756d206465706f736974206e6f7420726561636865640000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600061010080835261578881840161577b886152df565b6001600160a01b03169052565b615794602087016152df565b6101206157ab818601836001600160a01b03169052565b6157b7604089016152df565b91506101406157d0818701846001600160a01b03169052565b610160925060608901358387015261018060808a0135818801526157f760a08b018b6159bb565b6101c06101a081818c01526158116102c08c018486615544565b935061582060c08f018f6159bb565b8c860360ff1901848e01529350615838858583615544565b94505061584760e08f016152df565b6001600160a01b0381166101e08d01529250615864898f016152df565b6001600160a01b0381166102008d01529850615881878f016152ef565b63ffffffff166102208c0152948d01356102408b01525050938a01356102608801528901356102808701528801356102a08601525091506158c79050602083018561556d565b8260e0830152949350505050565b600060808201868352602060808185015281875180845260a0860191508289019350845b81811015615915578451835293830193918301916001016158f9565b5050848103604086015286548082528785528285209183019350845b818110156159565782546001600160a01b031685529383019360019283019201615931565b50505061ffff8516606085015250905095945050505050565b6000808335601e19843603018112615985578283fd5b83018035915067ffffffffffffffff82111561599f578283fd5b6020019150368190038213156159b457600080fd5b9250929050565b6000808335601e198436030181126159d1578283fd5b830160208101925035905067ffffffffffffffff8111156159f157600080fd5b8036038313156159b457600080fd5b60008219821115615a1357615a13615c06565b500190565b600082615a2757615a27615c1c565b500490565b600181815b80851115615a67578160001904821115615a4d57615a4d615c06565b80851615615a5a57918102915b93841c9390800290615a31565b509250929050565b6000610e1f8383600082615a8557506001610d6f565b81615a9257506000610d6f565b8160018114615aa85760028114615ab257615ace565b6001915050610d6f565b60ff841115615ac357615ac3615c06565b50506001821b610d6f565b5060208310610133831016604e8410600b8410161715615af1575081810a610d6f565b615afb8383615a2c565b8060001904821115615b0f57615b0f615c06565b029392505050565b6000816000190483118215151615615b3157615b31615c06565b500290565b600061ffff83811690831681811015615b5157615b51615c06565b039392505050565b600082821015615b6b57615b6b615c06565b500390565b60005b83811015615b8b578181015183820152602001615b73565b838111156140a95750506000910152565b600181811c90821680615bb057607f821691505b60208210811415615bd157634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615beb57615beb615c06565b5060010190565b600082615c0157615c01615c1c565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008135610d6f81615d6a565b60008135610d6f81615d8d565b60008135610d6f81615da2565b8135615c6481615d7f565b815460ff19811691151560ff1691821783556020840135615c8481615db7565b61ff008160081b169050808361ffff198416171784556040850135615ca881615d6a565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d00615ce360608501615c32565b82546001600160a01b0319166001600160a01b0391909116178255565b615d39615d0f60808501615c4c565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b50613bb3615d4960a08401615c3f565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b0381168114612c2657600080fd5b8015158114612c2657600080fd5b6001600160681b0381168114612c2657600080fd5b66ffffffffffffff81168114612c2657600080fd5b60ff81168114612c2657600080fdfea2646970667358221220bfd8b76dd3adda30ff911dad235dc4340796dbce4e51ed16f3169f142384f9ea64736f6c63430008040033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc720000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104965760003560e01c80638da5cb5b11610262578063b4befff211610151578063e73c63d5116100ce578063f656ba5111610092578063f656ba5114610c40578063f756fa2114610c68578063f79d8d1214610c70578063f957a06714610c93578063fc29738014610ca6578063fe56e23214610cb957600080fd5b8063e73c63d514610c00578063e74b981b14610c09578063ea00b1ca14610c1c578063ef78d4fd14610c24578063f2fde38b14610c2d57600080fd5b8063d164cc1511610115578063d164cc1514610b71578063d5f2638214610b84578063db006a7514610b8d578063db43e86214610ba0578063dd62ed3e14610bc757600080fd5b8063b4befff214610b1d578063b6b55f2514610b30578063b9f8092b14610b43578063ce7c2ac214610b4b578063d13f1b3e14610b5e57600080fd5b8063a285c9e8116101df578063a9059cbb116101a3578063a9059cbb14610ab5578063aced166114610ac8578063ad5c464814610adb578063ad7a672f14610b02578063afa6626414610b0a57600080fd5b8063a285c9e814610a60578063a2db9d8314610a75578063a457c2d714610a86578063a497e67414610a99578063a6f7f5d614610aac57600080fd5b806399530b061161022657806399530b06146109475780639b2e601b1461094f5780639be43daa146109585780639fcc2d751461096b578063a083ff1714610a0a57600080fd5b80638da5cb5b1461090b5780638fcc9cfb1461091c57806395d89b411461092f57806396d2a76b14610937578063990c56101461093f57600080fd5b80634af96d29116103895780636f31ab34116103065780637e108d52116102ca5780637e108d521461088e57806383536ff3146108a157806387153eb1146108aa57806387788782146108ca57806389a30271146108d35780638b10cc7c146108fa57600080fd5b80636f31ab34146107eb57806370897b23146107f357806370a0823114610806578063715018a61461082f5780637a9262a21461083757600080fd5b8063650cce8a1161034d578063650cce8a146107165780636719b2ee1461073d57806369b41170146107b45780636b64c769146107bc5780636c10f66c146107c457600080fd5b80634af96d29146106cc578063503c70aa146106df57806355489bb2146106e8578063600a2cfb146106fb578063601d6ba91461070357600080fd5b8063355274ea1161041757806341b3d185116103db57806341b3d18514610681578063432833a61461068a5780634603c0aa1461069357806346904840146106a657806347786d37146106b957600080fd5b8063355274ea1461061857806339509351146106295780633ec143d31461063c5780633f23bb731461065d5780633f90916a1461067057600080fd5b806323b872dd1161045e57806323b872dd1461053a5780632728f3331461054d5780632775d01c146105d357806330630da4146105e8578063313ce567146105fb57600080fd5b806306fdde031461049b578063095ea7b3146104b95780630cbf54c8146104dc57806318160ddd146104f35780631a92f6c2146104fb575b600080fd5b6104a3610ccc565b6040516104b0919061561f565b60405180910390f35b6104cc6104c7366004615397565b610d5e565b60405190151581526020016104b0565b6104e560de5481565b6040519081526020016104b0565b6099546104e5565b6105227f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc7281565b6040516001600160a01b0390911681526020016104b0565b6104cc610548366004615357565b610d75565b60cc5460cd5461058e9161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a0016104b0565b6105e66105e13660046154b7565b610e26565b005b6105e66105f6366004615303565b611067565b60c954610100900460ff1660405160ff90911681526020016104b0565b60cb546001600160681b03166104e5565b6104cc610637366004615397565b6110ff565b60e45461064a9061ffff1681565b60405161ffff90911681526020016104b0565b6104e561066b366004615303565b61113b565b60cd546001600160801b03166104e5565b6104e560e35481565b6104e560df5481565b6105e66106a1366004615303565b61118e565b60d554610522906001600160a01b031681565b6105e66106c73660046154b7565b611230565b6105e66106da366004615434565b61130b565b6104e560e05481565b6105e66106f6366004615490565b611681565b6105e6611742565b6105226107113660046154b7565b61189d565b6105227f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef81565b61078361074b366004615303565b60d06020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b0316908201526060016104b0565b6104e5600081565b6105e66118c7565b6105227f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10181565b6105e6611927565b6105e66108013660046154b7565b61195e565b6104e5610814366004615303565b6001600160a01b031660009081526097602052604090205490565b6105e6611a25565b61086c610845366004615303565b60d26020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b039091166020830152016104b0565b6105e661089c3660046154b7565b611a5b565b6104e560db5481565b6104e56108b83660046154b7565b60d16020526000908152604090205481565b6104e560d95481565b6105227f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b60cf546001600160a01b0316610522565b6065546001600160a01b0316610522565b6105e661092a3660046154b7565b611d61565b6104a3611dfa565b6105e6611e09565b6105e6611e63565b6104e5611f8d565b6104e560e25481565b6105e66109663660046154b7565b611fc7565b60c95460ca5460cb546109ba9260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c0016104b0565b60ce5460cf54610a34916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff16908201526060016104b0565b60cf54600160a01b900463ffffffff166104e5565b60ce546001600160a01b0316610522565b6104cc610a94366004615397565b6120c1565b6105e6610aa73660046154b7565b61215a565b6104e560da5481565b6104cc610ac3366004615397565b612217565b60d654610522906001600160a01b031681565b6105227f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6104e5612224565b60d754610522906001600160a01b031681565b6105e6610b2b3660046154b7565b6122c3565b6105e6610b3e3660046154b7565b6122f2565b6105e6612389565b6104e5610b59366004615303565b612550565b6105e6610b6c3660046154b7565b61256d565b6105e6610b7f366004615303565b612631565b6104e560dc5481565b6105e6610b9b3660046154b7565b6126c0565b6105227f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e81565b6104e5610bd536600461531f565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b6104e560dd5481565b6105e6610c17366004615303565b61272d565b6105e6612820565b6104e560e15481565b6105e6610c3b366004615303565b612b8e565b610c53610c4e366004615303565b612c29565b604080519283526020830191909152016104b0565b6105e6612d0c565b6104cc610c7e366004615303565b60d36020526000908152604090205460ff1681565b60d854610522906001600160a01b031681565b6105e6610cb43660046154b7565b612d61565b6105e6610cc73660046154b7565b612dfd565b6060609a8054610cdb90615b9c565b80601f0160208091040260200160405190810160405280929190818152602001828054610d0790615b9c565b8015610d545780601f10610d2957610100808354040283529160200191610d54565b820191906000526020600020905b815481529060010190602001808311610d3757829003601f168201915b5050505050905090565b6000610d6b338484612ec6565b5060015b92915050565b6000610d82848484612feb565b6001600160a01b038416600090815260986020908152604080832033845290915290205482811015610e0c5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610e198533858403612ec6565b60019150505b9392505050565b60026001541415610e495760405162461bcd60e51b8152600401610e039061572d565b600260015533600090815260d06020526040902060cc5461ffff1682610e9b5760405162461bcd60e51b815260206004820152600760248201526608585b5bdd5b9d60ca1b6044820152606401610e03565b815461ffff168114610edf5760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b6044820152606401610e03565b81546201000090046001600160681b031683811015610f305760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b6044820152606401610e03565b6000610f4582610f3f3361113b565b9061306d565b905084811115610f81576000610f5b8287613079565b905060e354811015610f7f5760405162461bcd60e51b8152600401610e03906156f6565b505b610f8b8286613079565b84546001600160681b0391909116620100000262010000600160781b031990911617845560cd54610fc5906001600160801b031686613079565b60cd80546001600160801b0319166001600160801b0392909216919091179055604080518681526020810185905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a283546201000090046001600160681b0316158015611044575061104233612550565b155b156110525761105233613085565b61105c33866132d6565b505060018055505050565b6065546001600160a01b031633146110915760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b0381166110dd5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b6044820152606401610e03565b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b3360008181526098602090815260408083206001600160a01b03871684529091528120549091610d6b918590611136908690615a00565b612ec6565b60c95460009060ff610100909104168161117161115760995490565b61115f612224565b60cd546001600160801b0316856132f3565b905061118661117f85612550565b8284613339565b949350505050565b6065546001600160a01b031633146111b85760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b03811661120e5760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d50726963657200000000000000006044820152606401610e03565b60d780546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b0316331461125a5760405162461bcd60e51b8152600401610e03906156c1565b600081116112945760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b6044820152606401610e03565b61129d8161339b565b60cb54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160cb80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b600054610100900460ff1680611324575060005460ff16155b6113405760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015611362576000805461ffff19166101011790555b6040516384d831a960e01b815273e1d00f9bafea5aa40a2192af12b68af3d390afe2906384d831a99061139f908690869061012c90600401615764565b60006040518083038186803b1580156113b757600080fd5b505af41580156113cb573d6000803e3d6000fd5b505050506113d76133e5565b6114636113e760a085018561596f565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506114299250505060c086018661596f565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061345892505050565b61146b6134d7565b61147b610c3b6020850185615303565b61148b6040840160208501615303565b60d680546001600160a01b0319166001600160a01b039290921691909117905561016083013560e1556114c5610100840160e08501615303565b60d780546001600160a01b0319166001600160a01b03929092169190911790556114f761012084016101008501615303565b60d880546001600160a01b0319166001600160a01b03929092169190911790556115296101408401610120850161550e565b63ffffffff1660db5561014083013560de5561154b6060840160408501615303565b60d580546001600160a01b0319166001600160a01b0392909216919091179055608083013560d955611580606084013561353e565b60da5561018083013560e2556101a083013560e3558160c96115a28282615c59565b505060cc805461ffff1916600117905560c9546040516370a0823160e01b81523060048201526000916001600160a01b036201000090910416906370a082319060240160206040518083038186803b1580156115fd57600080fd5b505afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611635919061541c565b90506116408161339b565b60cc80546001600160681b03909216600160781b02600160781b600160e01b0319909216919091179055801561167c576000805461ff00191690555b505050565b6065546001600160a01b031633146116ab5760405162461bcd60e51b8152600401610e03906156c1565b600260015414156116ce5760405162461bcd60e51b8152600401610e039061572d565b60026001556001600160801b0381166117185760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b6044820152606401610e03565b6001600160801b031660dd5560cc5460e4805461ffff191661ffff90921691909117905560018055565b60d6546001600160a01b0316331461176c5760405162461bcd60e51b8152600401610e0390615652565b6002600154141561178f5760405162461bcd60e51b8152600401610e039061572d565b600260015560cf546040516358ffbb3d60e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc7281166004830152909116602482015260009073e1d00f9bafea5aa40a2192af12b68af3d390afe2906358ffbb3d9060440160206040518083038186803b15801561181657600080fd5b505af415801561182a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184e919061541c565b60cc5490915061186d906201000090046001600160681b031682613079565b60cc80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d481815481106118ad57600080fd5b6000918252602090912001546001600160a01b0316905081565b60d6546001600160a01b031633146118f15760405162461bcd60e51b8152600401610e0390615652565b600260015414156119145760405162461bcd60e51b8152600401610e039061572d565b60026001556119216135a9565b60018055565b6002600154141561194a5760405162461bcd60e51b8152600401610e039061572d565b6002600181905550611921600060016137ee565b6065546001600160a01b031633146119885760405162461bcd60e51b8152600401610e03906156c1565b611996620f42406064615b17565b81106119e45760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e6365206665650000000000000000006044820152606401610e03565b60d95460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d955565b6065546001600160a01b03163314611a4f5760405162461bcd60e51b8152600401610e03906156c1565b611a59600061399d565b565b60026001541415611a7e5760405162461bcd60e51b8152600401610e039061572d565b600260015580611abd5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b6044820152606401610e03565b33600090815260d06020526040902080546201000090046001600160681b0316151580611afa57508054600160781b90046001600160801b031615155b15611b0b57611b0b600060016137ee565b60cc5433600081815260d260209081526040918290208054835188815261ffff96871693810184905292959194911692838614927f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a282546201000090046001600160801b031660008215611b9457611b8d828961306d565b9050611bf7565b8115611bd65760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b6044820152606401610e03565b5033600090815260d260205260409020805461ffff191661ffff8716179055865b60c954610100900460ff166000611c0d3361113b565b90506000611c538b8a60011415611c2e57611c2985600a615a6f565b611c4d565b60d16000611c3d60018e615b59565b8152602001908152602001600020545b85613339565b905080821115611c8f576000611c698383613079565b905060e354811015611c8d5760405162461bcd60e51b8152600401610e03906156f6565b505b611c98846139ef565b33600090815260d26020526040812080546001600160801b03808816620100000262010000600160901b03199092169190911790915560cd54611ce491600160801b909104168d61306d565b9050611cef816139ef565b60cd80546001600160801b03808416600160801b0291161790558a546001600160681b036201000090910416158015611d365750336000908152609760205260409020548c145b15611d4457611d4433613085565b611d4f33308e612feb565b50506001805550505050505050505050565b6065546001600160a01b03163314611d8b5760405162461bcd60e51b8152600401610e03906156c1565b60026001541415611dae5760405162461bcd60e51b8152600401610e039061572d565b600260015580611df15760405162461bcd60e51b815260206004820152600e60248201526d085b995dd35a5b91195c1bdcda5d60921b6044820152606401610e03565b60e35560018055565b6060609b8054610cdb90615b9c565b60d6546001600160a01b03163314611e335760405162461bcd60e51b8152600401610e0390615652565b60026001541415611e565760405162461bcd60e51b8152600401610e039061572d565b6002600155611921613a39565b60d6546001600160a01b03163314611e8d5760405162461bcd60e51b8152600401610e0390615652565b60026001541415611eb05760405162461bcd60e51b8152600401610e039061572d565b600260015560df54611ee3907f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10190613bb7565b6040516370a0823160e01b815230600482015262989680907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a082319060240160206040518083038186803b158015611f4757600080fd5b505afa158015611f5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f7f919061541c565b111561192157611921613a39565b6000611fc2611f9b60995490565b611fa3612224565b60cd5460c9546001600160801b0390911690610100900460ff166132f3565b905090565b60026001541415611fea5760405162461bcd60e51b8152600401610e039061572d565b6002600155806120295760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b6044820152606401610e03565b60cc5461ffff1660005b828110156120b85760006120478284615a00565b600081815260d16020526040902054909150156120945760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b6044820152606401610e03565b600090815260d16020526040902060019055806120b081615bd7565b915050612033565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156121435760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610e03565b6121503385858403612ec6565b5060019392505050565b6065546001600160a01b031633146121845760405162461bcd60e51b8152600401610e03906156c1565b61012c8110156121d65760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e00000000000000006044820152606401610e03565b60de5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160de55565b6000610d6b338484612feb565b60c9546040516370a0823160e01b8152306004820152600091611fc291620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561227357600080fd5b505afa158015612287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ab919061541c565b60cc546201000090046001600160681b03169061306d565b6065546001600160a01b031633146122ed5760405162461bcd60e51b8152600401610e03906156c1565b60dc55565b600260015414156123155760405162461bcd60e51b8152600401610e039061572d565b6002600155806123515760405162461bcd60e51b815260206004820152600760248201526608585b5bdd5b9d60ca1b6044820152606401610e03565b61235a33613c31565b6123648133613d70565b60c954612382906201000090046001600160a01b031633308461403e565b5060018055565b60d6546001600160a01b031633146123b35760405162461bcd60e51b8152600401610e0390615652565b600260015414156123d65760405162461bcd60e51b8152600401610e039061572d565b600260018190555060008060006123ee60e0546140af565b60e0819055919450925090506124038261339b565b60cc805462010000600160781b031916620100006001600160681b0385160217905560405182815233906001600160a01b038516907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc72811660048301527f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef81166024830152841660448201526064810183905273e1d00f9bafea5aa40a2192af12b68af3d390afe290632904c2399060840160206040518083038186803b15801561250f57600080fd5b505af4158015612523573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612547919061541c565b506120b86135a9565b600080600061255e84612c29565b9092509050611186828261306d565b6065546001600160a01b031633146125975760405162461bcd60e51b8152600401610e03906156c1565b6000811180156125b157506125ae600a6064615b17565b81105b6125f05760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b6044820152606401610e03565b60db5460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160db55565b6065546001600160a01b0316331461265b5760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b03811661269e5760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b6044820152606401610e03565b60d680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156126e35760405162461bcd60e51b8152600401610e039061572d565b6002600155806127225760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b6044820152606401610e03565b6123828160006137ee565b6065546001600160a01b031633146127575760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b0381166127a05760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b6044820152606401610e03565b60d5546001600160a01b03828116911614156127fe5760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e7400000000000000006044820152606401610e03565b60d580546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156128435760405162461bcd60e51b8152600401610e039061572d565b600260015560cf546040805160e0810182526001600160a01b037f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e811682527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881166020830190815293811682840181815260006060850181815260e4805461ffff9081166080890190815260dd5460a08a0190815260e15460c08b0190815260d85460d75460db549d51635a90235d60e01b8152918c1660048301528b166024820152604481019c909c528a518a1660648d01529b51891660848c0152955190971660a48a0152915160c48901529451169386019390935251610104850152935161012484015260c961014484015260cc610164840152909181908190819073e1d00f9bafea5aa40a2192af12b68af3d390afe290635a90235d906101840160806040518083038186803b15801561299b57600080fd5b505af41580156129af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d391906153c2565b93509350935093507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb08282604051612a15929190918252602082015260400190565b60405180910390a1612a268361339b565b6001600160681b03831660dc5560ce80546001600160a01b0319166001600160a01b0386161790556000612a5a428261306d565b905063ffffffff811115612ab05760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e526561647900000000000000006044820152606401610e03565b60cf805463ffffffff60a01b1916600160a01b63ffffffff841602179055612ad787614407565b6040516370a0823160e01b815230600482015262989680907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a082319060240160206040518083038186803b158015612b3b57600080fd5b505afa158015612b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b73919061541c565b1115612b8157612b81613a39565b5050600180555050505050565b6065546001600160a01b03163314612bb85760405162461bcd60e51b8152600401610e03906156c1565b6001600160a01b038116612c1d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610e03565b612c268161399d565b50565b6001600160a01b038116600090815260d0602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b031691810191909152829160011115612cab575050506001600160a01b031660009081526097602052604081205491565b60cc54815161ffff908116600090815260d1602052604081205460c9549193612ce193869391169190610100900460ff16614573565b9050612d02856001600160a01b031660009081526097602052604090205490565b9590945092505050565b60026001541415612d2f5760405162461bcd60e51b8152600401610e039061572d565b60026001556000612d3e6145e4565b60e054909150612d4e9082613079565b6001600160801b031660e0555060018055565b6065546001600160a01b03163314612d8b5760405162461bcd60e51b8152600401610e03906156c1565b60026001541415612dae5760405162461bcd60e51b8152600401610e039061572d565b600260015580612df45760405162461bcd60e51b8152602060048201526011602482015270216e65774d61784465706f7369746f727360781b6044820152606401610e03565b60e25560018055565b6065546001600160a01b03163314612e275760405162461bcd60e51b8152600401610e03906156c1565b612e35620f42406064615b17565b8110612e7c5760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b6044820152606401610e03565b612e858161353e565b60da81905560408051918252602082018390527f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a150565b6001600160a01b038316612f285760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610e03565b6001600160a01b038216612f895760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610e03565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b03821630148061300a57506001600160a01b03831630145b6130625760405162461bcd60e51b8152602060048201526024808201527f54726561737572792072546f6b656e206973206e6f74207472616e736665727260448201526361626c6560e01b6064820152608401610e03565b61167c8383836147a1565b6000610e1f8284615a00565b6000610e1f8284615b59565b60d480546001600160a01b038316600090815260d3602052604090205460ff166130f15760405162461bcd60e51b815260206004820152601860248201527f4465706f7369746f7220646f6573206e6f7420657869737400000000000000006044820152606401610e03565b6001600160a01b038316600090815260d360205260408120805460ff191690555b61311d600183615b59565b8110156132905782818154811061314457634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b038581169116141561327e578261316f600184615b59565b8154811061318d57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b03168382815481106131cb57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b031684838154811061320957634e487b7160e01b600052603260045260246000fd5b600091825260208220019086613220600188615b59565b8154811061323e57634e487b7160e01b600052603260045260246000fd5b600091825260209091200180546001600160a01b039485166001600160a01b031990911617905581549383166101009190910a9081029202199092161790555b8061328881615bd7565b915050613112565b50818054806132af57634e487b7160e01b600052603160045260246000fd5b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b60c9546201000090046001600160a01b031661167c81848461496f565b60008061330183600a615a6f565b905060008611613311578061332f565b61332f866133296133228888613079565b849061499f565b906149ab565b9695505050505050565b6000600183116133835760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610e03565b61118661339183600a615a6f565b613329868661499f565b6001600160681b03811115612c265760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b6044820152606401610e03565b600054610100900460ff16806133fe575060005460ff16155b61341a5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff1615801561343c576000805461ffff19166101011790555b6134446149b7565b8015612c26576000805461ff001916905550565b600054610100900460ff1680613471575060005460ff16155b61348d5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff161580156134af576000805461ffff19166101011790555b6134b7614a26565b6134c18383614a90565b801561167c576000805461ff0019169055505050565b600054610100900460ff16806134f0575060005460ff16155b61350c5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff1615801561352e576000805461ffff19166101011790555b613536614a26565b613444614b25565b60e15460009081613550601e83615bf2565b1561357357613560600783615a18565b61356e9063031ba309615a18565b613596565b61357e601e83615a18565b61358990600c615a18565b61359690620f4240615b17565b90506111868161332986620f424061499f565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915260dc54806136235760405162461bcd60e51b81526020600482015260156024820152742163757272656e744f746f6b656e5072656d69756d60581b6044820152606401610e03565b60007f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561367e57600080fd5b505afa158015613692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136b69190615528565b60cf546001600160a01b0390811685527f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d1018116602086019081527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488216604080880191825260ff94909416606088018181526080890188815260de5460a08b019081529651633baf17fd60e21b81528a51871660048201529451861660248601529251909416604484015292516064830152516084820152915160a4830152915073e1d00f9bafea5aa40a2192af12b68af3d390afe29063eebc5ff49060c40160206040518083038186803b1580156137ae57600080fd5b505af41580156137c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e6919061541c565b60df55505050565b33600090815260d0602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cc5491865260d19094529184205460c954919492909316926138699185918591610100900460ff16614573565b9050836138765784613878565b805b945084613886575050505050565b808511156138ca5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b6044820152606401610e03565b825161ffff168211156138f75733600090815260d060205260409020805462010000600160781b03191690555b613900856139ef565b61390a8186613079565b33600081815260d060205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916139839189825261ffff16602082015260400190565b60405180910390a2613996303387612feb565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160801b03811115612c265760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b6044820152606401610e03565b6040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906000906001600160a01b038316906370a082319060240160206040518083038186803b158015613a9d57600080fd5b505afa158015613ab1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ad5919061541c565b905060008111613b275760405162461bcd60e51b815260206004820152601860248201527f6e6f207072656d69756d20746f206469737472696275746500000000000000006044820152606401610e03565b613b318282614b85565b6040516370a0823160e01b8152306004820152613bb39083906001600160a01b038216906370a082319060240160206040518083038186803b158015613b7657600080fd5b505afa158015613b8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bae919061541c565b614c2b565b5050565b604051632e99361160e01b8152600481018290526001600160a01b03831690632e99361190602401602060405180830381600087803b158015613bf957600080fd5b505af1158015613c0d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167c919061541c565b6001600160a01b038116600090815260d3602052604090205460ff16612c26576001600160a01b038116613ca05760405162461bcd60e51b815260206004820152601660248201527511195c1bdcda5d1bdc881859191c995cdcc81b9d5b1b60521b6044820152606401610e03565b60e25460d454613cb1906001615a00565b1115613d0a5760405162461bcd60e51b815260206004820152602260248201527f4e756d626572206f66206465706f7369746f72732065786365656473206c696d6044820152611a5d60f21b6064820152608401610e03565b6001600160a01b0316600081815260d360205260408120805460ff1916600190811790915560d4805491820181559091527f9780e26d96b1f2a9a18ef8fc72d589dbf03ef788137b64f43897e83a91e7feec0180546001600160a01b0319169091179055565b60cc5461ffff166000613d8584610f3f612224565b6001600160a01b038416600090815260d0602090815260408083208151606081018352905461ffff811682526201000081046001600160681b0316938201849052600160781b90046001600160801b031691810191909152929350613df1908790610f3f90813361113b565b60cb549091506001600160681b0316831115613e3c5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b6044820152606401610e03565b60ca54600160a01b900466ffffffffffffff16831015613e955760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610e03565b60e354811015613eb75760405162461bcd60e51b8152600401610e03906156f6565b60408051878152602081018690526001600160a01b038716917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2815161ffff16600090815260d1602052604081205460c954613f29918591889190610100900460ff16614573565b8351909150879061ffff16861415613f5a576020840151600090613f56906001600160681b03168a61306d565b9150505b613f638161339b565b6040805160608101825261ffff80891682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038f16600090815260d090935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560cd5461400991168a61306d565b9050614014816139ef565b60cd80546001600160801b0319166001600160801b03929092169190911790555050505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526140a99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614dd9565b50505050565b60cf5460009081908190600160a01b900463ffffffff164210156140fe5760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b6044820152606401610e03565b60ce546001600160a01b03169250826141475760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b6044820152606401610e03565b60cc805460d5546040805160a08101825260c954610100810460ff16825291516370a0823160e01b815230600482015261ffff909416946001600160a01b03938416946000948594859473e1d00f9bafea5aa40a2192af12b68af3d390afe29463686fb2f2949093919260208401926201000090910416906370a082319060240160206040518083038186803b1580156141e057600080fd5b505afa1580156141f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614218919061541c565b815260200161422660995490565b81526020018d81526020018960011415614241576000614245565b60da545b9052604080516001600160e01b031960e086901b168152600481019390935281516024840152602082015160448401528101516064830152606081015160848301526080015160a482015260c40160a06040518083038186803b1580156142ab57600080fd5b505af41580156142bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e391906154cf565b809650819750829550839a50849b5050505050508760ce60010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060ce60000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508060d1600087815260200190815260200160002081905550836001600160a01b03167fd42b93f751df91c40b94860a26f5e43b9fc78089c1e3df5d4602eee07009e5c683876040516143a8929190918252602082015260400190565b60405180910390a260cd80546001600160801b03191690556143cb856001615a00565b60cc805461ffff191661ffff92909216919091179055506143ec3083614eab565b80156143fc576143fc83826132d6565b505050509193909250565b60cc546201000090046001600160681b03166001600160a01b0382161561444d5760cc8054600160781b600160e01b031916600160781b6001600160681b038416021790555b60cc805462010000600160781b031916905560cf80546001600160a01b03191690556001600160a01b03821615613bb357604051636c6fe87f60e11b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc7216600482015260009073e1d00f9bafea5aa40a2192af12b68af3d390afe29063d8dfd0fe9060240160206040518083038186803b1580156144f457600080fd5b505af4158015614508573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061452c919061541c565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f783604051612fde91815260200190565b835160009061ffff161580159061458e5750845161ffff1684115b156145d05760006145ad86602001516001600160681b03168585614f8a565b60408701519091506145c8906001600160801b03168261306d565b915050611186565b50505050604001516001600160801b031690565b33600090815260d26020526040812080546001600160801b03620100008204169061ffff16816146465760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b6044820152606401610e03565b60cc5461ffff16811061468e5760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b6044820152606401610e03565b33600090815260d260205260409020805462010000600160901b031916905560cd546146ca90600160801b90046001600160801b031683613079565b60cd80546001600160801b03928316600160801b029216919091179055600081815260d1602052604081205460c95461470d91859160ff61010090910416613339565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a26147553084614fed565b600081116147975760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b6044820152606401610e03565b61118633826132d6565b6001600160a01b0383166148055760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610e03565b6001600160a01b0382166148675760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610e03565b6001600160a01b038316600090815260976020526040902054818110156148df5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610e03565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290614916908490615a00565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161496291815260200190565b60405180910390a36140a9565b6040516001600160a01b03831660248201526044810182905261167c90849063a9059cbb60e01b90606401614072565b6000610e1f8284615b17565b6000610e1f8284615a18565b600054610100900460ff16806149d0575060005460ff16155b6149ec5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614a0e576000805461ffff19166101011790555b600180558015612c26576000805461ff001916905550565b600054610100900460ff1680614a3f575060005460ff16155b614a5b5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015613444576000805461ffff19166101011790558015612c26576000805461ff001916905550565b600054610100900460ff1680614aa9575060005460ff16155b614ac55760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614ae7576000805461ffff19166101011790555b8251614afa90609a906020860190615246565b508151614b0e90609b906020850190615246565b50801561167c576000805461ff0019169055505050565b600054610100900460ff1680614b3e575060005460ff16155b614b5a5760405162461bcd60e51b8152600401610e0390615673565b600054610100900460ff16158015614b7c576000805461ffff19166101011790555b6134443361399d565b60d5546001600160a01b03166000614bb2614ba4620f42406064615b17565b60d95461332990869061499f565b9050614bc86001600160a01b038516838361496f565b60cc546001600160a01b038316907f8df5f80a19898219351b3c8e70c8cffe1adc78e2394d21dc2da56a39e5dabfe8908390614c0a9060019061ffff16615b36565b6040805192835261ffff90911660208301520160405180910390a250505050565b60d4805460009067ffffffffffffffff811115614c5857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614c81578160200160208202803683370190505b509050600060e054614c9260995490565b614c9c9190615b59565b905060005b8354811015614d7e576000848281548110614ccc57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03169050614cfa8361332988614cf485612550565b9061499f565b848381518110614d1a57634e487b7160e01b600052603260045260246000fd5b602002602001018181525050614d6b81858481518110614d4a57634e487b7160e01b600052603260045260246000fd5b6020026020010151896001600160a01b031661496f9092919063ffffffff16565b5080614d7681615bd7565b915050614ca1565b5060cc547fe55fc5ddbb2bfc597b0e1d510ece6ff2733bd909a81db6e2b6e092aeda7af1c390859084908690614dba9060019061ffff16615b36565b604051614dca94939291906158d5565b60405180910390a15050505050565b6000614e2e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661513b9092919063ffffffff16565b80519091501561167c5780806020019051810190614e4c9190615400565b61167c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610e03565b6001600160a01b038216614f015760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610e03565b8060996000828254614f139190615a00565b90915550506001600160a01b03821660009081526097602052604081208054839290614f40908490615a00565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614fd45760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610e03565b61118683613329614fe685600a615a6f565b879061499f565b6001600160a01b03821661504d5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610e03565b6001600160a01b038216600090815260976020526040902054818110156150c15760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610e03565b6001600160a01b03831660009081526097602052604081208383039055609980548492906150f0908490615b59565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6060611186848460008585843b6151945760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e03565b600080866001600160a01b031685876040516151b09190615603565b60006040518083038185875af1925050503d80600081146151ed576040519150601f19603f3d011682016040523d82523d6000602084013e6151f2565b606091505b509150915061520282828661520d565b979650505050505050565b6060831561521c575081610e1f565b82511561522c5782518084602001fd5b8160405162461bcd60e51b8152600401610e03919061561f565b82805461525290615b9c565b90600052602060002090601f01602090048101928261527457600085556152ba565b82601f1061528d57805160ff19168380011785556152ba565b828001600101855582156152ba579182015b828111156152ba57825182559160200191906001019061529f565b506152c69291506152ca565b5090565b5b808211156152c657600081556001016152cb565b80356152ea81615d6a565b919050565b803563ffffffff811681146152ea57600080fd5b600060208284031215615314578081fd5b8135610e1f81615d6a565b60008060408385031215615331578081fd5b823561533c81615d6a565b9150602083013561534c81615d6a565b809150509250929050565b60008060006060848603121561536b578081fd5b833561537681615d6a565b9250602084013561538681615d6a565b929592945050506040919091013590565b600080604083850312156153a9578182fd5b82356153b481615d6a565b946020939093013593505050565b600080600080608085870312156153d7578081fd5b84516153e281615d6a565b60208601516040870151606090970151919890975090945092505050565b600060208284031215615411578081fd5b8151610e1f81615d7f565b60006020828403121561542d578081fd5b5051919050565b60008082840360e0811215615447578283fd5b833567ffffffffffffffff81111561545d578384fd5b84016101c0818703121561546f578384fd5b925060c0601f1982011215615482578182fd5b506020830190509250929050565b6000602082840312156154a1578081fd5b81356001600160801b0381168114610e1f578182fd5b6000602082840312156154c8578081fd5b5035919050565b600080600080600060a086880312156154e6578283fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b60006020828403121561551f578081fd5b610e1f826152ef565b600060208284031215615539578081fd5b8151610e1f81615db7565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b803561557881615d7f565b15158252602081013561558a81615db7565b60ff16602083015260408101356155a081615d6a565b6001600160a01b0390811660408401526060820135906155bf82615d6a565b16606083015260808101356155d381615da2565b66ffffffffffffff16608083015260a08101356155ef81615d8d565b6001600160681b03811660a0840152505050565b60008251615615818460208701615b70565b9190910192915050565b602081526000825180602084015261563e816040850160208701615b70565b601f01601f19169190910160400192915050565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601b908201527f4d696e696d756d206465706f736974206e6f7420726561636865640000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600061010080835261578881840161577b886152df565b6001600160a01b03169052565b615794602087016152df565b6101206157ab818601836001600160a01b03169052565b6157b7604089016152df565b91506101406157d0818701846001600160a01b03169052565b610160925060608901358387015261018060808a0135818801526157f760a08b018b6159bb565b6101c06101a081818c01526158116102c08c018486615544565b935061582060c08f018f6159bb565b8c860360ff1901848e01529350615838858583615544565b94505061584760e08f016152df565b6001600160a01b0381166101e08d01529250615864898f016152df565b6001600160a01b0381166102008d01529850615881878f016152ef565b63ffffffff166102208c0152948d01356102408b01525050938a01356102608801528901356102808701528801356102a08601525091506158c79050602083018561556d565b8260e0830152949350505050565b600060808201868352602060808185015281875180845260a0860191508289019350845b81811015615915578451835293830193918301916001016158f9565b5050848103604086015286548082528785528285209183019350845b818110156159565782546001600160a01b031685529383019360019283019201615931565b50505061ffff8516606085015250905095945050505050565b6000808335601e19843603018112615985578283fd5b83018035915067ffffffffffffffff82111561599f578283fd5b6020019150368190038213156159b457600080fd5b9250929050565b6000808335601e198436030181126159d1578283fd5b830160208101925035905067ffffffffffffffff8111156159f157600080fd5b8036038313156159b457600080fd5b60008219821115615a1357615a13615c06565b500190565b600082615a2757615a27615c1c565b500490565b600181815b80851115615a67578160001904821115615a4d57615a4d615c06565b80851615615a5a57918102915b93841c9390800290615a31565b509250929050565b6000610e1f8383600082615a8557506001610d6f565b81615a9257506000610d6f565b8160018114615aa85760028114615ab257615ace565b6001915050610d6f565b60ff841115615ac357615ac3615c06565b50506001821b610d6f565b5060208310610133831016604e8410600b8410161715615af1575081810a610d6f565b615afb8383615a2c565b8060001904821115615b0f57615b0f615c06565b029392505050565b6000816000190483118215151615615b3157615b31615c06565b500290565b600061ffff83811690831681811015615b5157615b51615c06565b039392505050565b600082821015615b6b57615b6b615c06565b500390565b60005b83811015615b8b578181015183820152602001615b73565b838111156140a95750506000910152565b600181811c90821680615bb057607f821691505b60208210811415615bd157634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615beb57615beb615c06565b5060010190565b600082615c0157615c01615c1c565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008135610d6f81615d6a565b60008135610d6f81615d8d565b60008135610d6f81615da2565b8135615c6481615d7f565b815460ff19811691151560ff1691821783556020840135615c8481615db7565b61ff008160081b169050808361ffff198416171784556040850135615ca881615d6a565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d00615ce360608501615c32565b82546001600160a01b0319166001600160a01b0391909116178255565b615d39615d0f60808501615c4c565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b50613bb3615d4960a08401615c3f565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b0381168114612c2657600080fd5b8015158114612c2657600080fd5b6001600160681b0381168114612c2657600080fd5b66ffffffffffffff81168114612c2657600080fd5b60ff81168114612c2657600080fdfea2646970667358221220bfd8b76dd3adda30ff911dad235dc4340796dbce4e51ed16f3169f142384f9ea64736f6c63430008040033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc720000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101
-----Decoded View---------------
Arg [0] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _usdc (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _oTokenFactory (address): 0x7C06792Af1632E77cb27a558Dc0885338F4Bdf8E
Arg [3] : _gammaController (address): 0x4ccc2339F87F6c59c6893E1A678c2266cA58dC72
Arg [4] : _marginPool (address): 0x5934807cC0654d46755eBd2848840b616256C6Ef
Arg [5] : _gnosisEasyAuction (address): 0x0b7fFc1f4AD541A4Ed16b40D8c37f0929158D101
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e
Arg [3] : 0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc72
Arg [4] : 0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef
Arg [5] : 0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.