Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
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 Source Code Verified (Exact Match)
Contract Name:
RibbonThetaVault
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 {GnosisAuction} from "../../libraries/GnosisAuction.sol"; import { RibbonThetaVaultStorage } from "../../storage/RibbonThetaVaultStorage.sol"; import {Vault} from "../../libraries/Vault.sol"; import {VaultLifecycle} from "../../libraries/VaultLifecycle.sol"; import {ShareMath} from "../../libraries/ShareMath.sol"; import {ILiquidityGauge} from "../../interfaces/ILiquidityGauge.sol"; import {IVaultPauser} from "../../interfaces/IVaultPauser.sol"; import {RibbonVault} from "./base/RibbonVault.sol"; /** * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe * the inheritance chain closely. * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage. * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage */ contract RibbonThetaVault is RibbonVault, RibbonThetaVaultStorage { using SafeERC20 for IERC20; using SafeMath for uint256; using ShareMath for Vault.DepositReceipt; /************************************************ * IMMUTABLES & CONSTANTS ***********************************************/ /// @notice oTokenFactory 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; /************************************************ * EVENTS ***********************************************/ 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 ); /************************************************ * STRUCTS ***********************************************/ /** * @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 */ struct InitParams { address _owner; address _keeper; address _feeRecipient; uint256 _managementFee; uint256 _performanceFee; string _tokenName; string _tokenSymbol; address _optionsPremiumPricer; address _strikeSelection; uint32 _premiumDiscount; uint256 _auctionDuration; } /************************************************ * 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 ) RibbonVault( _weth, _usdc, _gammaController, _marginPool, _gnosisEasyAuction ) { require(_oTokenFactory != address(0), "!_oTokenFactory"); OTOKEN_FACTORY = _oTokenFactory; } /** * @notice Initializes the OptionVault contract with storage variables. * @param _initParams is the struct with vault initialization parameters * @param _vaultParams is the struct with vault general data */ function initialize( InitParams calldata _initParams, Vault.VaultParams calldata _vaultParams ) external initializer { baseInitialize( _initParams._owner, _initParams._keeper, _initParams._feeRecipient, _initParams._managementFee, _initParams._performanceFee, _initParams._tokenName, _initParams._tokenSymbol, _vaultParams ); 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" ); optionsPremiumPricer = _initParams._optionsPremiumPricer; strikeSelection = _initParams._strikeSelection; premiumDiscount = _initParams._premiumDiscount; auctionDuration = _initParams._auctionDuration; } /************************************************ * SETTERS ***********************************************/ /** * @notice Sets the new discount on premiums for options we are selling * @param newPremiumDiscount is the premium discount */ function setPremiumDiscount(uint256 newPremiumDiscount) external onlyKeeper { 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 { require(strikePrice > 0, "!strikePrice"); overriddenStrikePrice = strikePrice; lastStrikeOverrideRound = vaultState.round; } /** * @notice Sets the new liquidityGauge contract for this vault * @param newLiquidityGauge is the address of the new liquidityGauge contract */ function setLiquidityGauge(address newLiquidityGauge) external onlyOwner { liquidityGauge = newLiquidityGauge; } /** * @notice Sets the new optionsPurchaseQueue contract for this vault * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract */ function setOptionsPurchaseQueue(address newOptionsPurchaseQueue) external onlyOwner { optionsPurchaseQueue = newOptionsPurchaseQueue; } /** * @notice Sets oToken Premium * @param minPrice is the new oToken Premium in the units of 10**18 */ function setMinPrice(uint256 minPrice) external onlyKeeper { require(minPrice > 0, "!minPrice"); currentOtokenPremium = minPrice; } /** * @notice Sets the new Vault Pauser contract for this vault * @param newVaultPauser is the address of the new vaultPauser contract */ function setVaultPauser(address newVaultPauser) external onlyOwner { vaultPauser = newVaultPauser; } /************************************************ * VAULT OPERATIONS ***********************************************/ /** * @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"); // 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); transferAsset(msg.sender, amount); } /** * @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 { _initiateWithdraw(numShares); currentQueuedWithdrawShares = currentQueuedWithdrawShares.add( numShares ); } /** * @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) ); } /** * @notice Stakes a users vault shares * @param numShares is the number of shares to stake */ function stake(uint256 numShares) external nonReentrant { address _liquidityGauge = liquidityGauge; require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit require(numShares > 0); uint256 heldByAccount = balanceOf(msg.sender); if (heldByAccount < numShares) { _redeem(numShares.sub(heldByAccount), false); } _transfer(msg.sender, address(this), numShares); _approve(address(this), _liquidityGauge, numShares); ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false); } /** * @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; VaultLifecycle.CloseParams memory closeParams = VaultLifecycle.CloseParams({ OTOKEN_FACTORY: OTOKEN_FACTORY, USDC: USDC, currentOption: oldOption, delay: DELAY, lastStrikeOverrideRound: lastStrikeOverrideRound, overriddenStrikePrice: overriddenStrikePrice, strikeSelection: strikeSelection, optionsPremiumPricer: optionsPremiumPricer, premiumDiscount: premiumDiscount }); (address otokenAddress, uint256 strikePrice, uint256 delta) = VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState); emit NewOptionStrikeSelected(strikePrice, delta); optionState.nextOption = otokenAddress; uint256 nextOptionReady = block.timestamp.add(DELAY); require( nextOptionReady <= type(uint32).max, "Overflow nextOptionReady" ); optionState.nextOptionReadyAt = uint32(nextOptionReady); _closeShort(oldOption); } /** * @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 = VaultLifecycle.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 { uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares; ( address newOption, uint256 lockedBalance, uint256 queuedWithdrawAmount ) = _rollToNextOption( lastQueuedWithdrawAmount, currQueuedWithdrawShares ); lastQueuedWithdrawAmount = queuedWithdrawAmount; uint256 newQueuedWithdrawShares = uint256(vaultState.queuedWithdrawShares).add( currQueuedWithdrawShares ); ShareMath.assertUint128(newQueuedWithdrawShares); vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares); currentQueuedWithdrawShares = 0; ShareMath.assertUint104(lockedBalance); vaultState.lockedAmount = uint104(lockedBalance); emit OpenShort(newOption, lockedBalance, msg.sender); uint256 optionsMintAmount = VaultLifecycle.createShort( GAMMA_CONTROLLER, MARGIN_POOL, newOption, lockedBalance ); VaultLifecycle.allocateOptions( optionsPurchaseQueue, newOption, optionsMintAmount, VaultLifecycle.QUEUE_OPTION_ALLOCATION ); _startAuction(); } /** * @notice Initiate the gnosis auction. */ function startAuction() external onlyKeeper nonReentrant { _startAuction(); } function _startAuction() private { GnosisAuction.AuctionDetails memory auctionDetails; address currentOtoken = optionState.currentOption; auctionDetails.oTokenAddress = currentOtoken; auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION; auctionDetails.asset = vaultParams.asset; auctionDetails.assetDecimals = vaultParams.decimals; auctionDetails.oTokenPremium = currentOtokenPremium; auctionDetails.duration = auctionDuration; optionAuctionID = VaultLifecycle.startAuction(auctionDetails); } /** * @notice Sell the allocated options to the purchase queue post auction settlement */ function sellOptionsToQueue() external onlyKeeper nonReentrant { VaultLifecycle.sellOptionsToQueue( optionsPurchaseQueue, GNOSIS_EASY_AUCTION, optionAuctionID ); } /** * @notice Burn the remaining oTokens left over from gnosis auction. */ function burnRemainingOTokens() external onlyKeeper nonReentrant { uint256 unlockedAssetAmount = VaultLifecycle.burnOtokens( GAMMA_CONTROLLER, optionState.currentOption ); vaultState.lockedAmount = uint104( uint256(vaultState.lockedAmount).sub(unlockedAssetAmount) ); } /** * @notice Recovery function that returns an ERC20 token to the recipient * @param token is the ERC20 token to recover from the vault * @param recipient is the recipient of the recovered tokens */ function recoverTokens(address token, address recipient) external onlyOwner { require(token != vaultParams.asset, "Vault asset not recoverable"); require(token != address(this), "Vault share not recoverable"); require(recipient != address(this), "Recipient cannot be vault"); IERC20(token).safeTransfer( recipient, IERC20(token).balanceOf(address(this)) ); } /** * @notice pause a user's vault position */ function pausePosition() external { address _vaultPauserAddress = vaultPauser; require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit _redeem(0, true); uint256 heldByAccount = balanceOf(msg.sender); _approve(msg.sender, _vaultPauserAddress, heldByAccount); IVaultPauser(_vaultPauserAddress).pausePosition( msg.sender, heldByAccount ); } }
// 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.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; abstract contract RibbonThetaVaultStorageV1 { // Logic contract used to price options address public optionsPremiumPricer; // Logic contract used to select strike prices address public strikeSelection; // Premium discount on options we are selling (thousandths place: 000 - 999) uint256 public premiumDiscount; // Current oToken premium uint256 public currentOtokenPremium; // Last round id at which the strike was manually overridden uint16 public lastStrikeOverrideRound; // Price last overridden strike set to uint256 public overriddenStrikePrice; // Auction duration uint256 public auctionDuration; // Auction id of current option uint256 public optionAuctionID; } abstract contract RibbonThetaVaultStorageV2 { // Amount locked for scheduled withdrawals last week; uint256 public lastQueuedWithdrawAmount; } abstract contract RibbonThetaVaultStorageV3 { // DEPRECATED: Auction will be denominated in USDC if true bool private _isUsdcAuction; // DEPRECATED: Path for swaps bytes private _swapPath; } abstract contract RibbonThetaVaultStorageV4 { // LiquidityGauge contract for the vault address public liquidityGauge; } abstract contract RibbonThetaVaultStorageV5 { // OptionsPurchaseQueue contract for selling options address public optionsPurchaseQueue; } abstract contract RibbonThetaVaultStorageV6 { // Queued withdraw shares for the current round uint256 public currentQueuedWithdrawShares; } abstract contract RibbonThetaVaultStorageV7 { // Vault Pauser Contract for the vault address public vaultPauser; } // 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 RibbonThetaVaultStorage // e.g. RibbonThetaVaultStorage<versionNumber>, so finally it would look like // contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2 abstract contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2, RibbonThetaVaultStorageV3, RibbonThetaVaultStorageV4, RibbonThetaVaultStorageV5, RibbonThetaVaultStorageV6, RibbonThetaVaultStorageV7 { }
// 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 { IOtokenFactory, IOtoken, IController, GammaTypes } from "../interfaces/GammaInterface.sol"; import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol"; import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol"; import {IOptionsPurchaseQueue} from "../interfaces/IOptionsPurchaseQueue.sol"; import {SupportsNonCompliantERC20} from "./SupportsNonCompliantERC20.sol"; import {IOptionsPremiumPricer} from "../interfaces/IRibbon.sol"; library VaultLifecycle { using SafeMath for uint256; using SupportsNonCompliantERC20 for IERC20; struct CloseParams { address OTOKEN_FACTORY; address USDC; address currentOption; uint256 delay; uint16 lastStrikeOverrideRound; uint256 overriddenStrikePrice; address strikeSelection; address optionsPremiumPricer; uint256 premiumDiscount; } /// @notice Default maximum option allocation for the queue (50%) uint256 internal constant QUEUE_OPTION_ALLOCATION = 5000; /** * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction * @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 strikePrice is the strike price of the new option * @return delta is the delta of the new option */ function commitAndClose( CloseParams calldata closeParams, Vault.VaultParams storage vaultParams, Vault.VaultState storage vaultState ) external returns ( address otokenAddress, uint256 strikePrice, uint256 delta ) { uint256 expiry = getNextExpiry(closeParams.currentOption); IStrikeSelection selection = IStrikeSelection(closeParams.strikeSelection); bool isPut = vaultParams.isPut; address underlying = vaultParams.underlying; address asset = vaultParams.asset; (strikePrice, delta) = closeParams.lastStrikeOverrideRound == vaultState.round ? (closeParams.overriddenStrikePrice, selection.delta()) : 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 ); return (otokenAddress, 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 decimals is the decimals of the asset * @param totalBalance is the vaults total balance of the asset * @param currentShareSupply is the supply of the shares invoked with totalSupply() * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals * @param performanceFee is the perf fee percent to charge on premiums * @param managementFee is the management fee percent to charge on the AUM * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round */ struct RolloverParams { uint256 decimals; uint256 totalBalance; uint256 currentShareSupply; uint256 lastQueuedWithdrawAmount; uint256 performanceFee; uint256 managementFee; uint256 currentQueuedWithdrawShares; } /** * @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 performanceFeeInAsset is the performance fee charged by vault * @return totalVaultFee is the total amount of 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 performanceFeeInAsset, uint256 totalVaultFee ) { uint256 currentBalance = params.totalBalance; uint256 pendingAmount = vaultState.totalPending; // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round) uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares; // Deduct older queued withdraws so we don't charge fees on them uint256 balanceForVaultFees = currentBalance.sub(params.lastQueuedWithdrawAmount); { (performanceFeeInAsset, , totalVaultFee) = VaultLifecycle .getVaultFees( balanceForVaultFees, vaultState.lastLockedAmount, vaultState.totalPending, params.performanceFee, params.managementFee ); } // Take into account the fee // so we can calculate the newPricePerShare currentBalance = currentBalance.sub(totalVaultFee); { newPricePerShare = ShareMath.pricePerShare( params.currentShareSupply.sub(lastQueuedWithdrawShares), currentBalance.sub(params.lastQueuedWithdrawAmount), pendingAmount, params.decimals ); queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add( ShareMath.sharesToAsset( params.currentQueuedWithdrawShares, newPricePerShare, 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 ); } return ( currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals queuedWithdrawAmount, newPricePerShare, mintShares, performanceFeeInAsset, totalVaultFee ); } /** * @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 performance and management fee for this week's round * @param currentBalance is the balance of funds held on the vault after closing short * @param lastLockedAmount is the amount of funds locked from the previous round * @param pendingAmount is the pending deposit amount * @param performanceFeePercent is the performance fee pct. * @param managementFeePercent is the management fee pct. * @return performanceFeeInAsset is the performance fee * @return managementFeeInAsset is the management fee * @return vaultFee is the total fees */ function getVaultFees( uint256 currentBalance, uint256 lastLockedAmount, uint256 pendingAmount, uint256 performanceFeePercent, uint256 managementFeePercent ) internal pure returns ( uint256 performanceFeeInAsset, uint256 managementFeeInAsset, uint256 vaultFee ) { // 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 _performanceFeeInAsset; uint256 _managementFeeInAsset; uint256 _vaultFee; // Take performance fee and management fee ONLY if difference between // last week and this week's vault deposits, taking into account pending // deposits and withdrawals, is positive. If it is negative, last week's // option expired ITM past breakeven, and the vault took a loss so we // do not collect performance fee for last week if (lockedBalanceSansPending > lastLockedAmount) { _performanceFeeInAsset = performanceFeePercent > 0 ? lockedBalanceSansPending .sub(lastLockedAmount) .mul(performanceFeePercent) .div(100 * Vault.FEE_MULTIPLIER) : 0; _managementFeeInAsset = managementFeePercent > 0 ? lockedBalanceSansPending.mul(managementFeePercent).div( 100 * Vault.FEE_MULTIPLIER ) : 0; _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset); } return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee); } /** * @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; } function getOTokenPremium( address oTokenAddress, address optionsPremiumPricer, uint256 premiumDiscount ) external view returns (uint256) { return _getOTokenPremium( oTokenAddress, optionsPremiumPricer, premiumDiscount ); } function _getOTokenPremium( 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 in the underlying asset for call option // and USDC for put option uint256 optionPremium = premiumPricer.getPremium( 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!" ); require(optionPremium > 0, "!optionPremium"); return optionPremium; } /** * @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 Allocates the vault's minted options to the OptionsPurchaseQueue contract * @dev Skipped if the optionsPurchaseQueue doesn't exist * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract * @param option is the minted option * @param optionsAmount is the amount of options minted * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate * up to the amount that is on the queue) * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue */ function allocateOptions( address optionsPurchaseQueue, address option, uint256 optionsAmount, uint256 optionAllocation ) external returns (uint256 allocatedOptions) { // Skip if optionsPurchaseQueue is address(0) if (optionsPurchaseQueue != address(0)) { allocatedOptions = optionsAmount.mul(optionAllocation).div( 100 * Vault.OPTION_ALLOCATION_MULTIPLIER ); allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue) .getOptionsAllocation(address(this), allocatedOptions); if (allocatedOptions != 0) { IERC20(option).approve(optionsPurchaseQueue, allocatedOptions); IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions( allocatedOptions ); } } return allocatedOptions; } /** * @notice Sell the allocated options to the purchase queue post auction settlement * @dev Reverts if the auction hasn't settled yet * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract * @return totalPremiums Total premiums earnt by the vault */ function sellOptionsToQueue( address optionsPurchaseQueue, address gnosisEasyAuction, uint256 optionAuctionID ) external returns (uint256) { uint256 settlementPrice = getAuctionSettlementPrice(gnosisEasyAuction, optionAuctionID); require(settlementPrice != 0, "!settlementPrice"); return IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers( settlementPrice ); } /** * @notice Gets the settlement price of a settled auction * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract * @return settlementPrice Auction settlement price */ function getAuctionSettlementPrice( address gnosisEasyAuction, uint256 optionAuctionID ) public view returns (uint256) { bytes32 clearingPriceOrder = IGnosisAuction(gnosisEasyAuction) .auctionData(optionAuctionID) .clearingPriceOrder; if (clearingPriceOrder == bytes32(0)) { // Current auction hasn't settled yet return 0; } else { // We decode the clearingPriceOrder to find the auction settlement price // settlementPrice = clearingPriceOrder.sellAmount / clearingPriceOrder.buyAmount return (10**Vault.OTOKEN_DECIMALS) .mul( uint96(uint256(clearingPriceOrder)) // sellAmount ) .div( uint96(uint256(clearingPriceOrder) >> 96) // buyAmount ); } } /** * @notice Verify the constructor params satisfy requirements * @param owner is the owner of the vault with critical permissions * @param feeRecipient is the address to recieve vault performance and management fees * @param performanceFee is the perfomance fee pct. * @param tokenName is the name of the token * @param tokenSymbol is the symbol of the token * @param _vaultParams is the struct with vault general data */ function verifyInitializerParams( address owner, address keeper, address feeRecipient, uint256 performanceFee, uint256 managementFee, string calldata tokenName, string calldata tokenSymbol, Vault.VaultParams calldata _vaultParams ) external pure { require(owner != address(0), "!owner"); require(keeper != address(0), "!keeper"); require(feeRecipient != address(0), "!feeRecipient"); require( performanceFee < 100 * Vault.FEE_MULTIPLIER, "performanceFee >= 100%" ); require( managementFee < 100 * Vault.FEE_MULTIPLIER, "managementFee >= 100%" ); require(bytes(tokenName).length > 0, "!tokenName"); require(bytes(tokenSymbol).length > 0, "!tokenSymbol"); 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 option expiry timestamp * @param currentOption is the otoken address that the vault is currently writing */ function getNextExpiry(address currentOption) internal view returns (uint256) { // uninitialized state if (currentOption == address(0)) { return getNextFriday(block.timestamp); } uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp(); // After options expiry if no options are written for >1 week // We need to give the ability continue writing options if (block.timestamp > currentExpiry + 7 days) { return getNextFriday(block.timestamp); } return getNextFriday(currentExpiry); } /** * @notice Gets the next options expiry timestamp * @param timestamp is the expiry timestamp of the current option * Reference: https://codereview.stackexchange.com/a/33532 * Examples: * getNextFriday(week 1 thursday) -> week 1 friday * getNextFriday(week 1 friday) -> week 2 friday * getNextFriday(week 1 saturday) -> week 2 friday */ function getNextFriday(uint256 timestamp) internal pure returns (uint256) { // dayOfWeek = 0 (sunday) - 6 (saturday) uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7; uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days; uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours); // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday if (timestamp >= friday8am) { friday8am += 7 days; } return friday8am; } }
// 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 ILiquidityGauge { function balanceOf(address) external view returns (uint256); function deposit( uint256 _value, address _addr, bool _claim_rewards ) external; function withdraw(uint256 _value) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.8.4; interface IVaultPauser { /// @notice pause vault position of an account with max amount /// @param _account the address of user /// @param _amount amount of shares function pausePosition(address _account, uint256 _amount) external; /// @notice resume vault position of an account with max amount /// @param _vaultAddress the address of vault function resumePosition(address _vaultAddress) external; }
// 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 {VaultLifecycle} from "../../../libraries/VaultLifecycle.sol"; import {ShareMath} from "../../../libraries/ShareMath.sol"; import {IWETH} from "../../../interfaces/IWETH.sol"; contract RibbonVault is ReentrancyGuardUpgradeable, OwnableUpgradeable, ERC20Upgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using ShareMath for Vault.DepositReceipt; /************************************************ * NON UPGRADEABLE STORAGE ***********************************************/ /// @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 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 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 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; // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure. uint256[30] private ____gap; // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage // or RibbonDeltaVaultStorage instead. Read this documentation to learn more: // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts /************************************************ * IMMUTABLES & CONSTANTS ***********************************************/ /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 address public immutable WETH; /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 address public immutable USDC; /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption. uint256 public constant DELAY = 0; /// @notice 7 day period between each options sale. uint256 public constant PERIOD = 7 days; // 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; /************************************************ * 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 CollectVaultFees( uint256 performanceFee, uint256 vaultFee, uint256 round, address indexed feeRecipient ); /************************************************ * CONSTRUCTOR & INITIALIZATION ***********************************************/ /** * @notice Initializes the contract with immutable variables * @param _weth is the Wrapped Ether contract * @param _usdc is the USDC contract * @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 _gammaController, address _marginPool, address _gnosisEasyAuction ) { require(_weth != address(0), "!_weth"); require(_usdc != address(0), "!_usdc"); require(_gnosisEasyAuction != address(0), "!_gnosisEasyAuction"); require(_gammaController != address(0), "!_gammaController"); require(_marginPool != address(0), "!_marginPool"); WETH = _weth; USDC = _usdc; GAMMA_CONTROLLER = _gammaController; MARGIN_POOL = _marginPool; GNOSIS_EASY_AUCTION = _gnosisEasyAuction; } /** * @notice Initializes the OptionVault contract with storage variables. */ function baseInitialize( address _owner, address _keeper, address _feeRecipient, uint256 _managementFee, uint256 _performanceFee, string memory _tokenName, string memory _tokenSymbol, Vault.VaultParams calldata _vaultParams ) internal initializer { VaultLifecycle.verifyInitializerParams( _owner, _keeper, _feeRecipient, _performanceFee, _managementFee, _tokenName, _tokenSymbol, _vaultParams ); __ReentrancyGuard_init(); __ERC20_init(_tokenName, _tokenSymbol); __Ownable_init(); transferOwnership(_owner); keeper = _keeper; feeRecipient = _feeRecipient; performanceFee = _performanceFee; managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div( WEEKS_PER_YEAR ); vaultParams = _vaultParams; uint256 assetBalance = IERC20(vaultParams.asset).balanceOf(address(this)); ShareMath.assertUint104(assetBalance); vaultState.lastLockedAmount = uint104(assetBalance); vaultState.round = 1; } /** * @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" ); // We are dividing annualized management fee by num weeks in a year uint256 tmpManagementFee = newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR); emit ManagementFeeSet(managementFee, newManagementFee); managementFee = tmpManagementFee; } /** * @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); } /************************************************ * DEPOSIT & WITHDRAWALS ***********************************************/ /** * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH. */ function depositETH() external payable nonReentrant { require(vaultParams.asset == WETH, "!WETH"); require(msg.value > 0, "!value"); _depositFor(msg.value, msg.sender); IWETH(WETH).deposit{value: msg.value}(); } /** * @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"); _depositFor(amount, msg.sender); // An approve() by the msg.sender is required beforehand IERC20(vaultParams.asset).safeTransferFrom( msg.sender, address(this), amount ); } /** * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit. * @notice Used for vault -> vault deposits on the user's behalf * @param amount is the amount of `asset` to deposit * @param creditor is the address that can claim/withdraw deposited amount */ function depositFor(uint256 amount, address creditor) external nonReentrant { require(amount > 0, "!amount"); require(creditor != address(0)); _depositFor(amount, creditor); // 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); require(totalWithDepositedAmount <= vaultParams.cap, "Exceed cap"); require( totalWithDepositedAmount >= vaultParams.minimumSupply, "Insufficient balance" ); emit Deposit(creditor, amount, currentRound); Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor]; // 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) internal { require(numShares > 0, "!numShares"); // We do a max redeem before initiating a withdrawal // But we check if they must first have unredeemed shares if ( depositReceipts[msg.sender].amount > 0 || depositReceipts[msg.sender].unredeemedShares > 0 ) { _redeem(0, true); } // This caches the `round` variable used in shareBalances uint256 currentRound = vaultState.round; Vault.Withdrawal storage withdrawal = withdrawals[msg.sender]; bool withdrawalIsSameRound = withdrawal.round == 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); } ShareMath.assertUint128(withdrawalShares); withdrawals[msg.sender].shares = uint128(withdrawalShares); _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); } /************************************************ * 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 * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round * @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, uint256 currentQueuedWithdrawShares ) internal returns ( address newOption, uint256 lockedBalance, uint256 queuedWithdrawAmount ) { require(block.timestamp >= optionState.nextOptionReadyAt, "!ready"); newOption = optionState.nextOption; require(newOption != address(0), "!nextOption"); address recipient = feeRecipient; uint256 mintShares; uint256 performanceFeeInAsset; uint256 totalVaultFee; { uint256 newPricePerShare; ( lockedBalance, queuedWithdrawAmount, newPricePerShare, mintShares, performanceFeeInAsset, totalVaultFee ) = VaultLifecycle.rollover( vaultState, VaultLifecycle.RolloverParams( vaultParams.decimals, IERC20(vaultParams.asset).balanceOf(address(this)), totalSupply(), lastQueuedWithdrawAmount, performanceFee, managementFee, currentQueuedWithdrawShares ) ); optionState.currentOption = newOption; optionState.nextOption = address(0); // Finalize the pricePerShare at the end of the round uint256 currentRound = vaultState.round; roundPricePerShare[currentRound] = newPricePerShare; emit CollectVaultFees( performanceFeeInAsset, totalVaultFee, currentRound, recipient ); vaultState.totalPending = 0; vaultState.round = uint16(currentRound + 1); } _mint(address(this), mintShares); if (totalVaultFee > 0) { transferAsset(payable(recipient), totalVaultFee); } return (newOption, lockedBalance, queuedWithdrawAmount); } /** * @notice Helper function to make either an ETH transfer or 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; if (asset == WETH) { IWETH(WETH).withdraw(amount); (bool success, ) = recipient.call{value: amount}(""); require(success, "Transfer failed"); return; } IERC20(asset).safeTransfer(recipient, amount); } /************************************************ * 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) external 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; } function cap() external view returns (uint256) { return vaultParams.cap; } function nextOptionReadyAt() external view returns (uint256) { return optionState.nextOptionReadyAt; } function currentOption() external view returns (address) { return optionState.currentOption; } function nextOption() external view returns (address) { return optionState.nextOption; } function totalPending() external view returns (uint256) { return vaultState.totalPending; } }
// 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 /// 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 {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; 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 {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 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); }
// 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; interface IOptionsPurchaseQueue { /** * @dev Contains purchase request info * @param optionsAmount Amount of options to purchase * @param premiums Total premiums the buyer is spending to purchase the options (optionsAmount * ceilingPrice) * We need to track the premiums here since the ceilingPrice could change between the time the purchase was * requested and when the options are sold * @param buyer The buyer requesting this purchase */ struct Purchase { uint128 optionsAmount; // Slot 0 uint128 premiums; address buyer; // Slot 1 } function purchases(address, uint256) external view returns ( uint128, uint128, address ); function totalOptionsAmount(address) external view returns (uint256); function vaultAllocatedOptions(address) external view returns (uint256); function whitelistedBuyer(address) external view returns (bool); function minPurchaseAmount(address) external view returns (uint256); function ceilingPrice(address) external view returns (uint256); function getPurchases(address vault) external view returns (Purchase[] memory); function getPremiums(address vault, uint256 optionsAmount) external view returns (uint256); function getOptionsAllocation(address vault, uint256 allocatedOptions) external view returns (uint256); function requestPurchase(address vault, uint256 optionsAmount) external returns (uint256); function allocateOptions(uint256 allocatedOptions) external returns (uint256); function sellToBuyers(uint256 settlementPrice) external returns (uint256); function cancelAllPurchases(address vault) external; function addWhitelist(address buyer) external; function removeWhitelist(address buyer) external; function setCeilingPrice(address vault, uint256 price) external; function setMinPurchaseAmount(address vault, uint256 amount) external; }
// 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 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; 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.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); }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "contracts/libraries/VaultLifecycle.sol": { "VaultLifecycle": "0x25af99ff07575ac55fb3c2d1e6b920ba3bf67b40" } } }
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":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"CollectVaultFees","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":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":"PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"commitAndClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completeWithdraw","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":"currentQueuedWithdrawShares","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":[],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"creditor","type":"address"}],"name":"depositFor","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":[],"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":"struct RibbonThetaVault.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":"liquidityGauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeem","outputs":[],"stateMutability":"nonpayable","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":"optionsPurchaseQueue","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":"pausePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"performanceFee","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":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"recoverTokens","outputs":[],"stateMutability":"nonpayable","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":[],"name":"sellOptionsToQueue","outputs":[],"stateMutability":"nonpayable","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":"address","name":"newFeeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLiquidityGauge","type":"address"}],"name":"setLiquidityGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"setManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minPrice","type":"uint256"}],"name":"setMinPrice","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":"address","name":"newOptionsPurchaseQueue","type":"address"}],"name":"setOptionsPurchaseQueue","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":"newVaultPauser","type":"address"}],"name":"setVaultPauser","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":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","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":"vaultPauser","outputs":[{"internalType":"address","name":"","type":"address"}],"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
6101406040523480156200001257600080fd5b506040516200619838038062006198833981016040819052620000359162000261565b85858484846001600160a01b0385166200007f5760405162461bcd60e51b8152602060048201526006602482015265042beeecae8d60d31b60448201526064015b60405180910390fd5b6001600160a01b038416620000c05760405162461bcd60e51b8152602060048201526006602482015265215f7573646360d01b604482015260640162000076565b6001600160a01b038116620001185760405162461bcd60e51b815260206004820152601360248201527f215f676e6f7369734561737941756374696f6e00000000000000000000000000604482015260640162000076565b6001600160a01b038316620001645760405162461bcd60e51b815260206004820152601160248201527010afb3b0b6b6b0a1b7b73a3937b63632b960791b604482015260640162000076565b6001600160a01b038216620001ab5760405162461bcd60e51b815260206004820152600c60248201526b0857db585c99da5b941bdbdb60a21b604482015260640162000076565b6001600160601b0319606095861b811660805293851b841660a05291841b831660c052831b821660e05290911b16610100526001600160a01b038416620002275760405162461bcd60e51b815260206004820152600f60248201526e215f6f546f6b656e466163746f727960881b604482015260640162000076565b50505060601b6001600160601b0319166101205250620002e19050565b80516001600160a01b03811681146200025c57600080fd5b919050565b60008060008060008060c087890312156200027a578182fd5b620002858762000244565b9550620002956020880162000244565b9450620002a56040880162000244565b9350620002b56060880162000244565b9250620002c56080880162000244565b9150620002d560a0880162000244565b90509295509295509295565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c615e0162000397600039600081816110000152612e67015260008181610a2f015281816120b20152613f4b01526000818161094d015261294e0152600081816105ad01528181611f4b0152818161292601526148ae015260008181610bc00152612e8c015260008181610ea001528181613137015281816131e3015281816137ef015261382c0152615e016000f3fe6080604052600436106104c05760003560e01c806387153eb111610276578063b4d1d7951161014f578063e73c63d5116100c1578063f656ba5111610085578063f656ba51146110db578063f756fa2114611110578063f957a06714611125578063f9a0be6814611145578063fba7dc6114611166578063fe56e2321461118757600080fd5b8063e73c63d514611068578063e74b981b1461107e578063ea00b1ca1461109e578063f2fde38b146110b3578063f6326fb3146110d357600080fd5b8063d13f1b3e11610113578063d13f1b3e14610f78578063d164cc1514610f98578063d5f2638214610fb8578063db006a7514610fce578063db43e86214610fee578063dd62ed3e1461102257600080fd5b8063b4d1d79514610ef7578063b6b55f2514610f0e578063b9f8092b14610f2e578063ca59409414610f43578063ce7c2ac214610f5857600080fd5b8063a285c9e8116101e8578063a6f7f5d6116101ac578063a6f7f5d614610e38578063a9059cbb14610e4e578063aced166114610e6e578063ad5c464814610e8e578063ad7a672f14610ec2578063afa6626414610ed757600080fd5b8063a285c9e814610d98578063a2db9d8314610dba578063a457c2d714610dd8578063a497e67414610df8578063a694fc3a14610e1857600080fd5b8063947061b51161023a578063947061b514610c1e57806395d89b4114610c3f57806399530b0614610c545780639be43daa14610c695780639fcc2d7514610c89578063a083ff1714610d3557600080fd5b806387153eb114610b6b5780638778878214610b9857806389a3027114610bae5780638b10cc7c14610be25780638da5cb5b14610c0057600080fd5b806347786d37116103a85780636719b2ee1161031a57806370897b23116102de57806370897b2314610a6657806370a0823114610a86578063715018a614610abc5780637a9262a214610ad15780637e108d5214610b3557806383536ff314610b5557600080fd5b80636719b2ee1461096f57806369b41170146109f35780636b64c76914610a085780636c10f66c14610a1d5780636f31ab3414610a5157600080fd5b8063573f0d6e1161036c578063573f0d6e146108b157806358358207146108d15780635ea8cd12146108f1578063600a2cfb14610911578063611b034d14610926578063650cce8a1461093b57600080fd5b806347786d37146108245780634ac88cb6146108445780634b2431d914610864578063503c70aa1461087b57806355489bb21461089157600080fd5b806330630da4116104415780633ec143d3116104055780633ec143d3146107625780633f23bb73146107905780633f90916a146107b0578063432833a6146107ce5780634603c0aa146107e4578063469048401461080457600080fd5b806330630da4146106ba578063313ce567146106da578063355274ea1461070457806336efd16f14610722578063395093511461074257600080fd5b806318160ddd1161048857806318160ddd146105865780631a92f6c21461059b57806323b872dd146105e75780632728f333146106075780632775d01c1461069a57600080fd5b8063048bf085146104c5578063056097ac146104e757806306fdde0314610507578063095ea7b3146105325780630cbf54c814610562575b600080fd5b3480156104d157600080fd5b506104e56104e036600461548a565b6111a7565b005b3480156104f357600080fd5b506104e56105023660046154a6565b6111fd565b34801561051357600080fd5b5061051c6113cf565b6040516105299190615821565b60405180910390f35b34801561053e57600080fd5b5061055261054d36600461551e565b611461565b6040519015158152602001610529565b34801561056e57600080fd5b5061057860fb5481565b604051908152602001610529565b34801561059257600080fd5b50609954610578565b3480156105a757600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610529565b3480156105f357600080fd5b506105526106023660046154de565b611478565b34801561061357600080fd5b5060cf5460d0546106559161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a001610529565b3480156106a657600080fd5b506104e56106b536600461561e565b611524565b3480156106c657600080fd5b506104e56106d536600461548a565b6116c7565b3480156106e657600080fd5b5060cc54610100900460ff1660405160ff9091168152602001610529565b34801561071057600080fd5b5060ce546001600160681b0316610578565b34801561072e57600080fd5b506104e561073d36600461564e565b61175f565b34801561074e57600080fd5b5061055261075d36600461551e565b6117e7565b34801561076e57600080fd5b5060f95461077d9061ffff1681565b60405161ffff9091168152602001610529565b34801561079c57600080fd5b506105786107ab36600461548a565b611823565b3480156107bc57600080fd5b5060d0546001600160801b0316610578565b3480156107da57600080fd5b5061057860fc5481565b3480156107f057600080fd5b506104e56107ff36600461548a565b611876565b34801561081057600080fd5b5060d3546105cf906001600160a01b031681565b34801561083057600080fd5b506104e561083f36600461561e565b611918565b34801561085057600080fd5b506104e561085f36600461548a565b6119f3565b34801561087057600080fd5b506105786101025481565b34801561088757600080fd5b5061057860fd5481565b34801561089d57600080fd5b506104e56108ac3660046155f7565b611a40565b3480156108bd57600080fd5b506104e56108cc36600461548a565b611ad8565b3480156108dd57600080fd5b506104e56108ec36600461559b565b611b25565b3480156108fd57600080fd5b506104e561090c36600461561e565b611e74565b34801561091d57600080fd5b506104e5611edf565b34801561093257600080fd5b506104e561203a565b34801561094757600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b34801561097b57600080fd5b506109c261098a36600461548a565b60c96020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b031690820152606001610529565b3480156109ff57600080fd5b50610578600081565b348015610a1457600080fd5b506104e5612154565b348015610a2957600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610a5d57600080fd5b506104e56121b4565b348015610a7257600080fd5b506104e5610a8136600461561e565b6121eb565b348015610a9257600080fd5b50610578610aa136600461548a565b6001600160a01b031660009081526097602052604090205490565b348015610ac857600080fd5b506104e56122b2565b348015610add57600080fd5b50610b13610aec36600461548a565b60cb6020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b03909116602083015201610529565b348015610b4157600080fd5b506104e5610b5036600461561e565b6122e8565b348015610b6157600080fd5b5061057860f75481565b348015610b7757600080fd5b50610578610b8636600461561e565b60ca6020526000908152604090205481565b348015610ba457600080fd5b5061057860d55481565b348015610bba57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610bee57600080fd5b5060d2546001600160a01b03166105cf565b348015610c0c57600080fd5b506065546001600160a01b03166105cf565b348015610c2a57600080fd5b50610103546105cf906001600160a01b031681565b348015610c4b57600080fd5b5061051c612332565b348015610c6057600080fd5b50610578612341565b348015610c7557600080fd5b506104e5610c8436600461561e565b61237b565b348015610c9557600080fd5b5060cc5460cd5460ce54610ce59260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c001610529565b348015610d4157600080fd5b5060d15460d254610d6c916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff1690820152606001610529565b348015610da457600080fd5b5060d254600160a01b900463ffffffff16610578565b348015610dc657600080fd5b5060d1546001600160a01b03166105cf565b348015610de457600080fd5b50610552610df336600461551e565b612475565b348015610e0457600080fd5b506104e5610e1336600461561e565b61250e565b348015610e2457600080fd5b506104e5610e3336600461561e565b6125cb565b348015610e4457600080fd5b5061057860d65481565b348015610e5a57600080fd5b50610552610e6936600461551e565b6126c9565b348015610e7a57600080fd5b5060d4546105cf906001600160a01b031681565b348015610e9a57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610ece57600080fd5b506105786126d6565b348015610ee357600080fd5b5060f5546105cf906001600160a01b031681565b348015610f0357600080fd5b5061057862093a8081565b348015610f1a57600080fd5b506104e5610f2936600461561e565b612775565b348015610f3a57600080fd5b506104e56127e2565b348015610f4f57600080fd5b506104e5612ab3565b348015610f6457600080fd5b50610578610f7336600461548a565b612b56565b348015610f8457600080fd5b506104e5610f9336600461561e565b612b73565b348015610fa457600080fd5b506104e5610fb336600461548a565b612c38565b348015610fc457600080fd5b5061057860f85481565b348015610fda57600080fd5b506104e5610fe936600461561e565b612cc7565b348015610ffa57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b34801561102e57600080fd5b5061057861103d3660046154a6565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b34801561107457600080fd5b5061057860fa5481565b34801561108a57600080fd5b506104e561109936600461548a565b612d34565b3480156110aa57600080fd5b506104e5612e27565b3480156110bf57600080fd5b506104e56110ce36600461548a565b61306f565b6104e561310a565b3480156110e757600080fd5b506110fb6110f636600461548a565b61325b565b60408051928352602083019190915201610529565b34801561111c57600080fd5b506104e561333e565b34801561113157600080fd5b5060f6546105cf906001600160a01b031681565b34801561115157600080fd5b50610100546105cf906001600160a01b031681565b34801561117257600080fd5b50610101546105cf906001600160a01b031681565b34801561119357600080fd5b506104e56111a236600461561e565b613393565b6065546001600160a01b031633146111da5760405162461bcd60e51b81526004016111d1906158a3565b60405180910390fd5b61010080546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146112275760405162461bcd60e51b81526004016111d1906158a3565b60cc546001600160a01b038381166201000090920416141561128b5760405162461bcd60e51b815260206004820152601b60248201527f5661756c74206173736574206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b0382163014156112e45760405162461bcd60e51b815260206004820152601b60248201527f5661756c74207368617265206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b03811630141561133d5760405162461bcd60e51b815260206004820152601960248201527f526563697069656e742063616e6e6f74206265207661756c740000000000000060448201526064016111d1565b6040516370a0823160e01b81523060048201526113cb9082906001600160a01b038516906370a082319060240160206040518083038186803b15801561138257600080fd5b505afa158015611396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ba9190615636565b6001600160a01b0385169190613474565b5050565b6060609a80546113de90615bcb565b80601f016020809104026020016040519081016040528092919081815260200182805461140a90615bcb565b80156114575780601f1061142c57610100808354040283529160200191611457565b820191906000526020600020905b81548152906001019060200180831161143a57829003601f168201915b5050505050905090565b600061146e3384846134d7565b5060015b92915050565b60006114858484846135fc565b6001600160a01b03841660009081526098602090815260408083203384529091529020548281101561150a5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084016111d1565b61151785338584036134d7565b60019150505b9392505050565b600260015414156115475760405162461bcd60e51b81526004016111d1906158f9565b600260015533600090815260c96020526040902060cf5461ffff168261157f5760405162461bcd60e51b81526004016111d1906158d8565b815461ffff1681146115c35760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b60448201526064016111d1565b81546201000090046001600160681b0316838110156116145760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b60448201526064016111d1565b61161e81856137cc565b83546001600160681b0391909116620100000262010000600160781b031990911617835560d054611658906001600160801b0316856137cc565b60d080546001600160801b0319166001600160801b0392909216919091179055604080518581526020810184905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a26116bd33856137d8565b5050600180555050565b6065546001600160a01b031633146116f15760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b03811661173d5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b60448201526064016111d1565b60f680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156117825760405162461bcd60e51b81526004016111d1906158f9565b6002600155816117a45760405162461bcd60e51b81526004016111d1906158d8565b6001600160a01b0381166117b757600080fd5b6117c18282613939565b60cc546117df906201000090046001600160a01b0316333085613bcb565b505060018055565b3360008181526098602090815260408083206001600160a01b0387168452909152812054909161146e91859061181e908690615a46565b6134d7565b60cc5460009060ff610100909104168161185961183f60995490565b6118476126d6565b60d0546001600160801b031685613c03565b905061186e61186785612b56565b8284613c43565b949350505050565b6065546001600160a01b031633146118a05760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166118f65760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d507269636572000000000000000060448201526064016111d1565b60f580546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146119425760405162461bcd60e51b81526004016111d1906158a3565b6000811161197c5760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b60448201526064016111d1565b61198581613ca5565b60ce54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160ce80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b6065546001600160a01b03163314611a1d5760405162461bcd60e51b81526004016111d1906158a3565b61010180546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b03163314611a6a5760405162461bcd60e51b81526004016111d1906158a3565b6000816001600160801b031611611ab25760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b60448201526064016111d1565b6001600160801b031660fa5560cf5460f9805461ffff191661ffff909216919091179055565b6065546001600160a01b03163314611b025760405162461bcd60e51b81526004016111d1906158a3565b61010380546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff1680611b3e575060005460ff16155b611b5a5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015611b7c576000805461ffff19166101011790555b611c41611b8c602085018561548a565b611b9c604086016020870161548a565b611bac606087016040880161548a565b60608701356080880135611bc360a08a018a6159fa565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c059250505060c08b018b6159fa565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250613cef915050565b6000611c54610100850160e0860161548a565b6001600160a01b03161415611ca45760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b60448201526064016111d1565b6000611cb86101208501610100860161548a565b6001600160a01b03161415611d035760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b60448201526064016111d1565b6000611d17610140850161012086016156bb565b63ffffffff16118015611d4b5750611d31600a6064615b69565b611d43610140850161012086016156bb565b63ffffffff16105b611d8b5760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b60448201526064016111d1565b61012c8361014001351015611dd65760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b60448201526064016111d1565b611de7610100840160e0850161548a565b60f580546001600160a01b0319166001600160a01b0392909216919091179055611e196101208401610100850161548a565b60f680546001600160a01b0319166001600160a01b0392909216919091179055611e4b610140840161012085016156bb565b63ffffffff1660f75561014083013560fb558015611e6f576000805461ff00191690555b505050565b60d4546001600160a01b03163314611e9e5760405162461bcd60e51b81526004016111d190615834565b60008111611eda5760405162461bcd60e51b8152602060048201526009602482015268216d696e507269636560b81b60448201526064016111d1565b60f855565b60d4546001600160a01b03163314611f095760405162461bcd60e51b81526004016111d190615834565b60026001541415611f2c5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d2546040516358ffbb3d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015290911660248201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b40906358ffbb3d9060440160206040518083038186803b158015611fb357600080fd5b505af4158015611fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611feb9190615636565b60cf5490915061200a906201000090046001600160681b0316826137cc565b60cf80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d4546001600160a01b031633146120645760405162461bcd60e51b81526004016111d190615834565b600260015414156120875760405162461bcd60e51b81526004016111d1906158f9565b60026001556101015460fc54604051632288d73760e11b81526001600160a01b0392831660048201527f0000000000000000000000000000000000000000000000000000000000000000909216602483015260448201527325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090634511ae6e9060640160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214d9190615636565b5060018055565b60d4546001600160a01b0316331461217e5760405162461bcd60e51b81526004016111d190615834565b600260015414156121a15760405162461bcd60e51b81526004016111d1906158f9565b60026001556121ae613f2e565b60018055565b600260015414156121d75760405162461bcd60e51b81526004016111d1906158f9565b60026001819055506121ae60006001614061565b6065546001600160a01b031633146122155760405162461bcd60e51b81526004016111d1906158a3565b612223620f42406064615b69565b81106122715760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e63652066656500000000000000000060448201526064016111d1565b60d55460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d555565b6065546001600160a01b031633146122dc5760405162461bcd60e51b81526004016111d1906158a3565b6122e66000614210565b565b6002600154141561230b5760405162461bcd60e51b81526004016111d1906158f9565b600260015561231981614262565b610102546123279082614429565b610102555060018055565b6060609b80546113de90615bcb565b600061237661234f60995490565b6123576126d6565b60d05460cc546001600160801b0390911690610100900460ff16613c03565b905090565b6002600154141561239e5760405162461bcd60e51b81526004016111d1906158f9565b6002600155806123dd5760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b60448201526064016111d1565b60cf5461ffff1660005b8281101561246c5760006123fb8284615a46565b600081815260ca6020526040902054909150156124485760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b60448201526064016111d1565b600090815260ca60205260409020600190558061246481615c06565b9150506123e7565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156124f75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016111d1565b61250433858584036134d7565b5060019392505050565b6065546001600160a01b031633146125385760405162461bcd60e51b81526004016111d1906158a3565b61012c81101561258a5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e000000000000000060448201526064016111d1565b60fb5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160fb55565b600260015414156125ee5760405162461bcd60e51b81526004016111d1906158f9565b6002600155610100546001600160a01b03168061260a57600080fd5b6000821161261757600080fd5b33600090815260976020526040902054828110156126435761264361263c84836137cc565b6000614061565b61264e3330856135fc565b6126593083856134d7565b6040516383df674760e01b815260048101849052336024820152600060448201526001600160a01b038316906383df674790606401600060405180830381600087803b1580156126a857600080fd5b505af11580156126bc573d6000803e3d6000fd5b5050600180555050505050565b600061146e3384846135fc565b60cc546040516370a0823160e01b815230600482015260009161237691620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561272557600080fd5b505afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190615636565b60cf546201000090046001600160681b031690614429565b600260015414156127985760405162461bcd60e51b81526004016111d1906158f9565b6002600155806127ba5760405162461bcd60e51b81526004016111d1906158d8565b6127c48133613939565b60cc5461214d906201000090046001600160a01b0316333084613bcb565b60d4546001600160a01b0316331461280c5760405162461bcd60e51b81526004016111d190615834565b6002600154141561282f5760405162461bcd60e51b81526004016111d1906158f9565b60026001556101025460fd546000908190819061284c9085614435565b60fd81905560d054929550909350915060009061287990600160801b90046001600160801b031686614429565b9050612884816147d0565b60d080546001600160801b03808416600160801b0291161790556000610102556128ad83613ca5565b60cf805462010000600160781b031916620100006001600160681b0386160217905560405183815233906001600160a01b038616907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000008116602483015285166044820152606481018490526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090632904c2399060840160206040518083038186803b1580156129bc57600080fd5b505af41580156129d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f49190615636565b61010154604051636648c73d60e11b81526001600160a01b03918216600482015290871660248201526044810182905261138860648201529091507325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063cc918e7a9060840160206040518083038186803b158015612a6657600080fd5b505af4158015612a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9e9190615636565b50612aa7613f2e565b50506001805550505050565b610103546001600160a01b031680612aca57600080fd5b612ad660006001614061565b3360008181526097602052604090205490612af29083836134d7565b60405163c9c2d4f560e01b8152336004820152602481018290526001600160a01b0383169063c9c2d4f590604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505050565b6000806000612b648461325b565b909250905061186e8282614429565b60d4546001600160a01b03163314612b9d5760405162461bcd60e51b81526004016111d190615834565b600081118015612bb85750612bb4600a6064615b69565b8111155b612bf75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b60448201526064016111d1565b60f75460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160f755565b6065546001600160a01b03163314612c625760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612ca55760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b60448201526064016111d1565b60d480546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612cea5760405162461bcd60e51b81526004016111d1906158f9565b600260015580612d295760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b61214d816000614061565b6065546001600160a01b03163314612d5e5760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612da75760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b60448201526064016111d1565b60d3546001600160a01b0382811691161415612e055760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e74000000000000000060448201526064016111d1565b60d380546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612e4a5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d25460408051610120810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f00000000000000000000000000000000000000000000000000000000000000008116602083015292831681830181905260006060830181905260f95461ffff16608084015260fa5460a084015260f654851660c084015260f55490941660e083015260f754610100830152915163f39ad79560e01b815291929091819081907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063f39ad79590612f3a90879060cc9060cf90600401615930565b60606040518083038186803b158015612f5257600080fd5b505af4158015612f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8a9190615549565b604080518381526020810183905293965091945092507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb0910160405180910390a160d180546001600160a01b0319166001600160a01b0385161790556000612ff24282614429565b905063ffffffff8111156130485760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e5265616479000000000000000060448201526064016111d1565b60d2805463ffffffff60a01b1916600160a01b63ffffffff841602179055612aa78661481a565b6065546001600160a01b031633146130995760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166130fe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016111d1565b61310781614210565b50565b6002600154141561312d5760405162461bcd60e51b81526004016111d1906158f9565b600260015560cc547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811662010000909204161461319e5760405162461bcd60e51b8152602060048201526005602482015264042ae8aa8960db1b60448201526064016111d1565b600034116131d75760405162461bcd60e51b81526020600482015260066024820152652176616c756560d01b60448201526064016111d1565b6131e13433613939565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323c57600080fd5b505af1158015613250573d6000803e3d6000fd5b505060018055505050565b6001600160a01b038116600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b0316918101919091528291600111156132dd575050506001600160a01b031660009081526097602052604081205491565b60cf54815161ffff908116600090815260ca602052604081205460cc54919361331393869391169190610100900460ff1661498c565b9050613334856001600160a01b031660009081526097602052604090205490565b9590945092505050565b600260015414156133615760405162461bcd60e51b81526004016111d1906158f9565b600260015560006133706149fd565b60fd5490915061338090826137cc565b6001600160801b031660fd555060018055565b6065546001600160a01b031633146133bd5760405162461bcd60e51b81526004016111d1906158a3565b6133cb620f42406064615b69565b81106134125760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b60448201526064016111d1565b600061342f63031ba30961342984620f4240614bba565b90614bc6565b60d65460408051918252602082018590529192507f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a160d65550565b6040516001600160a01b038316602482015260448101829052611e6f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bd2565b6001600160a01b0383166135395760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016111d1565b6001600160a01b03821661359a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016111d1565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166136605760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016111d1565b6001600160a01b0382166136c25760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016111d1565b6001600160a01b0383166000908152609760205260409020548181101561373a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016111d1565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290613771908490615a46565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516137bd91815260200190565b60405180910390a35b50505050565b600061151d8284615b88565b60cc546001600160a01b03620100009091048116907f00000000000000000000000000000000000000000000000000000000000000001681141561392557604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561387857600080fd5b505af115801561388c573d6000803e3d6000fd5b505050506000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146138dd576040519150601f19603f3d011682016040523d82523d6000602084013e6138e2565b606091505b50509050806137c65760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016111d1565b611e6f6001600160a01b0382168484613474565b60cf5461ffff1660006139548461394e6126d6565b90614429565b60ce549091506001600160681b031681111561399f5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b60448201526064016111d1565b60cd54600160a01b900466ffffffffffffff168110156139f85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016111d1565b60408051858152602081018490526001600160a01b038516917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a26001600160a01b038316600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031683860152600160781b9091046001600160801b031682840152845260ca90925282205460cc54919291613ab7918491879190610100900460ff1661498c565b8251909150869061ffff16851415613ae8576020830151600090613ae4906001600160681b031689614429565b9150505b613af181613ca5565b6040805160608101825261ffff80881682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038e16600090815260c990935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560d054613b97911689614429565b9050613ba2816147d0565b60d080546001600160801b0319166001600160801b039290921691909117905550505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526137c69085906323b872dd60e01b906084016134a0565b600080613c1183600a615ac1565b905060008611613c215780613c39565b613c3986613429613c3288886137cc565b8490614bba565b9695505050505050565b600060018311613c8d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e613c9b83600a615ac1565b6134298686614bba565b6001600160681b038111156131075760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b60448201526064016111d1565b600054610100900460ff1680613d08575060005460ff16155b613d245760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015613d46576000805461ffff19166101011790555b60405163c72733f760e01b81527325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063c72733f790613d8b908c908c908c908b908d908c908c908c90600401615727565b60006040518083038186803b158015613da357600080fd5b505af4158015613db7573d6000803e3d6000fd5b50505050613dc3614ca4565b613dcd8484614d17565b613dd5614d96565b613dde8961306f565b60d480546001600160a01b03808b166001600160a01b03199283161790925560d38054928a169290911691909117905560d5859055613e2863031ba30961342988620f4240614bba565b60d6558160cc613e388282615c5e565b505060cc546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a082319060240160206040518083038186803b158015613e8457600080fd5b505afa158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615636565b9050613ec781613ca5565b60cf805461ffff196001600160681b03909316600160781b02929092167fffffffff00000000000000000000000000ffffffffffffffffffffffffff00009092169190911760011790558015613f23576000805461ff00191690555b505050505050505050565b6040805160c08101825260d2546001600160a01b039081168083527f000000000000000000000000000000000000000000000000000000000000000082166020840190815260cc54620100008104841685870190815260ff610100909204919091166060860190815260f8546080870190815260fb5460a088019081529751633baf17fd60e21b8152875187166004820152935186166024850152915190941660448301529251606482015291516084830152925160a48201529091907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063eebc5ff49060c40160206040518083038186803b15801561402257600080fd5b505af4158015614036573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405a9190615636565b60fc555050565b33600090815260c9602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cf5491865260ca9094529184205460cc54919492909316926140dc9185918591610100900460ff1661498c565b9050836140e957846140eb565b805b9450846140f9575050505050565b8085111561413d5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b60448201526064016111d1565b825161ffff1682111561416a5733600090815260c960205260409020805462010000600160781b03191690555b614173856147d0565b61417d81866137cc565b33600081815260c960205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916141f69189825261ffff16602082015260400190565b60405180910390a26142093033876135fc565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000811161429f5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b33600090815260c960205260409020546201000090046001600160681b03161515806142e9575033600090815260c96020526040902054600160781b90046001600160801b031615155b156142fa576142fa60006001614061565b60cf5433600081815260cb60209081526040918290208054835187815261ffff96871693810184905292959194911685149290917f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a281546201000090046001600160801b0316600082156143835761437c8287614429565b90506143e6565b81156143c55760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b60448201526064016111d1565b5033600090815260cb60205260409020805461ffff191661ffff8616179055845b6143ef816147d0565b33600081815260cb60205260409020805462010000600160901b031916620100006001600160801b03851602179055612b4e9030886135fc565b600061151d8284615a46565b60d25460009081908190600160a01b900463ffffffff164210156144845760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b60448201526064016111d1565b60d1546001600160a01b03169250826144cd5760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b60448201526064016111d1565b60d3546040805160e08101825260cc54610100810460ff16825291516370a0823160e01b81523060048201526001600160a01b03938416936000938493849384937325af99ff07575ac55fb3c2d1e6b920ba3bf67b409363633c14269360cf939092602084019262010000900416906370a082319060240160206040518083038186803b15801561455d57600080fd5b505afa158015614571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145959190615636565b81526020016145a360995490565b81526020018e815260200160d554815260200160d65481526020018d8152506040518363ffffffff1660e01b815260040161463092919060006101008201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e08301529392505050565b60c06040518083038186803b15801561464857600080fd5b505af415801561465c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146809190615672565b809750819850829950839650849b50859c505050505050508760d160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060d160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060cf60000160009054906101000a900461ffff1661ffff1690508160ca600083815260200190815260200160002081905550856001600160a01b03167f0a242f7ecaf711036ca770774ceffae28e60ef042ac113ddd187f2631db0c00685858460405161476f939291909283526020830191909152604082015260600190565b60405180910390a260d080546001600160801b0319169055614792816001615a46565b60cf805461ffff191661ffff92909216919091179055506147b590503084614dfd565b80156147c5576147c584826137d8565b505050509250925092565b6001600160801b038111156131075760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b60448201526064016111d1565b60cf546201000090046001600160681b03166001600160a01b038216156148665760cf80546cffffffffffffffffffffffffff60781b1916600160781b6001600160681b038416021790555b60cf805462010000600160781b031916905560d280546001600160a01b03191690556001600160a01b038216156113cb57604051636c6fe87f60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063d8dfd0fe9060240160206040518083038186803b15801561490d57600080fd5b505af4158015614921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149459190615636565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f7836040516135ef91815260200190565b835160009061ffff16158015906149a75750845161ffff1684115b156149e95760006149c686602001516001600160681b03168585614edc565b60408701519091506149e1906001600160801b031682614429565b91505061186e565b50505050604001516001600160801b031690565b33600090815260cb6020526040812080546001600160801b03620100008204169061ffff1681614a5f5760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b60448201526064016111d1565b60cf5461ffff168110614aa75760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b60448201526064016111d1565b33600090815260cb60205260409020805462010000600160901b031916905560d054614ae390600160801b90046001600160801b0316836137cc565b60d080546001600160801b03928316600160801b029216919091179055600081815260ca602052604081205460cc54614b2691859160ff61010090910416613c43565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a2614b6e3084614f3f565b60008111614bb05760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b60448201526064016111d1565b61186e33826137d8565b600061151d8284615b69565b600061151d8284615a5e565b6000614c27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661508d9092919063ffffffff16565b805190915015611e6f5780806020019051810190614c45919061557f565b611e6f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016111d1565b600054610100900460ff1680614cbd575060005460ff16155b614cd95760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614cfb576000805461ffff19166101011790555b614d0361509c565b8015613107576000805461ff001916905550565b600054610100900460ff1680614d30575060005460ff16155b614d4c5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d6e576000805461ffff19166101011790555b614d7661510b565b614d808383615175565b8015611e6f576000805461ff0019169055505050565b600054610100900460ff1680614daf575060005460ff16155b614dcb5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614ded576000805461ffff19166101011790555b614df561510b565b614d0361520a565b6001600160a01b038216614e535760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016111d1565b8060996000828254614e659190615a46565b90915550506001600160a01b03821660009081526097602052604081208054839290614e92908490615a46565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614f265760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e83613429614f3885600a615ac1565b8790614bba565b6001600160a01b038216614f9f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016111d1565b6001600160a01b038216600090815260976020526040902054818110156150135760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016111d1565b6001600160a01b0383166000908152609760205260408120838303905560998054849290615042908490615b88565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b606061186e848460008561526a565b600054610100900460ff16806150b5575060005460ff16155b6150d15760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156150f3576000805461ffff19166101011790555b600180558015613107576000805461ff001916905550565b600054610100900460ff1680615124575060005460ff16155b6151405760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d03576000805461ffff19166101011790558015613107576000805461ff001916905550565b600054610100900460ff168061518e575060005460ff16155b6151aa5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156151cc576000805461ffff19166101011790555b82516151df90609a9060208601906153cb565b5081516151f390609b9060208501906153cb565b508015611e6f576000805461ff0019169055505050565b600054610100900460ff1680615223575060005460ff16155b61523f5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015615261576000805461ffff19166101011790555b614d0333614210565b6060824710156152cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016111d1565b843b6153195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016111d1565b600080866001600160a01b03168587604051615335919061570b565b60006040518083038185875af1925050503d8060008114615372576040519150601f19603f3d011682016040523d82523d6000602084013e615377565b606091505b5091509150615387828286615392565b979650505050505050565b606083156153a157508161151d565b8251156153b15782518084602001fd5b8160405162461bcd60e51b81526004016111d19190615821565b8280546153d790615bcb565b90600052602060002090601f0160209004810192826153f9576000855561543f565b82601f1061541257805160ff191683800117855561543f565b8280016001018555821561543f579182015b8281111561543f578251825591602001919060010190615424565b5061544b92915061544f565b5090565b5b8082111561544b5760008155600101615450565b803561546f81615d6f565b919050565b803561546f81615d92565b803561546f81615da7565b60006020828403121561549b578081fd5b813561151d81615d6f565b600080604083850312156154b8578081fd5b82356154c381615d6f565b915060208301356154d381615d6f565b809150509250929050565b6000806000606084860312156154f2578081fd5b83356154fd81615d6f565b9250602084013561550d81615d6f565b929592945050506040919091013590565b60008060408385031215615530578182fd5b823561553b81615d6f565b946020939093013593505050565b60008060006060848603121561555d578283fd5b835161556881615d6f565b602085015160409095015190969495509392505050565b600060208284031215615590578081fd5b815161151d81615d84565b60008082840360e08112156155ae578283fd5b833567ffffffffffffffff8111156155c4578384fd5b840161016081870312156155d6578384fd5b925060c0601f19820112156155e9578182fd5b506020830190509250929050565b600060208284031215615608578081fd5b81356001600160801b038116811461151d578182fd5b60006020828403121561562f578081fd5b5035919050565b600060208284031215615647578081fd5b5051919050565b60008060408385031215615660578182fd5b8235915060208301356154d381615d6f565b60008060008060008060c0878903121561568a578182fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6000602082840312156156cc578081fd5b813563ffffffff8116811461151d578182fd5b600081518084526156f7816020860160208601615b9f565b601f01601f19169290920160200192915050565b6000825161571d818460208701615b9f565b9190910192915050565b60006101a060018060a01b03808c168452808b166020850152808a1660408501528860608501528760808501528160a0850152615766828501886156df565b915083820360c085015261577a82876156df565b92508435915061578982615d84565b90151560e08401526020840135906157a082615dbc565b60ff8216610100850152604085013591506157ba82615d6f565b166101208301526157cd60608401615464565b6001600160a01b03166101408301526157e86080840161547f565b66ffffffffffffff1661016083015261580360a08401615474565b6001600160681b038116610180840152509998505050505050505050565b60208152600061151d60208301846156df565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526608585b5bdd5b9d60ca1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b83516001600160a01b0316815260208085015161016083019161595d908401826001600160a01b03169052565b50604085015161597860408401826001600160a01b03169052565b50606085015160608301526080850151615998608084018261ffff169052565b5060a085015160a083015260c08501516159bd60c08401826001600160a01b03169052565b5060e08501516159d860e08401826001600160a01b03169052565b5061010094850151948201949094526101208101929092526101409091015290565b6000808335601e19843603018112615a10578283fd5b83018035915067ffffffffffffffff821115615a2a578283fd5b602001915036819003821315615a3f57600080fd5b9250929050565b60008219821115615a5957615a59615c21565b500190565b600082615a7957634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115615ab9578160001904821115615a9f57615a9f615c21565b80851615615aac57918102915b93841c9390800290615a83565b509250929050565b600061151d8383600082615ad757506001611472565b81615ae457506000611472565b8160018114615afa5760028114615b0457615b20565b6001915050611472565b60ff841115615b1557615b15615c21565b50506001821b611472565b5060208310610133831016604e8410600b8410161715615b43575081810a611472565b615b4d8383615a7e565b8060001904821115615b6157615b61615c21565b029392505050565b6000816000190483118215151615615b8357615b83615c21565b500290565b600082821015615b9a57615b9a615c21565b500390565b60005b83811015615bba578181015183820152602001615ba2565b838111156137c65750506000910152565b600181811c90821680615bdf57607f821691505b60208210811415615c0057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c1a57615c1a615c21565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6000813561147281615d6f565b6000813561147281615d92565b6000813561147281615da7565b8135615c6981615d84565b815460ff19811691151560ff1691821783556020840135615c8981615dbc565b61ff008160081b169050808361ffff198416171784556040850135615cad81615d6f565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d05615ce860608501615c37565b82546001600160a01b0319166001600160a01b0391909116178255565b615d3e615d1460808501615c51565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b506113cb615d4e60a08401615c44565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b038116811461310757600080fd5b801515811461310757600080fd5b6001600160681b038116811461310757600080fd5b66ffffffffffffff8116811461310757600080fd5b60ff8116811461310757600080fdfea26469706673582212205fbcaa8781187325d25b31e13bacef69edce6157041a37dc50e9cf290afbc57364736f6c63430008040033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc720000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101
Deployed Bytecode
0x6080604052600436106104c05760003560e01c806387153eb111610276578063b4d1d7951161014f578063e73c63d5116100c1578063f656ba5111610085578063f656ba51146110db578063f756fa2114611110578063f957a06714611125578063f9a0be6814611145578063fba7dc6114611166578063fe56e2321461118757600080fd5b8063e73c63d514611068578063e74b981b1461107e578063ea00b1ca1461109e578063f2fde38b146110b3578063f6326fb3146110d357600080fd5b8063d13f1b3e11610113578063d13f1b3e14610f78578063d164cc1514610f98578063d5f2638214610fb8578063db006a7514610fce578063db43e86214610fee578063dd62ed3e1461102257600080fd5b8063b4d1d79514610ef7578063b6b55f2514610f0e578063b9f8092b14610f2e578063ca59409414610f43578063ce7c2ac214610f5857600080fd5b8063a285c9e8116101e8578063a6f7f5d6116101ac578063a6f7f5d614610e38578063a9059cbb14610e4e578063aced166114610e6e578063ad5c464814610e8e578063ad7a672f14610ec2578063afa6626414610ed757600080fd5b8063a285c9e814610d98578063a2db9d8314610dba578063a457c2d714610dd8578063a497e67414610df8578063a694fc3a14610e1857600080fd5b8063947061b51161023a578063947061b514610c1e57806395d89b4114610c3f57806399530b0614610c545780639be43daa14610c695780639fcc2d7514610c89578063a083ff1714610d3557600080fd5b806387153eb114610b6b5780638778878214610b9857806389a3027114610bae5780638b10cc7c14610be25780638da5cb5b14610c0057600080fd5b806347786d37116103a85780636719b2ee1161031a57806370897b23116102de57806370897b2314610a6657806370a0823114610a86578063715018a614610abc5780637a9262a214610ad15780637e108d5214610b3557806383536ff314610b5557600080fd5b80636719b2ee1461096f57806369b41170146109f35780636b64c76914610a085780636c10f66c14610a1d5780636f31ab3414610a5157600080fd5b8063573f0d6e1161036c578063573f0d6e146108b157806358358207146108d15780635ea8cd12146108f1578063600a2cfb14610911578063611b034d14610926578063650cce8a1461093b57600080fd5b806347786d37146108245780634ac88cb6146108445780634b2431d914610864578063503c70aa1461087b57806355489bb21461089157600080fd5b806330630da4116104415780633ec143d3116104055780633ec143d3146107625780633f23bb73146107905780633f90916a146107b0578063432833a6146107ce5780634603c0aa146107e4578063469048401461080457600080fd5b806330630da4146106ba578063313ce567146106da578063355274ea1461070457806336efd16f14610722578063395093511461074257600080fd5b806318160ddd1161048857806318160ddd146105865780631a92f6c21461059b57806323b872dd146105e75780632728f333146106075780632775d01c1461069a57600080fd5b8063048bf085146104c5578063056097ac146104e757806306fdde0314610507578063095ea7b3146105325780630cbf54c814610562575b600080fd5b3480156104d157600080fd5b506104e56104e036600461548a565b6111a7565b005b3480156104f357600080fd5b506104e56105023660046154a6565b6111fd565b34801561051357600080fd5b5061051c6113cf565b6040516105299190615821565b60405180910390f35b34801561053e57600080fd5b5061055261054d36600461551e565b611461565b6040519015158152602001610529565b34801561056e57600080fd5b5061057860fb5481565b604051908152602001610529565b34801561059257600080fd5b50609954610578565b3480156105a757600080fd5b506105cf7f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc7281565b6040516001600160a01b039091168152602001610529565b3480156105f357600080fd5b506105526106023660046154de565b611478565b34801561061357600080fd5b5060cf5460d0546106559161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a001610529565b3480156106a657600080fd5b506104e56106b536600461561e565b611524565b3480156106c657600080fd5b506104e56106d536600461548a565b6116c7565b3480156106e657600080fd5b5060cc54610100900460ff1660405160ff9091168152602001610529565b34801561071057600080fd5b5060ce546001600160681b0316610578565b34801561072e57600080fd5b506104e561073d36600461564e565b61175f565b34801561074e57600080fd5b5061055261075d36600461551e565b6117e7565b34801561076e57600080fd5b5060f95461077d9061ffff1681565b60405161ffff9091168152602001610529565b34801561079c57600080fd5b506105786107ab36600461548a565b611823565b3480156107bc57600080fd5b5060d0546001600160801b0316610578565b3480156107da57600080fd5b5061057860fc5481565b3480156107f057600080fd5b506104e56107ff36600461548a565b611876565b34801561081057600080fd5b5060d3546105cf906001600160a01b031681565b34801561083057600080fd5b506104e561083f36600461561e565b611918565b34801561085057600080fd5b506104e561085f36600461548a565b6119f3565b34801561087057600080fd5b506105786101025481565b34801561088757600080fd5b5061057860fd5481565b34801561089d57600080fd5b506104e56108ac3660046155f7565b611a40565b3480156108bd57600080fd5b506104e56108cc36600461548a565b611ad8565b3480156108dd57600080fd5b506104e56108ec36600461559b565b611b25565b3480156108fd57600080fd5b506104e561090c36600461561e565b611e74565b34801561091d57600080fd5b506104e5611edf565b34801561093257600080fd5b506104e561203a565b34801561094757600080fd5b506105cf7f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef81565b34801561097b57600080fd5b506109c261098a36600461548a565b60c96020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b031690820152606001610529565b3480156109ff57600080fd5b50610578600081565b348015610a1457600080fd5b506104e5612154565b348015610a2957600080fd5b506105cf7f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10181565b348015610a5d57600080fd5b506104e56121b4565b348015610a7257600080fd5b506104e5610a8136600461561e565b6121eb565b348015610a9257600080fd5b50610578610aa136600461548a565b6001600160a01b031660009081526097602052604090205490565b348015610ac857600080fd5b506104e56122b2565b348015610add57600080fd5b50610b13610aec36600461548a565b60cb6020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b03909116602083015201610529565b348015610b4157600080fd5b506104e5610b5036600461561e565b6122e8565b348015610b6157600080fd5b5061057860f75481565b348015610b7757600080fd5b50610578610b8636600461561e565b60ca6020526000908152604090205481565b348015610ba457600080fd5b5061057860d55481565b348015610bba57600080fd5b506105cf7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b348015610bee57600080fd5b5060d2546001600160a01b03166105cf565b348015610c0c57600080fd5b506065546001600160a01b03166105cf565b348015610c2a57600080fd5b50610103546105cf906001600160a01b031681565b348015610c4b57600080fd5b5061051c612332565b348015610c6057600080fd5b50610578612341565b348015610c7557600080fd5b506104e5610c8436600461561e565b61237b565b348015610c9557600080fd5b5060cc5460cd5460ce54610ce59260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c001610529565b348015610d4157600080fd5b5060d15460d254610d6c916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff1690820152606001610529565b348015610da457600080fd5b5060d254600160a01b900463ffffffff16610578565b348015610dc657600080fd5b5060d1546001600160a01b03166105cf565b348015610de457600080fd5b50610552610df336600461551e565b612475565b348015610e0457600080fd5b506104e5610e1336600461561e565b61250e565b348015610e2457600080fd5b506104e5610e3336600461561e565b6125cb565b348015610e4457600080fd5b5061057860d65481565b348015610e5a57600080fd5b50610552610e6936600461551e565b6126c9565b348015610e7a57600080fd5b5060d4546105cf906001600160a01b031681565b348015610e9a57600080fd5b506105cf7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b348015610ece57600080fd5b506105786126d6565b348015610ee357600080fd5b5060f5546105cf906001600160a01b031681565b348015610f0357600080fd5b5061057862093a8081565b348015610f1a57600080fd5b506104e5610f2936600461561e565b612775565b348015610f3a57600080fd5b506104e56127e2565b348015610f4f57600080fd5b506104e5612ab3565b348015610f6457600080fd5b50610578610f7336600461548a565b612b56565b348015610f8457600080fd5b506104e5610f9336600461561e565b612b73565b348015610fa457600080fd5b506104e5610fb336600461548a565b612c38565b348015610fc457600080fd5b5061057860f85481565b348015610fda57600080fd5b506104e5610fe936600461561e565b612cc7565b348015610ffa57600080fd5b506105cf7f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e81565b34801561102e57600080fd5b5061057861103d3660046154a6565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b34801561107457600080fd5b5061057860fa5481565b34801561108a57600080fd5b506104e561109936600461548a565b612d34565b3480156110aa57600080fd5b506104e5612e27565b3480156110bf57600080fd5b506104e56110ce36600461548a565b61306f565b6104e561310a565b3480156110e757600080fd5b506110fb6110f636600461548a565b61325b565b60408051928352602083019190915201610529565b34801561111c57600080fd5b506104e561333e565b34801561113157600080fd5b5060f6546105cf906001600160a01b031681565b34801561115157600080fd5b50610100546105cf906001600160a01b031681565b34801561117257600080fd5b50610101546105cf906001600160a01b031681565b34801561119357600080fd5b506104e56111a236600461561e565b613393565b6065546001600160a01b031633146111da5760405162461bcd60e51b81526004016111d1906158a3565b60405180910390fd5b61010080546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146112275760405162461bcd60e51b81526004016111d1906158a3565b60cc546001600160a01b038381166201000090920416141561128b5760405162461bcd60e51b815260206004820152601b60248201527f5661756c74206173736574206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b0382163014156112e45760405162461bcd60e51b815260206004820152601b60248201527f5661756c74207368617265206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b03811630141561133d5760405162461bcd60e51b815260206004820152601960248201527f526563697069656e742063616e6e6f74206265207661756c740000000000000060448201526064016111d1565b6040516370a0823160e01b81523060048201526113cb9082906001600160a01b038516906370a082319060240160206040518083038186803b15801561138257600080fd5b505afa158015611396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ba9190615636565b6001600160a01b0385169190613474565b5050565b6060609a80546113de90615bcb565b80601f016020809104026020016040519081016040528092919081815260200182805461140a90615bcb565b80156114575780601f1061142c57610100808354040283529160200191611457565b820191906000526020600020905b81548152906001019060200180831161143a57829003601f168201915b5050505050905090565b600061146e3384846134d7565b5060015b92915050565b60006114858484846135fc565b6001600160a01b03841660009081526098602090815260408083203384529091529020548281101561150a5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084016111d1565b61151785338584036134d7565b60019150505b9392505050565b600260015414156115475760405162461bcd60e51b81526004016111d1906158f9565b600260015533600090815260c96020526040902060cf5461ffff168261157f5760405162461bcd60e51b81526004016111d1906158d8565b815461ffff1681146115c35760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b60448201526064016111d1565b81546201000090046001600160681b0316838110156116145760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b60448201526064016111d1565b61161e81856137cc565b83546001600160681b0391909116620100000262010000600160781b031990911617835560d054611658906001600160801b0316856137cc565b60d080546001600160801b0319166001600160801b0392909216919091179055604080518581526020810184905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a26116bd33856137d8565b5050600180555050565b6065546001600160a01b031633146116f15760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b03811661173d5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b60448201526064016111d1565b60f680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156117825760405162461bcd60e51b81526004016111d1906158f9565b6002600155816117a45760405162461bcd60e51b81526004016111d1906158d8565b6001600160a01b0381166117b757600080fd5b6117c18282613939565b60cc546117df906201000090046001600160a01b0316333085613bcb565b505060018055565b3360008181526098602090815260408083206001600160a01b0387168452909152812054909161146e91859061181e908690615a46565b6134d7565b60cc5460009060ff610100909104168161185961183f60995490565b6118476126d6565b60d0546001600160801b031685613c03565b905061186e61186785612b56565b8284613c43565b949350505050565b6065546001600160a01b031633146118a05760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166118f65760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d507269636572000000000000000060448201526064016111d1565b60f580546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146119425760405162461bcd60e51b81526004016111d1906158a3565b6000811161197c5760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b60448201526064016111d1565b61198581613ca5565b60ce54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160ce80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b6065546001600160a01b03163314611a1d5760405162461bcd60e51b81526004016111d1906158a3565b61010180546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b03163314611a6a5760405162461bcd60e51b81526004016111d1906158a3565b6000816001600160801b031611611ab25760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b60448201526064016111d1565b6001600160801b031660fa5560cf5460f9805461ffff191661ffff909216919091179055565b6065546001600160a01b03163314611b025760405162461bcd60e51b81526004016111d1906158a3565b61010380546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff1680611b3e575060005460ff16155b611b5a5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015611b7c576000805461ffff19166101011790555b611c41611b8c602085018561548a565b611b9c604086016020870161548a565b611bac606087016040880161548a565b60608701356080880135611bc360a08a018a6159fa565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c059250505060c08b018b6159fa565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250613cef915050565b6000611c54610100850160e0860161548a565b6001600160a01b03161415611ca45760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b60448201526064016111d1565b6000611cb86101208501610100860161548a565b6001600160a01b03161415611d035760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b60448201526064016111d1565b6000611d17610140850161012086016156bb565b63ffffffff16118015611d4b5750611d31600a6064615b69565b611d43610140850161012086016156bb565b63ffffffff16105b611d8b5760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b60448201526064016111d1565b61012c8361014001351015611dd65760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b60448201526064016111d1565b611de7610100840160e0850161548a565b60f580546001600160a01b0319166001600160a01b0392909216919091179055611e196101208401610100850161548a565b60f680546001600160a01b0319166001600160a01b0392909216919091179055611e4b610140840161012085016156bb565b63ffffffff1660f75561014083013560fb558015611e6f576000805461ff00191690555b505050565b60d4546001600160a01b03163314611e9e5760405162461bcd60e51b81526004016111d190615834565b60008111611eda5760405162461bcd60e51b8152602060048201526009602482015268216d696e507269636560b81b60448201526064016111d1565b60f855565b60d4546001600160a01b03163314611f095760405162461bcd60e51b81526004016111d190615834565b60026001541415611f2c5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d2546040516358ffbb3d60e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc728116600483015290911660248201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b40906358ffbb3d9060440160206040518083038186803b158015611fb357600080fd5b505af4158015611fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611feb9190615636565b60cf5490915061200a906201000090046001600160681b0316826137cc565b60cf80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d4546001600160a01b031633146120645760405162461bcd60e51b81526004016111d190615834565b600260015414156120875760405162461bcd60e51b81526004016111d1906158f9565b60026001556101015460fc54604051632288d73760e11b81526001600160a01b0392831660048201527f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101909216602483015260448201527325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090634511ae6e9060640160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214d9190615636565b5060018055565b60d4546001600160a01b0316331461217e5760405162461bcd60e51b81526004016111d190615834565b600260015414156121a15760405162461bcd60e51b81526004016111d1906158f9565b60026001556121ae613f2e565b60018055565b600260015414156121d75760405162461bcd60e51b81526004016111d1906158f9565b60026001819055506121ae60006001614061565b6065546001600160a01b031633146122155760405162461bcd60e51b81526004016111d1906158a3565b612223620f42406064615b69565b81106122715760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e63652066656500000000000000000060448201526064016111d1565b60d55460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d555565b6065546001600160a01b031633146122dc5760405162461bcd60e51b81526004016111d1906158a3565b6122e66000614210565b565b6002600154141561230b5760405162461bcd60e51b81526004016111d1906158f9565b600260015561231981614262565b610102546123279082614429565b610102555060018055565b6060609b80546113de90615bcb565b600061237661234f60995490565b6123576126d6565b60d05460cc546001600160801b0390911690610100900460ff16613c03565b905090565b6002600154141561239e5760405162461bcd60e51b81526004016111d1906158f9565b6002600155806123dd5760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b60448201526064016111d1565b60cf5461ffff1660005b8281101561246c5760006123fb8284615a46565b600081815260ca6020526040902054909150156124485760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b60448201526064016111d1565b600090815260ca60205260409020600190558061246481615c06565b9150506123e7565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156124f75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016111d1565b61250433858584036134d7565b5060019392505050565b6065546001600160a01b031633146125385760405162461bcd60e51b81526004016111d1906158a3565b61012c81101561258a5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e000000000000000060448201526064016111d1565b60fb5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160fb55565b600260015414156125ee5760405162461bcd60e51b81526004016111d1906158f9565b6002600155610100546001600160a01b03168061260a57600080fd5b6000821161261757600080fd5b33600090815260976020526040902054828110156126435761264361263c84836137cc565b6000614061565b61264e3330856135fc565b6126593083856134d7565b6040516383df674760e01b815260048101849052336024820152600060448201526001600160a01b038316906383df674790606401600060405180830381600087803b1580156126a857600080fd5b505af11580156126bc573d6000803e3d6000fd5b5050600180555050505050565b600061146e3384846135fc565b60cc546040516370a0823160e01b815230600482015260009161237691620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561272557600080fd5b505afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190615636565b60cf546201000090046001600160681b031690614429565b600260015414156127985760405162461bcd60e51b81526004016111d1906158f9565b6002600155806127ba5760405162461bcd60e51b81526004016111d1906158d8565b6127c48133613939565b60cc5461214d906201000090046001600160a01b0316333084613bcb565b60d4546001600160a01b0316331461280c5760405162461bcd60e51b81526004016111d190615834565b6002600154141561282f5760405162461bcd60e51b81526004016111d1906158f9565b60026001556101025460fd546000908190819061284c9085614435565b60fd81905560d054929550909350915060009061287990600160801b90046001600160801b031686614429565b9050612884816147d0565b60d080546001600160801b03808416600160801b0291161790556000610102556128ad83613ca5565b60cf805462010000600160781b031916620100006001600160681b0386160217905560405183815233906001600160a01b038616907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc72811660048301527f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef8116602483015285166044820152606481018490526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090632904c2399060840160206040518083038186803b1580156129bc57600080fd5b505af41580156129d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f49190615636565b61010154604051636648c73d60e11b81526001600160a01b03918216600482015290871660248201526044810182905261138860648201529091507325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063cc918e7a9060840160206040518083038186803b158015612a6657600080fd5b505af4158015612a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9e9190615636565b50612aa7613f2e565b50506001805550505050565b610103546001600160a01b031680612aca57600080fd5b612ad660006001614061565b3360008181526097602052604090205490612af29083836134d7565b60405163c9c2d4f560e01b8152336004820152602481018290526001600160a01b0383169063c9c2d4f590604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505050565b6000806000612b648461325b565b909250905061186e8282614429565b60d4546001600160a01b03163314612b9d5760405162461bcd60e51b81526004016111d190615834565b600081118015612bb85750612bb4600a6064615b69565b8111155b612bf75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b60448201526064016111d1565b60f75460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160f755565b6065546001600160a01b03163314612c625760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612ca55760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b60448201526064016111d1565b60d480546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612cea5760405162461bcd60e51b81526004016111d1906158f9565b600260015580612d295760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b61214d816000614061565b6065546001600160a01b03163314612d5e5760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612da75760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b60448201526064016111d1565b60d3546001600160a01b0382811691161415612e055760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e74000000000000000060448201526064016111d1565b60d380546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612e4a5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d25460408051610120810182526001600160a01b037f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e811682527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116602083015292831681830181905260006060830181905260f95461ffff16608084015260fa5460a084015260f654851660c084015260f55490941660e083015260f754610100830152915163f39ad79560e01b815291929091819081907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063f39ad79590612f3a90879060cc9060cf90600401615930565b60606040518083038186803b158015612f5257600080fd5b505af4158015612f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8a9190615549565b604080518381526020810183905293965091945092507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb0910160405180910390a160d180546001600160a01b0319166001600160a01b0385161790556000612ff24282614429565b905063ffffffff8111156130485760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e5265616479000000000000000060448201526064016111d1565b60d2805463ffffffff60a01b1916600160a01b63ffffffff841602179055612aa78661481a565b6065546001600160a01b031633146130995760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166130fe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016111d1565b61310781614210565b50565b6002600154141561312d5760405162461bcd60e51b81526004016111d1906158f9565b600260015560cc547f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0390811662010000909204161461319e5760405162461bcd60e51b8152602060048201526005602482015264042ae8aa8960db1b60448201526064016111d1565b600034116131d75760405162461bcd60e51b81526020600482015260066024820152652176616c756560d01b60448201526064016111d1565b6131e13433613939565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323c57600080fd5b505af1158015613250573d6000803e3d6000fd5b505060018055505050565b6001600160a01b038116600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b0316918101919091528291600111156132dd575050506001600160a01b031660009081526097602052604081205491565b60cf54815161ffff908116600090815260ca602052604081205460cc54919361331393869391169190610100900460ff1661498c565b9050613334856001600160a01b031660009081526097602052604090205490565b9590945092505050565b600260015414156133615760405162461bcd60e51b81526004016111d1906158f9565b600260015560006133706149fd565b60fd5490915061338090826137cc565b6001600160801b031660fd555060018055565b6065546001600160a01b031633146133bd5760405162461bcd60e51b81526004016111d1906158a3565b6133cb620f42406064615b69565b81106134125760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b60448201526064016111d1565b600061342f63031ba30961342984620f4240614bba565b90614bc6565b60d65460408051918252602082018590529192507f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a160d65550565b6040516001600160a01b038316602482015260448101829052611e6f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bd2565b6001600160a01b0383166135395760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016111d1565b6001600160a01b03821661359a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016111d1565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166136605760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016111d1565b6001600160a01b0382166136c25760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016111d1565b6001600160a01b0383166000908152609760205260409020548181101561373a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016111d1565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290613771908490615a46565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516137bd91815260200190565b60405180910390a35b50505050565b600061151d8284615b88565b60cc546001600160a01b03620100009091048116907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21681141561392557604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561387857600080fd5b505af115801561388c573d6000803e3d6000fd5b505050506000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146138dd576040519150601f19603f3d011682016040523d82523d6000602084013e6138e2565b606091505b50509050806137c65760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016111d1565b611e6f6001600160a01b0382168484613474565b60cf5461ffff1660006139548461394e6126d6565b90614429565b60ce549091506001600160681b031681111561399f5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b60448201526064016111d1565b60cd54600160a01b900466ffffffffffffff168110156139f85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016111d1565b60408051858152602081018490526001600160a01b038516917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a26001600160a01b038316600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031683860152600160781b9091046001600160801b031682840152845260ca90925282205460cc54919291613ab7918491879190610100900460ff1661498c565b8251909150869061ffff16851415613ae8576020830151600090613ae4906001600160681b031689614429565b9150505b613af181613ca5565b6040805160608101825261ffff80881682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038e16600090815260c990935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560d054613b97911689614429565b9050613ba2816147d0565b60d080546001600160801b0319166001600160801b039290921691909117905550505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526137c69085906323b872dd60e01b906084016134a0565b600080613c1183600a615ac1565b905060008611613c215780613c39565b613c3986613429613c3288886137cc565b8490614bba565b9695505050505050565b600060018311613c8d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e613c9b83600a615ac1565b6134298686614bba565b6001600160681b038111156131075760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b60448201526064016111d1565b600054610100900460ff1680613d08575060005460ff16155b613d245760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015613d46576000805461ffff19166101011790555b60405163c72733f760e01b81527325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063c72733f790613d8b908c908c908c908b908d908c908c908c90600401615727565b60006040518083038186803b158015613da357600080fd5b505af4158015613db7573d6000803e3d6000fd5b50505050613dc3614ca4565b613dcd8484614d17565b613dd5614d96565b613dde8961306f565b60d480546001600160a01b03808b166001600160a01b03199283161790925560d38054928a169290911691909117905560d5859055613e2863031ba30961342988620f4240614bba565b60d6558160cc613e388282615c5e565b505060cc546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a082319060240160206040518083038186803b158015613e8457600080fd5b505afa158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615636565b9050613ec781613ca5565b60cf805461ffff196001600160681b03909316600160781b02929092167fffffffff00000000000000000000000000ffffffffffffffffffffffffff00009092169190911760011790558015613f23576000805461ff00191690555b505050505050505050565b6040805160c08101825260d2546001600160a01b039081168083527f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10182166020840190815260cc54620100008104841685870190815260ff610100909204919091166060860190815260f8546080870190815260fb5460a088019081529751633baf17fd60e21b8152875187166004820152935186166024850152915190941660448301529251606482015291516084830152925160a48201529091907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063eebc5ff49060c40160206040518083038186803b15801561402257600080fd5b505af4158015614036573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405a9190615636565b60fc555050565b33600090815260c9602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cf5491865260ca9094529184205460cc54919492909316926140dc9185918591610100900460ff1661498c565b9050836140e957846140eb565b805b9450846140f9575050505050565b8085111561413d5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b60448201526064016111d1565b825161ffff1682111561416a5733600090815260c960205260409020805462010000600160781b03191690555b614173856147d0565b61417d81866137cc565b33600081815260c960205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916141f69189825261ffff16602082015260400190565b60405180910390a26142093033876135fc565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000811161429f5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b33600090815260c960205260409020546201000090046001600160681b03161515806142e9575033600090815260c96020526040902054600160781b90046001600160801b031615155b156142fa576142fa60006001614061565b60cf5433600081815260cb60209081526040918290208054835187815261ffff96871693810184905292959194911685149290917f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a281546201000090046001600160801b0316600082156143835761437c8287614429565b90506143e6565b81156143c55760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b60448201526064016111d1565b5033600090815260cb60205260409020805461ffff191661ffff8616179055845b6143ef816147d0565b33600081815260cb60205260409020805462010000600160901b031916620100006001600160801b03851602179055612b4e9030886135fc565b600061151d8284615a46565b60d25460009081908190600160a01b900463ffffffff164210156144845760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b60448201526064016111d1565b60d1546001600160a01b03169250826144cd5760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b60448201526064016111d1565b60d3546040805160e08101825260cc54610100810460ff16825291516370a0823160e01b81523060048201526001600160a01b03938416936000938493849384937325af99ff07575ac55fb3c2d1e6b920ba3bf67b409363633c14269360cf939092602084019262010000900416906370a082319060240160206040518083038186803b15801561455d57600080fd5b505afa158015614571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145959190615636565b81526020016145a360995490565b81526020018e815260200160d554815260200160d65481526020018d8152506040518363ffffffff1660e01b815260040161463092919060006101008201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e08301529392505050565b60c06040518083038186803b15801561464857600080fd5b505af415801561465c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146809190615672565b809750819850829950839650849b50859c505050505050508760d160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060d160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060cf60000160009054906101000a900461ffff1661ffff1690508160ca600083815260200190815260200160002081905550856001600160a01b03167f0a242f7ecaf711036ca770774ceffae28e60ef042ac113ddd187f2631db0c00685858460405161476f939291909283526020830191909152604082015260600190565b60405180910390a260d080546001600160801b0319169055614792816001615a46565b60cf805461ffff191661ffff92909216919091179055506147b590503084614dfd565b80156147c5576147c584826137d8565b505050509250925092565b6001600160801b038111156131075760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b60448201526064016111d1565b60cf546201000090046001600160681b03166001600160a01b038216156148665760cf80546cffffffffffffffffffffffffff60781b1916600160781b6001600160681b038416021790555b60cf805462010000600160781b031916905560d280546001600160a01b03191690556001600160a01b038216156113cb57604051636c6fe87f60e11b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc721660048201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063d8dfd0fe9060240160206040518083038186803b15801561490d57600080fd5b505af4158015614921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149459190615636565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f7836040516135ef91815260200190565b835160009061ffff16158015906149a75750845161ffff1684115b156149e95760006149c686602001516001600160681b03168585614edc565b60408701519091506149e1906001600160801b031682614429565b91505061186e565b50505050604001516001600160801b031690565b33600090815260cb6020526040812080546001600160801b03620100008204169061ffff1681614a5f5760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b60448201526064016111d1565b60cf5461ffff168110614aa75760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b60448201526064016111d1565b33600090815260cb60205260409020805462010000600160901b031916905560d054614ae390600160801b90046001600160801b0316836137cc565b60d080546001600160801b03928316600160801b029216919091179055600081815260ca602052604081205460cc54614b2691859160ff61010090910416613c43565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a2614b6e3084614f3f565b60008111614bb05760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b60448201526064016111d1565b61186e33826137d8565b600061151d8284615b69565b600061151d8284615a5e565b6000614c27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661508d9092919063ffffffff16565b805190915015611e6f5780806020019051810190614c45919061557f565b611e6f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016111d1565b600054610100900460ff1680614cbd575060005460ff16155b614cd95760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614cfb576000805461ffff19166101011790555b614d0361509c565b8015613107576000805461ff001916905550565b600054610100900460ff1680614d30575060005460ff16155b614d4c5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d6e576000805461ffff19166101011790555b614d7661510b565b614d808383615175565b8015611e6f576000805461ff0019169055505050565b600054610100900460ff1680614daf575060005460ff16155b614dcb5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614ded576000805461ffff19166101011790555b614df561510b565b614d0361520a565b6001600160a01b038216614e535760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016111d1565b8060996000828254614e659190615a46565b90915550506001600160a01b03821660009081526097602052604081208054839290614e92908490615a46565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614f265760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e83613429614f3885600a615ac1565b8790614bba565b6001600160a01b038216614f9f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016111d1565b6001600160a01b038216600090815260976020526040902054818110156150135760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016111d1565b6001600160a01b0383166000908152609760205260408120838303905560998054849290615042908490615b88565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b606061186e848460008561526a565b600054610100900460ff16806150b5575060005460ff16155b6150d15760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156150f3576000805461ffff19166101011790555b600180558015613107576000805461ff001916905550565b600054610100900460ff1680615124575060005460ff16155b6151405760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d03576000805461ffff19166101011790558015613107576000805461ff001916905550565b600054610100900460ff168061518e575060005460ff16155b6151aa5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156151cc576000805461ffff19166101011790555b82516151df90609a9060208601906153cb565b5081516151f390609b9060208501906153cb565b508015611e6f576000805461ff0019169055505050565b600054610100900460ff1680615223575060005460ff16155b61523f5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015615261576000805461ffff19166101011790555b614d0333614210565b6060824710156152cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016111d1565b843b6153195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016111d1565b600080866001600160a01b03168587604051615335919061570b565b60006040518083038185875af1925050503d8060008114615372576040519150601f19603f3d011682016040523d82523d6000602084013e615377565b606091505b5091509150615387828286615392565b979650505050505050565b606083156153a157508161151d565b8251156153b15782518084602001fd5b8160405162461bcd60e51b81526004016111d19190615821565b8280546153d790615bcb565b90600052602060002090601f0160209004810192826153f9576000855561543f565b82601f1061541257805160ff191683800117855561543f565b8280016001018555821561543f579182015b8281111561543f578251825591602001919060010190615424565b5061544b92915061544f565b5090565b5b8082111561544b5760008155600101615450565b803561546f81615d6f565b919050565b803561546f81615d92565b803561546f81615da7565b60006020828403121561549b578081fd5b813561151d81615d6f565b600080604083850312156154b8578081fd5b82356154c381615d6f565b915060208301356154d381615d6f565b809150509250929050565b6000806000606084860312156154f2578081fd5b83356154fd81615d6f565b9250602084013561550d81615d6f565b929592945050506040919091013590565b60008060408385031215615530578182fd5b823561553b81615d6f565b946020939093013593505050565b60008060006060848603121561555d578283fd5b835161556881615d6f565b602085015160409095015190969495509392505050565b600060208284031215615590578081fd5b815161151d81615d84565b60008082840360e08112156155ae578283fd5b833567ffffffffffffffff8111156155c4578384fd5b840161016081870312156155d6578384fd5b925060c0601f19820112156155e9578182fd5b506020830190509250929050565b600060208284031215615608578081fd5b81356001600160801b038116811461151d578182fd5b60006020828403121561562f578081fd5b5035919050565b600060208284031215615647578081fd5b5051919050565b60008060408385031215615660578182fd5b8235915060208301356154d381615d6f565b60008060008060008060c0878903121561568a578182fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6000602082840312156156cc578081fd5b813563ffffffff8116811461151d578182fd5b600081518084526156f7816020860160208601615b9f565b601f01601f19169290920160200192915050565b6000825161571d818460208701615b9f565b9190910192915050565b60006101a060018060a01b03808c168452808b166020850152808a1660408501528860608501528760808501528160a0850152615766828501886156df565b915083820360c085015261577a82876156df565b92508435915061578982615d84565b90151560e08401526020840135906157a082615dbc565b60ff8216610100850152604085013591506157ba82615d6f565b166101208301526157cd60608401615464565b6001600160a01b03166101408301526157e86080840161547f565b66ffffffffffffff1661016083015261580360a08401615474565b6001600160681b038116610180840152509998505050505050505050565b60208152600061151d60208301846156df565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526608585b5bdd5b9d60ca1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b83516001600160a01b0316815260208085015161016083019161595d908401826001600160a01b03169052565b50604085015161597860408401826001600160a01b03169052565b50606085015160608301526080850151615998608084018261ffff169052565b5060a085015160a083015260c08501516159bd60c08401826001600160a01b03169052565b5060e08501516159d860e08401826001600160a01b03169052565b5061010094850151948201949094526101208101929092526101409091015290565b6000808335601e19843603018112615a10578283fd5b83018035915067ffffffffffffffff821115615a2a578283fd5b602001915036819003821315615a3f57600080fd5b9250929050565b60008219821115615a5957615a59615c21565b500190565b600082615a7957634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115615ab9578160001904821115615a9f57615a9f615c21565b80851615615aac57918102915b93841c9390800290615a83565b509250929050565b600061151d8383600082615ad757506001611472565b81615ae457506000611472565b8160018114615afa5760028114615b0457615b20565b6001915050611472565b60ff841115615b1557615b15615c21565b50506001821b611472565b5060208310610133831016604e8410600b8410161715615b43575081810a611472565b615b4d8383615a7e565b8060001904821115615b6157615b61615c21565b029392505050565b6000816000190483118215151615615b8357615b83615c21565b500290565b600082821015615b9a57615b9a615c21565b500390565b60005b83811015615bba578181015183820152602001615ba2565b838111156137c65750506000910152565b600181811c90821680615bdf57607f821691505b60208210811415615c0057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c1a57615c1a615c21565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6000813561147281615d6f565b6000813561147281615d92565b6000813561147281615da7565b8135615c6981615d84565b815460ff19811691151560ff1691821783556020840135615c8981615dbc565b61ff008160081b169050808361ffff198416171784556040850135615cad81615d6f565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d05615ce860608501615c37565b82546001600160a01b0319166001600160a01b0391909116178255565b615d3e615d1460808501615c51565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b506113cb615d4e60a08401615c44565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b038116811461310757600080fd5b801515811461310757600080fd5b6001600160681b038116811461310757600080fd5b66ffffffffffffff8116811461310757600080fd5b60ff8116811461310757600080fdfea26469706673582212205fbcaa8781187325d25b31e13bacef69edce6157041a37dc50e9cf290afbc57364736f6c63430008040033
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 | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.