My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
EthWeeklyAtlanticPutsV3
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library Counters { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
//SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; // Libraries import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol"; import {OwnableRoles} from "solady/src/auth/OwnableRoles.sol"; // Libraries import {StructuredLinkedList} from "solidity-linked-list/contracts/StructuredLinkedList.sol"; import {Counters} from "@openzeppelin/contracts/utils/Counters.sol"; // Contracts import {ReentrancyGuard} from "../helpers/ReentrancyGuard.sol"; import {Pausable} from "../helpers/Pausable.sol"; import {AtlanticPutsPoolState} from "./AtlanticPutsPoolState.sol"; // Interfaces import {IERC20} from "../interfaces/IERC20.sol"; import {IOptionPricing} from "../interfaces/IOptionPricing.sol"; import {IPriceOracle} from "../interfaces/IPriceOracle.sol"; import {IVolatilityOracle} from "../interfaces/IVolatilityOracle.sol"; import {IOptionPricing} from "../interfaces/IOptionPricing.sol"; import {IDopexFeeStrategy} from "../fees/interfaces/IDopexFeeStrategy.sol"; // Enums import {OptionsState, EpochState, Contracts, VaultConfig} from "./AtlanticPutsPoolEnums.sol"; // Structs import {EpochData, MaxStrikesRange, Checkpoint, OptionsPurchase, DepositPosition, EpochRewards, MaxStrike} from "./AtlanticPutsPoolStructs.sol"; contract AtlanticPutsPool is AtlanticPutsPoolState, Pausable, ReentrancyGuard, OwnableRoles { using StructuredLinkedList for StructuredLinkedList.List; uint256 internal constant ADMIN_ROLE = _ROLE_0; uint256 internal constant MANAGED_CONTRACT_ROLE = _ROLE_1; uint256 internal constant BOOSTRAPPER_ROLE = _ROLE_2; uint256 internal constant WHITELISTED_CONTRACT_ROLE = _ROLE_3; /** * @notice Structured linked list for max strikes * @dev epoch => strike list */ mapping(uint256 => StructuredLinkedList.List) private epochStrikesList; /// @dev Number of deicmals of deposit/premium token uint256 private immutable COLLATERAL_TOKEN_DECIMALS; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTRUCTOR */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ constructor(address collateralToken) { COLLATERAL_TOKEN_DECIMALS = IERC20(collateralToken).decimals(); _setOwner(msg.sender); _grantRoles(msg.sender, ADMIN_ROLE); _grantRoles(msg.sender, MANAGED_CONTRACT_ROLE); _grantRoles(msg.sender, BOOSTRAPPER_ROLE); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC VIEWS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Get amount amount of underlying to * be unwinded against options. * @param _optionStrike Strike price of the option. * @param _optionsAmount Amount of options to unwind. * @return unwindAmount */ function getUnwindAmount(uint256 _optionStrike, uint256 _optionsAmount) public view returns (uint256 unwindAmount) { if (_optionStrike < getUsdPrice()) { unwindAmount = (_optionsAmount * _optionStrike) / getUsdPrice(); } else { unwindAmount = _optionsAmount; } } /** * @notice Calculate Pnl for exercising options. * @param price price of BaseToken. * @param strike strike price of the option. * @param amount amount of options. */ function calculatePnl( uint256 price, uint256 strike, uint256 amount ) public view returns (uint256) { if (price == 0) price = getUsdPrice(); return strike > price ? (strikeMulAmount((strike - price), amount)) : 0; } /** * @notice Calculate funding fees based on days * left till expiry. * @param _collateralAccess Amount of collateral borrowed. * @param _entryTimestamp Timestamp of entry of unlockCollatera(). * which is used to calc. how much funding * is to be charged. * @return fees */ function calculateFundingFees( uint256 _collateralAccess, uint256 _entryTimestamp ) public view returns (uint256 fees) { fees = ((_epochData[currentEpoch].expiryTime - _entryTimestamp) / vaultConfig[VaultConfig.FundingInterval]) * vaultConfig[VaultConfig.BaseFundingRate]; fees = ((_collateralAccess * (FEE_BPS_PRECISION + fees)) / FEE_BPS_PRECISION) - _collateralAccess; } /** * @notice Calculate Fees for purchase. * @param strike strike price of the BaseToken option. * @param amount amount of options being bought. * @return finalFee purchase fee in QuoteToken. */ function calculatePurchaseFees( address account, uint256 strike, uint256 amount ) public view returns (uint256 finalFee) { uint256 feeBps = _getFeeBps(PURCHASE_FEES_KEY, account); finalFee = (((amount * (FEE_BPS_PRECISION + feeBps)) / FEE_BPS_PRECISION) - amount) / 10**(OPTION_TOKEN_DECIMALS - COLLATERAL_TOKEN_DECIMALS); if (getUsdPrice() < strike) { uint256 feeMultiplier = (strike * (10**STRIKE_DECIMALS)) / getUsdPrice(); finalFee = (finalFee * feeMultiplier) / 10**STRIKE_DECIMALS; } } /** * @notice Calculate premium for an option. * @param _strike Strike price of the option. * @param _amount Amount of options. * @return premium in QuoteToken. */ function calculatePremium(uint256 _strike, uint256 _amount) public view returns (uint256 premium) { uint256 currentPrice = getUsdPrice(); premium = strikeMulAmount( IOptionPricing(addresses[Contracts.OptionPricing]).getOptionPrice( true, _epochData[currentEpoch].expiryTime, _strike, currentPrice, getVolatility(_strike) ), _amount ); } /** * @notice Returns the price of the BaseToken in USD. * @return price Price of the base token in 1e8 decimals. */ function getUsdPrice() public view returns (uint256) { return IPriceOracle(addresses[Contracts.PriceOracle]).getPrice( addresses[Contracts.BaseToken], false, false, false ) / 10**(PRICE_ORACLE_DECIMALS - STRIKE_DECIMALS); } /** * @notice Returns the volatility from the volatility oracle * @param _strike Strike of the option */ function getVolatility(uint256 _strike) public view returns (uint256) { return (IVolatilityOracle(addresses[Contracts.VolatilityOracle]) .getVolatility(_strike) * (FEE_BPS_PRECISION + vaultConfig[VaultConfig.IvBoost])) / FEE_BPS_PRECISION; } /** * @notice Multiply strike and amount depending on strike * and options decimals. * @param strike Option strike. * @param amount Amount of options. * @return result Product of strike and amount in collateral/quote * token decimals. */ function strikeMulAmount(uint256 strike, uint256 amount) public view returns (uint256 result) { uint256 divisor = (STRIKE_DECIMALS + OPTION_TOKEN_DECIMALS) - COLLATERAL_TOKEN_DECIMALS; return ((strike * amount) / 10**divisor); } /** * @notice A view fn to check if the current epoch of the * pool is within the exercise window or not. * @return whether Whether the current epoch is within exercise * window of options. */ function isWithinBlackoutWindow() public view returns (bool) { uint256 expiry = _epochData[currentEpoch].expiryTime; if (expiry == 0) return false; return block.timestamp >= (expiry - vaultConfig[VaultConfig.BlackoutWindow]); } /** * @notice A view fn to get the state of the options. * Although by default it returns the state of * the option but if the epoch of the options * are expired it will return the state as * settled. * @param _purchaseId ID of the options purchase. * @return state State of the options. */ function getOptionsState(uint256 _purchaseId) public view returns (OptionsState) { uint256 epoch = _optionsPositions[_purchaseId].epoch; if (block.timestamp >= _epochData[epoch].expiryTime) { return OptionsState.Settled; } else { return _optionsPositions[_purchaseId].state; } } /** * @param _depositId Epoch of atlantic pool to inquire * @return depositAmount Total deposits of user * @return premium Total premiums earned * @return borrowFees Total borrowFees fees earned * @return underlying Total underlying earned on unwinds */ function getWithdrawable(uint256 _depositId) public view returns ( uint256 depositAmount, uint256 premium, uint256 borrowFees, uint256 underlying, uint256[] memory rewards ) { DepositPosition memory userDeposit = _depositPositions[_depositId]; rewards = new uint256[]( epochMaxStrikeCheckpoints[userDeposit.epoch][userDeposit.strike] .rewardRates .length ); rewards = epochMaxStrikeCheckpoints[userDeposit.epoch][ userDeposit.strike ].rewardRates; Checkpoint memory checkpoint = epochMaxStrikeCheckpoints[ userDeposit.epoch ][userDeposit.strike].checkpoints[userDeposit.checkpoint]; for (uint256 i; i < rewards.length; ) { rewards[i] = (((userDeposit.liquidity * checkpoint.activeCollateral) / checkpoint.totalLiquidity) * rewards[i]) / 10**COLLATERAL_TOKEN_DECIMALS; unchecked { ++i; } } borrowFees += (userDeposit.liquidity * checkpoint.borrowFeesAccrued) / checkpoint.totalLiquidity; premium += (userDeposit.liquidity * checkpoint.premiumAccrued) / checkpoint.totalLiquidity; underlying += (userDeposit.liquidity * checkpoint.underlyingAccrued) / checkpoint.totalLiquidity; depositAmount += (userDeposit.liquidity * checkpoint.liquidityBalance) / checkpoint.totalLiquidity; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EXTERNAL METHODS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Rollover deposits from a previous epoch to current epoch. * @param _depositIds IDs of the deposit positinos. */ function rolloverDeposits( uint256[] calldata _depositIds, address _feeReceiver ) external { DepositPosition memory _depositPosition; uint256[] memory rewards; uint256 premium; uint256 depositBalance; uint256 borrowFees; uint256 underlying; for (uint256 i; i < _depositIds.length; ) { _depositPosition = _depositPositions[_depositIds[i]]; if (_depositPosition.rollover) { _validate( _epochData[_depositPosition.epoch].state == EpochState.Expired, 4 ); _validate(_depositPosition.epoch < currentEpoch, 21); ( depositBalance, premium, borrowFees, underlying, rewards ) = getWithdrawable(_depositIds[i]); delete _depositPositions[_depositIds[i]]; uint256 depositFee = ((depositBalance * (FEE_BPS_PRECISION + _getFeeBps( ROLLOVER_FEES_KEY, _depositPosition.depositor ))) / FEE_BPS_PRECISION) - depositBalance; if (depositFee > vaultConfig[VaultConfig.MaxDepositFee]) { depositFee = vaultConfig[VaultConfig.MaxDepositFee]; } _deposit( currentEpoch, _depositPosition.strike, depositBalance - depositFee, _depositPosition.depositor, _depositPosition.rollover ); if (underlying != 0) { _safeTransfer( addresses[Contracts.BaseToken], _depositPosition.depositor, underlying ); } if (premium + borrowFees != 0) { _safeTransfer( addresses[Contracts.QuoteToken], _depositPosition.depositor, premium + borrowFees ); } for (uint256 j; j < rewards.length; ) { if (rewards[j] != 0) { _safeTransfer( _epochRewards[_depositPosition.epoch].rewardTokens[ j ], _depositPosition.depositor, rewards[j] ); } unchecked { ++j; } } _safeTransfer( addresses[Contracts.QuoteToken], _feeReceiver, depositFee ); emit Withdraw( _depositIds[i], _depositPosition.depositor, 0, borrowFees, premium, underlying, rewards ); } unchecked { ++i; } } } function setDepositRollover(uint256 _depositId, bool _setAs) external { _validate(_depositPositions[_depositId].depositor == msg.sender, 16); _depositPositions[_depositId].rollover = _setAs; emit rolloverDepositSet(_depositId, _setAs); } /** * @notice Gracefully exercises an atlantic * sends collateral to integrated protocol, * underlying to writers. * @param unwindAmount Amount charged from caller (unwind amount + fees). * @param purchaseId Options purchase id. */ function unwind(uint256 purchaseId, uint256 unwindAmount) external onlyRoles(MANAGED_CONTRACT_ROLE) { _whenNotPaused(); _validate(_isVaultBootstrapped(currentEpoch), 7); OptionsPurchase memory _userOptionsPurchase = _optionsPositions[ purchaseId ]; _validate(_userOptionsPurchase.delegate == msg.sender, 9); _validate(_userOptionsPurchase.state == OptionsState.Unlocked, 10); uint256 expectedUnwindAmount = getUnwindAmount( _userOptionsPurchase.optionStrike, _userOptionsPurchase.optionsAmount ); uint256 collateralAccess = strikeMulAmount( _userOptionsPurchase.optionStrike, _userOptionsPurchase.optionsAmount ); for (uint256 i; i < _userOptionsPurchase.strikes.length; ) { _unwind( _userOptionsPurchase.epoch, _userOptionsPurchase.strikes[i], (( unwindAmount > expectedUnwindAmount ? expectedUnwindAmount : unwindAmount ) * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV, (collateralAccess * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV, _userOptionsPurchase.checkpoints[i] ); unchecked { ++i; } } // Transfer excess to user. if (unwindAmount > expectedUnwindAmount) { _safeTransfer( addresses[Contracts.BaseToken], _userOptionsPurchase.user, unwindAmount - expectedUnwindAmount ); } _safeTransferFrom( addresses[Contracts.BaseToken], msg.sender, address(this), unwindAmount ); delete _optionsPositions[purchaseId]; } /** * @notice Callable by managed contracts that wish * to relock collateral that was unlocked previously. * @param relockAmount Amount of collateral to relock. * @param purchaseId User options purchase id. */ function relockCollateral(uint256 purchaseId, uint256 relockAmount) external onlyRoles(MANAGED_CONTRACT_ROLE) { _whenNotPaused(); _validate(_isVaultBootstrapped(currentEpoch), 7); OptionsPurchase memory _userOptionsPurchase = _optionsPositions[ purchaseId ]; _validate(_userOptionsPurchase.delegate == msg.sender, 9); _validate(_userOptionsPurchase.state == OptionsState.Unlocked, 13); uint256 collateralAccess = strikeMulAmount( _userOptionsPurchase.optionStrike, _userOptionsPurchase.optionsAmount ); uint256 fundingRefund = calculateFundingFees( collateralAccess, _userOptionsPurchase.unlockEntryTimestamp ); /// @dev refund = funding charged previsouly - funding charged for borrowing fundingRefund = fundingRefund - (fundingRefund - calculateFundingFees(collateralAccess, block.timestamp)); if (collateralAccess > relockAmount) { /** * Settle the option if fail to relock atleast collateral amount * to disallow reuse of options. * */ _optionsPositions[purchaseId].state = OptionsState.Settled; delete fundingRefund; } else { _optionsPositions[purchaseId].state = OptionsState.Active; } for (uint256 i; i < _userOptionsPurchase.strikes.length; ) { _relockCollateral( _userOptionsPurchase.epoch, _userOptionsPurchase.strikes[i], ((( relockAmount > collateralAccess ? collateralAccess : relockAmount ) * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV), ((fundingRefund * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV), _userOptionsPurchase.checkpoints[i] ); unchecked { ++i; } } // Transfer to user any excess. if (collateralAccess < relockAmount) { _safeTransfer( addresses[Contracts.QuoteToken], _userOptionsPurchase.user, relockAmount - collateralAccess ); } _safeTransferFrom( addresses[Contracts.QuoteToken], msg.sender, address(this), relockAmount ); if (fundingRefund != 0) { _safeTransfer( addresses[Contracts.QuoteToken], _userOptionsPurchase.user, fundingRefund ); } } /** * @notice Unlock collateral to borrow against AP option. * Only Callable by managed contracts. * @param purchaseId User options purchase ID * @param to Collateral to transfer to * @return unlockedCollateral Amount of collateral unlocked plus fees */ function unlockCollateral(uint256 purchaseId, address to) external nonReentrant onlyRoles(MANAGED_CONTRACT_ROLE) returns (uint256 unlockedCollateral) { _whenNotPaused(); _validate(_isVaultBootstrapped(currentEpoch), 7); OptionsPurchase memory _userOptionsPurchase = _optionsPositions[ purchaseId ]; unlockedCollateral = strikeMulAmount( _userOptionsPurchase.optionStrike, _userOptionsPurchase.optionsAmount ); _validate(_userOptionsPurchase.delegate == msg.sender, 9); // Cannot unlock collateral after expiry _validate(getOptionsState(purchaseId) == OptionsState.Active, 10); _userOptionsPurchase.state = OptionsState.Unlocked; _userOptionsPurchase.unlockEntryTimestamp = block.timestamp; uint256 borrowFees = calculateFundingFees( unlockedCollateral, block.timestamp ); for (uint256 i; i < _userOptionsPurchase.strikes.length; ) { _unlockCollateral( _userOptionsPurchase.epoch, _userOptionsPurchase.strikes[i], (_userOptionsPurchase.weights[i] * unlockedCollateral) / WEIGHTS_MUL_DIV, (_userOptionsPurchase.weights[i] * borrowFees) / WEIGHTS_MUL_DIV, _userOptionsPurchase.checkpoints[i] ); unchecked { ++i; } } _optionsPositions[purchaseId] = _userOptionsPurchase; /// @dev Transfer out collateral _safeTransfer(addresses[Contracts.QuoteToken], to, unlockedCollateral); _safeTransferFrom( addresses[Contracts.QuoteToken], msg.sender, address(this), borrowFees ); } /** * @notice Purchases puts for the current epoch * @param _strike Strike index for current epoch * @param _amount Amount of puts to purchase * @param _account Address of the user options were purchased * on behalf of. * @param _delegate Address of the delegate who will be in charge * of the options. * @return purchaseId */ function purchase( uint256 _strike, uint256 _amount, address _delegate, address _account ) external nonReentrant onlyRoles(MANAGED_CONTRACT_ROLE) returns (uint256 purchaseId) { _whenNotPaused(); _validate(!isWithinBlackoutWindow(), 20); uint256 epoch = currentEpoch; _validate(_isVaultBootstrapped(epoch), 7); _validate(_account != address(0), 1); _validateParams(_strike, _amount, epoch, _delegate); // Calculate liquidity required uint256 collateralRequired = strikeMulAmount(_strike, _amount); // Should have adequate cumulative liquidity _validate(_epochData[epoch].totalLiquidity >= collateralRequired, 11); // Price/premium of option uint256 premium = calculatePremium(_strike, _amount); // Fees on top of premium for fee distributor uint256 fees = calculatePurchaseFees(_account, _strike, _amount); purchaseId = _newPurchasePosition( _account, _delegate, _strike, _amount, epoch ); _squeezeMaxStrikes( epoch, _strike, collateralRequired, premium, purchaseId ); _epochData[epoch].totalLiquidity -= collateralRequired; _epochData[epoch].totalActiveCollateral += collateralRequired; _safeTransferFrom( addresses[Contracts.QuoteToken], msg.sender, address(this), premium ); _safeTransferFrom( addresses[Contracts.QuoteToken], msg.sender, addresses[Contracts.FeeDistributor], fees ); emit NewPurchase( epoch, purchaseId, premium, fees, _account, msg.sender ); } /** * @notice Deposit liquidity into a max strike for * current epoch for selected strikes. * @param _maxStrike Exact price of strike in 1e8 decimals. * @param _liquidity Amount of liquidity to provide in quote token decimals/ * @param _user Address of the user to deposit for. */ function deposit( uint256 _maxStrike, uint256 _liquidity, address _user, bool _rollover ) public nonReentrant returns (uint256 depositId) { depositId = _deposit( currentEpoch, _maxStrike, _liquidity, _user, _rollover ); _safeTransferFrom( addresses[Contracts.QuoteToken], msg.sender, address(this), _liquidity ); } /** * @notice Withdraws balances for a strike from epoch * deposted in a epoch. * @param depositIds Deposit Ids of the deposit positions. * @param receiver Address of the receiver of tokens. */ function withdraw(uint256[] calldata depositIds, address receiver) external nonReentrant { _whenNotPaused(); uint256 epoch; uint256[] memory rewards; uint256 premium; uint256 depositBalance; uint256 borrowFees; uint256 underlying; for (uint256 i; i < depositIds.length; ) { epoch = _depositPositions[depositIds[i]].epoch; _validate( _depositPositions[depositIds[i]].depositor == msg.sender, 16 ); _validate(_epochData[epoch].state == EpochState.Expired, 4); ( depositBalance, premium, borrowFees, underlying, rewards ) = getWithdrawable(depositIds[i]); delete _depositPositions[depositIds[i]]; if (underlying != 0) { _safeTransfer( addresses[Contracts.BaseToken], receiver, underlying ); } if (premium + depositBalance + borrowFees != 0) { _safeTransfer( addresses[Contracts.QuoteToken], receiver, premium + depositBalance + borrowFees ); } for (uint256 j; j < rewards.length; ) { if (rewards[j] != 0) { _safeTransfer( _epochRewards[epoch].rewardTokens[j], receiver, rewards[j] ); } unchecked { ++j; } } emit Withdraw( depositIds[i], receiver, depositBalance, borrowFees, premium, underlying, rewards ); unchecked { ++i; } } } /// @notice Sets the current epoch as expired. function expireEpoch() external nonReentrant { uint256 epoch = currentEpoch; _validate(_epochData[epoch].state != EpochState.Expired, 17); uint256 epochExpiry = _epochData[epoch].expiryTime; _validate((block.timestamp >= epochExpiry), 18); _allocateRewardsForStrikes(epoch); _epochData[epoch].state = EpochState.Expired; emit EpochExpired(msg.sender); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EXTERNAL VIEWS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Get OptionsPurchase instance for a given positionId. * @param _positionId ID of the options purchase. * @return OptionsPurchase Options purchase data. */ function getOptionsPurchase(uint256 _positionId) external view returns (OptionsPurchase memory) { return _optionsPositions[_positionId]; } /** * @notice Get OptionsPurchase instance for a given positionId. * @param _positionId ID of the options purchase. * @return DepositPosition Deposit position data. */ function getDepositPosition(uint256 _positionId) external view returns (DepositPosition memory) { return _depositPositions[_positionId]; } /** * @notice Get checkpoints of a maxstrike in a epoch. * @param _epoch Epoch of the pool. * @param _maxStrike Max strike to query for. * @return _checkpoints array of checkpoints of a max strike. */ function getEpochCheckpoints(uint256 _epoch, uint256 _maxStrike) external view returns (Checkpoint[] memory _checkpoints) { _checkpoints = new Checkpoint[]( epochMaxStrikeCheckpointsLength[_epoch][_maxStrike] ); for ( uint256 i; i < epochMaxStrikeCheckpointsLength[_epoch][_maxStrike]; ) { _checkpoints[i] = epochMaxStrikeCheckpoints[_epoch][_maxStrike] .checkpoints[i]; unchecked { ++i; } } } /** * @notice Fetches all max strikes written in a epoch. * @param epoch Epoch of the pool. * @return maxStrikes */ function getEpochStrikes(uint256 epoch) external view returns (uint256[] memory maxStrikes) { maxStrikes = new uint256[](epochStrikesList[epoch].sizeOf()); uint256 nextNode = _epochData[epoch].maxStrikesRange.highest; uint256 iterator; while (nextNode != 0) { maxStrikes[iterator] = nextNode; iterator++; (, nextNode) = epochStrikesList[epoch].getNextNode(nextNode); } } /** * @notice Fetch the tick size set for the onGoing epoch. * @return tickSize */ function getEpochTickSize(uint256 _epoch) external view returns (uint256) { return _epochData[_epoch].tickSize; } /** * @notice Fetch epoch data of an epoch. * @return DataOfTheEpoch. */ function getEpochData(uint256 _epoch) external view returns (EpochData memory) { return _epochData[_epoch]; } /** * @notice Fetch rewards set for an epoch. * @return RewardsAllocated. */ function getEpochRewards(uint256 _epoch) external view returns (EpochRewards memory) { return _epochRewards[_epoch]; } /** * @notice Get MaxStrike type data. * @param _epoch Epoch of the pool. * @param _maxStrike Max strike to query for. */ function getEpochMaxStrikeData(uint256 _epoch, uint256 _maxStrike) external view returns (uint256 activeCollateral, uint256[] memory rewardRates) { activeCollateral = epochMaxStrikeCheckpoints[_epoch][_maxStrike] .activeCollateral; rewardRates = new uint256[]( epochMaxStrikeCheckpoints[_epoch][_maxStrike].rewardRates.length ); rewardRates = epochMaxStrikeCheckpoints[_epoch][_maxStrike].rewardRates; } /** * @notice Fetch checkpoint data of a max strike. * @return Checkpoint data. */ function getEpochMaxStrikeCheckpoint( uint256 _epoch, uint256 _maxStrike, uint256 _checkpoint ) external view returns (Checkpoint memory) { return epochMaxStrikeCheckpoints[_epoch][_maxStrike].checkpoints[ _checkpoint ]; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL / PRIVATE METHODS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Creates deposition position and updates checkpoint. * @param _epoch Epoch to deposit for. * @param _maxStrike Max strike to deposit into. * @param _liquidity Amount of liquidity provided. * @param _user Address of the depositor. * @param _rollover Whether to rollover the deposit. * @return depositId ID of the deposit position. */ function _deposit( uint256 _epoch, uint256 _maxStrike, uint256 _liquidity, address _user, bool _rollover ) private returns (uint256 depositId) { _isEligibleSender(); _whenNotPaused(); _validate(_isVaultBootstrapped(_epoch), 7); _validateParams(_maxStrike, _liquidity, _epoch, _user); uint256 checkpoint = _updateCheckpoint(_epoch, _maxStrike, _liquidity); depositId = _newDepositPosition( _epoch, _liquidity, _maxStrike, checkpoint, _user, _rollover ); _epochData[_epoch].totalLiquidity += _liquidity; // Emit event emit NewDeposit( _epoch, depositId, _maxStrike, _liquidity, _user, msg.sender ); } /** * @notice Add max strike to strikesList (linked list). * @param _strike Strike to add to strikesList. * @param _epoch Epoch of the pool. */ function _addMaxStrike(uint256 _strike, uint256 _epoch) internal { uint256 highestMaxStrike = _epochData[_epoch].maxStrikesRange.highest; uint256 lowestMaxStrike = _epochData[_epoch].maxStrikesRange.lowest; if (_strike > highestMaxStrike) { _epochData[_epoch].maxStrikesRange.highest = _strike; } if (_strike < lowestMaxStrike || lowestMaxStrike == 0) { _epochData[_epoch].maxStrikesRange.lowest = _strike; } // Add new max strike after the next largest strike uint256 strikeToInsertAfter = _getSortedSpot(_strike, _epoch); if (strikeToInsertAfter == 0) epochStrikesList[_epoch].pushBack(_strike); else epochStrikesList[_epoch].insertBefore(strikeToInsertAfter, _strike); } /** * @notice Helper function for unlockCollateral(). * @param epoch epoch of the vault. * @param maxStrike Max strike to unlock collateral from. * @param collateralAmount Amount of collateral to unlock. * @param checkpoint Checkpoint of the max strike. */ function _unlockCollateral( uint256 epoch, uint256 maxStrike, uint256 collateralAmount, uint256 borrowFees, uint256 checkpoint ) internal { epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .unlockedCollateral += collateralAmount; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .borrowFeesAccrued += borrowFees; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .liquidityBalance -= collateralAmount; emit UnlockCollateral(epoch, collateralAmount, msg.sender); } /** * @notice Update checkpoint states and total unlocked * collateral for a max strike. * @param epoch Epoch of the pool. * @param maxStrike maxStrike to update states for. * @param collateralAmount Collateral token amount relocked. * @param borrowFeesRefund Borrow fees to be refunded. * @param checkpoint Checkpoint pointer to update. * */ function _relockCollateral( uint256 epoch, uint256 maxStrike, uint256 collateralAmount, uint256 borrowFeesRefund, uint256 checkpoint ) internal { epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .liquidityBalance += collateralAmount; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .unlockedCollateral -= collateralAmount; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .borrowFeesAccrued -= borrowFeesRefund; emit RelockCollateral(epoch, maxStrike, collateralAmount, msg.sender); } /** * * @notice Update unwind related states for corr- * esponding max strikes. * @param epoch Epoch of the options. * @param maxStrike Max strike to update. * @param underlyingAmount Amount of underlying to unwind. * @param collateralAmount Equivalent collateral amount com- * pared to options unwinded. * @param checkpoint Checkpoint to update. * */ function _unwind( uint256 epoch, uint256 maxStrike, uint256 underlyingAmount, uint256 collateralAmount, uint256 checkpoint ) internal { epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .underlyingAccrued += underlyingAmount; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[checkpoint] .unlockedCollateral -= collateralAmount; emit Unwind(epoch, maxStrike, underlyingAmount, msg.sender); } /** * @notice Creates a new checkpoint or update existing * checkpoint. * @param epoch Epoch of the pool. * @param maxStrike Max strike deposited into. * @param liquidity Amount of deposits / liquidity to add * to totalLiquidity, totalLiquidityBalance. * @return index Returns the checkpoint number. */ function _updateCheckpoint( uint256 epoch, uint256 maxStrike, uint256 liquidity ) internal returns (uint256 index) { index = epochMaxStrikeCheckpointsLength[epoch][maxStrike]; // Add `maxStrike` if it doesn't exist if (epochMaxStrikeCheckpoints[epoch][maxStrike].maxStrike == 0) { _addMaxStrike(maxStrike, epoch); epochMaxStrikeCheckpoints[epoch][maxStrike].maxStrike = maxStrike; } if (index == 0) { epochMaxStrikeCheckpoints[epoch][maxStrike].checkpoints[index] = ( Checkpoint(block.timestamp, 0, 0, 0, 0, liquidity, liquidity, 0) ); unchecked { ++epochMaxStrikeCheckpointsLength[epoch][maxStrike]; } } else { Checkpoint memory currentCheckpoint = epochMaxStrikeCheckpoints[ epoch ][maxStrike].checkpoints[index - 1]; /** * @dev Check if checkpoint interval was exceeded * compared to previous checkpoint start time * if yes then create a new checkpoint or * else accumulate to previous checkpoint. */ /** @dev If a checkpoint's options have active collateral, * add liquidity to next checkpoint. */ if (currentCheckpoint.activeCollateral != 0) { epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[index] .startTime = block.timestamp; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[index] .totalLiquidity += liquidity; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[index] .liquidityBalance += liquidity; epochMaxStrikeCheckpointsLength[epoch][maxStrike]++; } else { --index; currentCheckpoint.totalLiquidity += liquidity; currentCheckpoint.liquidityBalance += liquidity; epochMaxStrikeCheckpoints[epoch][maxStrike].checkpoints[ index ] = currentCheckpoint; } } } /** * @notice Create a deposit position instance and update ID counter. * @param _epoch Epoch of the pool. * @param _liquidity Amount of collateral token deposited. * @param _maxStrike Max strike deposited into. * @param _checkpoint Checkpoint of the max strike deposited into. * @param _user Address of the user to deposit for / is depositing. */ function _newDepositPosition( uint256 _epoch, uint256 _liquidity, uint256 _maxStrike, uint256 _checkpoint, address _user, bool _rollover ) internal returns (uint256 depositId) { depositId = depositPositionsCounter; ++depositPositionsCounter; _depositPositions[depositId].epoch = _epoch; _depositPositions[depositId].strike = _maxStrike; _depositPositions[depositId].liquidity = _liquidity; _depositPositions[depositId].checkpoint = _checkpoint; _depositPositions[depositId].depositor = _user; _depositPositions[depositId].rollover = _rollover; } function _safeTransfer( address _token, address _to, uint256 _amount ) internal { SafeTransferLib.safeTransfer(_token, _to, _amount); } function _safeTransferFrom( address _token, address _from, address _to, uint256 _amount ) internal { SafeTransferLib.safeTransferFrom(_token, _from, _to, _amount); } /** * @notice Create new purchase position positon. * @param _user Address of the user to create for. * @param _delegate Address of the delagate who will be * in charge of the options. * @param _strike Strike price of the option. * @param _amount Amount of options. * @param _epoch Epoch of the pool. * @return purchaseId TokenID and positionID of the purchase position. */ function _newPurchasePosition( address _user, address _delegate, uint256 _strike, uint256 _amount, uint256 _epoch ) internal returns (uint256 purchaseId) { purchaseId = purchasePositionsCounter; _optionsPositions[purchaseId].user = _user; _optionsPositions[purchaseId].delegate = _delegate; _optionsPositions[purchaseId].optionStrike = _strike; _optionsPositions[purchaseId].optionsAmount = _amount; _optionsPositions[purchaseId].epoch = _epoch; _optionsPositions[purchaseId].state = OptionsState.Active; unchecked { ++purchasePositionsCounter; } } /** * @notice Loop through max strike allocating * for liquidity for options. * @param epoch Epoch of the pool. * @param putStrike Strike to purchase. * @param collateralRequired Amount of collateral to squeeze from * max strike. * @param premium Amount of premium to distribute. */ function _squeezeMaxStrikes( uint256 epoch, uint256 putStrike, uint256 collateralRequired, uint256 premium, uint256 purchaseId ) internal { uint256 liquidityFromMaxStrikes; uint256 liquidityProvided; uint256 nextStrike = _epochData[epoch].maxStrikesRange.highest; uint256 _liquidityRequired; while (liquidityFromMaxStrikes != collateralRequired) { _liquidityRequired = collateralRequired - liquidityFromMaxStrikes; _validate(putStrike <= nextStrike, 12); liquidityProvided = _squeezeMaxStrikeCheckpoints( epoch, nextStrike, collateralRequired, _liquidityRequired, premium, purchaseId ); epochMaxStrikeCheckpoints[epoch][nextStrike] .activeCollateral += liquidityProvided; liquidityFromMaxStrikes += liquidityProvided; (, nextStrike) = epochStrikesList[epoch].getNextNode(nextStrike); } } /** * @notice Squeezes out liquidity from checkpoints within * each max strike/ * @param epoch Epoch of the pool * @param maxStrike Max strike to squeeze liquidity from * @param totalCollateralRequired Total amount of liquidity required for the option * purchase/ * @param collateralRequired As the loop _squeezeMaxStrikes() accumulates * liquidity, this value deducts liquidity is * accumulated. * collateralRequired = totalCollateralRequired - liquidity * accumulated till the max strike in the context of the loop * @param premium Premium to distribute among the checkpoints and maxstrike * @param purchaseId Options purchase ID */ function _squeezeMaxStrikeCheckpoints( uint256 epoch, uint256 maxStrike, uint256 totalCollateralRequired, uint256 collateralRequired, uint256 premium, uint256 purchaseId ) internal returns (uint256 liquidityProvided) { uint256 startIndex = epochMaxStrikeCheckpointStartIndex[epoch][ maxStrike ]; //check if previous checkpoint liquidity all consumed if ( startIndex != 0 && epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex - 1] .totalLiquidity > epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex - 1] .activeCollateral ) { --startIndex; } uint256 endIndex; // Unchecked since only max strikes with checkpoints != 0 will come to this point endIndex = epochMaxStrikeCheckpointsLength[epoch][maxStrike] - 1; uint256 liquidityProvidedFromCurrentMaxStrike; while ( startIndex <= endIndex && liquidityProvided != collateralRequired ) { uint256 availableLiquidity = epochMaxStrikeCheckpoints[epoch][ maxStrike ].checkpoints[startIndex].totalLiquidity - epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex] .activeCollateral; uint256 _requiredLiquidity = collateralRequired - liquidityProvided; /// @dev if checkpoint has more than required liquidity if (availableLiquidity >= _requiredLiquidity) { /// @dev Liquidity provided from current max strike at current index unchecked { liquidityProvidedFromCurrentMaxStrike = _requiredLiquidity; liquidityProvided += liquidityProvidedFromCurrentMaxStrike; /// @dev Add to active collateral, later if activeCollateral == totalliquidity, then we stop // coming back to this checkpoint epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex] .activeCollateral += _requiredLiquidity; /// @dev Add to premium accured epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex] .premiumAccrued += (liquidityProvidedFromCurrentMaxStrike * premium) / totalCollateralRequired; } _updatePurchasePositionMaxStrikesLiquidity( purchaseId, maxStrike, startIndex, (liquidityProvidedFromCurrentMaxStrike * WEIGHTS_MUL_DIV) / totalCollateralRequired ); } else if (availableLiquidity != 0) { /// @dev if checkpoint has less than required liquidity liquidityProvidedFromCurrentMaxStrike = availableLiquidity; unchecked { liquidityProvided += liquidityProvidedFromCurrentMaxStrike; epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex] .activeCollateral += liquidityProvidedFromCurrentMaxStrike; /// @dev Add to premium accured epochMaxStrikeCheckpoints[epoch][maxStrike] .checkpoints[startIndex] .premiumAccrued += (liquidityProvidedFromCurrentMaxStrike * premium) / totalCollateralRequired; } _updatePurchasePositionMaxStrikesLiquidity( purchaseId, maxStrike, startIndex, (liquidityProvidedFromCurrentMaxStrike * WEIGHTS_MUL_DIV) / totalCollateralRequired ); unchecked { ++epochMaxStrikeCheckpointStartIndex[epoch][maxStrike]; } } unchecked { ++startIndex; } } } /** * @notice Allocate rewards for strikes based * on active collateral present. * @param epoch Epoch of the pool */ function _allocateRewardsForStrikes(uint256 epoch) internal { uint256 nextNode = _epochData[epoch].maxStrikesRange.highest; uint256 iterator; EpochRewards memory epochRewards = _epochRewards[epoch]; uint256 activeCollateral; uint256 totalEpochActiveCollateral = _epochData[epoch] .totalActiveCollateral; while (nextNode != 0) { activeCollateral = epochMaxStrikeCheckpoints[epoch][nextNode] .activeCollateral; for (uint256 i; i < epochRewards.rewardTokens.length; ) { /** * rewards allocated for a strike: * strike's active collateral * rewards * -------------------------- * total active collateral * * Reward rate per active collateral: * * rewards allocated * ------------------ * strike's active collateral */ if (activeCollateral != 0) { epochMaxStrikeCheckpoints[epoch][nextNode].rewardRates.push( (((activeCollateral * epochRewards.amounts[i]) / totalEpochActiveCollateral) * (10**COLLATERAL_TOKEN_DECIMALS)) / activeCollateral ); } unchecked { ++i; } } iterator++; (, nextNode) = epochStrikesList[epoch].getNextNode(nextNode); } } /** * @notice Pushes new item into strikes, checkpoints and * weights in a single-go for a options purchase * instance. * @param _purchaseId Options purchase ID * @param _maxStrike Maxstrike to push into strikes array of the * options purchase. * @param _checkpoint Checkpoint to push into checkpoints array of * the options purchase. * @param _weight Weight (%) to push into weights array of the * options purchase in 1e18 decimals. */ function _updatePurchasePositionMaxStrikesLiquidity( uint256 _purchaseId, uint256 _maxStrike, uint256 _checkpoint, uint256 _weight ) internal { _optionsPositions[_purchaseId].strikes.push(_maxStrike); _optionsPositions[_purchaseId].checkpoints.push(_checkpoint); _optionsPositions[_purchaseId].weights.push(_weight); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL / PRIVATE VIEWS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Validate params for purchase and deposit. * @param _strike Strike price for the option. * @param _amount Amount of options or liquidity added. * @param _epoch Epoch of the pool. * @param _user User address provided. */ function _validateParams( uint256 _strike, uint256 _amount, uint256 _epoch, address _user ) internal view { _validate(_user != address(0), 1); _validate(_amount != 0, 3); _validate( _strike != 0 && _strike % _epochData[_epoch].tickSize == 0, 5 ); } /** * @notice Revert-er function to revert with string error message. * @param trueCondition Similar to require, a condition that has to be false * to revert. * @param errorCode Index in the errors[] that was set in error controller. */ function _validate(bool trueCondition, uint256 errorCode) internal pure { if (!trueCondition) { revert AtlanticPutsPoolError(errorCode); } } /** * @notice Checks if vault is not expired and bootstrapped. * @param epoch Epoch of the pool. * @return isVaultBootstrapped */ function _isVaultBootstrapped(uint256 epoch) internal view returns (bool) { return _epochData[epoch].state == EpochState.BootStrapped && block.timestamp <= _epochData[epoch].expiryTime; } /** * @param _value Value of max strike / node * @param _epoch Epoch of the pool * @return tail of the linked list */ function _getSortedSpot(uint256 _value, uint256 _epoch) private view returns (uint256) { if (epochStrikesList[_epoch].sizeOf() == 0) { return 0; } uint256 next; (, next) = epochStrikesList[_epoch].getAdjacent(0, true); // Switch to descending while ( (next != 0) && ( (_value < ( epochMaxStrikeCheckpoints[_epoch][next].maxStrike != 0 ? next : 0 )) ) ) { next = epochStrikesList[_epoch].list[next][true]; } return next; } /** * @notice Get fee basis points from dopex fee strategy. * @param _feeKey Identifier Key for fee type. * @param _account Address of the account for discount purposes. */ function _getFeeBps(uint256 _feeKey, address _account) private view returns (uint256 feeBps) { feeBps = IDopexFeeStrategy(addresses[Contracts.FeeStrategy]).getFeeBps( _feeKey, _account, vaultConfig[VaultConfig.UseDiscount] == 1 ? true : false ); } /** * @dev checks for contract or eoa addresses * @param addr the address to check * @return bool whether the passed address is a contract address */ function _isContract(address addr) private view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size != 0; } /** * @notice Check for whitelisted contracts. */ function _isEligibleSender() internal view { // the below condition checks whether the caller is a contract or not if (msg.sender != tx.origin) { _checkRoles(WHITELISTED_CONTRACT_ROLE); } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ADMIN METHODS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @notice Set vault configurations. * @param _types Configuration type. * @param _configs Configuration parameter. */ function setVaultConfigs( VaultConfig[] calldata _types, uint256[] calldata _configs ) external onlyRoles(ADMIN_ROLE) { _validate(_types.length == _configs.length, 0); for (uint256 i; i < _types.length; ) { vaultConfig[_types[i]] = _configs[i]; emit VaultConfigSet(_types[i], _configs[i]); unchecked { ++i; } } } /** * @notice Sets (adds) a list of addresses to the address list. * @dev an only be called by the owner. * @param _types Contract type to set from Contracs enum * @param _addresses address of the contract. */ function setAddresses( Contracts[] calldata _types, address[] calldata _addresses ) external onlyRoles(ADMIN_ROLE) { _validate(_types.length == _addresses.length, 0); for (uint256 i; i < _types.length; ) { _validate(_addresses[i] != address(0), 1); addresses[_types[i]] = _addresses[i]; emit AddressSet(_types[i], _addresses[i]); unchecked { ++i; } } } /** * @notice Pauses the vault for emergency cases. * @dev Can only be called by the owner. */ function pause() external onlyRoles(ADMIN_ROLE) { _pause(); } /** * @notice Unpauses the vault * @dev Can only be called by the owner */ function unpause() external onlyRoles(ADMIN_ROLE) { _unpause(); } /** * @notice Transfers all funds to msg.sender * @dev Can only be called by DEFAULT_ADMIN_ROLE * @param tokens The list of erc20 tokens to withdraw * @param transferNative Whether should transfer the native currency */ function emergencyWithdraw(address[] calldata tokens, bool transferNative) external onlyRoles(ADMIN_ROLE) returns (bool) { _whenPaused(); if (transferNative) payable(msg.sender).transfer(address(this).balance); for (uint256 i; i < tokens.length; ) { _safeTransfer( tokens[i], msg.sender, IERC20(tokens[i]).balanceOf(address(this)) ); unchecked { ++i; } } emit EmergencyWithdraw(msg.sender); return true; } /** * @notice Set rewards for an upcoming epoch. * @param _rewardTokens Addresses of the reward tokens. * @param _amounts Amounts of tokens to reward. * @param _epoch Upcoming epoch. */ function setEpochRewards( address[] calldata _rewardTokens, uint256[] calldata _amounts, uint256 _epoch ) external onlyRoles(ADMIN_ROLE) { _validate(_rewardTokens.length == _amounts.length, 0); for (uint256 i; i < _rewardTokens.length; ) { _safeTransferFrom( _rewardTokens[i], msg.sender, address(this), _amounts[i] ); _epochRewards[_epoch].rewardTokens.push(_rewardTokens[i]); _epochRewards[_epoch].amounts.push(_amounts[i]); emit EpochRewardsSet(_epoch, _amounts[i], _rewardTokens[i]); unchecked { ++i; } } } /** * @notice Bootstraps a new epoch, sets the strike based on offset% set. To be called after expiry * of every epoch. Ensure strike offset is set before calling this function * @param expiry Expiry of the epoch to set. * @param tickSize Spacing between max strikes. * @return success */ function bootstrap(uint256 expiry, uint256 tickSize) external nonReentrant onlyRoles(BOOSTRAPPER_ROLE) returns (bool) { _validate(expiry > block.timestamp, 2); _validate(tickSize != 0, 3); uint256 nextEpoch = currentEpoch + 1; EpochData memory _vaultState = _epochData[nextEpoch]; // Prev epoch must be expired if (currentEpoch > 0) _validate(_epochData[nextEpoch - 1].state == EpochState.Expired, 4); _vaultState.startTime = block.timestamp; _vaultState.tickSize = tickSize; _vaultState.expiryTime = expiry; _vaultState.state = EpochState.BootStrapped; currentEpoch = nextEpoch; _epochData[nextEpoch] = _vaultState; emit Bootstrap(nextEpoch); return true; } }
//SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; enum OptionsState { Settled, Active, Unlocked } enum EpochState { InActive, BootStrapped, Expired, Paused } enum Contracts { QuoteToken, BaseToken, FeeDistributor, FeeStrategy, OptionPricing, PriceOracle, VolatilityOracle, Gov } enum VaultConfig { IvBoost, BlackoutWindow, FundingInterval, BaseFundingRate, UseDiscount, MaxDepositFee }
//SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; // Structs import {EpochData, MaxStrikesRange, Checkpoint, OptionsPurchase, DepositPosition, EpochRewards, MaxStrike} from "./AtlanticPutsPoolStructs.sol"; // Enums import {OptionsState, EpochState, Contracts, VaultConfig} from "./AtlanticPutsPoolEnums.sol"; contract AtlanticPutsPoolState { uint256 internal constant PURCHASE_FEES_KEY = 0; uint256 internal constant ROLLOVER_FEES_KEY = 2; uint256 internal constant FEE_BPS_PRECISION = 10000000; uint256 internal constant PRICE_ORACLE_DECIMALS = 30; /// @dev Options amounts precision uint256 internal constant OPTION_TOKEN_DECIMALS = 18; /// @dev Number of decimals for max strikes uint256 internal constant STRIKE_DECIMALS = 8; /// @dev Max strike weights divisor/multiplier uint256 internal constant WEIGHTS_MUL_DIV = 1e18; uint256 public currentEpoch; uint256 public purchasePositionsCounter = 1; uint256 public depositPositionsCounter = 0; mapping(VaultConfig => uint256) public vaultConfig; mapping(Contracts => address) public addresses; mapping(uint256 => EpochData) internal _epochData; mapping(uint256 => EpochRewards) internal _epochRewards; mapping(uint256 => DepositPosition) internal _depositPositions; mapping(uint256 => OptionsPurchase) internal _optionsPositions; /** * @notice Checkpoints for a max strike in a epoch * @dev epoch => max strike => Checkpoint[] */ mapping(uint256 => mapping(uint256 => MaxStrike)) internal epochMaxStrikeCheckpoints; mapping(uint256 => mapping(uint256 => uint256)) internal epochMaxStrikeCheckpointsLength; /** * @notice Start index of checkpoint (reference point to * loop from on _squeeze()) * @dev epoch => index */ mapping(uint256 => mapping(uint256 => uint256)) internal epochMaxStrikeCheckpointStartIndex; event EmergencyWithdraw(address sender); event Bootstrap(uint256 epoch); event NewDeposit( uint256 epoch, uint256 depositId, uint256 strike, uint256 amount, address user, address sender ); event NewPurchase( uint256 epoch, uint256 purchaseId, uint256 premium, uint256 fee, address user, address sender ); event Withdraw( uint256 depositId, address receiver, uint256 withdrawableAmount, uint256 borrowFees, uint256 premium, uint256 underlying, uint256[] rewards ); event EpochRewardsSet(uint256 epoch, uint256 amount, address rewardToken); event Unwind(uint256 epoch, uint256 strike, uint256 amount, address caller); event UnlockCollateral( uint256 epoch, uint256 totalCollateral, address caller ); event NewSettle( uint256 epoch, uint256 strike, address user, uint256 amount, uint256 pnl ); event RelockCollateral( uint256 epoch, uint256 strike, uint256 totalCollateral, address caller ); event AddressSet(Contracts _type, address _address); event EpochExpired(address _sender); event VaultConfigSet(VaultConfig _type, uint256 _config); event rolloverDepositSet(uint256 _depositId, bool _setAs); error AtlanticPutsPoolError(uint256 errorCode); }
//SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; import {OptionsState, EpochState} from "./AtlanticPutsPoolEnums.sol"; struct EpochData { uint256 startTime; uint256 expiryTime; uint256 totalLiquidity; uint256 totalActiveCollateral; uint256 fundingRate; uint256 tickSize; MaxStrikesRange maxStrikesRange; EpochState state; } struct MaxStrikesRange { uint256 highest; uint256 lowest; } struct Checkpoint { uint256 startTime; uint256 unlockedCollateral; uint256 premiumAccrued; uint256 borrowFeesAccrued; uint256 underlyingAccrued; uint256 totalLiquidity; uint256 liquidityBalance; uint256 activeCollateral; } struct EpochRewards { address[] rewardTokens; uint256[] amounts; } struct OptionsPurchase { uint256 epoch; uint256 optionStrike; uint256 optionsAmount; uint256 unlockEntryTimestamp; uint256[] strikes; uint256[] checkpoints; uint256[] weights; OptionsState state; address user; address delegate; } struct DepositPosition { uint256 epoch; uint256 strike; uint256 liquidity; uint256 checkpoint; address depositor; bool rollover; } struct MaxStrike { uint256 maxStrike; uint256 activeCollateral; uint256[] rewardRates; mapping(uint256 => Checkpoint) checkpoints; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; interface IDopexFeeStrategy { function getFeeBps( uint256 feeType, address user, bool useDiscount ) external view returns (uint256 _feeBps); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; /// @title Lighter version of the Openzeppelin Pausable contract /// @author witherblock /// @notice Helps pause a contract to block the execution of selected functions /// @dev Difference from the Openzeppelin version is changing the modifiers to internal fns and requires to reverts abstract contract Pausable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Internal function to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ function _whenNotPaused() internal view { if (paused()) revert ContractPaused(); } /** * @dev Internal function to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ function _whenPaused() internal view { if (!paused()) revert ContractNotPaused(); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual { _whenNotPaused(); _paused = true; emit Paused(msg.sender); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual { _whenPaused(); _paused = false; emit Unpaused(msg.sender); } error ContractPaused(); error ContractNotPaused(); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity 0.8.17; /** * @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 ReentrancyGuard { // 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; error ReentrancyCall(); constructor() { _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 making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true if (_status == _ENTERED) revert ReentrancyCall(); // 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; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. * NOTE: Modified to include symbols and decimals. */ interface IERC20 { function symbol() external view returns (string memory); function decimals() external view returns (uint8); 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); }
//SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; interface IOptionPricing { function getOptionPrice( bool isPut, uint256 expiry, uint256 strike, uint256 lastPrice, uint256 baseIv ) external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; interface IPriceOracle { function latestAnswer() external view returns (uint256); function getUnderlyingPrice() external view returns (uint256); function getCollateralPrice() external view returns (uint256); function getPrice( address token, bool maximise, bool includeAmmPrice, bool useSwapPricing ) external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; interface IVolatilityOracle { function getVolatility(uint256 strike) external view returns (uint256); }
//SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; // Contracts import {AtlanticPutsPool} from "../atlantic-pools/AtlanticPutsPool.sol"; /// @title BTC Weekly Puts SSOV V3 contract contract EthWeeklyAtlanticPutsV3 is AtlanticPutsPool { constructor() AtlanticPutsPool(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover /// may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`. uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900; /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`. uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae; /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`. uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`. /// It is intentionally choosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(not(_OWNER_SLOT_NOT), newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { let ownerSlot := not(_OWNER_SLOT_NOT) // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR) revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will be automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR) revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(not(_OWNER_SLOT_NOT)) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. function ownershipHandoverValidFor() public view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./Ownable.sol"; /// @notice Simple single owner and multiroles authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover and roles /// may be unique to this codebase. abstract contract OwnableRoles is Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`. uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The role slot of `user` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED)) /// let roleSlot := keccak256(0x00, 0x20) /// ``` /// This automatically ignores the upper bits of the `user` in case /// they are not clean, as well as keep the `keccak256` under 32-bytes. /// /// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency. uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Grants the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn on. function _grantRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) let roleSlot := keccak256(0x0c, 0x20) // Load the current value and `or` it with `roles`. roles := or(sload(roleSlot), roles) // Store the new value. sstore(roleSlot, roles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) } } /// @dev Removes the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn off. function _removeRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) let roleSlot := keccak256(0x0c, 0x20) // Load the current value. let currentRoles := sload(roleSlot) // Use `and` to compute the intersection of `currentRoles` and `roles`, // `xor` it with `currentRoles` to flip the bits in the intersection. roles := xor(currentRoles, and(currentRoles, roles)) // Then, store the new value. sstore(roleSlot, roles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) } } /// @dev Throws if the sender does not have any of the `roles`. function _checkRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } /// @dev Throws if the sender is not the owner, /// and does not have any of the `roles`. /// Checks for ownership first, then lazily checks for roles. function _checkOwnerOrRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } } /// @dev Throws if the sender does not have any of the `roles`, /// and is not the owner. /// Checks for roles first, then lazily checks for ownership. function _checkRolesOrOwner(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to grant `user` `roles`. /// If the `user` already has a role, then it will be an no-op for the role. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _grantRoles(user, roles); } /// @dev Allows the owner to remove `user` `roles`. /// If the `user` does not have a role, then it will be an no-op for the role. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _removeRoles(user, roles); } /// @dev Allow the caller to remove their own roles. /// If the caller does not have a role, then it will be an no-op for the role. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `user` has any of `roles`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Load the stored value, and set the result to whether the // `and` intersection of the value and `roles` is not zero. result := iszero(iszero(and(sload(keccak256(0x0c, 0x20)), roles))) } } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Whether the stored value is contains all the set bits in `roles`. result := eq(and(sload(keccak256(0x0c, 0x20)), roles), roles) } } /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Load the stored value. roles := sload(keccak256(0x0c, 0x20)) } } /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } { // We don't need to mask the values of `ordinals`, as Solidity // cleans dirty upper bits when storing variables into memory. roles := or(shl(mload(add(ordinals, i)), 1), roles) } } } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) { /// @solidity memory-safe-assembly assembly { // Grab the pointer to the free memory. ordinals := mload(0x40) let ptr := add(ordinals, 0x20) let o := 0 // The absence of lookup tables, De Bruijn, etc., here is intentional for // smaller bytecode, as this function is not meant to be called on-chain. for { let t := roles } 1 {} { mstore(ptr, o) // `shr` 5 is equivalent to multiplying by 0x20. // Push back into the ordinals array if the bit is set. ptr := add(ptr, shl(5, and(t, 1))) o := add(o, 1) t := shr(o, roles) if iszero(t) { break } } // Store the length of `ordinals`. mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) // Allocate the memory. mstore(0x40, ptr) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(uint256 roles) virtual { _checkRoles(roles); _; } /// @dev Marks a function as only callable by the owner or by an account /// with `roles`. Checks for ownership first, then lazily checks for roles. modifier onlyOwnerOrRoles(uint256 roles) virtual { _checkOwnerOrRoles(roles); _; } /// @dev Marks a function as only callable by an account with `roles` /// or the owner. Checks for roles first, then lazily checks for ownership. modifier onlyRolesOrOwner(uint256 roles) virtual { _checkRolesOrOwner(roles); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLE CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // IYKYK uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH /// that disallows any storage writes. uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. /// Multiply by a small constant (e.g. 2), if needed. uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` (in wei) ETH to `to`. /// Reverts upon failure. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. if iszero(call(gas(), to, amount, 0, 0, 0, 0)) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 pop(create(amount, 0x0b, 0x16)) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default /// for 99% of cases and can be overriden with the three-argument version of this /// function if necessary. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount) internal { // Manually inlined because the compiler doesn't inline functions with branches. /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 pop(create(amount, 0x0b, 0x16)) } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend. /// /// Note: Does NOT revert upon failure. /// Returns whether the transfer of ETH is successful instead. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. success := call(gasStipend, to, amount, 0, 0, 0, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) mstore(0x20, from) // Store the `from` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x60, amount) // Store the `amount` argument. if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, from) // Store the `from` argument. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) mstore(0x40, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x6a. amount := mload(0x60) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x1a, to) // Store the `to` argument. mstore(0x3a, amount) // Store the `amount` argument. // Store the function selector of `transfer(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0xa9059cbb000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x1a, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x3a. amount := mload(0x3a) // Store the function selector of `transfer(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0xa9059cbb000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x1a, to) // Store the `to` argument. mstore(0x3a, amount) // Store the `amount` argument. // Store the function selector of `approve(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0x095ea7b3000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `ApproveFailed()`. mstore(0x00, 0x3e3f8f73) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, account) // Store the `account` argument. amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IStructureInterface { function getValue(uint256 _id) external view returns (uint256); } /** * @title StructuredLinkedList * @author Vittorio Minacori (https://github.com/vittominacori) * @dev An utility library for using sorted linked list data structures in your Solidity project. */ library StructuredLinkedList { uint256 private constant _NULL = 0; uint256 private constant _HEAD = 0; bool private constant _PREV = false; bool private constant _NEXT = true; struct List { uint256 size; mapping(uint256 => mapping(bool => uint256)) list; } /** * @dev Checks if the list exists * @param self stored linked list from contract * @return bool true if list exists, false otherwise */ function listExists(List storage self) internal view returns (bool) { // if the head nodes previous or next pointers both point to itself, then there are no items in the list if (self.list[_HEAD][_PREV] != _HEAD || self.list[_HEAD][_NEXT] != _HEAD) { return true; } else { return false; } } /** * @dev Checks if the node exists * @param self stored linked list from contract * @param _node a node to search for * @return bool true if node exists, false otherwise */ function nodeExists(List storage self, uint256 _node) internal view returns (bool) { if (self.list[_node][_PREV] == _HEAD && self.list[_node][_NEXT] == _HEAD) { if (self.list[_HEAD][_NEXT] == _node) { return true; } else { return false; } } else { return true; } } /** * @dev Returns the number of elements in the list * @param self stored linked list from contract * @return uint256 */ function sizeOf(List storage self) internal view returns (uint256) { return self.size; } /** * @dev Returns the links of a node as a tuple * @param self stored linked list from contract * @param _node id of the node to get * @return bool, uint256, uint256 true if node exists or false otherwise, previous node, next node */ function getNode(List storage self, uint256 _node) internal view returns (bool, uint256, uint256) { if (!nodeExists(self, _node)) { return (false, 0, 0); } else { return (true, self.list[_node][_PREV], self.list[_node][_NEXT]); } } /** * @dev Returns the link of a node `_node` in direction `_direction`. * @param self stored linked list from contract * @param _node id of the node to step from * @param _direction direction to step in * @return bool, uint256 true if node exists or false otherwise, node in _direction */ function getAdjacent(List storage self, uint256 _node, bool _direction) internal view returns (bool, uint256) { if (!nodeExists(self, _node)) { return (false, 0); } else { return (true, self.list[_node][_direction]); } } /** * @dev Returns the link of a node `_node` in direction `_NEXT`. * @param self stored linked list from contract * @param _node id of the node to step from * @return bool, uint256 true if node exists or false otherwise, next node */ function getNextNode(List storage self, uint256 _node) internal view returns (bool, uint256) { return getAdjacent(self, _node, _NEXT); } /** * @dev Returns the link of a node `_node` in direction `_PREV`. * @param self stored linked list from contract * @param _node id of the node to step from * @return bool, uint256 true if node exists or false otherwise, previous node */ function getPreviousNode(List storage self, uint256 _node) internal view returns (bool, uint256) { return getAdjacent(self, _node, _PREV); } /** * @dev Can be used before `insert` to build an ordered list. * @dev Get the node and then `insertBefore` or `insertAfter` basing on your list order. * @dev If you want to order basing on other than `structure.getValue()` override this function * @param self stored linked list from contract * @param _structure the structure instance * @param _value value to seek * @return uint256 next node with a value less than _value */ function getSortedSpot(List storage self, address _structure, uint256 _value) internal view returns (uint256) { if (sizeOf(self) == 0) { return 0; } uint256 next; (, next) = getAdjacent(self, _HEAD, _NEXT); while ((next != 0) && ((_value < IStructureInterface(_structure).getValue(next)) != _NEXT)) { next = self.list[next][_NEXT]; } return next; } /** * @dev Insert node `_new` beside existing node `_node` in direction `_NEXT`. * @param self stored linked list from contract * @param _node existing node * @param _new new node to insert * @return bool true if success, false otherwise */ function insertAfter(List storage self, uint256 _node, uint256 _new) internal returns (bool) { return _insert(self, _node, _new, _NEXT); } /** * @dev Insert node `_new` beside existing node `_node` in direction `_PREV`. * @param self stored linked list from contract * @param _node existing node * @param _new new node to insert * @return bool true if success, false otherwise */ function insertBefore(List storage self, uint256 _node, uint256 _new) internal returns (bool) { return _insert(self, _node, _new, _PREV); } /** * @dev Removes an entry from the linked list * @param self stored linked list from contract * @param _node node to remove from the list * @return uint256 the removed node */ function remove(List storage self, uint256 _node) internal returns (uint256) { if ((_node == _NULL) || (!nodeExists(self, _node))) { return 0; } _createLink(self, self.list[_node][_PREV], self.list[_node][_NEXT], _NEXT); delete self.list[_node][_PREV]; delete self.list[_node][_NEXT]; self.size -= 1; // NOT: SafeMath library should be used here to decrement. return _node; } /** * @dev Pushes an entry to the head of the linked list * @param self stored linked list from contract * @param _node new entry to push to the head * @return bool true if success, false otherwise */ function pushFront(List storage self, uint256 _node) internal returns (bool) { return _push(self, _node, _NEXT); } /** * @dev Pushes an entry to the tail of the linked list * @param self stored linked list from contract * @param _node new entry to push to the tail * @return bool true if success, false otherwise */ function pushBack(List storage self, uint256 _node) internal returns (bool) { return _push(self, _node, _PREV); } /** * @dev Pops the first entry from the head of the linked list * @param self stored linked list from contract * @return uint256 the removed node */ function popFront(List storage self) internal returns (uint256) { return _pop(self, _NEXT); } /** * @dev Pops the first entry from the tail of the linked list * @param self stored linked list from contract * @return uint256 the removed node */ function popBack(List storage self) internal returns (uint256) { return _pop(self, _PREV); } /** * @dev Pushes an entry to the head of the linked list * @param self stored linked list from contract * @param _node new entry to push to the head * @param _direction push to the head (_NEXT) or tail (_PREV) * @return bool true if success, false otherwise */ function _push(List storage self, uint256 _node, bool _direction) private returns (bool) { return _insert(self, _HEAD, _node, _direction); } /** * @dev Pops the first entry from the linked list * @param self stored linked list from contract * @param _direction pop from the head (_NEXT) or the tail (_PREV) * @return uint256 the removed node */ function _pop(List storage self, bool _direction) private returns (uint256) { uint256 adj; (, adj) = getAdjacent(self, _HEAD, _direction); return remove(self, adj); } /** * @dev Insert node `_new` beside existing node `_node` in direction `_direction`. * @param self stored linked list from contract * @param _node existing node * @param _new new node to insert * @param _direction direction to insert node in * @return bool true if success, false otherwise */ function _insert(List storage self, uint256 _node, uint256 _new, bool _direction) private returns (bool) { if (!nodeExists(self, _new) && nodeExists(self, _node)) { uint256 c = self.list[_node][_direction]; _createLink(self, _node, _new, _direction); _createLink(self, _new, c, _direction); self.size += 1; // NOT: SafeMath library should be used here to increment. return true; } return false; } /** * @dev Creates a bidirectional link between two nodes on direction `_direction` * @param self stored linked list from contract * @param _node existing node * @param _link node to link to in the _direction * @param _direction direction to insert node in */ function _createLink(List storage self, uint256 _node, uint256 _link, bool _direction) private { self.list[_link][!_direction] = _node; self.list[_node][_direction] = _link; } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"AtlanticPutsPoolError","type":"error"},{"inputs":[],"name":"ContractNotPaused","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"ReentrancyCall","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum Contracts","name":"_type","type":"uint8"},{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"AddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Bootstrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"}],"name":"EpochExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"}],"name":"EpochRewardsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NewDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"purchaseId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NewPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pnl","type":"uint256"}],"name":"NewSettle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RelockCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"UnlockCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Unwind","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum VaultConfig","name":"_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"_config","type":"uint256"}],"name":"VaultConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawableAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlying","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_depositId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_setAs","type":"bool"}],"name":"rolloverDepositSet","type":"event"},{"inputs":[{"internalType":"enum Contracts","name":"","type":"uint8"}],"name":"addresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"tickSize","type":"uint256"}],"name":"bootstrap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAccess","type":"uint256"},{"internalType":"uint256","name":"_entryTimestamp","type":"uint256"}],"name":"calculateFundingFees","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePnl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"calculatePremium","outputs":[{"internalType":"uint256","name":"premium","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePurchaseFees","outputs":[{"internalType":"uint256","name":"finalFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxStrike","type":"uint256"},{"internalType":"uint256","name":"_liquidity","type":"uint256"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"bool","name":"_rollover","type":"bool"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositPositionsCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"bool","name":"transferNative","type":"bool"}],"name":"emergencyWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expireEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"name":"getDepositPosition","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"checkpoint","type":"uint256"},{"internalType":"address","name":"depositor","type":"address"},{"internalType":"bool","name":"rollover","type":"bool"}],"internalType":"struct DepositPosition","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"}],"name":"getEpochCheckpoints","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"},{"internalType":"uint256","name":"premiumAccrued","type":"uint256"},{"internalType":"uint256","name":"borrowFeesAccrued","type":"uint256"},{"internalType":"uint256","name":"underlyingAccrued","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"liquidityBalance","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"}],"internalType":"struct Checkpoint[]","name":"_checkpoints","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochData","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"expiryTime","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"totalActiveCollateral","type":"uint256"},{"internalType":"uint256","name":"fundingRate","type":"uint256"},{"internalType":"uint256","name":"tickSize","type":"uint256"},{"components":[{"internalType":"uint256","name":"highest","type":"uint256"},{"internalType":"uint256","name":"lowest","type":"uint256"}],"internalType":"struct MaxStrikesRange","name":"maxStrikesRange","type":"tuple"},{"internalType":"enum EpochState","name":"state","type":"uint8"}],"internalType":"struct EpochData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"},{"internalType":"uint256","name":"_checkpoint","type":"uint256"}],"name":"getEpochMaxStrikeCheckpoint","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"},{"internalType":"uint256","name":"premiumAccrued","type":"uint256"},{"internalType":"uint256","name":"borrowFeesAccrued","type":"uint256"},{"internalType":"uint256","name":"underlyingAccrued","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"liquidityBalance","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"}],"internalType":"struct Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"}],"name":"getEpochMaxStrikeData","outputs":[{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256[]","name":"rewardRates","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochRewards","outputs":[{"components":[{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"internalType":"struct EpochRewards","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochStrikes","outputs":[{"internalType":"uint256[]","name":"maxStrikes","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochTickSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"name":"getOptionsPurchase","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"optionStrike","type":"uint256"},{"internalType":"uint256","name":"optionsAmount","type":"uint256"},{"internalType":"uint256","name":"unlockEntryTimestamp","type":"uint256"},{"internalType":"uint256[]","name":"strikes","type":"uint256[]"},{"internalType":"uint256[]","name":"checkpoints","type":"uint256[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"enum OptionsState","name":"state","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"delegate","type":"address"}],"internalType":"struct OptionsPurchase","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_purchaseId","type":"uint256"}],"name":"getOptionsState","outputs":[{"internalType":"enum OptionsState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_optionStrike","type":"uint256"},{"internalType":"uint256","name":"_optionsAmount","type":"uint256"}],"name":"getUnwindAmount","outputs":[{"internalType":"uint256","name":"unwindAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"}],"name":"getVolatility","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositId","type":"uint256"}],"name":"getWithdrawable","outputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"borrowFees","type":"uint256"},{"internalType":"uint256","name":"underlying","type":"uint256"},{"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithinBlackoutWindow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"ordinalsFromRoles","outputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"purchasePositionsCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"uint256","name":"relockAmount","type":"uint256"}],"name":"relockCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"name":"rolesFromOrdinals","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_depositIds","type":"uint256[]"},{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"rolloverDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Contracts[]","name":"_types","type":"uint8[]"},{"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositId","type":"uint256"},{"internalType":"bool","name":"_setAs","type":"bool"}],"name":"setDepositRollover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"setEpochRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum VaultConfig[]","name":"_types","type":"uint8[]"},{"internalType":"uint256[]","name":"_configs","type":"uint256[]"}],"name":"setVaultConfigs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"strikeMulAmount","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"unlockCollateral","outputs":[{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"uint256","name":"unwindAmount","type":"uint256"}],"name":"unwind","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum VaultConfig","name":"","type":"uint8"}],"name":"vaultConfig","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a06040526001805560006002553480156200001a57600080fd5b50600c805460ff191690556001600d556040805163313ce56760e01b8152905173ff970a61a04b1ca14834a43f5de4533ebddb5cc891829163313ce567916004808201926020929091908290030181865afa1580156200007e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a491906200016c565b60ff16608052620000b533620000e3565b620000c233600162000121565b620000cf33600262000121565b620000dc33600462000121565b5062000198565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b6000602082840312156200017f57600080fd5b815160ff811681146200019157600080fd5b9392505050565b608051615edb620001c9600039600081816119a9015281816120ec015281816133f6015261422b0152615edb6000f3fe6080604052600436106103805760003560e01c806376671808116101d1578063a750a58511610102578063d2a33ea9116100a0578063e4d2e8471161006f578063e4d2e84714610afb578063f04e283e14610b1b578063f2fde38b14610b2e578063fee81cf414610b4157600080fd5b8063d2a33ea914610a69578063d5fc893e14610a9a578063d7533f0214610ac8578063dfe31b2714610ae657600080fd5b8063bd731b58116100dc578063bd731b58146109dc578063c189c19b146109fc578063cf587fa314610a1c578063d1d6b50b14610a3c57600080fd5b8063a750a5851461097c578063a7bf1b6c1461099c578063b8f4bd7b146109bc57600080fd5b806389518c871161016f578063902c81db11610149578063902c81db146108e25780639437e133146109025780639d186fb31461092f578063a6e67cae1461094f57600080fd5b806389518c87146108895780638b25096a146108a95780638da5cb5b146108c957600080fd5b80638456cb59116101ab5780638456cb591461080757806384ff818c1461081c578063859e7d321461083c57806388e18e131461086957600080fd5b806376671808146107bb5780637c4b52cb146107d15780637e0d0974146107f157600080fd5b806341022fd2116102b657806354d1f13d116102545780636db29f6d116102235780636db29f6d14610744578063715018a61461075957806371dd5300146107615780637359e41f1461078e57600080fd5b806354d1f13d146106d75780635c975abb146106df5780635f0c0f2b146106f7578063695f3f5d1461072457600080fd5b80634a59c3c7116102905780634a59c3c7146105f45780634ff9a4e814610625578063514e62fc1461065257806353459cca1461068957600080fd5b806341022fd2146105a157806343eea6a1146105c15780634a4ee7b1146105e157600080fd5b80631c10893f116103235780632de94807116102fd5780632de94807146105235780633130a4d3146105565780633c1fc384146105765780633f4ba83a1461058c57600080fd5b80631c10893f146104c15780631cd64df4146104d4578063256929621461051b57600080fd5b806313a661ed1161035f57806313a661ed1461044c57806315943b991461046c578063183a4f6e146104995780631acf5582146104ac57600080fd5b806212100c1461038557806302cade38146103b8578063134fc6011461042a575b600080fd5b34801561039157600080fd5b506103a56103a03660046153d4565b610b74565b6040519081526020015b60405180910390f35b3480156103c457600080fd5b506103d86103d33660046153f6565b610c63565b6040516103af9190815181526020808301519082015260408083015190820152606080830151908201526080808301516001600160a01b03169082015260a09182015115159181019190915260c00190565b34801561043657600080fd5b5061044a610445366004615454565b610cd5565b005b34801561045857600080fd5b506103a56104673660046154ec565b610e71565b34801561047857600080fd5b5061048c6104873660046153f6565b610e9a565b6040516103af91906155ec565b61044a6104a73660046153f6565b610f5e565b3480156104b857600080fd5b506103a5610f6b565b61044a6104cf366004615616565b61106b565b3480156104e057600080fd5b5061050b6104ef366004615616565b638b78c6d8600c90815260009290925260209091205481161490565b60405190151581526020016103af565b61044a611081565b34801561052f57600080fd5b506103a561053e366004615640565b638b78c6d8600c908152600091909152602090205490565b34801561056257600080fd5b5061044a61057136600461565b565b6110d1565b34801561058257600080fd5b506103a560015481565b34801561059857600080fd5b5061044a611283565b3480156105ad57600080fd5b5061044a6105bc366004615454565b611296565b3480156105cd57600080fd5b5061044a6105dc3660046156cf565b6113af565b61044a6105ef366004615616565b6117cc565b34801561060057600080fd5b5061061461060f3660046153f6565b6117de565b6040516103af959493929190615723565b34801561063157600080fd5b506106456106403660046153f6565b611b0d565b6040516103af9190615783565b34801561065e57600080fd5b5061050b61066d366004615616565b638b78c6d8600c90815260009290925260209091205416151590565b34801561069557600080fd5b506106bf6106a4366004615852565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016103af565b61044a611cb8565b3480156106eb57600080fd5b50600c5460ff1661050b565b34801561070357600080fd5b506107176107123660046153f6565b611cf4565b6040516103af9190615873565b34801561073057600080fd5b506103a561073f3660046158ed565b611dd3565b34801561075057600080fd5b5061044a611e4b565b61044a611f2e565b34801561076d57600080fd5b506103a561077c366004615933565b60036020526000908152604090205481565b34801561079a57600080fd5b506107ae6107a93660046153f6565b611f42565b6040516103af9190615954565b3480156107c757600080fd5b506103a560005481565b3480156107dd57600080fd5b5061050b6107ec36600461599b565b611f7b565b3480156107fd57600080fd5b506103a560025481565b34801561081357600080fd5b5061044a6120d4565b34801561082857600080fd5b506103a56108373660046153d4565b6120e7565b34801561084857600080fd5b5061085c6108573660046153f6565b612148565b6040516103af91906159e6565b34801561087557600080fd5b506103a5610884366004615a5f565b6121fc565b34801561089557600080fd5b506103a56108a43660046153d4565b61223c565b3480156108b557600080fd5b506103a56108c4366004615a8b565b6122e5565b3480156108d557600080fd5b50638b78c6d819546106bf565b3480156108ee57600080fd5b5061044a6108fd366004615ab7565b6127a0565b34801561090e57600080fd5b5061092261091d3660046153d4565b612830565b6040516103af9190615b28565b34801561093b57600080fd5b506103a561094a3660046153d4565b612971565b34801561095b57600080fd5b5061096f61096a366004615a5f565b6129a5565b6040516103af9190615b6b565b34801561098857600080fd5b5061044a6109973660046153d4565b612a2d565b3480156109a857600080fd5b506103a56109b7366004615b7a565b612e39565b3480156109c857600080fd5b5061044a6109d73660046156cf565b6130d6565b3480156109e857600080fd5b506103a56109f7366004615bb5565b6133e1565b348015610a0857600080fd5b506103a5610a173660046153f6565b6134c5565b348015610a2857600080fd5b5061044a610a373660046153d4565b6135ae565b348015610a4857600080fd5b50610a5c610a573660046153f6565b613974565b6040516103af9190615be8565b348015610a7557600080fd5b506103a5610a843660046153f6565b6000908152600560208190526040909120015490565b348015610aa657600080fd5b50610aba610ab53660046153d4565b6139bd565b6040516103af929190615bf6565b348015610ad457600080fd5b506040516202a30081526020016103af565b348015610af257600080fd5b5061050b613a97565b348015610b0757600080fd5b5061050b610b163660046153d4565b613af8565b61044a610b29366004615640565b613d2f565b61044a610b3c366004615640565b613d6c565b348015610b4d57600080fd5b506103a5610b5c366004615640565b63389a75e1600c908152600091909152602090205490565b600080610b7f610f6b565b7f1a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050f54600080548152600560205260409020600190810154929350610c5b926001600160a01b0390921691635b7b6d8891908886610bdb826134c5565b6040516001600160e01b031960e088901b1681529415156004860152602485019390935260448401919091526064830152608482015260a401602060405180830381865afa158015610c31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c559190615c0f565b846120e7565b949350505050565b610c6b615211565b50600090815260076020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004909101546001600160a01b0381166080830152600160a01b900460ff16151560a082015290565b6001610ce081613d93565b610ced8483146000613db9565b60005b84811015610e6957610d366000858584818110610d0f57610d0f615c28565b9050602002016020810190610d249190615640565b6001600160a01b031614156001613db9565b838382818110610d4857610d48615c28565b9050602002016020810190610d5d9190615640565b60046000888885818110610d7357610d73615c28565b9050602002016020810190610d889190615852565b6007811115610d9957610d99615759565b6007811115610daa57610daa615759565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507fd62439407fa4dc0b1a83747f5f139b50c16c44dab05e472e1cdeb0f3900fb2b1868683818110610e0f57610e0f615c28565b9050602002016020810190610e249190615852565b858584818110610e3657610e36615c28565b9050602002016020810190610e4b9190615640565b604051610e59929190615c3e565b60405180910390a1600101610cf0565b505050505050565b6000815160051b5b8015610e9457828101516001901b90911790601f1901610e79565b50919050565b6000818152600e602052604090205460609067ffffffffffffffff811115610ec457610ec46154c0565b604051908082528060200260200182016040528015610eed578160200160208202803683370190505b506000838152600560205260408120600601549192505b8115610f575781838281518110610f1d57610f1d615c28565b602090810291909101015280610f3281615c80565b6000868152600e60205260409020909250610f4e915083613dde565b9250610f049050565b5050919050565b610f683382613df9565b50565b6000610f796008601e615c99565b610f8490600a615d90565b600460208181527f04cde762ef08b6b6c5ded8e8c4c0b3f4e5c9ad7342c88fcc93681b4588b73f0554600160009081527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe0554604080516317e1d38560e11b81526001600160a01b0392831696810196909652602486018390526044860183905260648601929092529051911692632fc3a70a9260848083019391928290030181865afa158015611038573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105c9190615c0f565b6110669190615db2565b905090565b611073613e48565b61107d8282613e63565b5050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60016110dc81613d93565b6110e98584146000613db9565b60005b8581101561127a5761113e87878381811061110957611109615c28565b905060200201602081019061111e9190615640565b333088888681811061113257611132615c28565b90506020020135613eae565b600083815260066020526040902087878381811061115e5761115e615c28565b90506020020160208101906111739190615640565b8154600180820184556000938452602080852090920180546001600160a01b0319166001600160a01b039490941693909317909255858352600690526040909120018585838181106111c7576111c7615c28565b835460018101855560009485526020948590209190940292909201359190920155507f7ed061f2bc8591b4b7f3d75c0e045beef7e1f5d4ce3d3b861b641ab0e70cf5b78386868481811061121d5761121d615c28565b9050602002013589898581811061123657611236615c28565b905060200201602081019061124b9190615640565b6040805193845260208401929092526001600160a01b03169082015260600160405180910390a16001016110ec565b50505050505050565b600161128e81613d93565b610f68613ec0565b60016112a181613d93565b6112ae8483146000613db9565b60005b84811015610e69578383828181106112cb576112cb615c28565b90506020020135600360008888858181106112e8576112e8615c28565b90506020020160208101906112fd9190615933565b600581111561130e5761130e615759565b600581111561131f5761131f615759565b81526020810191909152604001600020557f313d1112b36b5f39a16a758ed9c767d12e0e9b3e3d3456700e5f83aa9abc3f2a86868381811061136357611363615c28565b90506020020160208101906113789190615933565b85858481811061138a5761138a615c28565b9050602002013560405161139f929190615dc6565b60405180910390a16001016112b1565b6113b7615211565b606060008060008060005b888110156117c057600760008b8b848181106113e0576113e0615c28565b60209081029290920135835250818101929092526040908101600020815160c0810183528154815260018201549381019390935260028101549183019190915260038101546060830152600401546001600160a01b0381166080830152600160a01b900460ff1615801560a08301529097506117b85761148b6002885160009081526005602052604090206008015460ff16600381111561148357611483615759565b146004613db9565b61149e6000548860000151106015613db9565b6114bf8a8a838181106114b3576114b3615c28565b905060200201356117de565b99509197509195509093509150600760008b8b848181106114e2576114e2615c28565b60209081029290920135835250810191909152604001600090812081815560018101829055600280820183905560038201839055600490910180546001600160a81b031916905560808901518691629896809161153f9190613f08565b61154c9062989680615de4565b6115569088615df7565b6115609190615db2565b61156a9190615c99565b600560005260036020527f405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250549091508111156115ce5750600560005260036020527f405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250545b6115f4600054896020015183886115e59190615c99565b8b608001518c60a00151613ff6565b50821561164057600160005260046020527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05546080890151611640916001600160a01b031690856140d1565b61164a8487615de4565b1561168a57600080526004602052600080516020615e8683398151915254608089015161168a916001600160a01b031690611685878a615de4565b6140d1565b60005b8751811015611728578781815181106116a8576116a8615c28565b6020026020010151600014611720578851600090815260066020526040902080546117209190839081106116de576116de615c28565b9060005260206000200160009054906101000a90046001600160a01b03168a608001518a848151811061171357611713615c28565b60200260200101516140d1565b60010161168d565b50600080526004602052600080516020615e8683398151915254611756906001600160a01b03168a836140d1565b7f58e36523aa3ba8011c478b3f463bf4fa154a4c2b0084b7e43f43d076184629358b8b8481811061178957611789615c28565b9050602002013589608001516000878a888d6040516117ae9796959493929190615e0e565b60405180910390a1505b6001016113c2565b50505050505050505050565b6117d4613e48565b61107d8282613df9565b6000818152600760209081526040808320815160c081018352815480825260018301548286019081526002808501548487015260038501546060808601919091526004909501546001600160a01b0381166080860152600160a01b900460ff16151560a085015291875260098652848720905187529094529184209092015483928392839267ffffffffffffffff81111561187b5761187b6154c0565b6040519080825280602002602001820160405280156118a4578160200160208202803683370190505b5081516000908152600960209081526040808320828601518452825291829020600201805483518184028101840190945280845293955091929083018282801561190d57602002820191906000526020600020905b8154815260200190600101908083116118f9575b50508451600090815260096020908152604080832082890151845282528083206060808a015185526003918201845282852083516101008101855281548152600182015495810195909552600281015493850193909352908201549083015260048101546080830152600581015460a0830152600681015460c08301526007015460e0820152949650925050505b8351811015611a45576119cf7f0000000000000000000000000000000000000000000000000000000000000000600a615d90565b8482815181106119e1576119e1615c28565b60200260200101518360a001518460e001518660400151611a029190615df7565b611a0c9190615db2565b611a169190615df7565b611a209190615db2565b848281518110611a3257611a32615c28565b602090810291909101015260010161199b565b508060a0015181606001518360400151611a5f9190615df7565b611a699190615db2565b611a739086615de4565b94508060a0015181604001518360400151611a8e9190615df7565b611a989190615db2565b611aa29087615de4565b95508060a0015181608001518360400151611abd9190615df7565b611ac79190615db2565b611ad19085615de4565b93508060a001518160c001518360400151611aec9190615df7565b611af69190615db2565b611b009088615de4565b9650505091939590929450565b611b15615252565b60008281526008602090815260409182902082516101408101845281548152600182015481840152600282015481850152600382015460608201526004820180548551818602810186019096528086529194929360808601939290830182828015611b9f57602002820191906000526020600020905b815481526020019060010190808311611b8b575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015611bf757602002820191906000526020600020905b815481526020019060010190808311611be3575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020018280548015611c4f57602002820191906000526020600020905b815481526020019060010190808311611c3b575b5050509183525050600782015460209091019060ff166002811115611c7657611c76615759565b6002811115611c8757611c87615759565b815260078201546001600160a01b036101009091048116602083015260089092015490911660409091015292915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6040805180820182526060808252602080830182905260008581526006825284902084518154928302810184018652948501828152939493909284928491840182828015611d6b57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d4d575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611dc357602002820191906000526020600020905b815481526020019060010190808311611daf575b5050505050815250509050919050565b60006002600d5403611df85760405163414f764b60e11b815260040160405180910390fd5b6002600d55600054611e0d9086868686613ff6565b600080526004602052600080516020615e8683398151915254909150611e3e906001600160a01b0316333087613eae565b6001600d55949350505050565b6002600d5403611e6e5760405163414f764b60e11b815260040160405180910390fd5b6002600d81905560005490611eac9060008381526005602052604090206008015460ff166003811115611ea357611ea3615759565b14156011613db9565b600081815260056020526040902060010154611ecc428211156012613db9565b611ed5826140e1565b600082815260056020908152604091829020600801805460ff1916600217905590513381527f6a4de20bb9fa8fea199f1022f29eff6be1752c446674d16913f2afc2b3c5a8a5910160405180910390a150506001600d55565b611f36613e48565b611f4060006142e8565b565b604051602081016000835b81835260051b6020169091019060010183811c80611f4d575050601f198282030160051c8252604052919050565b60006001611f8881613d93565b611f90614326565b8215611fc45760405133904780156108fc02916000818181858888f19350505050158015611fc2573d6000803e3d6000fd5b505b60005b848110156120935761208b868683818110611fe457611fe4615c28565b9050602002016020810190611ff99190615640565b3388888581811061200c5761200c615c28565b90506020020160208101906120219190615640565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612067573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116859190615c0f565b600101611fc7565b506040513381527f5e7b34819cd91b239220bec92fcfd3c10da2214ba13e4e2b1f6c9cfdbd68a9a29060200160405180910390a1600191505b509392505050565b60016120df81613d93565b610f68614349565b6000807f000000000000000000000000000000000000000000000000000000000000000061211760126008615de4565b6121219190615c99565b905061212e81600a615d90565b6121388486615df7565b610c5b9190615db2565b92915050565b6121506152b6565b600082815260056020818152604092839020835161010081018552815481526001820154818401526002820154818601526003808301546060830152600483015460808301529382015460a082015284518086019095526006820154855260078201549285019290925260c0820193909352600883015490929160e084019160ff16908111156121e2576121e2615759565b60038111156121f3576121f3615759565b90525092915050565b6000836000036122115761220e610f6b565b93505b83831161221f576000612232565b61223261222c8585615c99565b836120e7565b90505b9392505050565b7fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af547fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460008054815260056020526040812060010154909291906122a2908590615c99565b6122ac9190615db2565b6122b69190615df7565b905082629896806122c78382615de4565b6122d19086615df7565b6122db9190615db2565b6122359190615c99565b60006002600d540361230a5760405163414f764b60e11b815260040160405180910390fd5b6002600d81905561231a81613d93565b61232261438e565b6123376123306000546143b2565b6007613db9565b600084815260086020908152604080832081516101408101835281548152600182015481850152600282015481840152600382015460608201526004820180548451818702810187019095528085529194929360808601939092908301828280156123c157602002820191906000526020600020905b8154815260200190600101908083116123ad575b505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561241957602002820191906000526020600020905b815481526020019060010190808311612405575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561247157602002820191906000526020600020905b81548152602001906001019080831161245d575b5050509183525050600782015460209091019060ff16600281111561249857612498615759565b60028111156124a9576124a9615759565b815260078201546001600160a01b0361010090910481166020808401919091526008909301541660409182015290820151908201519192506124ea916120e7565b9250612510336001600160a01b03168261012001516001600160a01b0316146009613db9565b612537600161251e87613974565b600281111561252f5761252f615759565b14600a613db9565b600260e0820152426060820181905260009061255490859061223c565b905060005b82608001515181101561262f5761262783600001518460800151838151811061258457612584615c28565b6020026020010151670de0b6b3a7640000888760c0015186815181106125ac576125ac615c28565b60200260200101516125be9190615df7565b6125c89190615db2565b670de0b6b3a7640000868860c0015187815181106125e8576125e8615c28565b60200260200101516125fa9190615df7565b6126049190615db2565b8760a00151868151811061261a5761261a615c28565b60200260200101516143fd565b600101612559565b5060008681526008602090815260409182902084518155818501516001820155918401516002830155606084015160038301556080840151805185939261267d926004850192910190615315565b5060a08201518051612699916005840191602090910190615315565b5060c082015180516126b5916006840191602090910190615315565b5060e082015160078201805460ff191660018360028111156126d9576126d9615759565b021790555061010082810151600783018054610100600160a81b0319166001600160a01b039283169093029290921790915561012090920151600890910180546001600160a01b031916919092161790556127646004600080600781111561274357612743615759565b81526020810191909152604001600020546001600160a01b031686866140d1565b600080526004602052600080516020615e8683398151915254612792906001600160a01b0316333084613eae565b50506001600d555092915050565b6000828152600760205260409020600401546127c8906001600160a01b031633146010613db9565b600082815260076020908152604091829020600401805460ff60a01b1916600160a01b851515908102919091179091558251858152918201527f94d4ebac570b7e51b033a8ea25e744f5f3cabf1f43aeda01a5ef3cf37c54848a910160405180910390a15050565b6000828152600a6020908152604080832084845290915290205460609067ffffffffffffffff811115612865576128656154c0565b60405190808252806020026020018201604052801561289e57816020015b61288b615360565b8152602001906001900390816128835790505b50905060005b6000848152600a6020908152604080832086845290915290205481101561296a576000848152600960209081526040808320868452825280832084845260039081018352928190208151610100810183528154815260018201549381019390935260028101549183019190915291820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e0820152825183908390811061295757612957615c28565b60209081029190910101526001016128a4565b5092915050565b600061297b610f6b565b831015610e945761298a610f6b565b6129948484615df7565b61299e9190615db2565b9050612142565b6129ad615360565b506000928352600960209081526040808520938552928152828420918452600391820181529282902082516101008101845281548152600182015494810194909452600281015492840192909252810154606083015260048101546080830152600581015460a0830152600681015460c08301526007015460e082015290565b6002612a3881613d93565b612a4061438e565b612a4e6123306000546143b2565b60008381526008602090815260408083208151610140810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919492936080860193909290830182828015612ad857602002820191906000526020600020905b815481526020019060010190808311612ac4575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015612b3057602002820191906000526020600020905b815481526020019060010190808311612b1c575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020018280548015612b8857602002820191906000526020600020905b815481526020019060010190808311612b74575b5050509183525050600782015460209091019060ff166002811115612baf57612baf615759565b6002811115612bc057612bc0615759565b815260078201546001600160a01b03610100909104811660208301526008909201548216604090910152610120820151919250612c01911633146009613db9565b612c1c60028260e00151600281111561252f5761252f615759565b6000612c3082602001518360400151612971565b90506000612c46836020015184604001516120e7565b905060005b836080015151811015612d2f57612d27846000015185608001518381518110612c7657612c76615c28565b6020026020010151670de0b6b3a76400008760c001518581518110612c9d57612c9d615c28565b6020026020010151878b11612cb2578a612cb4565b875b612cbe9190615df7565b612cc89190615db2565b670de0b6b3a76400008860c001518681518110612ce757612ce7615c28565b602002602001015187612cfa9190615df7565b612d049190615db2565b8860a001518681518110612d1a57612d1a615c28565b60200260200101516144fc565b600101612c4b565b5081851115612d8057612d806004600060015b6007811115612d5357612d53615759565b81526020810191909152604001600020546101008501516001600160a01b03909116906116858589615c99565b612dbd6004600060015b6007811115612d9b57612d9b615759565b81526020810191909152604001600020546001600160a01b0316333088613eae565b600086815260086020526040812081815560018101829055600281018290556003810182905590612df160048301826153a5565b612dff6005830160006153a5565b612e0d6006830160006153a5565b506007810180546001600160a81b031916905560080180546001600160a01b0319169055505050505050565b60006002600d5403612e5e5760405163414f764b60e11b815260040160405180910390fd5b6002600d819055612e6e81613d93565b612e7661438e565b612e89612e81613a97565b156014613db9565b600054612e98612330826143b2565b612eae6001600160a01b03851615156001613db9565b612eba878783886145b8565b6000612ec688886120e7565b600083815260056020526040902060020154909150612ee990821115600b613db9565b6000612ef58989610b74565b90506000612f04878b8b6133e1565b9050612f8887898c8c8860018054600081815260086020819052604090912060078101805492820180546001600160a01b0319166001600160a01b039a8b16179055818501979097556002810195909555929093556001600160a81b031990911660ff19610100969095169590950293909316939093178217905580548101905590565b9550612f97848b85858a61460e565b60008481526005602052604081206002018054859290612fb8908490615c99565b909155505060008481526005602052604081206003018054859290612fde908490615de4565b9091555050600080526004602052600080516020615e8683398151915254613011906001600160a01b0316333085613eae565b6004602052600080516020615e868339815191525460026000527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754613066916001600160a01b039081169133911684613eae565b6040805185815260208101889052908101839052606081018290526001600160a01b03881660808201523360a08201527f9039a4c733a9179a327c244f2d657df5bc5bcafb45e79a5e155b24ea8ad26b249060c00160405180910390a150506001600d5550919695505050505050565b6002600d54036130f95760405163414f764b60e11b815260040160405180910390fd5b6002600d5561310661438e565b6000606060008060008060005b888110156133d057600760008b8b8481811061313157613131615c28565b90506020020135815260200190815260200160002060000154965061319d336001600160a01b0316600760008d8d8681811061316f5761316f615c28565b60209081029290920135835250810191909152604001600020600401546001600160a01b0316146010613db9565b6131c8600260008981526005602052604090206008015460ff16600381111561148357611483615759565b6131dd8a8a838181106114b3576114b3615c28565b99509197509195509093509150600760008b8b8481811061320057613200615c28565b6020908102929092013583525081019190915260400160009081208181556001810182905560028101829055600381019190915560040180546001600160a81b0319169055811561328b57600160005260046020527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe055461328b906001600160a01b031689846140d1565b826132968587615de4565b6132a09190615de4565b156132e157600080526004602052600080516020615e86833981519152546132e1906001600160a01b031689856132d7888a615de4565b6116859190615de4565b60005b865181101561336c578681815181106132ff576132ff615c28565b6020026020010151600014613364576000888152600660205260409020805461336491908390811061333357613333615c28565b9060005260206000200160009054906101000a90046001600160a01b03168a89848151811061171357611713615c28565b6001016132e4565b507f58e36523aa3ba8011c478b3f463bf4fa154a4c2b0084b7e43f43d076184629358a8a838181106133a0576133a0615c28565b9050602002013589868689878c6040516133c09796959493929190615e0e565b60405180910390a1600101613113565b50506001600d555050505050505050565b6000806133ef600086613f08565b905061341c7f00000000000000000000000000000000000000000000000000000000000000006012615c99565b61342790600a615d90565b83629896806134368482615de4565b6134409087615df7565b61344a9190615db2565b6134549190615c99565b61345e9190615db2565b915083613469610f6b565b10156120cc576000613479610f6b565b6134856008600a615d90565b61348f9087615df7565b6134999190615db2565b90506134a76008600a615d90565b6134b18285615df7565b6134bb9190615db2565b9695505050505050565b600080805260036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff5462989680906135009082615de4565b6006600052600460208181527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f546040805163c189c19b60e01b8152938401889052516001600160a01b039091169263c189c19b9260248083019391928290030181865afa158015613576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359a9190615c0f565b6135a49190615df7565b6121429190615db2565b60026135b981613d93565b6135c161438e565b6135cf6123306000546143b2565b6000838152600860209081526040808320815161014081018352815481526001820154818501526002820154818401526003820154606082015260048201805484518187028101870190955280855291949293608086019390929083018282801561365957602002820191906000526020600020905b815481526020019060010190808311613645575b50505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156136b157602002820191906000526020600020905b81548152602001906001019080831161369d575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561370957602002820191906000526020600020905b8154815260200190600101908083116136f5575b5050509183525050600782015460209091019060ff16600281111561373057613730615759565b600281111561374157613741615759565b815260078201546001600160a01b03610100909104811660208301526008909201548216604090910152610120820151919250613782911633146009613db9565b6137a560028260e00151600281111561379d5761379d615759565b14600d613db9565b60006137b9826020015183604001516120e7565b905060006137cb82846060015161223c565b90506137d7824261223c565b6137e19082615c99565b6137eb9082615c99565b90508482111561381457506000858152600860205260408120600701805460ff19169055613831565b6000868152600860205260409020600701805460ff191660011790555b60005b8360800151518110156139185761391084600001518560800151838151811061385f5761385f615c28565b6020026020010151670de0b6b3a76400008760c00151858151811061388657613886615c28565b6020026020010151878b1161389b578a61389d565b875b6138a79190615df7565b6138b19190615db2565b670de0b6b3a76400008860c0015186815181106138d0576138d0615c28565b6020026020010151876138e39190615df7565b6138ed9190615db2565b8860a00151868151811061390357613903615c28565b60200260200101516146c3565b600101613834565b508482101561392e5761392e6004600080612d42565b61393b6004600080612d8a565b8015610e6957600080526004602052600080516020615e8683398151915254610100840151610e69916001600160a01b031690836140d1565b600081815260086020908152604080832054808452600590925282206001015442106139a35750600092915050565b505060009081526008602052604090206007015460ff1690565b60008281526009602090815260408083208484529091529020600181015460029091015460609067ffffffffffffffff8111156139fc576139fc6154c0565b604051908082528060200260200182016040528015613a25578160200160208202803683370190505b5060008581526009602090815260408083208784528252918290206002018054835181840281018401909452808452939450919290830182828015613a8957602002820191906000526020600020905b815481526020019060010190808311613a75575b505050505090509250929050565b60008054815260056020526040812060010154808203613ab957600091505090565b600160005260036020527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c54613aef9082615c99565b42101591505090565b60006002600d5403613b1d5760405163414f764b60e11b815260040160405180910390fd5b6002600d556004613b2d81613d93565b613b3a4285116002613db9565b613b478315156003613db9565b60008054613b56906001615de4565b6000818152600560208181526040808420815161010081018352815481526001820154818501526002820154818401526003808301546060830152600483015460808301529482015460a082015282518084019093526006820154835260078201549383019390935260c08301919091526008810154949550929390929160e084019160ff1690811115613bec57613bec615759565b6003811115613bfd57613bfd615759565b90525060005490915015613c4757613c47600260056000613c1f600187615c99565b815260208101919091526040016000206008015460ff16600381111561148357611483615759565b42815260a081018581526020808301888152600160e08501818152600087815587815260058086526040918290208851815594518585015590870151600285015560608701516003808601919091556080880151600486015595519084015560c0860151805160068501559093015160078301559151600882018054869593949293919260ff19909116918490811115613ce357613ce3615759565b0217905550506040518381527fb5ca1ca1b7b47549eb8af476f3ef702fc63bcd8b8c01dc163b009bb818f97997915060200160405180910390a1600193505050506001600d5592915050565b613d37613e48565b63389a75e1600c52806000526020600c208054421115613d5f57636f5e88186000526004601cfd5b60009055610f68816142e8565b613d74613e48565b8060601b613d8a57637448fbae6000526004601cfd5b610f68816142e8565b638b78c6d8600c5233600052806020600c205416610f68576382b429006000526004601cfd5b8161107d57604051636f9963a160e11b81526004810182905260240160405180910390fd5b600080613ded848460016147bb565b915091505b9250929050565b638b78c6d8600c52816000526020600c20805482811681189250508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b638b78c6d819543314611f40576382b429006000526004601cfd5b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b613eba84848484614802565b50505050565b613ec8614326565b600c805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa0546004600090815260036020527f83ec6a1f0257b830b5e016457c9cf1435391bf56cc98f369a58a54fe937724655490916001600160a01b03169063303eadfb9085908590600114613f7c576000613f7f565b60015b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015215156044820152606401602060405180830381865afa158015613fd2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122359190615c0f565b6000614000614850565b61400861438e565b614014612330876143b2565b614020858588866145b8565b600061402d878787614861565b905061403d878688848888614b7b565b9150846005600089815260200190815260200160002060020160008282546140659190615de4565b90915550506040805188815260208101849052908101879052606081018690526001600160a01b03851660808201523360a08201527f6fa330b6e4da8eb081261834b2c05817b24b88caf5f7fcdb1a54e73caf206e119060c00160405180910390a15095945050505050565b6140dc838383614bed565b505050565b6000818152600560209081526040808320600690810154908352818420825181546060958102820186018552938101848152929594859491938492909184919084018282801561415a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161413c575b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156141b257602002820191906000526020600020905b81548152602001906001019080831161419e575b50505091909252505050600085815260056020526040812060030154919250905b8415610e6957600086815260096020908152604080832088845290915281206001015492505b8351518110156142b85782156142b05760008781526009602090815260408083208984529091529020600201836142517f0000000000000000000000000000000000000000000000000000000000000000600a615d90565b848760200151858151811061426857614268615c28565b60200260200101518761427b9190615df7565b6142859190615db2565b61428f9190615df7565b6142999190615db2565b815460018101835560009283526020909220909101555b6001016141f9565b50836142c381615c80565b6000888152600e602052604090209095506142df915086613dde565b95506141d39050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600c5460ff16611f405760405163dcdde9dd60e01b815260040160405180910390fd5b61435161438e565b600c805460ff191660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001613efe565b600c5460ff1615611f405760405163ab35696f60e01b815260040160405180910390fd5b6000600160008381526005602052604090206008015460ff1660038111156143dc576143dc615759565b14801561214257505060009081526005602052604090206001015442111590565b6000858152600960209081526040808320878452825280832084845260030190915281206001018054859290614434908490615de4565b90915550506000858152600960209081526040808320878452825280832084845260039081019092528220018054849290614470908490615de4565b909155505060008581526009602090815260408083208784528252808320848452600301909152812060060180548592906144ac908490615c99565b9091555050604080518681526020810185905233918101919091527f6df3166c311c515f6d954b1144d8a8fe1575d42d23369a699f97024033caf1ef906060015b60405180910390a15050505050565b6000858152600960209081526040808320878452825280832084845260030190915281206004018054859290614533908490615de4565b9091555050600085815260096020908152604080832087845282528083208484526003019091528120600101805484929061456f908490615c99565b909155505060408051868152602081018690529081018490523360608201527fdb338dac4e401648bcae51c564238a2161f21cf257ba09e51d20fd068ca79691906080016144ed565b6145ce6001600160a01b03821615156001613db9565b6145db8315156003613db9565b613eba84158015906146075750600083815260056020819052604090912001546146059086615e5a565b155b6005613db9565b6000858152600560205260408120600601548190815b8684146146b8576146358488615c99565b905061464582891115600c613db9565b614653898389848a8a614c36565b60008a8152600960209081526040808320868452909152812060010180549295508592909190614684908490615de4565b9091555061469490508385615de4565b60008a8152600e602052604090209094506146af9083613dde565b92506146249050565b505050505050505050565b60008581526009602090815260408083208784528252808320848452600301909152812060060180548592906146fa908490615de4565b90915550506000858152600960209081526040808320878452825280832084845260030190915281206001018054859290614736908490615c99565b90915550506000858152600960209081526040808320878452825280832084845260039081019092528220018054849290614772908490615c99565b909155505060408051868152602081018690529081018490523360608201527fe2c0b0d5b60b3365ee30759e6358dc333ed40ece550937d37b33951f84a4eb4a906080016144ed565b6000806147c88585614f68565b6147d7575060009050806147fa565b505060008281526001848101602090815260408084208515158552909152909120545b935093915050565b6040516323b872dd600052836020528260405281606052602060006064601c6000895af13d15600160005114171661484257637939f4246000526004601cfd5b600060605260405250505050565b333214611f4057611f406008613d93565b6000838152600a60209081526040808320858452825280832054868452600983528184208685529092528220549091036148bc5761489f8385614fec565b600084815260096020908152604080832086845290915290208390555b8060000361498157604080516101008101825242815260006020808301828152838501838152606085018481526080860185815260a087018a815260c088018b815260e089018881528e8952600988528a89208e8a5288528a89208c8a52600390810189528b8a209a518b5596516001808c0191909155955160028b015593519589019590955590516004880155516005870155915160068601559051600790940193909355878252600a815283822087835290529190912080549091019055612235565b60008481526009602090815260408083208684529091528120600301816149a9600185615c99565b8152602001908152602001600020604051806101000160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152505090508060e00151600014614acb576000858152600960209081526040808320878452825280832085845260030190915281204281556005018054859290614a5b908490615de4565b90915550506000858152600960209081526040808320878452825280832085845260030190915281206006018054859290614a97908490615de4565b90915550506000858152600a602090815260408083208784529091528120805491614ac183615c80565b91905055506120cc565b614ad482615e6e565b9150828160a001818151614ae89190615de4565b90525060c081018051849190614aff908390615de4565b9052506000858152600960209081526040808320878452825280832085845260039081018352928190208451815591840151600183015583015160028201556060830151918101919091556080820151600482015560a0820151600582015560c0820151600682015560e0820151600790910155509392505050565b60028054906000614b8b83615c80565b90915550600081815260076020526040902096875560018701949094556002860194909455600385019190915560049093018054921515600160a01b026001600160a81b03199093166001600160a01b03909416939093179190911790915590565b81601a5280603a5269a9059cbb00000000000060005260206000604460166000875af13d156001600051141716614c2c576390b8ec186000526004601cfd5b6000603a52505050565b6000868152600b602090815260408083208884529091528120548015801590614cd1575060008881526009602090815260408083208a8452909152812060030190614c82600184615c99565b815260208082019290925260409081016000908120600701548b8252600984528282208b835290935290812060030190614cbd600185615c99565b815260200190815260200160002060050154115b15614ce257614cdf81615e6e565b90505b6000888152600a602090815260408083208a8452909152812054614d0890600190615c99565b905060005b818311158015614d1d5750868414155b15614f5b5760008a81526009602090815260408083208c8452825280832086845260030190915281206007810154600590910154614d5b9190615c99565b90506000614d69868a615c99565b9050808210614e7c5760008c81526009602090815260408083208e845282528083208884526003019091529020600701805482019055948501949150818988820281614db757614db7615d9c565b04600960008e815260200190815260200160002060008d8152602001908152602001600020600301600087815260200190815260200160002060020160008282540192505081905550614e77878c878d670de0b6b3a764000088614e1b9190615df7565b614e259190615db2565b6000938452600860209081526040852060048101805460018181018355918852838820019590955560058101805480870182559087528287200193909355600690920180549384018155845292200155565b614f4e565b8115614f4e5760008c81526009602090815260408083208e8452825280832088845260030190915290206007018054830190559481019490915081908988830281614ec957614ec9615d9c565b04600960008e815260200190815260200160002060008d8152602001908152602001600020600301600087815260200190815260200160002060020160008282540192505081905550614f2d878c878d670de0b6b3a764000088614e1b9190615df7565b60008c8152600b602090815260408083208e84529091529020805460010190555b8460010194505050614d0d565b5050509695505050505050565b60008181526001830160209081526040808320838052909152812054158015614fab57506000828152600180850160209081526040808420928452919052902054155b15614fe4576000808052600180850160209081526040808420928452919052902054829003614fdc57506001612142565b506000612142565b506001612142565b60008181526005602052604090206006810154600790910154818411156150225760008381526005602052604090206006018490555b8084108061502e575080155b156150485760008381526005602052604090206007018490555b6000615054858561509c565b90508060000361507c576000848152600e60205260409020615076908661513e565b50615095565b6000848152600e60205260409020610e6990828761514c565b5050505050565b6000818152600e60205260408120546000036150ba57506000612142565b6000828152600e602052604081206150d4908260016147bb565b9150505b801580159061510e57506000838152600960209081526040808320848452909152812054900361510957600061510b565b805b84105b15612235576000838152600e602090815260408083209383526001938401825280832093835292905220546150d8565b60006122358383600061515b565b60006122328484846000615166565b600061223284600085855b60006151728584614f68565b15801561518457506151848585614f68565b156152065760008481526001868101602081815260408085208715801580885291845282872080548b8952868652848920838a52808752858a208e9055918c90558089529585528388209188529084528287208a905590865290915283208190558754909288916151f6908490615de4565b9091555060019250610c5b915050565b506000949350505050565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000151581525090565b60405180610140016040528060008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001600060028111156152a2576152a2615759565b815260006020820181905260409091015290565b604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001615309604051806040016040528060008152602001600081525090565b81526020016000905290565b828054828255906000526020600020908101928215615350579160200282015b82811115615350578251825591602001919060010190615335565b5061535c9291506153bf565b5090565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5080546000825590600052602060002090810190610f6891905b5b8082111561535c57600081556001016153c0565b600080604083850312156153e757600080fd5b50508035926020909101359150565b60006020828403121561540857600080fd5b5035919050565b60008083601f84011261542157600080fd5b50813567ffffffffffffffff81111561543957600080fd5b6020830191508360208260051b8501011115613df257600080fd5b6000806000806040858703121561546a57600080fd5b843567ffffffffffffffff8082111561548257600080fd5b61548e8883890161540f565b909650945060208701359150808211156154a757600080fd5b506154b48782880161540f565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b803560ff811681146154e757600080fd5b919050565b600060208083850312156154ff57600080fd5b823567ffffffffffffffff8082111561551757600080fd5b818501915085601f83011261552b57600080fd5b81358181111561553d5761553d6154c0565b8060051b604051601f19603f83011681018181108582111715615562576155626154c0565b60405291825284820192508381018501918883111561558057600080fd5b938501935b828510156155a557615596856154d6565b84529385019392850192615585565b98975050505050505050565b600081518084526020808501945080840160005b838110156155e1578151875295820195908201906001016155c5565b509495945050505050565b60208152600061223560208301846155b1565b80356001600160a01b03811681146154e757600080fd5b6000806040838503121561562957600080fd5b615632836155ff565b946020939093013593505050565b60006020828403121561565257600080fd5b612235826155ff565b60008060008060006060868803121561567357600080fd5b853567ffffffffffffffff8082111561568b57600080fd5b61569789838a0161540f565b909750955060208801359150808211156156b057600080fd5b506156bd8882890161540f565b96999598509660400135949350505050565b6000806000604084860312156156e457600080fd5b833567ffffffffffffffff8111156156fb57600080fd5b6157078682870161540f565b909450925061571a9050602085016155ff565b90509250925092565b85815284602082015283604082015282606082015260a06080820152600061574e60a08301846155b1565b979650505050505050565b634e487b7160e01b600052602160045260246000fd5b6003811061577f5761577f615759565b9052565b6020815281516020820152602082015160408201526040820151606082015260608201516080820152600060808301516101408060a08501526157ca6101608501836155b1565b915060a0850151601f19808685030160c08701526157e884836155b1565b935060c08701519150808685030160e08701525061580683826155b1565b92505060e085015161010061581d8187018361576f565b8601519050610120615839868201836001600160a01b03169052565b909501516001600160a01b031693019290925250919050565b60006020828403121561586457600080fd5b81356008811061223557600080fd5b6020808252825160408383015280516060840181905260009291820190839060808601905b808310156158c15783516001600160a01b03168252928401926001929092019190840190615898565b5092860151858403601f190160408701529261574e81856155b1565b803580151581146154e757600080fd5b6000806000806080858703121561590357600080fd5b843593506020850135925061591a604086016155ff565b9150615928606086016158dd565b905092959194509250565b60006020828403121561594557600080fd5b81356006811061223557600080fd5b6020808252825182820181905260009190848201906040850190845b8181101561598f57835160ff1683529284019291840191600101615970565b50909695505050505050565b6000806000604084860312156159b057600080fd5b833567ffffffffffffffff8111156159c757600080fd5b6159d38682870161540f565b909450925061571a9050602085016158dd565b600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c0830151805160c0840152602081015160e08401525060e083015160048110615a5157615a51615759565b806101008401525092915050565b600080600060608486031215615a7457600080fd5b505081359360208301359350604090920135919050565b60008060408385031215615a9e57600080fd5b82359150615aae602084016155ff565b90509250929050565b60008060408385031215615aca57600080fd5b82359150615aae602084016158dd565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301525050565b6020808252825182820181905260009190848201906040850190845b8181101561598f57615b57838551615ada565b928401926101009290920191600101615b44565b61010081016121428284615ada565b60008060008060808587031215615b9057600080fd5b8435935060208501359250615ba7604086016155ff565b9150615928606086016155ff565b600080600060608486031215615bca57600080fd5b615bd3846155ff565b95602085013595506040909401359392505050565b60208101612142828461576f565b82815260406020820152600061223260408301846155b1565b600060208284031215615c2157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b6040810160088410615c5257615c52615759565b9281526001600160a01b039190911660209091015290565b634e487b7160e01b600052601160045260246000fd5b600060018201615c9257615c92615c6a565b5060010190565b8181038181111561214257612142615c6a565b600181815b80851115615ce7578160001904821115615ccd57615ccd615c6a565b80851615615cda57918102915b93841c9390800290615cb1565b509250929050565b600082615cfe57506001612142565b81615d0b57506000612142565b8160018114615d215760028114615d2b57615d47565b6001915050612142565b60ff841115615d3c57615d3c615c6a565b50506001821b612142565b5060208310610133831016604e8410600b8410161715615d6a575081810a612142565b615d748383615cac565b8060001904821115615d8857615d88615c6a565b029392505050565b60006122358383615cef565b634e487b7160e01b600052601260045260246000fd5b600082615dc157615dc1615d9c565b500490565b6040810160068410615dda57615dda615759565b9281526020015290565b8082018082111561214257612142615c6a565b808202811582820484141761214257612142615c6a565b87815260018060a01b03871660208201528560408201528460608201528360808201528260a082015260e060c08201526000615e4d60e08301846155b1565b9998505050505050505050565b600082615e6957615e69615d9c565b500690565b600081615e7d57615e7d615c6a565b50600019019056fe17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3eca2646970667358221220570b73e0507a2024e257e1ad1b33d47281d5a452f8f4d0b1fab9c940bad4d55964736f6c63430008110033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.