Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x6101e060 | 15975658 | 645 days ago | IN | 0 ETH | 0.06045712 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PoolIO
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {EnumerableSet} from "@solidstate/contracts/utils/EnumerableSet.sol"; import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol"; import {ABDKMath64x64Token} from "@solidstate/abdk-math-extensions/contracts/ABDKMath64x64Token.sol"; import {IPoolIO} from "./IPoolIO.sol"; import {IPoolInternal} from "./IPoolInternal.sol"; import {PoolInternal} from "./PoolInternal.sol"; import {PoolStorage} from "./PoolStorage.sol"; import {IPremiaMining} from "../mining/IPremiaMining.sol"; /** * @title Premia option pool * @dev deployed standalone and referenced by PoolProxy */ contract PoolIO is IPoolIO, PoolInternal { using ABDKMath64x64 for int128; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using PoolStorage for PoolStorage.Layout; struct ReassignBatchArgsInternal { uint256[] tokenIds; uint256[] contractSizes; bool divest; int128 newPrice64x64; int128 utilizationCall64x64; int128 utilizationPut64x64; } struct ReassignBatchResultInternal { uint256[] baseCosts; uint256[] feeCosts; uint256 amountOutCall; uint256 amountOutPut; } constructor( address ivolOracle, address wrappedNativeToken, address premiaMining, address feeReceiver, address feeDiscountAddress, int128 feePremium64x64, address exchangeProxy ) PoolInternal( ivolOracle, wrappedNativeToken, premiaMining, feeReceiver, feeDiscountAddress, feePremium64x64, exchangeProxy ) {} /** * @inheritdoc IPoolIO */ function setDivestmentTimestamp(uint64 timestamp, bool isCallPool) external { PoolStorage.Layout storage l = PoolStorage.layout(); require( timestamp == 0 || timestamp >= l.depositedAt[msg.sender][isCallPool] + (1 days), "liq lock 1d" ); l.divestmentTimestamps[msg.sender][isCallPool] = timestamp; } /** * @inheritdoc IPoolIO */ function deposit(uint256 amount, bool isCallPool) external payable { PoolStorage.Layout storage l = PoolStorage.layout(); _pullFrom(l, msg.sender, amount, isCallPool, true); _deposit(l, amount, isCallPool); } /** * @inheritdoc IPoolIO */ function swapAndDeposit(IPoolInternal.SwapArgs memory s, bool isCallPool) external payable { PoolStorage.Layout storage l = PoolStorage.layout(); address tokenOut = l.getPoolToken(isCallPool); uint256 creditAmount = _swapForPoolTokens(s, tokenOut); _deposit(l, creditAmount, isCallPool); } /** * @inheritdoc IPoolIO */ function withdraw(uint256 amount, bool isCallPool) public { PoolStorage.Layout storage l = PoolStorage.layout(); uint256 depositedAt = l.depositedAt[msg.sender][isCallPool]; require(depositedAt + (1 days) < block.timestamp, "liq lock 1d"); _processPendingDeposits(l, isCallPool); int128 utilization64x64 = l.getUtilization64x64(isCallPool); int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCallPool); uint256 reservedLiqTokenId = _getReservedLiquidityTokenId(isCallPool); uint256 reservedLiquidity = _balanceOf(msg.sender, reservedLiqTokenId); uint256 reservedLiqToWithdraw = reservedLiquidity < amount ? reservedLiquidity : amount; uint256 freeLiqToWithdraw = amount - reservedLiqToWithdraw; if (reservedLiqToWithdraw > 0) { // burn reserved liquidity tokens from sender _burn(msg.sender, reservedLiqTokenId, reservedLiqToWithdraw); } if (freeLiqToWithdraw > 0) { // burn free liquidity tokens from sender _burn( msg.sender, _getFreeLiquidityTokenId(isCallPool), freeLiqToWithdraw ); int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64( isCallPool ); _setCLevel( l, oldLiquidity64x64, newLiquidity64x64, isCallPool, utilization64x64 ); } _subUserTVL( l, msg.sender, isCallPool, freeLiqToWithdraw, utilization64x64 ); _processAvailableFunds(msg.sender, amount, isCallPool, true, true); emit Withdrawal(msg.sender, isCallPool, depositedAt, amount); } /** * @inheritdoc IPoolIO */ function reassign( uint256 tokenId, uint256 contractSize, bool divest ) external returns ( uint256 baseCost, uint256 feeCost, uint256 amountOut ) { bool isCall; int128 utilization64x64; { int128 newPrice64x64 = _update(PoolStorage.layout()); uint64 maturity; int128 strike64x64; { PoolStorage.TokenType tokenType; (tokenType, maturity, strike64x64) = PoolStorage.parseTokenId( tokenId ); isCall = tokenType == PoolStorage.TokenType.SHORT_CALL || tokenType == PoolStorage.TokenType.LONG_CALL; } utilization64x64 = PoolStorage.layout().getUtilization64x64(isCall); (baseCost, feeCost, amountOut) = _reassign( PoolStorage.layout(), msg.sender, maturity, strike64x64, isCall, contractSize, newPrice64x64 ); } _processAvailableFunds(msg.sender, amountOut, isCall, divest, true); _subUserTVL( PoolStorage.layout(), msg.sender, isCall, divest ? baseCost + feeCost + amountOut : baseCost + feeCost, utilization64x64 ); } /** * @inheritdoc IPoolIO */ function reassignBatch( uint256[] calldata tokenIds, uint256[] calldata contractSizes, bool divest ) public returns ( uint256[] memory baseCosts, uint256[] memory feeCosts, uint256 amountOutCall, uint256 amountOutPut ) { require(tokenIds.length == contractSizes.length, "diff array length"); int128 newPrice64x64 = _update(PoolStorage.layout()); int128 utilizationCall64x64 = PoolStorage.layout().getUtilization64x64( true ); int128 utilizationPut64x64 = PoolStorage.layout().getUtilization64x64( false ); ReassignBatchResultInternal memory result = _reassignBatch( ReassignBatchArgsInternal( tokenIds, contractSizes, divest, newPrice64x64, utilizationCall64x64, utilizationPut64x64 ) ); return ( result.baseCosts, result.feeCosts, result.amountOutCall, result.amountOutPut ); } function _reassignBatch(ReassignBatchArgsInternal memory args) internal returns (ReassignBatchResultInternal memory result) { result.baseCosts = new uint256[](args.tokenIds.length); result.feeCosts = new uint256[](args.tokenIds.length); bool[] memory isCallToken = new bool[](args.tokenIds.length); for (uint256 i; i < args.tokenIds.length; i++) { ( PoolStorage.TokenType tokenType, uint64 maturity, int128 strike64x64 ) = PoolStorage.parseTokenId(args.tokenIds[i]); bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL || tokenType == PoolStorage.TokenType.LONG_CALL; uint256 amountOut; isCallToken[i] = isCall; (result.baseCosts[i], result.feeCosts[i], amountOut) = _reassign( PoolStorage.layout(), msg.sender, maturity, strike64x64, isCall, args.contractSizes[i], args.newPrice64x64 ); if (isCall) { result.amountOutCall += amountOut; } else { result.amountOutPut += amountOut; } } if (result.amountOutCall > 0) { uint256 reassignmentCost; for (uint256 i; i < args.tokenIds.length; i++) { if (isCallToken[i] == false) continue; reassignmentCost += result.baseCosts[i]; reassignmentCost += result.feeCosts[i]; } _processAvailableFunds( msg.sender, result.amountOutCall, true, args.divest, true ); _subUserTVL( PoolStorage.layout(), msg.sender, true, args.divest ? reassignmentCost + result.amountOutCall : reassignmentCost, args.utilizationCall64x64 ); } if (result.amountOutPut > 0) { uint256 reassignmentCost; for (uint256 i; i < args.tokenIds.length; i++) { if (isCallToken[i] == true) continue; reassignmentCost += result.baseCosts[i]; reassignmentCost += result.feeCosts[i]; } _processAvailableFunds( msg.sender, result.amountOutPut, false, args.divest, true ); _subUserTVL( PoolStorage.layout(), msg.sender, false, args.divest ? reassignmentCost + result.amountOutPut : reassignmentCost, args.utilizationPut64x64 ); } } /** * @inheritdoc IPoolIO */ function withdrawFees() external returns (uint256 amountOutCall, uint256 amountOutPut) { amountOutCall = _withdrawFees(true); amountOutPut = _withdrawFees(false); } /** * @inheritdoc IPoolIO */ function annihilate( uint256 tokenId, uint256 contractSize, bool divest ) external { ( PoolStorage.TokenType tokenType, uint64 maturity, int128 strike64x64 ) = PoolStorage.parseTokenId(tokenId); bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL || tokenType == PoolStorage.TokenType.LONG_CALL; PoolStorage.Layout storage l = PoolStorage.layout(); int128 utilization64x64 = l.getUtilization64x64(isCall); uint256 collateralFreed = _annihilate( l, msg.sender, maturity, strike64x64, isCall, contractSize ); uint256 tokenAmount = l.contractSizeToBaseTokenAmount( contractSize, strike64x64, isCall ); _processAvailableFunds( msg.sender, collateralFreed, isCall, divest, true ); _subUserTVL( l, msg.sender, isCall, divest ? tokenAmount : collateralFreed - tokenAmount, utilization64x64 ); } /** * @inheritdoc IPoolIO */ function claimRewards(bool isCallPool) external { claimRewards(msg.sender, isCallPool); } /** * @inheritdoc IPoolIO */ function claimRewards(address account, bool isCallPool) public { PoolStorage.Layout storage l = PoolStorage.layout(); uint256 userTVL = l.userTVL[account][isCallPool]; uint256 totalTVL = l.totalTVL[isCallPool]; int128 utilization64x64 = l.getUtilization64x64(isCallPool); IPremiaMining(PREMIA_MINING_ADDRESS).claim( account, address(this), isCallPool, userTVL, userTVL, totalTVL, ABDKMath64x64Token.toDecimals(utilization64x64, 4) ); } /** * @inheritdoc IPoolIO */ function updateMiningPools() external { PoolStorage.Layout storage l = PoolStorage.layout(); IPremiaMining(PREMIA_MINING_ADDRESS).updatePool( address(this), true, l.totalTVL[true], ABDKMath64x64Token.toDecimals(l.getUtilization64x64(true), 4) ); IPremiaMining(PREMIA_MINING_ADDRESS).updatePool( address(this), false, l.totalTVL[false], ABDKMath64x64Token.toDecimals(l.getUtilization64x64(false), 4) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { ABDKMath64x64 } from 'abdk-libraries-solidity/ABDKMath64x64.sol'; /** * @title SolidState token extensions for ABDKMath64x64 library */ library ABDKMath64x64Token { using ABDKMath64x64 for int128; /** * @notice convert 64x64 fixed point representation of token amount to decimal * @param value64x64 64x64 fixed point representation of token amount * @param decimals token display decimals * @return value decimal representation of token amount */ function toDecimals(int128 value64x64, uint8 decimals) internal pure returns (uint256 value) { value = value64x64.mulu(10**decimals); } /** * @notice convert decimal representation of token amount to 64x64 fixed point * @param value decimal representation of token amount * @param decimals token display decimals * @return value64x64 64x64 fixed point representation of token amount */ function fromDecimals(uint256 value, uint8 decimals) internal pure returns (int128 value64x64) { value64x64 = ABDKMath64x64.divu(value, 10**decimals); } /** * @notice convert 64x64 fixed point representation of token amount to wei (18 decimals) * @param value64x64 64x64 fixed point representation of token amount * @return value wei representation of token amount */ function toWei(int128 value64x64) internal pure returns (uint256 value) { value = toDecimals(value64x64, 18); } /** * @notice convert wei representation (18 decimals) of token amount to 64x64 fixed point * @param value wei representation of token amount * @return value64x64 64x64 fixed point representation of token amount */ function fromWei(uint256 value) internal pure returns (int128 value64x64) { value64x64 = fromDecimals(value, 18); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library OwnableStorage { struct Layout { address owner; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.Ownable'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function setOwner(Layout storage l, address owner) internal { l.owner = owner; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC1155 interface needed by internal functions */ interface IERC1155Internal { event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value ); event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); event ApprovalForAll( address indexed account, address indexed operator, bool approved ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC165 } from './IERC165.sol'; /** * @title ERC1155 transfer receiver interface */ interface IERC1155Receiver is IERC165 { /** * @notice validate receipt of ERC1155 transfer * @param operator executor of transfer * @param from sender of tokens * @param id token ID received * @param value quantity of tokens received * @param data data payload * @return function's own selector if transfer is accepted */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @notice validate receipt of ERC1155 batch transfer * @param operator executor of transfer * @param from sender of tokens * @param ids token IDs received * @param values quantities of tokens received * @param data data payload * @return function's own selector if transfer is accepted */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title ERC165 interface registration interface * @dev see https://eips.ethereum.org/EIPS/eip-165 */ interface IERC165 { /** * @notice query whether contract has registered support for given interface * @param interfaceId interface id * @return bool whether interface is supported */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC173Internal } from './IERC173Internal.sol'; /** * @title Contract ownership standard interface * @dev see https://eips.ethereum.org/EIPS/eip-173 */ interface IERC173 is IERC173Internal { /** * @notice get the ERC173 contract owner * @return conrtact owner */ function owner() external view returns (address); /** * @notice transfer contract ownership to new account * @param account address of new owner */ function transferOwnership(address account) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC173 interface needed by internal functions */ interface IERC173Internal { event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Internal } from './IERC20Internal.sol'; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 is IERC20Internal { /** * @notice query the total minted token supply * @return token supply */ function totalSupply() external view returns (uint256); /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function balanceOf(address account) external view returns (uint256); /** * @notice query the allowance granted from given holder to given spender * @param holder approver of allowance * @param spender recipient of allowance * @return token allowance */ function allowance(address holder, address spender) external view returns (uint256); /** * @notice grant approval to spender to spend tokens * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) * @param spender recipient of allowance * @param amount quantity of tokens approved for spending * @return success status (always true; otherwise function should revert) */ function approve(address spender, uint256 amount) external returns (bool); /** * @notice transfer tokens to given recipient * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @notice transfer tokens to given recipient on behalf of given holder * @param holder holder of tokens prior to transfer * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transferFrom( address holder, address recipient, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC20 interface needed by internal functions */ interface IERC20Internal { event Transfer(address indexed from, address indexed to, uint256 value); event Approval( address indexed owner, address indexed spender, uint256 value ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Metadata } from '../token/ERC20/metadata/IERC20Metadata.sol'; import { IERC20 } from './IERC20.sol'; /** * @title WETH (Wrapped ETH) interface */ interface IWETH is IERC20, IERC20Metadata { /** * @notice convert ETH to WETH */ function deposit() external payable; /** * @notice convert WETH to ETH * @dev if caller is a contract, it should have a fallback or receive function * @param amount quantity of WETH to convert, denominated in wei */ function withdraw(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC1155Receiver } from '../../../interfaces/IERC1155Receiver.sol'; import { AddressUtils } from '../../../utils/AddressUtils.sol'; import { IERC1155BaseInternal } from './IERC1155BaseInternal.sol'; import { ERC1155BaseStorage } from './ERC1155BaseStorage.sol'; /** * @title Base ERC1155 internal functions * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ abstract contract ERC1155BaseInternal is IERC1155BaseInternal { using AddressUtils for address; /** * @notice query the balance of given token held by given address * @param account address to query * @param id token to query * @return token balance */ function _balanceOf(address account, uint256 id) internal view virtual returns (uint256) { if (account == address(0)) revert ERC1155Base__BalanceQueryZeroAddress(); return ERC1155BaseStorage.layout().balances[id][account]; } /** * @notice mint given quantity of tokens for given address * @dev ERC1155Receiver implementation is not checked * @param account beneficiary of minting * @param id token ID * @param amount quantity of tokens to mint * @param data data payload */ function _mint( address account, uint256 id, uint256 amount, bytes memory data ) internal virtual { if (account == address(0)) revert ERC1155Base__MintToZeroAddress(); _beforeTokenTransfer( msg.sender, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data ); ERC1155BaseStorage.layout().balances[id][account] += amount; emit TransferSingle(msg.sender, address(0), account, id, amount); } /** * @notice mint given quantity of tokens for given address * @param account beneficiary of minting * @param id token ID * @param amount quantity of tokens to mint * @param data data payload */ function _safeMint( address account, uint256 id, uint256 amount, bytes memory data ) internal virtual { _mint(account, id, amount, data); _doSafeTransferAcceptanceCheck( msg.sender, address(0), account, id, amount, data ); } /** * @notice mint batch of tokens for given address * @dev ERC1155Receiver implementation is not checked * @param account beneficiary of minting * @param ids list of token IDs * @param amounts list of quantities of tokens to mint * @param data data payload */ function _mintBatch( address account, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { if (account == address(0)) revert ERC1155Base__MintToZeroAddress(); if (ids.length != amounts.length) revert ERC1155Base__ArrayLengthMismatch(); _beforeTokenTransfer( msg.sender, address(0), account, ids, amounts, data ); mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; for (uint256 i; i < ids.length; ) { balances[ids[i]][account] += amounts[i]; unchecked { i++; } } emit TransferBatch(msg.sender, address(0), account, ids, amounts); } /** * @notice mint batch of tokens for given address * @param account beneficiary of minting * @param ids list of token IDs * @param amounts list of quantities of tokens to mint * @param data data payload */ function _safeMintBatch( address account, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { _mintBatch(account, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck( msg.sender, address(0), account, ids, amounts, data ); } /** * @notice burn given quantity of tokens held by given address * @param account holder of tokens to burn * @param id token ID * @param amount quantity of tokens to burn */ function _burn( address account, uint256 id, uint256 amount ) internal virtual { if (account == address(0)) revert ERC1155Base__BurnFromZeroAddress(); _beforeTokenTransfer( msg.sender, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), '' ); mapping(address => uint256) storage balances = ERC1155BaseStorage .layout() .balances[id]; unchecked { if (amount > balances[account]) revert ERC1155Base__BurnExceedsBalance(); balances[account] -= amount; } emit TransferSingle(msg.sender, account, address(0), id, amount); } /** * @notice burn given batch of tokens held by given address * @param account holder of tokens to burn * @param ids token IDs * @param amounts quantities of tokens to burn */ function _burnBatch( address account, uint256[] memory ids, uint256[] memory amounts ) internal virtual { if (account == address(0)) revert ERC1155Base__BurnFromZeroAddress(); if (ids.length != amounts.length) revert ERC1155Base__ArrayLengthMismatch(); _beforeTokenTransfer(msg.sender, account, address(0), ids, amounts, ''); mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; unchecked { for (uint256 i; i < ids.length; i++) { uint256 id = ids[i]; if (amounts[i] > balances[id][account]) revert ERC1155Base__BurnExceedsBalance(); balances[id][account] -= amounts[i]; } } emit TransferBatch(msg.sender, account, address(0), ids, amounts); } /** * @notice transfer tokens between given addresses * @dev ERC1155Receiver implementation is not checked * @param operator executor of transfer * @param sender sender of tokens * @param recipient receiver of tokens * @param id token ID * @param amount quantity of tokens to transfer * @param data data payload */ function _transfer( address operator, address sender, address recipient, uint256 id, uint256 amount, bytes memory data ) internal virtual { if (recipient == address(0)) revert ERC1155Base__TransferToZeroAddress(); _beforeTokenTransfer( operator, sender, recipient, _asSingletonArray(id), _asSingletonArray(amount), data ); mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; unchecked { uint256 senderBalance = balances[id][sender]; if (amount > senderBalance) revert ERC1155Base__TransferExceedsBalance(); balances[id][sender] = senderBalance - amount; } balances[id][recipient] += amount; emit TransferSingle(operator, sender, recipient, id, amount); } /** * @notice transfer tokens between given addresses * @param operator executor of transfer * @param sender sender of tokens * @param recipient receiver of tokens * @param id token ID * @param amount quantity of tokens to transfer * @param data data payload */ function _safeTransfer( address operator, address sender, address recipient, uint256 id, uint256 amount, bytes memory data ) internal virtual { _transfer(operator, sender, recipient, id, amount, data); _doSafeTransferAcceptanceCheck( operator, sender, recipient, id, amount, data ); } /** * @notice transfer batch of tokens between given addresses * @dev ERC1155Receiver implementation is not checked * @param operator executor of transfer * @param sender sender of tokens * @param recipient receiver of tokens * @param ids token IDs * @param amounts quantities of tokens to transfer * @param data data payload */ function _transferBatch( address operator, address sender, address recipient, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { if (recipient == address(0)) revert ERC1155Base__TransferToZeroAddress(); if (ids.length != amounts.length) revert ERC1155Base__ArrayLengthMismatch(); _beforeTokenTransfer(operator, sender, recipient, ids, amounts, data); mapping(uint256 => mapping(address => uint256)) storage balances = ERC1155BaseStorage.layout().balances; for (uint256 i; i < ids.length; ) { uint256 token = ids[i]; uint256 amount = amounts[i]; unchecked { uint256 senderBalance = balances[token][sender]; if (amount > senderBalance) revert ERC1155Base__TransferExceedsBalance(); balances[token][sender] = senderBalance - amount; i++; } // balance increase cannot be unchecked because ERC1155Base neither tracks nor validates a totalSupply balances[token][recipient] += amount; } emit TransferBatch(operator, sender, recipient, ids, amounts); } /** * @notice transfer batch of tokens between given addresses * @param operator executor of transfer * @param sender sender of tokens * @param recipient receiver of tokens * @param ids token IDs * @param amounts quantities of tokens to transfer * @param data data payload */ function _safeTransferBatch( address operator, address sender, address recipient, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { _transferBatch(operator, sender, recipient, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck( operator, sender, recipient, ids, amounts, data ); } /** * @notice wrap given element in array of length 1 * @param element element to wrap * @return singleton array */ function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { uint256[] memory array = new uint256[](1); array[0] = element; return array; } /** * @notice revert if applicable transfer recipient is not valid ERC1155Receiver * @param operator executor of transfer * @param from sender of tokens * @param to receiver of tokens * @param id token ID * @param amount quantity of tokens to transfer * @param data data payload */ function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 amount, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155Received( operator, from, id, amount, data ) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) revert ERC1155Base__ERC1155ReceiverRejected(); } catch Error(string memory reason) { revert(reason); } catch { revert ERC1155Base__ERC1155ReceiverNotImplemented(); } } } /** * @notice revert if applicable transfer recipient is not valid ERC1155Receiver * @param operator executor of transfer * @param from sender of tokens * @param to receiver of tokens * @param ids token IDs * @param amounts quantities of tokens to transfer * @param data data payload */ function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived( operator, from, ids, amounts, data ) returns (bytes4 response) { if ( response != IERC1155Receiver.onERC1155BatchReceived.selector ) revert ERC1155Base__ERC1155ReceiverRejected(); } catch Error(string memory reason) { revert(reason); } catch { revert ERC1155Base__ERC1155ReceiverNotImplemented(); } } } /** * @notice ERC1155 hook, called before all transfers including mint and burn * @dev function should be overridden and new implementation must call super * @dev called for both single and batch transfers * @param operator executor of transfer * @param from sender of tokens * @param to receiver of tokens * @param ids token IDs * @param amounts quantities of tokens to transfer * @param data data payload */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library ERC1155BaseStorage { struct Layout { mapping(uint256 => mapping(address => uint256)) balances; mapping(address => mapping(address => bool)) operatorApprovals; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC1155Base'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC1155Internal } from '../../../interfaces/IERC1155Internal.sol'; /** * @title ERC1155 base interface */ interface IERC1155BaseInternal is IERC1155Internal { error ERC1155Base__ArrayLengthMismatch(); error ERC1155Base__BalanceQueryZeroAddress(); error ERC1155Base__NotOwnerOrApproved(); error ERC1155Base__SelfApproval(); error ERC1155Base__BurnExceedsBalance(); error ERC1155Base__BurnFromZeroAddress(); error ERC1155Base__ERC1155ReceiverRejected(); error ERC1155Base__ERC1155ReceiverNotImplemented(); error ERC1155Base__MintToZeroAddress(); error ERC1155Base__TransferExceedsBalance(); error ERC1155Base__TransferToZeroAddress(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { EnumerableSet } from '../../../utils/EnumerableSet.sol'; import { ERC1155BaseInternal } from '../base/ERC1155BaseInternal.sol'; import { IERC1155Enumerable } from './IERC1155Enumerable.sol'; import { ERC1155EnumerableInternal, ERC1155EnumerableStorage } from './ERC1155EnumerableInternal.sol'; /** * @title ERC1155 implementation including enumerable and aggregate functions */ abstract contract ERC1155Enumerable is IERC1155Enumerable, ERC1155EnumerableInternal { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; /** * @inheritdoc IERC1155Enumerable */ function totalSupply(uint256 id) public view virtual returns (uint256) { return _totalSupply(id); } /** * @inheritdoc IERC1155Enumerable */ function totalHolders(uint256 id) public view virtual returns (uint256) { return _totalHolders(id); } /** * @inheritdoc IERC1155Enumerable */ function accountsByToken(uint256 id) public view virtual returns (address[] memory) { return _accountsByToken(id); } /** * @inheritdoc IERC1155Enumerable */ function tokensByAccount(address account) public view virtual returns (uint256[] memory) { return _tokensByAccount(account); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { EnumerableSet } from '../../../utils/EnumerableSet.sol'; import { ERC1155BaseInternal, ERC1155BaseStorage } from '../base/ERC1155BaseInternal.sol'; import { ERC1155EnumerableStorage } from './ERC1155EnumerableStorage.sol'; /** * @title ERC1155Enumerable internal functions */ abstract contract ERC1155EnumerableInternal is ERC1155BaseInternal { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; /** * @notice query total minted supply of given token * @param id token id to query * @return token supply */ function _totalSupply(uint256 id) internal view virtual returns (uint256) { return ERC1155EnumerableStorage.layout().totalSupply[id]; } /** * @notice query total number of holders for given token * @param id token id to query * @return quantity of holders */ function _totalHolders(uint256 id) internal view virtual returns (uint256) { return ERC1155EnumerableStorage.layout().accountsByToken[id].length(); } /** * @notice query holders of given token * @param id token id to query * @return list of holder addresses */ function _accountsByToken(uint256 id) internal view virtual returns (address[] memory) { EnumerableSet.AddressSet storage accounts = ERC1155EnumerableStorage .layout() .accountsByToken[id]; address[] memory addresses = new address[](accounts.length()); unchecked { for (uint256 i; i < accounts.length(); i++) { addresses[i] = accounts.at(i); } } return addresses; } /** * @notice query tokens held by given address * @param account address to query * @return list of token ids */ function _tokensByAccount(address account) internal view virtual returns (uint256[] memory) { EnumerableSet.UintSet storage tokens = ERC1155EnumerableStorage .layout() .tokensByAccount[account]; uint256[] memory ids = new uint256[](tokens.length()); unchecked { for (uint256 i; i < tokens.length(); i++) { ids[i] = tokens.at(i); } } return ids; } /** * @notice ERC1155 hook: update aggregate values * @inheritdoc ERC1155BaseInternal */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); if (from != to) { ERC1155EnumerableStorage.Layout storage l = ERC1155EnumerableStorage .layout(); mapping(uint256 => EnumerableSet.AddressSet) storage tokenAccounts = l.accountsByToken; EnumerableSet.UintSet storage fromTokens = l.tokensByAccount[from]; EnumerableSet.UintSet storage toTokens = l.tokensByAccount[to]; for (uint256 i; i < ids.length; ) { uint256 amount = amounts[i]; if (amount > 0) { uint256 id = ids[i]; if (from == address(0)) { l.totalSupply[id] += amount; } else if (_balanceOf(from, id) == amount) { tokenAccounts[id].remove(from); fromTokens.remove(id); } if (to == address(0)) { l.totalSupply[id] -= amount; } else if (_balanceOf(to, id) == 0) { tokenAccounts[id].add(to); toTokens.add(id); } } unchecked { i++; } } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { EnumerableSet } from '../../../utils/EnumerableSet.sol'; library ERC1155EnumerableStorage { struct Layout { mapping(uint256 => uint256) totalSupply; mapping(uint256 => EnumerableSet.AddressSet) accountsByToken; mapping(address => EnumerableSet.UintSet) tokensByAccount; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC1155Enumerable'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC1155BaseInternal } from '../base/IERC1155BaseInternal.sol'; /** * @title ERC1155 enumerable and aggregate function interface */ interface IERC1155Enumerable is IERC1155BaseInternal { /** * @notice query total minted supply of given token * @param id token id to query * @return token supply */ function totalSupply(uint256 id) external view returns (uint256); /** * @notice query total number of holders for given token * @param id token id to query * @return quantity of holders */ function totalHolders(uint256 id) external view returns (uint256); /** * @notice query holders of given token * @param id token id to query * @return list of holder addresses */ function accountsByToken(uint256 id) external view returns (address[] memory); /** * @notice query tokens held by given address * @param account address to query * @return list of token ids */ function tokensByAccount(address account) external view returns (uint256[] memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { IERC20BaseInternal } from './IERC20BaseInternal.sol'; /** * @title ERC20 base interface */ interface IERC20Base is IERC20BaseInternal, IERC20 { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Internal } from '../../../interfaces/IERC20Internal.sol'; /** * @title ERC20 base interface */ interface IERC20BaseInternal is IERC20Internal { error ERC20Base__ApproveFromZeroAddress(); error ERC20Base__ApproveToZeroAddress(); error ERC20Base__BurnExceedsBalance(); error ERC20Base__BurnFromZeroAddress(); error ERC20Base__InsufficientAllowance(); error ERC20Base__MintToZeroAddress(); error ERC20Base__TransferExceedsBalance(); error ERC20Base__TransferFromZeroAddress(); error ERC20Base__TransferToZeroAddress(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20ExtendedInternal } from './IERC20ExtendedInternal.sol'; /** * @title ERC20 extended interface */ interface IERC20Extended is IERC20ExtendedInternal { /** * @notice increase spend amount granted to spender * @param spender address whose allowance to increase * @param amount quantity by which to increase allowance * @return success status (always true; otherwise function will revert) */ function increaseAllowance(address spender, uint256 amount) external returns (bool); /** * @notice decrease spend amount granted to spender * @param spender address whose allowance to decrease * @param amount quantity by which to decrease allowance * @return success status (always true; otherwise function will revert) */ function decreaseAllowance(address spender, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20BaseInternal } from '../base/IERC20BaseInternal.sol'; /** * @title ERC20 extended internal interface */ interface IERC20ExtendedInternal is IERC20BaseInternal { error ERC20Extended__ExcessiveAllowance(); error ERC20Extended__InsufficientAllowance(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Base } from './base/IERC20Base.sol'; import { IERC20Extended } from './extended/IERC20Extended.sol'; import { IERC20Metadata } from './metadata/IERC20Metadata.sol'; import { IERC20Permit } from './permit/IERC20Permit.sol'; interface ISolidStateERC20 is IERC20Base, IERC20Extended, IERC20Metadata, IERC20Permit {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20MetadataInternal } from './IERC20MetadataInternal.sol'; /** * @title ERC20 metadata interface */ interface IERC20Metadata is IERC20MetadataInternal { /** * @notice return token name * @return token name */ function name() external view returns (string memory); /** * @notice return token symbol * @return token symbol */ function symbol() external view returns (string memory); /** * @notice return token decimals, generally used only for display purposes * @return token decimals */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title ERC20 metadata internal interface */ interface IERC20MetadataInternal { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Metadata } from '../metadata/IERC20Metadata.sol'; import { IERC2612 } from './IERC2612.sol'; import { IERC20PermitInternal } from './IERC20PermitInternal.sol'; // TODO: note that IERC20Metadata is needed for eth-permit library interface IERC20Permit is IERC20PermitInternal, IERC2612 { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC2612Internal } from './IERC2612Internal.sol'; interface IERC20PermitInternal is IERC2612Internal { error ERC20Permit__ExpiredDeadline(); error ERC20Permit__InvalidSignature(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC2612Internal } from './IERC2612Internal.sol'; /** * @title ERC2612 interface * @dev see https://eips.ethereum.org/EIPS/eip-2612. */ interface IERC2612 is IERC2612Internal { /** * @notice return the EIP-712 domain separator unique to contract and chain * @return domainSeparator domain separator */ function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator); /** * @notice get the current ERC2612 nonce for the given address * @return current nonce */ function nonces(address owner) external view returns (uint256); /** * @notice approve spender to transfer tokens held by owner via signature * @dev this function may be vulnerable to approval replay attacks * @param owner holder of tokens and signer of permit * @param spender beneficiary of approval * @param amount quantity of tokens to approve * @param v secp256k1 'v' value * @param r secp256k1 'r' value * @param s secp256k1 's' value */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; interface IERC2612Internal {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { UintUtils } from './UintUtils.sol'; library AddressUtils { using UintUtils for uint256; error AddressUtils__InsufficientBalance(); error AddressUtils__NotContract(); error AddressUtils__SendValueFailed(); function toString(address account) internal pure returns (string memory) { return uint256(uint160(account)).toHexString(20); } function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable account, uint256 amount) internal { (bool success, ) = account.call{ value: amount }(''); if (!success) revert AddressUtils__SendValueFailed(); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, 'AddressUtils: failed low-level call'); } function functionCall( address target, bytes memory data, string memory error ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, error); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, 'AddressUtils: failed low-level call with value' ); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) internal returns (bytes memory) { if (value > address(this).balance) revert AddressUtils__InsufficientBalance(); return _functionCallWithValue(target, data, value, error); } function _functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) private returns (bytes memory) { if (!isContract(target)) revert AddressUtils__NotContract(); (bool success, bytes memory returnData) = target.call{ value: value }( data ); if (success) { return returnData; } else if (returnData.length > 0) { assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(error); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Set implementation with enumeration functions * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license) */ library EnumerableSet { error EnumerableSet__IndexOutOfBounds(); struct Set { bytes32[] _values; // 1-indexed to allow 0 to signify nonexistence mapping(bytes32 => uint256) _indexes; } struct Bytes32Set { Set _inner; } struct AddressSet { Set _inner; } struct UintSet { Set _inner; } function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } function indexOf(Bytes32Set storage set, bytes32 value) internal view returns (uint256) { return _indexOf(set._inner, value); } function indexOf(AddressSet storage set, address value) internal view returns (uint256) { return _indexOf(set._inner, bytes32(uint256(uint160(value)))); } function indexOf(UintSet storage set, uint256 value) internal view returns (uint256) { return _indexOf(set._inner, bytes32(value)); } function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } function _at(Set storage set, uint256 index) private view returns (bytes32) { if (index >= set._values.length) revert EnumerableSet__IndexOutOfBounds(); return set._values[index]; } function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } function _indexOf(Set storage set, bytes32 value) private view returns (uint256) { unchecked { return set._indexes[value] - 1; } } function _length(Set storage set) private view returns (uint256) { return set._values.length; } function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); set._indexes[value] = set._values.length; return true; } else { return false; } } function _remove(Set storage set, bytes32 value) private returns (bool) { uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { unchecked { bytes32 last = set._values[set._values.length - 1]; // move last value to now-vacant index set._values[valueIndex - 1] = last; set._indexes[last] = valueIndex; } // clear last index set._values.pop(); delete set._indexes[value]; return true; } else { return false; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../interfaces/IERC20.sol'; import { AddressUtils } from './AddressUtils.sol'; /** * @title Safe ERC20 interaction library * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library SafeERC20 { using AddressUtils for address; error SafeERC20__ApproveFromNonZeroToNonZero(); error SafeERC20__DecreaseAllowanceBelowZero(); error SafeERC20__OperationFailed(); function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev safeApprove (like approve) should only be called when setting an initial allowance or when resetting it to zero; otherwise prefer safeIncreaseAllowance and safeDecreaseAllowance */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { if ((value != 0) && (token.allowance(address(this), spender) != 0)) revert SafeERC20__ApproveFromNonZeroToNonZero(); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); if (oldAllowance < value) revert SafeERC20__DecreaseAllowanceBelowZero(); uint256 newAllowance = oldAllowance - value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } } /** * @notice send transaction data and check validity of return value, if present * @param token ERC20 token interface * @param data transaction data */ function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, 'SafeERC20: low-level call failed' ); if (returndata.length > 0) { if (!abi.decode(returndata, (bool))) revert SafeERC20__OperationFailed(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title utility functions for uint256 operations * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library UintUtils { error UintUtils__InsufficientHexLength(); bytes16 private constant HEX_SYMBOLS = '0123456789abcdef'; function toString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0'; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0x00'; } uint256 length = 0; for (uint256 temp = value; temp != 0; temp >>= 8) { unchecked { length++; } } return toHexString(value, length); } function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = '0'; buffer[1] = 'x'; unchecked { for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_SYMBOLS[value & 0xf]; value >>= 4; } } if (value != 0) revert UintUtils__InsufficientHexLength(); return string(buffer); } }
// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity ^0.8.0; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt (int256 x) internal pure returns (int128) { unchecked { require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); return int128 (x << 64); } } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt (int128 x) internal pure returns (int64) { unchecked { return int64 (x >> 64); } } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt (uint256 x) internal pure returns (int128) { unchecked { require (x <= 0x7FFFFFFFFFFFFFFF); return int128 (int256 (x << 64)); } } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt (int128 x) internal pure returns (uint64) { unchecked { require (x >= 0); return uint64 (uint128 (x >> 64)); } } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128 (int256 x) internal pure returns (int128) { unchecked { int256 result = x >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128 (int128 x) internal pure returns (int256) { unchecked { return int256 (x) << 64; } } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) + y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) - y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) * y >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli (int128 x, int256 y) internal pure returns (int256) { unchecked { if (x == MIN_64x64) { require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu (x, uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); return -int256 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int256 (absoluteResult); } } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu (int128 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) return 0; require (x >= 0); uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256 (int256 (x)) * (y >> 128); require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); hi <<= 64; require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); return hi + lo; } } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div (int128 x, int128 y) internal pure returns (int128) { unchecked { require (y != 0); int256 result = (int256 (x) << 64) / y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi (int256 x, int256 y) internal pure returns (int128) { unchecked { require (y != 0); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x80000000000000000000000000000000); return -int128 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128 (absoluteResult); // We rely on overflow behavior here } } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu (uint256 x, uint256 y) internal pure returns (int128) { unchecked { require (y != 0); uint128 result = divuu (x, y); require (result <= uint128 (MAX_64x64)); return int128 (result); } } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return -x; } } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return x < 0 ? -x : x; } } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv (int128 x) internal pure returns (int128) { unchecked { require (x != 0); int256 result = int256 (0x100000000000000000000000000000000) / x; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg (int128 x, int128 y) internal pure returns (int128) { unchecked { return int128 ((int256 (x) + int256 (y)) >> 1); } } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 m = int256 (x) * int256 (y); require (m >= 0); require (m < 0x4000000000000000000000000000000000000000000000000000000000000000); return int128 (sqrtu (uint256 (m))); } } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow (int128 x, uint256 y) internal pure returns (int128) { unchecked { bool negative = x < 0 && y & 1 == 1; uint256 absX = uint128 (x < 0 ? -x : x); uint256 absResult; absResult = 0x100000000000000000000000000000000; if (absX <= 0x10000000000000000) { absX <<= 63; while (y != 0) { if (y & 0x1 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x2 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x4 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x8 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; y >>= 4; } absResult >>= 64; } else { uint256 absXShift = 63; if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } uint256 resultShift = 0; while (y != 0) { require (absXShift < 64); if (y & 0x1 != 0) { absResult = absResult * absX >> 127; resultShift += absXShift; if (absResult > 0x100000000000000000000000000000000) { absResult >>= 1; resultShift += 1; } } absX = absX * absX >> 127; absXShift <<= 1; if (absX >= 0x100000000000000000000000000000000) { absX >>= 1; absXShift += 1; } y >>= 1; } require (resultShift < 64); absResult >>= 64 - resultShift; } int256 result = negative ? -int256 (absResult) : int256 (absResult); require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt (int128 x) internal pure returns (int128) { unchecked { require (x >= 0); return int128 (sqrtu (uint256 (int256 (x)) << 64)); } } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2 (int128 x) internal pure returns (int128) { unchecked { require (x > 0); int256 msb = 0; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = msb - 64 << 64; uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256 (b); } return int128 (result); } } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln (int128 x) internal pure returns (int128) { unchecked { require (x > 0); return int128 (int256 ( uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); } } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2 (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; if (x & 0x4000000000000000 > 0) result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; if (x & 0x2000000000000000 > 0) result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; if (x & 0x1000000000000000 > 0) result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; if (x & 0x800000000000000 > 0) result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; if (x & 0x400000000000000 > 0) result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; if (x & 0x200000000000000 > 0) result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; if (x & 0x100000000000000 > 0) result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; if (x & 0x80000000000000 > 0) result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; if (x & 0x40000000000000 > 0) result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; if (x & 0x20000000000000 > 0) result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; if (x & 0x10000000000000 > 0) result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; if (x & 0x8000000000000 > 0) result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; if (x & 0x4000000000000 > 0) result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; if (x & 0x2000000000000 > 0) result = result * 0x1000162E525EE054754457D5995292026 >> 128; if (x & 0x1000000000000 > 0) result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; if (x & 0x800000000000 > 0) result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; if (x & 0x400000000000 > 0) result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; if (x & 0x200000000000 > 0) result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; if (x & 0x100000000000 > 0) result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; if (x & 0x80000000000 > 0) result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; if (x & 0x40000000000 > 0) result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; if (x & 0x20000000000 > 0) result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; if (x & 0x10000000000 > 0) result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; if (x & 0x8000000000 > 0) result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; if (x & 0x4000000000 > 0) result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; if (x & 0x2000000000 > 0) result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; if (x & 0x1000000000 > 0) result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; if (x & 0x800000000 > 0) result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; if (x & 0x400000000 > 0) result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; if (x & 0x200000000 > 0) result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; if (x & 0x100000000 > 0) result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; if (x & 0x80000000 > 0) result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; if (x & 0x40000000 > 0) result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; if (x & 0x20000000 > 0) result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; if (x & 0x10000000 > 0) result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; if (x & 0x8000000 > 0) result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; if (x & 0x4000000 > 0) result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; if (x & 0x2000000 > 0) result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; if (x & 0x1000000 > 0) result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; if (x & 0x800000 > 0) result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; if (x & 0x400000 > 0) result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; if (x & 0x200000 > 0) result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; if (x & 0x100000 > 0) result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; if (x & 0x80000 > 0) result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; if (x & 0x40000 > 0) result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; if (x & 0x20000 > 0) result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; if (x & 0x10000 > 0) result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; if (x & 0x8000 > 0) result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; if (x & 0x4000 > 0) result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; if (x & 0x2000 > 0) result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; if (x & 0x1000 > 0) result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; if (x & 0x800 > 0) result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; if (x & 0x400 > 0) result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; if (x & 0x200 > 0) result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; if (x & 0x100 > 0) result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; if (x & 0x80 > 0) result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; if (x & 0x40 > 0) result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; if (x & 0x20 > 0) result = result * 0x100000000000000162E42FEFA39EF366F >> 128; if (x & 0x10 > 0) result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; if (x & 0x8 > 0) result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; if (x & 0x4 > 0) result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; if (x & 0x2 > 0) result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; if (x & 0x1 > 0) result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; result >>= uint256 (int256 (63 - (x >> 64))); require (result <= uint256 (int256 (MAX_64x64))); return int128 (int256 (result)); } } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2 ( int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu (uint256 x, uint256 y) private pure returns (uint128) { unchecked { require (y != 0); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here assert (xh == hi >> 128); result += xl / y; } require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return uint128 (result); } } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu (uint256 x) private pure returns (uint128) { unchecked { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x4) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128 (r < r1 ? r : r1); } } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; /** * @title Premia Exchange Helper * @dev deployed standalone and referenced by internal functions * @dev do NOT set approval to this contract! */ interface IExchangeHelper { /** * @notice perform arbitrary swap transaction * @param sourceToken source token to pull into this address * @param targetToken target token to buy * @param sourceTokenAmount amount of source token to start the trade * @param callee exchange address to call to execute the trade. * @param allowanceTarget address for which to set allowance for the trade * @param data calldata to execute the trade * @param refundAddress address that un-used source token goes to * @return amountOut quantity of targetToken yielded by swap */ function swapWithToken( address sourceToken, address targetToken, uint256 sourceTokenAmount, address callee, address allowanceTarget, bytes calldata data, address refundAddress ) external returns (uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IOFTCore} from "./IOFTCore.sol"; import {ISolidStateERC20} from "@solidstate/contracts/token/ERC20/ISolidStateERC20.sol"; /** * @dev Interface of the OFT standard */ interface IOFT is IOFTCore, ISolidStateERC20 { error OFT_InsufficientAllowance(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC20} from "@solidstate/contracts/interfaces/IERC20.sol"; import {IERC165} from "@solidstate/contracts/interfaces/IERC165.sol"; /** * @dev Interface of the IOFT core standard */ interface IOFTCore is IERC165 { /** * @dev estimate send token `tokenId` to (`dstChainId`, `toAddress`) * dstChainId - L0 defined chain id to send tokens too * toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain * amount - amount of the tokens to transfer * useZro - indicates to use zro to pay L0 fees * adapterParam - flexible bytes array to indicate messaging adapter services in L0 */ function estimateSendFee( uint16 dstChainId, bytes calldata toAddress, uint256 amount, bool useZro, bytes calldata adapterParams ) external view returns (uint256 nativeFee, uint256 zroFee); /** * @dev send `amount` amount of token to (`dstChainId`, `toAddress`) from `from` * `from` the owner of token * `dstChainId` the destination chain identifier * `toAddress` can be any size depending on the `dstChainId`. * `amount` the quantity of tokens in wei * `refundAddress` the address LayerZero refunds if too much message fee is sent * `zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token) * `adapterParams` is a flexible bytes array to indicate messaging adapter services */ function sendFrom( address from, uint16 dstChainId, bytes calldata toAddress, uint256 amount, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams ) external payable; /** * @dev returns the circulating amount of tokens on current chain */ function circulatingSupply() external view returns (uint256); /** * @dev Emitted when `amount` tokens are moved from the `sender` to (`dstChainId`, `toAddress`) * `nonce` is the outbound nonce */ event SendToChain( address indexed sender, uint16 indexed dstChainId, bytes indexed toAddress, uint256 amount ); /** * @dev Emitted when `amount` tokens are received from `srcChainId` into the `toAddress` on the local chain. * `nonce` is the inbound nonce. */ event ReceiveFromChain( uint16 indexed srcChainId, bytes indexed srcAddress, address indexed toAddress, uint256 amount ); event SetUseCustomAdapterParams(bool _useCustomAdapterParams); }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol"; library OptionMath { using ABDKMath64x64 for int128; struct QuoteArgs { int128 varianceAnnualized64x64; // 64x64 fixed point representation of annualized variance int128 strike64x64; // 64x64 fixed point representation of strike price int128 spot64x64; // 64x64 fixed point representation of spot price int128 timeToMaturity64x64; // 64x64 fixed point representation of duration of option contract (in years) int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level of Pool before purchase int128 oldPoolState; // 64x64 fixed point representation of current state of the pool int128 newPoolState; // 64x64 fixed point representation of state of the pool after trade int128 steepness64x64; // 64x64 fixed point representation of Pool state delta multiplier int128 minAPY64x64; // 64x64 fixed point representation of minimum APY for capital locked up to underwrite options bool isCall; // whether to price "call" or "put" option } struct CalculateCLevelDecayArgs { int128 timeIntervalsElapsed64x64; // 64x64 fixed point representation of quantity of discrete arbitrary intervals elapsed since last update int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level prior to accounting for decay int128 utilization64x64; // 64x64 fixed point representation of pool capital utilization rate int128 utilizationLowerBound64x64; int128 utilizationUpperBound64x64; int128 cLevelLowerBound64x64; int128 cLevelUpperBound64x64; int128 cConvergenceULowerBound64x64; int128 cConvergenceUUpperBound64x64; } // 64x64 fixed point integer constants int128 internal constant ONE_64x64 = 0x10000000000000000; int128 internal constant THREE_64x64 = 0x30000000000000000; // 64x64 fixed point constants used in Choudhury’s approximation of the Black-Scholes CDF int128 private constant CDF_CONST_0 = 0x09109f285df452394; // 2260 / 3989 int128 private constant CDF_CONST_1 = 0x19abac0ea1da65036; // 6400 / 3989 int128 private constant CDF_CONST_2 = 0x0d3c84b78b749bd6b; // 3300 / 3989 /** * @notice recalculate C-Level based on change in liquidity * @param initialCLevel64x64 64x64 fixed point representation of C-Level of Pool before update * @param oldPoolState64x64 64x64 fixed point representation of liquidity in pool before update * @param newPoolState64x64 64x64 fixed point representation of liquidity in pool after update * @param steepness64x64 64x64 fixed point representation of steepness coefficient * @return 64x64 fixed point representation of new C-Level */ function calculateCLevel( int128 initialCLevel64x64, int128 oldPoolState64x64, int128 newPoolState64x64, int128 steepness64x64 ) external pure returns (int128) { return newPoolState64x64 .sub(oldPoolState64x64) .div( oldPoolState64x64 > newPoolState64x64 ? oldPoolState64x64 : newPoolState64x64 ) .mul(steepness64x64) .neg() .exp() .mul(initialCLevel64x64); } /** * @notice calculate the price of an option using the Premia Finance model * @param args arguments of quotePrice * @return premiaPrice64x64 64x64 fixed point representation of Premia option price * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after purchase */ function quotePrice(QuoteArgs memory args) external pure returns ( int128 premiaPrice64x64, int128 cLevel64x64, int128 slippageCoefficient64x64 ) { int128 deltaPoolState64x64 = args .newPoolState .sub(args.oldPoolState) .div(args.oldPoolState) .mul(args.steepness64x64); int128 tradingDelta64x64 = deltaPoolState64x64.neg().exp(); int128 blackScholesPrice64x64 = _blackScholesPrice( args.varianceAnnualized64x64, args.strike64x64, args.spot64x64, args.timeToMaturity64x64, args.isCall ); cLevel64x64 = tradingDelta64x64.mul(args.oldCLevel64x64); slippageCoefficient64x64 = ONE_64x64.sub(tradingDelta64x64).div( deltaPoolState64x64 ); premiaPrice64x64 = blackScholesPrice64x64.mul(cLevel64x64).mul( slippageCoefficient64x64 ); int128 intrinsicValue64x64; if (args.isCall && args.strike64x64 < args.spot64x64) { intrinsicValue64x64 = args.spot64x64.sub(args.strike64x64); } else if (!args.isCall && args.strike64x64 > args.spot64x64) { intrinsicValue64x64 = args.strike64x64.sub(args.spot64x64); } int128 collateralValue64x64 = args.isCall ? args.spot64x64 : args.strike64x64; int128 minPrice64x64 = intrinsicValue64x64.add( collateralValue64x64.mul(args.minAPY64x64).mul( args.timeToMaturity64x64 ) ); if (minPrice64x64 > premiaPrice64x64) { premiaPrice64x64 = minPrice64x64; } } /** * @notice calculate the decay of C-Level based on heat diffusion function * @param args structured CalculateCLevelDecayArgs * @return cLevelDecayed64x64 C-Level after accounting for decay */ function calculateCLevelDecay(CalculateCLevelDecayArgs memory args) external pure returns (int128 cLevelDecayed64x64) { int128 convFHighU64x64 = (args.utilization64x64 >= args.utilizationUpperBound64x64 && args.oldCLevel64x64 <= args.cLevelLowerBound64x64) ? ONE_64x64 : int128(0); int128 convFLowU64x64 = (args.utilization64x64 <= args.utilizationLowerBound64x64 && args.oldCLevel64x64 >= args.cLevelUpperBound64x64) ? ONE_64x64 : int128(0); cLevelDecayed64x64 = args .oldCLevel64x64 .sub(args.cConvergenceULowerBound64x64.mul(convFLowU64x64)) .sub(args.cConvergenceUUpperBound64x64.mul(convFHighU64x64)) .mul( convFLowU64x64 .mul(ONE_64x64.sub(args.utilization64x64)) .add(convFHighU64x64.mul(args.utilization64x64)) .mul(args.timeIntervalsElapsed64x64) .neg() .exp() ) .add( args.cConvergenceULowerBound64x64.mul(convFLowU64x64).add( args.cConvergenceUUpperBound64x64.mul(convFHighU64x64) ) ); } /** * @notice calculate the exponential decay coefficient for a given interval * @param oldTimestamp timestamp of previous update * @param newTimestamp current timestamp * @return 64x64 fixed point representation of exponential decay coefficient */ function _decay(uint256 oldTimestamp, uint256 newTimestamp) internal pure returns (int128) { return ONE_64x64.sub( (-ABDKMath64x64.divu(newTimestamp - oldTimestamp, 7 days)).exp() ); } /** * @notice calculate Choudhury’s approximation of the Black-Scholes CDF * @param input64x64 64x64 fixed point representation of random variable * @return 64x64 fixed point representation of the approximated CDF of x */ function _N(int128 input64x64) internal pure returns (int128) { // squaring via mul is cheaper than via pow int128 inputSquared64x64 = input64x64.mul(input64x64); int128 value64x64 = (-inputSquared64x64 >> 1).exp().div( CDF_CONST_0.add(CDF_CONST_1.mul(input64x64.abs())).add( CDF_CONST_2.mul(inputSquared64x64.add(THREE_64x64).sqrt()) ) ); return input64x64 > 0 ? ONE_64x64.sub(value64x64) : value64x64; } /** * @notice calculate the price of an option using the Black-Scholes model * @param varianceAnnualized64x64 64x64 fixed point representation of annualized variance * @param strike64x64 64x64 fixed point representation of strike price * @param spot64x64 64x64 fixed point representation of spot price * @param timeToMaturity64x64 64x64 fixed point representation of duration of option contract (in years) * @param isCall whether to price "call" or "put" option * @return 64x64 fixed point representation of Black-Scholes option price */ function _blackScholesPrice( int128 varianceAnnualized64x64, int128 strike64x64, int128 spot64x64, int128 timeToMaturity64x64, bool isCall ) internal pure returns (int128) { int128 cumulativeVariance64x64 = timeToMaturity64x64.mul( varianceAnnualized64x64 ); int128 cumulativeVarianceSqrt64x64 = cumulativeVariance64x64.sqrt(); int128 d1_64x64 = spot64x64 .div(strike64x64) .ln() .add(cumulativeVariance64x64 >> 1) .div(cumulativeVarianceSqrt64x64); int128 d2_64x64 = d1_64x64.sub(cumulativeVarianceSqrt64x64); if (isCall) { return spot64x64.mul(_N(d1_64x64)).sub(strike64x64.mul(_N(d2_64x64))); } else { return -spot64x64.mul(_N(-d1_64x64)).sub( strike64x64.mul(_N(-d2_64x64)) ); } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {PremiaMiningStorage} from "./PremiaMiningStorage.sol"; interface IPremiaMining { struct PoolAllocPoints { address pool; bool isCallPool; uint256 votes; uint256 poolUtilizationRateBPS; // 100% = 1e4 } event Claim( address indexed user, address indexed pool, bool indexed isCallPool, uint256 rewardAmount ); event UpdatePoolAlloc( address indexed pool, bool indexed isCallPool, uint256 votes, uint256 poolUtilizationRateBPS ); function addPremiaRewards(uint256 _amount) external; function premiaRewardsAvailable() external view returns (uint256); function getTotalAllocationPoints() external view returns (uint256); function getPoolInfo(address pool, bool isCallPool) external view returns (PremiaMiningStorage.PoolInfo memory); function getPremiaPerYear() external view returns (uint256); function pendingPremia( address _pool, bool _isCallPool, address _user ) external view returns (uint256); function updatePool( address _pool, bool _isCallPool, uint256 _totalTVL, uint256 _utilizationRate ) external; function allocatePending( address _user, address _pool, bool _isCallPool, uint256 _userTVLOld, uint256 _userTVLNew, uint256 _totalTVL, uint256 _utilizationRate ) external; function claim( address _user, address _pool, bool _isCallPool, uint256 _userTVLOld, uint256 _userTVLNew, uint256 _totalTVL, uint256 _utilizationRate ) external; }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; library PremiaMiningStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.storage.PremiaMining"); // Info of each pool. struct PoolInfo { uint256 allocPoint; // How many allocation points assigned to this pool. PREMIA to distribute per block. uint256 lastRewardTimestamp; // Last timestamp that PREMIA distribution occurs uint256 accPremiaPerShare; // Accumulated PREMIA per share, times 1e12. See below. } // Info of each user. struct UserInfo { uint256 reward; // Total allocated unclaimed reward uint256 rewardDebt; // Reward debt. See explanation below. // // We do some fancy math here. Basically, any point in time, the amount of PREMIA // entitled to a user but is pending to be distributed is: // // pending reward = (user.amount * pool.accPremiaPerShare) - user.rewardDebt // // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: // 1. The pool's `accPremiaPerShare` (and `lastRewardBlock`) gets updated. // 2. User receives the pending reward sent to his/her address. // 3. User's `amount` gets updated. // 4. User's `rewardDebt` gets updated. } struct Layout { // Total PREMIA left to distribute uint256 premiaAvailable; // Amount of premia distributed per year uint256 premiaPerYear; // pool -> isCallPool -> PoolInfo mapping(address => mapping(bool => PoolInfo)) poolInfo; // pool -> isCallPool -> user -> UserInfo mapping(address => mapping(bool => mapping(address => UserInfo))) userInfo; // Total allocation points. Must be the sum of all allocation points in all pools. uint256 totalAllocPoint; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {VolatilitySurfaceOracleStorage} from "./VolatilitySurfaceOracleStorage.sol"; interface IVolatilitySurfaceOracle { /** * @notice Pack IV model parameters into a single bytes32 * @dev This function is used to pack the parameters into a single variable, which is then used as input in `update` * @param params Parameters of IV model to pack * @return result The packed parameters of IV model */ function formatParams(int256[5] memory params) external pure returns (bytes32 result); /** * @notice Unpack IV model parameters from a bytes32 * @param input Packed IV model parameters to unpack * @return params The unpacked parameters of the IV model */ function parseParams(bytes32 input) external pure returns (int256[] memory params); /** * @notice Get the list of whitelisted relayers * @return The list of whitelisted relayers */ function getWhitelistedRelayers() external view returns (address[] memory); /** * @notice Get the IV model parameters of a token pair * @param base The base token of the pair * @param underlying The underlying token of the pair * @return The IV model parameters */ function getParams(address base, address underlying) external view returns (VolatilitySurfaceOracleStorage.Update memory); /** * @notice Get unpacked IV model parameters * @param base The base token of the pair * @param underlying The underlying token of the pair * @return The unpacked IV model parameters */ function getParamsUnpacked(address base, address underlying) external view returns (int256[] memory); /** * @notice Get time to maturity in years, as a 64x64 fixed point representation * @param maturity Maturity timestamp * @return Time to maturity (in years), as a 64x64 fixed point representation */ function getTimeToMaturity64x64(uint64 maturity) external view returns (int128); /** * @notice calculate the annualized volatility for given set of parameters * @param base The base token of the pair * @param underlying The underlying token of the pair * @param spot64x64 64x64 fixed point representation of spot price * @param strike64x64 64x64 fixed point representation of strike price * @param timeToMaturity64x64 64x64 fixed point representation of time to maturity (denominated in years) * @return 64x64 fixed point representation of annualized implied volatility, where 1 is defined as 100% */ function getAnnualizedVolatility64x64( address base, address underlying, int128 spot64x64, int128 strike64x64, int128 timeToMaturity64x64 ) external view returns (int128); /** * @notice calculate the price of an option using the Black-Scholes model * @param base The base token of the pair * @param underlying The underlying token of the pair * @param spot64x64 Spot price, as a 64x64 fixed point representation * @param strike64x64 Strike, as a64x64 fixed point representation * @param timeToMaturity64x64 64x64 fixed point representation of time to maturity (denominated in years) * @param isCall Whether it is for call or put * @return 64x64 fixed point representation of the Black Scholes price */ function getBlackScholesPrice64x64( address base, address underlying, int128 spot64x64, int128 strike64x64, int128 timeToMaturity64x64, bool isCall ) external view returns (int128); /** * @notice Get Black Scholes price as an uint256 with 18 decimals * @param base The base token of the pair * @param underlying The underlying token of the pair * @param spot64x64 Spot price, as a 64x64 fixed point representation * @param strike64x64 Strike, as a64x64 fixed point representation * @param timeToMaturity64x64 64x64 fixed point representation of time to maturity (denominated in years) * @param isCall Whether it is for call or put * @return Black scholes price, as an uint256 with 18 decimals */ function getBlackScholesPrice( address base, address underlying, int128 spot64x64, int128 strike64x64, int128 timeToMaturity64x64, bool isCall ) external view returns (uint256); /** * @notice Add relayers to the whitelist so that they can add oracle surfaces * @param accounts The addresses to add to the whitelist */ function addWhitelistedRelayers(address[] memory accounts) external; /** * @notice Remove relayers from the whitelist so that they cannot add oracle surfaces * @param accounts The addresses to remove from the whitelist */ function removeWhitelistedRelayers(address[] memory accounts) external; /** * @notice Update a list of IV model parameters * @param base List of base tokens * @param underlying List of underlying tokens * @param parameters List of IV model parameters */ function updateParams( address[] memory base, address[] memory underlying, bytes32[] memory parameters ) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import {EnumerableSet} from "@solidstate/contracts/utils/EnumerableSet.sol"; library VolatilitySurfaceOracleStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.storage.VolatilitySurfaceOracle"); uint256 internal constant PARAM_BITS = 51; uint256 internal constant PARAM_BITS_MINUS_ONE = 50; uint256 internal constant PARAM_AMOUNT = 5; // START_BIT = PARAM_BITS * (PARAM_AMOUNT - 1) uint256 internal constant START_BIT = 204; struct Update { uint256 updatedAt; bytes32 params; } struct Layout { // Base token -> Underlying token -> Update mapping(address => mapping(address => Update)) parameters; // Relayer addresses which can be trusted to provide accurate option trades EnumerableSet.AddressSet whitelistedRelayers; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function getParams( Layout storage l, address base, address underlying ) internal view returns (bytes32) { return l.parameters[base][underlying].params; } function parseParams(bytes32 input) internal pure returns (int256[] memory params) { params = new int256[](PARAM_AMOUNT); // Value to add to negative numbers to cast them to int256 int256 toAdd = (int256(-1) >> PARAM_BITS) << PARAM_BITS; assembly { let i := 0 // Value equal to -1 let mid := shl(PARAM_BITS_MINUS_ONE, 1) for { } lt(i, PARAM_AMOUNT) { } { let offset := sub(START_BIT, mul(PARAM_BITS, i)) let param := shr( offset, sub( input, shl( add(offset, PARAM_BITS), shr(add(offset, PARAM_BITS), input) ) ) ) // Check if value is a negative number and needs casting if or(eq(param, mid), gt(param, mid)) { param := add(param, toAdd) } // Store result in the params array mstore(add(params, add(0x20, mul(0x20, i))), param) i := add(i, 1) } } } function formatParams(int256[5] memory params) internal pure returns (bytes32 result) { int256 max = int256(1 << PARAM_BITS_MINUS_ONE); unchecked { for (uint256 i = 0; i < PARAM_AMOUNT; i++) { require(params[i] < max && params[i] > -max, "Out of bounds"); } } assembly { let i := 0 for { } lt(i, PARAM_AMOUNT) { } { let offset := sub(START_BIT, mul(PARAM_BITS, i)) let param := mload(add(params, mul(0x20, i))) result := add( result, shl( offset, sub(param, shl(PARAM_BITS, shr(PARAM_BITS, param))) ) ) i := add(i, 1) } } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; interface IPoolEvents { event Purchase( address indexed user, uint256 longTokenId, uint256 contractSize, uint256 baseCost, uint256 feeCost, int128 spot64x64 ); event Sell( address indexed user, uint256 longTokenId, uint256 contractSize, uint256 baseCost, uint256 feeCost, int128 spot64x64 ); event Exercise( address indexed user, uint256 longTokenId, uint256 contractSize, uint256 exerciseValue, uint256 fee ); event Underwrite( address indexed underwriter, address indexed longReceiver, uint256 shortTokenId, uint256 intervalContractSize, uint256 intervalPremium, bool isManualUnderwrite ); event AssignExercise( address indexed underwriter, uint256 shortTokenId, uint256 freedAmount, uint256 intervalContractSize, uint256 fee ); event AssignSale( address indexed underwriter, uint256 shortTokenId, uint256 freedAmount, uint256 intervalContractSize ); event Deposit(address indexed user, bool isCallPool, uint256 amount); event Withdrawal( address indexed user, bool isCallPool, uint256 depositedAt, uint256 amount ); event FeeWithdrawal(bool indexed isCallPool, uint256 amount); event APYFeeReserved( address underwriter, uint256 shortTokenId, uint256 amount ); event APYFeePaid(address underwriter, uint256 shortTokenId, uint256 amount); event Annihilate(uint256 shortTokenId, uint256 amount); event UpdateCLevel( bool indexed isCall, int128 cLevel64x64, int128 oldLiquidity64x64, int128 newLiquidity64x64 ); event UpdateSteepness(int128 steepness64x64, bool isCallPool); event UpdateSpotOffset(int128 spotOffset64x64); }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; interface IPoolInternal { struct SwapArgs { // token to pass in to swap address tokenIn; // amount of tokenIn to trade uint256 amountInMax; //min amount out to be used to purchase uint256 amountOutMin; // exchange address to call to execute the trade address callee; // address for which to set allowance for the trade address allowanceTarget; // data to execute the trade bytes data; // address to which refund excess tokens address refundAddress; } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {IPoolInternal} from "./IPoolInternal.sol"; /** * @notice Pool interface for LP position and platform fee management functions */ interface IPoolIO { /** * @notice set timestamp after which reinvestment is disabled * @param timestamp timestamp to begin divestment * @param isCallPool whether we set divestment timestamp for the call pool or put pool */ function setDivestmentTimestamp(uint64 timestamp, bool isCallPool) external; /** * @notice deposit underlying currency, underwriting calls of that currency with respect to base currency * @param amount quantity of underlying currency to deposit * @param isCallPool whether to deposit underlying in the call pool or base in the put pool */ function deposit(uint256 amount, bool isCallPool) external payable; /** * @notice swap any token to collateral asset through exchange proxy and deposit * @dev any attached msg.value will be wrapped. * if tokenIn is wrappedNativeToken, both msg.value and {amountInMax} amount of wrappedNativeToken will be used * @param s swap arguments * @param isCallPool whether to deposit underlying in the call pool or base in the put pool */ function swapAndDeposit(IPoolInternal.SwapArgs memory s, bool isCallPool) external payable; /** * @notice redeem pool share tokens for underlying asset * @param amount quantity of share tokens to redeem * @param isCallPool whether to deposit underlying in the call pool or base in the put pool */ function withdraw(uint256 amount, bool isCallPool) external; /** * @notice reassign short position to new underwriter * @param tokenId ERC1155 token id (long or short) * @param contractSize quantity of option contract tokens to reassign * @param divest whether to withdraw freed funds after reassignment * @return baseCost quantity of tokens required to reassign short position * @return feeCost quantity of tokens required to pay fees * @return amountOut quantity of liquidity freed and transferred to owner */ function reassign( uint256 tokenId, uint256 contractSize, bool divest ) external returns ( uint256 baseCost, uint256 feeCost, uint256 amountOut ); /** * @notice reassign set of short position to new underwriter * @param tokenIds array of ERC1155 token ids (long or short) * @param contractSizes array of quantities of option contract tokens to reassign * @param divest whether to withdraw freed funds after reassignment * @return baseCosts quantities of tokens required to reassign each short position * @return feeCosts quantities of tokens required to pay fees * @return amountOutCall quantity of call pool liquidity freed and transferred to owner * @return amountOutPut quantity of put pool liquidity freed and transferred to owner */ function reassignBatch( uint256[] calldata tokenIds, uint256[] calldata contractSizes, bool divest ) external returns ( uint256[] memory baseCosts, uint256[] memory feeCosts, uint256 amountOutCall, uint256 amountOutPut ); /** * @notice transfer accumulated fees to the fee receiver * @return amountOutCall quantity of underlying tokens transferred * @return amountOutPut quantity of base tokens transferred */ function withdrawFees() external returns (uint256 amountOutCall, uint256 amountOutPut); /** * @notice burn corresponding long and short option tokens and withdraw collateral * @param tokenId ERC1155 token id (long or short) * @param contractSize quantity of option contract tokens to annihilate * @param divest whether to withdraw freed funds after annihilation */ function annihilate( uint256 tokenId, uint256 contractSize, bool divest ) external; /** * @notice claim earned PREMIA emissions * @param isCallPool true for call, false for put */ function claimRewards(bool isCallPool) external; /** * @notice claim earned PREMIA emissions on behalf of given account * @param account account on whose behalf to claim rewards * @param isCallPool true for call, false for put */ function claimRewards(address account, bool isCallPool) external; /** * @notice TODO */ function updateMiningPools() external; }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {ABDKMath64x64Token} from "@solidstate/abdk-math-extensions/contracts/ABDKMath64x64Token.sol"; import {IERC173} from "@solidstate/contracts/interfaces/IERC173.sol"; import {OwnableStorage} from "@solidstate/contracts/access/ownable/OwnableStorage.sol"; import {SafeERC20} from "@solidstate/contracts/utils/SafeERC20.sol"; import {IERC20} from "@solidstate/contracts/interfaces/IERC20.sol"; import {ERC1155EnumerableInternal, ERC1155EnumerableStorage, EnumerableSet} from "@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol"; import {IWETH} from "@solidstate/contracts/interfaces/IWETH.sol"; import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol"; import {IExchangeHelper} from "../interfaces/IExchangeHelper.sol"; import {OptionMath} from "../libraries/OptionMath.sol"; import {IPremiaMining} from "../mining/IPremiaMining.sol"; import {IVolatilitySurfaceOracle} from "../oracle/IVolatilitySurfaceOracle.sol"; import {IPremiaStaking} from "../staking/IPremiaStaking.sol"; import {IPoolEvents} from "./IPoolEvents.sol"; import {IPoolInternal} from "./IPoolInternal.sol"; import {PoolStorage} from "./PoolStorage.sol"; /** * @title Premia option pool * @dev deployed standalone and referenced by PoolProxy */ contract PoolInternal is IPoolInternal, IPoolEvents, ERC1155EnumerableInternal { using ABDKMath64x64 for int128; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using PoolStorage for PoolStorage.Layout; using SafeERC20 for IERC20; struct Interval { uint256 contractSize; uint256 tokenAmount; uint256 payment; uint256 apyFee; } address internal immutable WRAPPED_NATIVE_TOKEN; address internal immutable PREMIA_MINING_ADDRESS; address internal immutable FEE_RECEIVER_ADDRESS; address internal immutable FEE_DISCOUNT_ADDRESS; address internal immutable IVOL_ORACLE_ADDRESS; address internal immutable EXCHANGE_HELPER; int128 internal immutable FEE_PREMIUM_64x64; uint256 internal immutable UNDERLYING_FREE_LIQ_TOKEN_ID; uint256 internal immutable BASE_FREE_LIQ_TOKEN_ID; uint256 internal immutable UNDERLYING_RESERVED_LIQ_TOKEN_ID; uint256 internal immutable BASE_RESERVED_LIQ_TOKEN_ID; uint256 internal constant INVERSE_BASIS_POINT = 1e4; uint256 internal constant BATCHING_PERIOD = 260; // Multiply sell quote by this constant int128 internal constant SELL_COEFFICIENT_64x64 = 0xb333333333333333; // 0.7 constructor( address ivolOracle, address wrappedNativeToken, address premiaMining, address feeReceiver, address feeDiscountAddress, int128 feePremium64x64, address exchangeHelper ) { IVOL_ORACLE_ADDRESS = ivolOracle; WRAPPED_NATIVE_TOKEN = wrappedNativeToken; PREMIA_MINING_ADDRESS = premiaMining; FEE_RECEIVER_ADDRESS = feeReceiver; // PremiaFeeDiscount contract address FEE_DISCOUNT_ADDRESS = feeDiscountAddress; FEE_PREMIUM_64x64 = feePremium64x64; EXCHANGE_HELPER = exchangeHelper; UNDERLYING_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId( PoolStorage.TokenType.UNDERLYING_FREE_LIQ, 0, 0 ); BASE_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId( PoolStorage.TokenType.BASE_FREE_LIQ, 0, 0 ); UNDERLYING_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId( PoolStorage.TokenType.UNDERLYING_RESERVED_LIQ, 0, 0 ); BASE_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId( PoolStorage.TokenType.BASE_RESERVED_LIQ, 0, 0 ); } modifier onlyProtocolOwner() { require( msg.sender == IERC173(OwnableStorage.layout().owner).owner(), "Not protocol owner" ); _; } function _fetchFeeDiscount64x64(address feePayer) internal view returns (int128 discount64x64) { if (FEE_DISCOUNT_ADDRESS != address(0)) { discount64x64 = ABDKMath64x64.divu( IPremiaStaking(FEE_DISCOUNT_ADDRESS).getDiscountBPS(feePayer), INVERSE_BASIS_POINT ); } } function _withdrawFees(bool isCall) internal returns (uint256 amount) { uint256 tokenId = _getReservedLiquidityTokenId(isCall); amount = _balanceOf(FEE_RECEIVER_ADDRESS, tokenId); if (amount > 0) { _burn(FEE_RECEIVER_ADDRESS, tokenId, amount); _pushTo( FEE_RECEIVER_ADDRESS, PoolStorage.layout().getPoolToken(isCall), amount ); emit FeeWithdrawal(isCall, amount); } } /** * @notice calculate price of option contract * @param args structured quote arguments * @return result quote result */ function _quotePurchasePrice(PoolStorage.QuoteArgsInternal memory args) internal view returns (PoolStorage.QuoteResultInternal memory result) { require( args.strike64x64 > 0 && args.spot64x64 > 0 && args.maturity > 0, "invalid args" ); PoolStorage.Layout storage l = PoolStorage.layout(); // pessimistically adjust spot price to account for price feed lag if (args.isCall) { args.spot64x64 = args.spot64x64.add( args.spot64x64.mul(l.spotOffset64x64) ); } else { args.spot64x64 = args.spot64x64.sub( args.spot64x64.mul(l.spotOffset64x64) ); } int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals( args.contractSize, l.underlyingDecimals ); (int128 adjustedCLevel64x64, int128 oldLiquidity64x64) = l .getRealPoolState(args.isCall); require(oldLiquidity64x64 > 0, "no liq"); int128 timeToMaturity64x64 = ABDKMath64x64.divu( args.maturity - block.timestamp, 365 days ); int128 annualizedVolatility64x64 = IVolatilitySurfaceOracle( IVOL_ORACLE_ADDRESS ).getAnnualizedVolatility64x64( l.base, l.underlying, args.spot64x64, args.strike64x64, timeToMaturity64x64 ); require(annualizedVolatility64x64 > 0, "vol = 0"); int128 collateral64x64 = args.isCall ? contractSize64x64 : contractSize64x64.mul(args.strike64x64); ( int128 price64x64, int128 cLevel64x64, int128 slippageCoefficient64x64 ) = OptionMath.quotePrice( OptionMath.QuoteArgs( annualizedVolatility64x64.mul(annualizedVolatility64x64), args.strike64x64, args.spot64x64, timeToMaturity64x64, adjustedCLevel64x64, oldLiquidity64x64, oldLiquidity64x64.sub(collateral64x64), 0x10000000000000000, // 64x64 fixed point representation of 1 l.getMinApy64x64(), args.isCall ) ); result.baseCost64x64 = args.isCall ? price64x64.mul(contractSize64x64).div(args.spot64x64) : price64x64.mul(contractSize64x64); result.feeCost64x64 = result.baseCost64x64.mul(FEE_PREMIUM_64x64); result.cLevel64x64 = cLevel64x64; result.slippageCoefficient64x64 = slippageCoefficient64x64; result.feeCost64x64 -= result.feeCost64x64.mul( _fetchFeeDiscount64x64(args.feePayer) ); } function _quoteSalePrice(PoolStorage.QuoteArgsInternal memory args) internal view returns (int128 baseCost64x64, int128 feeCost64x64) { require( args.strike64x64 > 0 && args.spot64x64 > 0 && args.maturity > 0, "invalid args" ); PoolStorage.Layout storage l = PoolStorage.layout(); int128 timeToMaturity64x64 = ABDKMath64x64.divu( args.maturity - block.timestamp, 365 days ); int128 annualizedVolatility64x64 = IVolatilitySurfaceOracle( IVOL_ORACLE_ADDRESS ).getAnnualizedVolatility64x64( l.base, l.underlying, args.spot64x64, args.strike64x64, timeToMaturity64x64 ); require(annualizedVolatility64x64 > 0, "vol = 0"); int128 blackScholesPrice64x64 = OptionMath._blackScholesPrice( annualizedVolatility64x64.mul(annualizedVolatility64x64), args.strike64x64, args.spot64x64, timeToMaturity64x64, args.isCall ); int128 exerciseValue64x64 = ABDKMath64x64Token.fromDecimals( _calculateExerciseValue( l, args.contractSize, args.spot64x64, args.strike64x64, args.isCall ), args.isCall ? l.underlyingDecimals : l.baseDecimals ); if (args.isCall) { exerciseValue64x64 = exerciseValue64x64.mul(args.spot64x64); } int128 sellCLevel64x64; { uint256 longTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(args.isCall, true), args.maturity, args.strike64x64 ); // Initialize to min value, and replace by current if min not set or current is lower sellCLevel64x64 = l.minCLevel64x64[longTokenId]; { (int128 currentCLevel64x64, ) = l.getRealPoolState(args.isCall); if ( sellCLevel64x64 == 0 || currentCLevel64x64 < sellCLevel64x64 ) { sellCLevel64x64 = currentCLevel64x64; } } } int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals( args.contractSize, l.underlyingDecimals ); baseCost64x64 = SELL_COEFFICIENT_64x64 .mul(sellCLevel64x64) .mul( blackScholesPrice64x64.mul(contractSize64x64).sub( exerciseValue64x64 ) ) .add(exerciseValue64x64); if (args.isCall) { baseCost64x64 = baseCost64x64.div(args.spot64x64); } feeCost64x64 = baseCost64x64.mul(FEE_PREMIUM_64x64); feeCost64x64 -= feeCost64x64.mul(_fetchFeeDiscount64x64(args.feePayer)); baseCost64x64 -= feeCost64x64; } function _getAvailableBuybackLiquidity(uint256 shortTokenId) internal view returns (uint256 totalLiquidity) { PoolStorage.Layout storage l = PoolStorage.layout(); EnumerableSet.AddressSet storage accounts = ERC1155EnumerableStorage .layout() .accountsByToken[shortTokenId]; (PoolStorage.TokenType tokenType, , ) = PoolStorage.parseTokenId( shortTokenId ); bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL; uint256 length = accounts.length(); for (uint256 i = 0; i < length; i++) { address lp = accounts.at(i); if (l.isBuybackEnabled[lp][isCall]) { totalLiquidity += _balanceOf(lp, shortTokenId); } } } /** * @notice burn corresponding long and short option tokens * @param l storage layout struct * @param account holder of tokens to annihilate * @param maturity timestamp of option maturity * @param strike64x64 64x64 fixed point representation of strike price * @param isCall true for call, false for put * @param contractSize quantity of option contract tokens to annihilate * @return collateralFreed amount of collateral freed, including APY fee rebate */ function _annihilate( PoolStorage.Layout storage l, address account, uint64 maturity, int128 strike64x64, bool isCall, uint256 contractSize ) internal returns (uint256 collateralFreed) { uint256 longTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(isCall, true), maturity, strike64x64 ); uint256 shortTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(isCall, false), maturity, strike64x64 ); uint256 tokenAmount = l.contractSizeToBaseTokenAmount( contractSize, strike64x64, isCall ); // calculate unconsumed APY fee so that it may be refunded uint256 intervalApyFee = _calculateApyFee( l, shortTokenId, tokenAmount, maturity ); _burn(account, longTokenId, contractSize); uint256 rebate = _fulfillApyFee( l, account, shortTokenId, contractSize, intervalApyFee, isCall ); _burn(account, shortTokenId, contractSize); collateralFreed = tokenAmount + rebate + intervalApyFee; emit Annihilate(shortTokenId, contractSize); } /** * @notice deposit underlying currency, underwriting calls of that currency with respect to base currency * @param amount quantity of underlying currency to deposit * @param isCallPool whether to deposit underlying in the call pool or base in the put pool */ function _deposit( PoolStorage.Layout storage l, uint256 amount, bool isCallPool ) internal { // Reset gradual divestment timestamp delete l.divestmentTimestamps[msg.sender][isCallPool]; _processPendingDeposits(l, isCallPool); l.depositedAt[msg.sender][isCallPool] = block.timestamp; _addUserTVL( l, msg.sender, isCallPool, amount, l.getUtilization64x64(isCallPool) ); _processAvailableFunds(msg.sender, amount, isCallPool, false, false); emit Deposit(msg.sender, isCallPool, amount); } /** * @notice purchase option * @param l storage layout struct * @param account recipient of purchased option * @param maturity timestamp of option maturity * @param strike64x64 64x64 fixed point representation of strike price * @param isCall true for call, false for put * @param contractSize size of option contract * @param newPrice64x64 64x64 fixed point representation of current spot price * @return baseCost quantity of tokens required to purchase long position * @return feeCost quantity of tokens required to pay fees */ function _purchase( PoolStorage.Layout storage l, address account, uint64 maturity, int128 strike64x64, bool isCall, uint256 contractSize, int128 newPrice64x64 ) internal returns (uint256 baseCost, uint256 feeCost) { require(maturity > block.timestamp, "expired"); require(contractSize >= l.underlyingMinimum, "too small"); int128 utilization64x64 = l.getUtilization64x64(isCall); { uint256 tokenAmount = l.contractSizeToBaseTokenAmount( contractSize, strike64x64, isCall ); uint256 freeLiquidityTokenId = _getFreeLiquidityTokenId(isCall); require( tokenAmount <= _totalSupply(freeLiquidityTokenId) - l.totalPendingDeposits(isCall) - (_balanceOf(account, freeLiquidityTokenId) - l.pendingDepositsOf(account, isCall)), "insuf liq" ); } PoolStorage.QuoteResultInternal memory quote = _quotePurchasePrice( PoolStorage.QuoteArgsInternal( account, maturity, strike64x64, newPrice64x64, contractSize, isCall ) ); baseCost = ABDKMath64x64Token.toDecimals( quote.baseCost64x64, l.getTokenDecimals(isCall) ); feeCost = ABDKMath64x64Token.toDecimals( quote.feeCost64x64, l.getTokenDecimals(isCall) ); uint256 longTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(isCall, true), maturity, strike64x64 ); { int128 minCLevel64x64 = l.minCLevel64x64[longTokenId]; if (minCLevel64x64 == 0 || quote.cLevel64x64 < minCLevel64x64) { l.minCLevel64x64[longTokenId] = quote.cLevel64x64; } } // mint long option token for buyer _mint(account, longTokenId, contractSize); int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall); // burn free liquidity tokens from other underwriters _mintShortTokenLoop( l, account, maturity, strike64x64, contractSize, baseCost, isCall, utilization64x64 ); int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall); _setCLevel( l, oldLiquidity64x64, newLiquidity64x64, isCall, utilization64x64 ); // mint reserved liquidity tokens for fee receiver _processAvailableFunds( FEE_RECEIVER_ADDRESS, feeCost, isCall, true, false ); emit Purchase( account, longTokenId, contractSize, baseCost, feeCost, newPrice64x64 ); } /** * @notice reassign short position to new underwriter * @param l storage layout struct * @param account holder of positions to be reassigned * @param maturity timestamp of option maturity * @param strike64x64 64x64 fixed point representation of strike price * @param isCall true for call, false for put * @param contractSize quantity of option contract tokens to reassign * @param newPrice64x64 64x64 fixed point representation of current spot price * @return baseCost quantity of tokens required to reassign short position * @return feeCost quantity of tokens required to pay fees * @return netCollateralFreed quantity of liquidity freed */ function _reassign( PoolStorage.Layout storage l, address account, uint64 maturity, int128 strike64x64, bool isCall, uint256 contractSize, int128 newPrice64x64 ) internal returns ( uint256 baseCost, uint256 feeCost, uint256 netCollateralFreed ) { (baseCost, feeCost) = _purchase( l, account, maturity, strike64x64, isCall, contractSize, newPrice64x64 ); uint256 totalCollateralFreed = _annihilate( l, account, maturity, strike64x64, isCall, contractSize ); netCollateralFreed = totalCollateralFreed - baseCost - feeCost; } /** * @notice exercise option on behalf of holder * @dev used for processing of expired options if passed holder is zero address * @param holder owner of long option tokens to exercise * @param longTokenId long option token id * @param contractSize quantity of tokens to exercise */ function _exercise( address holder, uint256 longTokenId, uint256 contractSize ) internal { uint64 maturity; int128 strike64x64; bool isCall; bool onlyExpired = holder == address(0); { PoolStorage.TokenType tokenType; (tokenType, maturity, strike64x64) = PoolStorage.parseTokenId( longTokenId ); require( tokenType == PoolStorage.TokenType.LONG_CALL || tokenType == PoolStorage.TokenType.LONG_PUT, "invalid type" ); require(!onlyExpired || maturity < block.timestamp, "not expired"); isCall = tokenType == PoolStorage.TokenType.LONG_CALL; } PoolStorage.Layout storage l = PoolStorage.layout(); int128 utilization64x64 = l.getUtilization64x64(isCall); int128 spot64x64 = _update(l); if (maturity < block.timestamp) { spot64x64 = l.getPriceUpdateAfter(maturity); } require( onlyExpired || ( isCall ? (spot64x64 > strike64x64) : (spot64x64 < strike64x64) ), "not ITM" ); uint256 exerciseValue = _calculateExerciseValue( l, contractSize, spot64x64, strike64x64, isCall ); if (onlyExpired) { // burn long option tokens from multiple holders // transfer profit to and emit Exercise event for each holder in loop _burnLongTokenLoop( contractSize, exerciseValue, longTokenId, isCall ); } else { // burn long option tokens from sender _burnLongTokenInterval( holder, longTokenId, contractSize, exerciseValue, isCall ); } // burn short option tokens from multiple underwriters _burnShortTokenLoop( l, maturity, strike64x64, contractSize, exerciseValue, isCall, false, utilization64x64 ); } function _calculateExerciseValue( PoolStorage.Layout storage l, uint256 contractSize, int128 spot64x64, int128 strike64x64, bool isCall ) internal view returns (uint256 exerciseValue) { // calculate exercise value if option is in-the-money if (isCall) { if (spot64x64 > strike64x64) { exerciseValue = spot64x64.sub(strike64x64).div(spot64x64).mulu( contractSize ); } } else { if (spot64x64 < strike64x64) { exerciseValue = l.contractSizeToBaseTokenAmount( contractSize, strike64x64.sub(spot64x64), isCall ); } } } function _mintShortTokenLoop( PoolStorage.Layout storage l, address buyer, uint64 maturity, int128 strike64x64, uint256 contractSize, uint256 premium, bool isCall, int128 utilization64x64 ) internal { uint256 shortTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(isCall, false), maturity, strike64x64 ); uint256 tokenAmount = l.contractSizeToBaseTokenAmount( contractSize, strike64x64, isCall ); // calculate anticipated APY fee so that it may be reserved uint256 apyFee = _calculateApyFee( l, shortTokenId, tokenAmount, maturity ); while (tokenAmount > 0) { address underwriter = l.liquidityQueueAscending[isCall][address(0)]; uint256 balance = _balanceOf( underwriter, _getFreeLiquidityTokenId(isCall) ); // if underwriter is in process of divestment, remove from queue if (!l.getReinvestmentStatus(underwriter, isCall)) { _burn(underwriter, _getFreeLiquidityTokenId(isCall), balance); _processAvailableFunds( underwriter, balance, isCall, true, false ); _subUserTVL(l, underwriter, isCall, balance, utilization64x64); continue; } // if underwriter has insufficient liquidity, remove from queue if (balance < l.getMinimumAmount(isCall)) { l.removeUnderwriter(underwriter, isCall); continue; } // move interval to end of queue if underwriter is buyer if (underwriter == buyer) { l.removeUnderwriter(underwriter, isCall); l.addUnderwriter(underwriter, isCall); continue; } balance -= l.pendingDepositsOf(underwriter, isCall); Interval memory interval; // amount of liquidity provided by underwriter, accounting for reinvested premium interval.tokenAmount = (balance * (tokenAmount + premium - apyFee)) / tokenAmount; // skip underwriters whose liquidity is pending deposit processing if (interval.tokenAmount == 0) continue; // truncate interval if underwriter has excess liquidity available if (interval.tokenAmount > tokenAmount) interval.tokenAmount = tokenAmount; // calculate derived interval variables interval.contractSize = (contractSize * interval.tokenAmount) / tokenAmount; interval.payment = (premium * interval.tokenAmount) / tokenAmount; interval.apyFee = (apyFee * interval.tokenAmount) / tokenAmount; _mintShortTokenInterval( l, underwriter, buyer, shortTokenId, interval, isCall, utilization64x64 ); tokenAmount -= interval.tokenAmount; contractSize -= interval.contractSize; premium -= interval.payment; apyFee -= interval.apyFee; } } function _mintShortTokenInterval( PoolStorage.Layout storage l, address underwriter, address longReceiver, uint256 shortTokenId, Interval memory interval, bool isCallPool, int128 utilization64x64 ) internal { // track prepaid APY fees _reserveApyFee(l, underwriter, shortTokenId, interval.apyFee); // if payment is equal to collateral amount plus APY fee, this is a manual underwrite bool isManualUnderwrite = interval.payment == interval.tokenAmount + interval.apyFee; if (!isManualUnderwrite) { // burn free liquidity tokens from underwriter _burn( underwriter, _getFreeLiquidityTokenId(isCallPool), interval.tokenAmount + interval.apyFee - interval.payment ); } // mint short option tokens for underwriter _mint(underwriter, shortTokenId, interval.contractSize); _addUserTVL( l, underwriter, isCallPool, interval.payment - interval.apyFee, utilization64x64 ); emit Underwrite( underwriter, longReceiver, shortTokenId, interval.contractSize, isManualUnderwrite ? 0 : interval.payment, isManualUnderwrite ); } function _burnLongTokenLoop( uint256 contractSize, uint256 exerciseValue, uint256 longTokenId, bool isCallPool ) internal { EnumerableSet.AddressSet storage holders = ERC1155EnumerableStorage .layout() .accountsByToken[longTokenId]; while (contractSize > 0) { address longTokenHolder = holders.at(holders.length() - 1); uint256 intervalContractSize = _balanceOf( longTokenHolder, longTokenId ); // truncate interval if holder has excess long position size if (intervalContractSize > contractSize) intervalContractSize = contractSize; uint256 intervalExerciseValue = (exerciseValue * intervalContractSize) / contractSize; _burnLongTokenInterval( longTokenHolder, longTokenId, intervalContractSize, intervalExerciseValue, isCallPool ); contractSize -= intervalContractSize; exerciseValue -= intervalExerciseValue; } } function _burnLongTokenInterval( address holder, uint256 longTokenId, uint256 contractSize, uint256 exerciseValue, bool isCallPool ) internal { _burn(holder, longTokenId, contractSize); if (exerciseValue > 0) { _processAvailableFunds( holder, exerciseValue, isCallPool, true, true ); } emit Exercise(holder, longTokenId, contractSize, exerciseValue, 0); } function _burnShortTokenLoop( PoolStorage.Layout storage l, uint64 maturity, int128 strike64x64, uint256 contractSize, uint256 payment, bool isCall, bool onlyBuybackLiquidity, int128 utilization64x64 ) internal { uint256 shortTokenId = PoolStorage.formatTokenId( PoolStorage.getTokenType(isCall, false), maturity, strike64x64 ); uint256 tokenAmount = l.contractSizeToBaseTokenAmount( contractSize, strike64x64, isCall ); // calculate unconsumed APY fee so that it may be refunded uint256 apyFee = _calculateApyFee( l, shortTokenId, tokenAmount, maturity ); EnumerableSet.AddressSet storage underwriters = ERC1155EnumerableStorage .layout() .accountsByToken[shortTokenId]; uint256 index = underwriters.length(); while (contractSize > 0) { address underwriter = underwriters.at(--index); // skip underwriters who do not provide buyback liqudity, if applicable if ( onlyBuybackLiquidity && !l.isBuybackEnabled[underwriter][isCall] ) continue; Interval memory interval; // amount of liquidity provided by underwriter interval.contractSize = _balanceOf(underwriter, shortTokenId); // truncate interval if underwriter has excess short position size if (interval.contractSize > contractSize) interval.contractSize = contractSize; // calculate derived interval variables interval.tokenAmount = (tokenAmount * interval.contractSize) / contractSize; interval.payment = (payment * interval.contractSize) / contractSize; interval.apyFee = (apyFee * interval.contractSize) / contractSize; _burnShortTokenInterval( l, underwriter, shortTokenId, interval, isCall, onlyBuybackLiquidity, utilization64x64 ); contractSize -= interval.contractSize; tokenAmount -= interval.tokenAmount; payment -= interval.payment; apyFee -= interval.apyFee; } } function _burnShortTokenInterval( PoolStorage.Layout storage l, address underwriter, uint256 shortTokenId, Interval memory interval, bool isCallPool, bool isSale, int128 utilization64x64 ) internal { // track prepaid APY fees uint256 refundWithRebate = interval.apyFee + _fulfillApyFee( l, underwriter, shortTokenId, interval.contractSize, interval.apyFee, isCallPool ); // burn short option tokens from underwriter _burn(underwriter, shortTokenId, interval.contractSize); bool divest = !l.getReinvestmentStatus(underwriter, isCallPool); _processAvailableFunds( underwriter, interval.tokenAmount - interval.payment + refundWithRebate, isCallPool, divest, false ); if (divest) { _subUserTVL( l, underwriter, isCallPool, interval.tokenAmount, utilization64x64 ); } else { if (refundWithRebate > interval.payment) { _addUserTVL( l, underwriter, isCallPool, refundWithRebate - interval.payment, utilization64x64 ); } else if (interval.payment > refundWithRebate) { _subUserTVL( l, underwriter, isCallPool, interval.payment - refundWithRebate, utilization64x64 ); } } if (isSale) { emit AssignSale( underwriter, shortTokenId, interval.tokenAmount - interval.payment, interval.contractSize ); } else { emit AssignExercise( underwriter, shortTokenId, interval.tokenAmount - interval.payment, interval.contractSize, 0 ); } } function _calculateApyFee( PoolStorage.Layout storage l, uint256 shortTokenId, uint256 tokenAmount, uint64 maturity ) internal view returns (uint256 apyFee) { if (block.timestamp < maturity) { int128 apyFeeRate64x64 = _totalSupply(shortTokenId) == 0 ? l.getFeeApy64x64() : l.feeReserveRates[shortTokenId]; apyFee = apyFeeRate64x64.mulu( (tokenAmount * (maturity - block.timestamp)) / (365 days) ); } } function _reserveApyFee( PoolStorage.Layout storage l, address underwriter, uint256 shortTokenId, uint256 amount ) internal { l.feesReserved[underwriter][shortTokenId] += amount; emit APYFeeReserved(underwriter, shortTokenId, amount); } /** * @notice credit fee receiver with fees earned and calculate rebate for underwriter * @dev short tokens which have acrrued fee must not be burned or transferred until after this helper is called * @param l storage layout struct * @param underwriter holder of short position who reserved fees * @param shortTokenId short token id whose reserved fees to pay and rebate * @param intervalContractSize size of position for which to calculate accrued fees * @param intervalApyFee quantity of fees reserved but not yet accrued * @param isCallPool true for call, false for put */ function _fulfillApyFee( PoolStorage.Layout storage l, address underwriter, uint256 shortTokenId, uint256 intervalContractSize, uint256 intervalApyFee, bool isCallPool ) internal returns (uint256 rebate) { // calculate proportion of fees reserved corresponding to interval uint256 feesReserved = l.feesReserved[underwriter][shortTokenId]; uint256 intervalFeesReserved = (feesReserved * intervalContractSize) / _balanceOf(underwriter, shortTokenId); // deduct fees for time not elapsed l.feesReserved[underwriter][shortTokenId] -= intervalFeesReserved; // apply rebate to fees accrued rebate = _fetchFeeDiscount64x64(underwriter).mulu( intervalFeesReserved - intervalApyFee ); // credit fee receiver with fees paid uint256 intervalFeesPaid = intervalFeesReserved - intervalApyFee - rebate; _processAvailableFunds( FEE_RECEIVER_ADDRESS, intervalFeesPaid, isCallPool, true, false ); emit APYFeePaid(underwriter, shortTokenId, intervalFeesPaid); } function _addToDepositQueue( address account, uint256 amount, bool isCallPool ) internal { PoolStorage.Layout storage l = PoolStorage.layout(); uint256 freeLiqTokenId = _getFreeLiquidityTokenId(isCallPool); if (_totalSupply(freeLiqTokenId) > 0) { uint256 nextBatch = (block.timestamp / BATCHING_PERIOD) * BATCHING_PERIOD + BATCHING_PERIOD; l.pendingDeposits[account][nextBatch][isCallPool] += amount; PoolStorage.BatchData storage batchData = l.nextDeposits[ isCallPool ]; batchData.totalPendingDeposits += amount; batchData.eta = nextBatch; } _mint(account, freeLiqTokenId, amount); } function _processPendingDeposits(PoolStorage.Layout storage l, bool isCall) internal { PoolStorage.BatchData storage batchData = l.nextDeposits[isCall]; if (batchData.eta == 0 || block.timestamp < batchData.eta) return; int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall); _setCLevel( l, oldLiquidity64x64, oldLiquidity64x64.add( ABDKMath64x64Token.fromDecimals( batchData.totalPendingDeposits, l.getTokenDecimals(isCall) ) ), isCall, l.getUtilization64x64(isCall) ); delete l.nextDeposits[isCall]; } function _getFreeLiquidityTokenId(bool isCall) internal view returns (uint256 freeLiqTokenId) { freeLiqTokenId = isCall ? UNDERLYING_FREE_LIQ_TOKEN_ID : BASE_FREE_LIQ_TOKEN_ID; } function _getReservedLiquidityTokenId(bool isCall) internal view returns (uint256 reservedLiqTokenId) { reservedLiqTokenId = isCall ? UNDERLYING_RESERVED_LIQ_TOKEN_ID : BASE_RESERVED_LIQ_TOKEN_ID; } function _setCLevel( PoolStorage.Layout storage l, int128 oldLiquidity64x64, int128 newLiquidity64x64, bool isCallPool, int128 utilization64x64 ) internal { int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64( isCallPool, utilization64x64 ); int128 cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment( oldCLevel64x64, oldLiquidity64x64, newLiquidity64x64, isCallPool ); l.setCLevel(cLevel64x64, isCallPool); emit UpdateCLevel( isCallPool, cLevel64x64, oldLiquidity64x64, newLiquidity64x64 ); } /** * @notice calculate and store updated market state * @param l storage layout struct * @return newPrice64x64 64x64 fixed point representation of current spot price */ function _update(PoolStorage.Layout storage l) internal returns (int128 newPrice64x64) { if (l.updatedAt == block.timestamp) { return (l.getPriceUpdate(block.timestamp)); } newPrice64x64 = l.fetchPriceUpdate(); if (l.getPriceUpdate(block.timestamp) == 0) { l.setPriceUpdate(block.timestamp, newPrice64x64); } l.updatedAt = block.timestamp; _processPendingDeposits(l, true); _processPendingDeposits(l, false); } /** * @notice transfer ERC20 tokens to message sender * @param token ERC20 token address * @param amount quantity of token to transfer */ function _pushTo( address to, address token, uint256 amount ) internal { if (amount == 0) return; require(IERC20(token).transfer(to, amount), "ERC20 transfer failed"); } /** * @notice transfer ERC20 tokens from message sender * @param l storage layout struct * @param from address from which tokens are pulled from * @param amount quantity of token to transfer * @param isCallPool whether funds correspond to call or put pool * @param creditMessageValue whether to attempt to treat message value as credit */ function _pullFrom( PoolStorage.Layout storage l, address from, uint256 amount, bool isCallPool, bool creditMessageValue ) internal { uint256 credit; if (creditMessageValue) { credit = _creditMessageValue(amount, isCallPool); } if (amount > credit) { credit += _creditReservedLiquidity( from, amount - credit, isCallPool ); } if (amount > credit) { require( IERC20(l.getPoolToken(isCallPool)).transferFrom( from, address(this), amount - credit ), "ERC20 transfer failed" ); } } /** * @notice transfer or reinvest available user funds * @param account owner of funds * @param amount quantity of funds available * @param isCallPool whether funds correspond to call or put pool * @param divest whether to reserve funds or reinvest * @param transferOnDivest whether to transfer divested funds to owner */ function _processAvailableFunds( address account, uint256 amount, bool isCallPool, bool divest, bool transferOnDivest ) internal { if (divest) { if (transferOnDivest) { _pushTo( account, PoolStorage.layout().getPoolToken(isCallPool), amount ); } else { _mint( account, _getReservedLiquidityTokenId(isCallPool), amount ); } } else { _addToDepositQueue(account, amount, isCallPool); } } /** * @notice validate that pool accepts ether deposits and calculate credit amount from message value * @param amount total deposit quantity * @param isCallPool whether to deposit underlying in the call pool or base in the put pool * @return credit quantity of credit to apply */ function _creditMessageValue(uint256 amount, bool isCallPool) internal returns (uint256 credit) { if (msg.value > 0) { require( PoolStorage.layout().getPoolToken(isCallPool) == WRAPPED_NATIVE_TOKEN, "not WETH deposit" ); if (msg.value > amount) { unchecked { (bool success, ) = payable(msg.sender).call{ value: msg.value - amount }(""); require(success, "ETH refund failed"); credit = amount; } } else { credit = msg.value; } IWETH(WRAPPED_NATIVE_TOKEN).deposit{value: credit}(); } } /** * @notice calculate credit amount from reserved liquidity * @param account address whose reserved liquidity to use as credit * @param amount total deposit quantity * @param isCallPool whether to deposit underlying in the call pool or base in the put pool * @return credit quantity of credit to apply */ function _creditReservedLiquidity( address account, uint256 amount, bool isCallPool ) internal returns (uint256 credit) { uint256 reservedLiqTokenId = _getReservedLiquidityTokenId(isCallPool); uint256 balance = _balanceOf(account, reservedLiqTokenId); if (balance > 0) { credit = balance > amount ? amount : balance; _burn(account, reservedLiqTokenId, credit); } } /* * @notice mint ERC1155 token without pasing data payload or calling safe transfer acceptance check * @param account recipient of minted tokens * @param tokenId id of token to mint * @param amount quantity of tokens to mint */ function _mint( address account, uint256 tokenId, uint256 amount ) internal { _mint(account, tokenId, amount, ""); } function _addUserTVL( PoolStorage.Layout storage l, address user, bool isCallPool, uint256 amount, int128 utilization64x64 ) internal { uint256 userTVL = l.userTVL[user][isCallPool]; uint256 totalTVL = l.totalTVL[isCallPool]; IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending( user, address(this), isCallPool, userTVL, userTVL + amount, totalTVL, ABDKMath64x64Token.toDecimals(utilization64x64, 4) ); l.userTVL[user][isCallPool] = userTVL + amount; l.totalTVL[isCallPool] = totalTVL + amount; } function _subUserTVL( PoolStorage.Layout storage l, address user, bool isCallPool, uint256 amount, int128 utilization64x64 ) internal { uint256 userTVL = l.userTVL[user][isCallPool]; uint256 totalTVL = l.totalTVL[isCallPool]; uint256 newUserTVL; uint256 newTotalTVL; if (userTVL < amount) { amount = userTVL; } newUserTVL = userTVL - amount; newTotalTVL = totalTVL - amount; IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending( user, address(this), isCallPool, userTVL, newUserTVL, totalTVL, ABDKMath64x64Token.toDecimals(utilization64x64, 4) ); l.userTVL[user][isCallPool] = newUserTVL; l.totalTVL[isCallPool] = newTotalTVL; } function _transferUserTVL( PoolStorage.Layout storage l, address from, address to, bool isCallPool, uint256 amount, int128 utilization64x64 ) internal { uint256 fromTVL = l.userTVL[from][isCallPool]; uint256 toTVL = l.userTVL[to][isCallPool]; uint256 totalTVL = l.totalTVL[isCallPool]; uint256 newFromTVL; if (fromTVL > amount) { newFromTVL = fromTVL - amount; } uint256 newToTVL = toTVL + amount; IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending( from, address(this), isCallPool, fromTVL, newFromTVL, totalTVL, ABDKMath64x64Token.toDecimals(utilization64x64, 4) ); IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending( to, address(this), isCallPool, toTVL, newToTVL, totalTVL, ABDKMath64x64Token.toDecimals(utilization64x64, 4) ); l.userTVL[from][isCallPool] = newFromTVL; l.userTVL[to][isCallPool] = newToTVL; } /** * @dev pull token from user, send to exchangeHelper and trigger a trade from exchangeHelper * @param s swap arguments * @param tokenOut token to swap for. should always equal to the pool token. * @return amountCredited amount of tokenOut we got from the trade. */ function _swapForPoolTokens( IPoolInternal.SwapArgs memory s, address tokenOut ) internal returns (uint256 amountCredited) { if (msg.value > 0) { require(s.tokenIn == WRAPPED_NATIVE_TOKEN, "wrong tokenIn"); IWETH(WRAPPED_NATIVE_TOKEN).deposit{value: msg.value}(); IWETH(WRAPPED_NATIVE_TOKEN).transfer(EXCHANGE_HELPER, msg.value); } if (s.amountInMax > 0) { IERC20(s.tokenIn).safeTransferFrom( msg.sender, EXCHANGE_HELPER, s.amountInMax ); } amountCredited = IExchangeHelper(EXCHANGE_HELPER).swapWithToken( s.tokenIn, tokenOut, s.amountInMax + msg.value, s.callee, s.allowanceTarget, s.data, s.refundAddress ); require( amountCredited >= s.amountOutMin, "not enough output from trade" ); } /** * @notice ERC1155 hook: track eligible underwriters * @param operator transaction sender * @param from token sender * @param to token receiver * @param ids token ids transferred * @param amounts token quantities transferred * @param data data payload */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); PoolStorage.Layout storage l = PoolStorage.layout(); for (uint256 i; i < ids.length; i++) { uint256 id = ids[i]; uint256 amount = amounts[i]; if (amount == 0) continue; if (from == address(0)) { l.tokenIds.add(id); } if (to == address(0) && _totalSupply(id) == 0) { l.tokenIds.remove(id); } // prevent transfer of free and reserved liquidity during waiting period if ( id == UNDERLYING_FREE_LIQ_TOKEN_ID || id == BASE_FREE_LIQ_TOKEN_ID || id == UNDERLYING_RESERVED_LIQ_TOKEN_ID || id == BASE_RESERVED_LIQ_TOKEN_ID ) { if (from != address(0) && to != address(0)) { bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID || id == UNDERLYING_RESERVED_LIQ_TOKEN_ID; require( l.depositedAt[from][isCallPool] + (1 days) < block.timestamp, "liq lock 1d" ); } } if ( id == UNDERLYING_FREE_LIQ_TOKEN_ID || id == BASE_FREE_LIQ_TOKEN_ID ) { bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID; uint256 minimum = l.getMinimumAmount(isCallPool); if (from != address(0)) { uint256 balance = _balanceOf(from, id); if (balance > minimum && balance <= amount + minimum) { require( balance - l.pendingDepositsOf(from, isCallPool) >= amount, "Insuf balance" ); l.removeUnderwriter(from, isCallPool); } if (to != address(0)) { _transferUserTVL( l, from, to, isCallPool, amount, l.getUtilization64x64(isCallPool) ); } } if (to != address(0)) { uint256 balance = _balanceOf(to, id); if (balance <= minimum && balance + amount >= minimum) { l.addUnderwriter(to, isCallPool); } } } // Update userTVL on SHORT options transfers (PoolStorage.TokenType tokenType, , ) = PoolStorage.parseTokenId( id ); if ( tokenType == PoolStorage.TokenType.SHORT_CALL || tokenType == PoolStorage.TokenType.SHORT_PUT ) { int128 utilization64x64 = l.getUtilization64x64( tokenType == PoolStorage.TokenType.SHORT_CALL ); _beforeShortTokenTransfer( l, from, to, id, amount, utilization64x64 ); } } } function _beforeShortTokenTransfer( PoolStorage.Layout storage l, address from, address to, uint256 id, uint256 amount, int128 utilization64x64 ) private { // total supply has already been updated, so compare to amount rather than 0 if (from == address(0) && _totalSupply(id) == amount) { l.feeReserveRates[id] = l.getFeeApy64x64(); } if (to == address(0) && _totalSupply(id) == 0) { delete l.feeReserveRates[id]; } if (from != address(0) && to != address(0)) { bool isCall; uint256 collateral; uint256 intervalApyFee; { ( PoolStorage.TokenType tokenType, uint64 maturity, int128 strike64x64 ) = PoolStorage.parseTokenId(id); isCall = tokenType == PoolStorage.TokenType.SHORT_CALL; collateral = l.contractSizeToBaseTokenAmount( amount, strike64x64, isCall ); intervalApyFee = _calculateApyFee(l, id, collateral, maturity); } uint256 rebate = _fulfillApyFee( l, from, id, amount, intervalApyFee, isCall ); _reserveApyFee(l, to, id, intervalApyFee); bool divest = !l.getReinvestmentStatus(from, isCall); if (rebate > 0) { _processAvailableFunds(from, rebate, isCall, divest, false); } _subUserTVL( l, from, isCall, divest ? collateral : collateral - rebate, utilization64x64 ); _addUserTVL(l, to, isCall, collateral, utilization64x64); } } }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {AggregatorInterface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol"; import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {ABDKMath64x64Token} from "@solidstate/abdk-math-extensions/contracts/ABDKMath64x64Token.sol"; import {EnumerableSet, ERC1155EnumerableStorage} from "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol"; import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol"; import {OptionMath} from "../libraries/OptionMath.sol"; library PoolStorage { using ABDKMath64x64 for int128; using PoolStorage for PoolStorage.Layout; enum TokenType { UNDERLYING_FREE_LIQ, BASE_FREE_LIQ, UNDERLYING_RESERVED_LIQ, BASE_RESERVED_LIQ, LONG_CALL, SHORT_CALL, LONG_PUT, SHORT_PUT } struct PoolSettings { address underlying; address base; address underlyingOracle; address baseOracle; } struct QuoteArgsInternal { address feePayer; // address of the fee payer uint64 maturity; // timestamp of option maturity int128 strike64x64; // 64x64 fixed point representation of strike price int128 spot64x64; // 64x64 fixed point representation of spot price uint256 contractSize; // size of option contract bool isCall; // true for call, false for put } struct QuoteResultInternal { int128 baseCost64x64; // 64x64 fixed point representation of option cost denominated in underlying currency (without fee) int128 feeCost64x64; // 64x64 fixed point representation of option fee cost denominated in underlying currency for call, or base currency for put int128 cLevel64x64; // 64x64 fixed point representation of C-Level of Pool after purchase int128 slippageCoefficient64x64; // 64x64 fixed point representation of slippage coefficient for given order size } struct BatchData { uint256 eta; uint256 totalPendingDeposits; } bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.storage.Pool"); uint256 private constant C_DECAY_BUFFER = 12 hours; uint256 private constant C_DECAY_INTERVAL = 4 hours; int128 internal constant ONE_64x64 = 0x10000000000000000; struct Layout { // ERC20 token addresses address base; address underlying; // AggregatorV3Interface oracle addresses address baseOracle; address underlyingOracle; // token metadata uint8 underlyingDecimals; uint8 baseDecimals; // minimum amounts uint256 baseMinimum; uint256 underlyingMinimum; // deposit caps uint256 _deprecated_basePoolCap; uint256 _deprecated_underlyingPoolCap; // market state int128 _deprecated_steepness64x64; int128 cLevelBase64x64; int128 cLevelUnderlying64x64; uint256 cLevelBaseUpdatedAt; uint256 cLevelUnderlyingUpdatedAt; uint256 updatedAt; // User -> isCall -> depositedAt mapping(address => mapping(bool => uint256)) depositedAt; mapping(address => mapping(bool => uint256)) divestmentTimestamps; // doubly linked list of free liquidity intervals // isCall -> User -> User mapping(bool => mapping(address => address)) liquidityQueueAscending; mapping(bool => mapping(address => address)) liquidityQueueDescending; // minimum resolution price bucket => price mapping(uint256 => int128) bucketPrices64x64; // sequence id (minimum resolution price bucket / 256) => price update sequence mapping(uint256 => uint256) priceUpdateSequences; // isCall -> batch data mapping(bool => BatchData) nextDeposits; // user -> batch timestamp -> isCall -> pending amount mapping(address => mapping(uint256 => mapping(bool => uint256))) pendingDeposits; EnumerableSet.UintSet tokenIds; // user -> isCallPool -> total value locked of user (Used for liquidity mining) mapping(address => mapping(bool => uint256)) userTVL; // isCallPool -> total value locked mapping(bool => uint256) totalTVL; // steepness values int128 steepnessBase64x64; int128 steepnessUnderlying64x64; // User -> isCallPool -> isBuybackEnabled mapping(address => mapping(bool => bool)) isBuybackEnabled; // LongTokenId -> minC mapping(uint256 => int128) minCLevel64x64; // APY fee tracking // underwriter -> shortTokenId -> amount mapping(address => mapping(uint256 => uint256)) feesReserved; // shortTokenId -> 64x64 fixed point representation of apy fee mapping(uint256 => int128) feeReserveRates; // APY fee paid by underwriters // Also used along with multiplier to calculate minimum option price as APY int128 feeApy64x64; // adjustment applied to spot price for puchase calculations int128 spotOffset64x64; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } /** * @notice calculate ERC1155 token id for given option parameters * @param tokenType TokenType enum * @param maturity timestamp of option maturity * @param strike64x64 64x64 fixed point representation of strike price * @return tokenId token id */ function formatTokenId( TokenType tokenType, uint64 maturity, int128 strike64x64 ) internal pure returns (uint256 tokenId) { tokenId = (uint256(tokenType) << 248) + (uint256(maturity) << 128) + uint256(int256(strike64x64)); } /** * @notice derive option maturity and strike price from ERC1155 token id * @param tokenId token id * @return tokenType TokenType enum * @return maturity timestamp of option maturity * @return strike64x64 option strike price */ function parseTokenId(uint256 tokenId) internal pure returns ( TokenType tokenType, uint64 maturity, int128 strike64x64 ) { assembly { tokenType := shr(248, tokenId) maturity := shr(128, tokenId) strike64x64 := tokenId } } function getTokenType(bool isCall, bool isLong) internal pure returns (TokenType tokenType) { if (isCall) { tokenType = isLong ? TokenType.LONG_CALL : TokenType.SHORT_CALL; } else { tokenType = isLong ? TokenType.LONG_PUT : TokenType.SHORT_PUT; } } function getPoolToken(Layout storage l, bool isCall) internal view returns (address token) { token = isCall ? l.underlying : l.base; } function getTokenDecimals(Layout storage l, bool isCall) internal view returns (uint8 decimals) { decimals = isCall ? l.underlyingDecimals : l.baseDecimals; } function getMinimumAmount(Layout storage l, bool isCall) internal view returns (uint256 minimumAmount) { minimumAmount = isCall ? l.underlyingMinimum : l.baseMinimum; } /** * @notice get the total supply of free liquidity tokens, minus pending deposits * @param l storage layout struct * @param isCall whether query is for call or put pool * @return 64x64 fixed point representation of total free liquidity */ function totalFreeLiquiditySupply64x64(Layout storage l, bool isCall) internal view returns (int128) { uint256 tokenId = formatTokenId( isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ, 0, 0 ); return ABDKMath64x64Token.fromDecimals( ERC1155EnumerableStorage.layout().totalSupply[tokenId] - l.totalPendingDeposits(isCall), l.getTokenDecimals(isCall) ); } function getReinvestmentStatus( Layout storage l, address account, bool isCallPool ) internal view returns (bool) { uint256 timestamp = l.divestmentTimestamps[account][isCallPool]; return timestamp == 0 || timestamp > block.timestamp; } function getFeeApy64x64(Layout storage l) internal view returns (int128 feeApy64x64) { feeApy64x64 = l.feeApy64x64; if (feeApy64x64 == 0) { // if APY fee is not set, set to 0.025 feeApy64x64 = 0x666666666666666; } } function getMinApy64x64(Layout storage l) internal view returns (int128 feeApy64x64) { feeApy64x64 = l.getFeeApy64x64() << 3; } function addUnderwriter( Layout storage l, address account, bool isCallPool ) internal { require(account != address(0)); mapping(address => address) storage asc = l.liquidityQueueAscending[ isCallPool ]; mapping(address => address) storage desc = l.liquidityQueueDescending[ isCallPool ]; if (_isInQueue(account, asc, desc)) return; address last = desc[address(0)]; asc[last] = account; desc[account] = last; desc[address(0)] = account; } function removeUnderwriter( Layout storage l, address account, bool isCallPool ) internal { require(account != address(0)); mapping(address => address) storage asc = l.liquidityQueueAscending[ isCallPool ]; mapping(address => address) storage desc = l.liquidityQueueDescending[ isCallPool ]; if (!_isInQueue(account, asc, desc)) return; address prev = desc[account]; address next = asc[account]; asc[prev] = next; desc[next] = prev; delete asc[account]; delete desc[account]; } function isInQueue( Layout storage l, address account, bool isCallPool ) internal view returns (bool) { mapping(address => address) storage asc = l.liquidityQueueAscending[ isCallPool ]; mapping(address => address) storage desc = l.liquidityQueueDescending[ isCallPool ]; return _isInQueue(account, asc, desc); } function _isInQueue( address account, mapping(address => address) storage asc, mapping(address => address) storage desc ) private view returns (bool) { return asc[account] != address(0) || desc[address(0)] == account; } /** * @notice get current C-Level, without accounting for pending adjustments * @param l storage layout struct * @param isCall whether query is for call or put pool * @return cLevel64x64 64x64 fixed point representation of C-Level */ function getRawCLevel64x64(Layout storage l, bool isCall) internal view returns (int128 cLevel64x64) { cLevel64x64 = isCall ? l.cLevelUnderlying64x64 : l.cLevelBase64x64; } /** * @notice get current C-Level, accounting for unrealized decay * @param l storage layout struct * @param isCall whether query is for call or put pool * @param utilization64x64 utilization of the pool * @return cLevel64x64 64x64 fixed point representation of C-Level */ function getDecayAdjustedCLevel64x64( Layout storage l, bool isCall, int128 utilization64x64 ) internal view returns (int128 cLevel64x64) { // get raw C-Level from storage cLevel64x64 = l.getRawCLevel64x64(isCall); // account for C-Level decay cLevel64x64 = l.applyCLevelDecayAdjustment( cLevel64x64, isCall, utilization64x64 ); } /** * @notice get updated C-Level and pool liquidity level, accounting for decay and pending deposits * @param l storage layout struct * @param isCall whether to update C-Level for call or put pool * @return cLevel64x64 64x64 fixed point representation of C-Level * @return liquidity64x64 64x64 fixed point representation of new liquidity amount */ function getRealPoolState(Layout storage l, bool isCall) internal view returns (int128 cLevel64x64, int128 liquidity64x64) { PoolStorage.BatchData storage batchData = l.nextDeposits[isCall]; int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64( isCall, l.getUtilization64x64(isCall) ); int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall); if ( batchData.totalPendingDeposits > 0 && batchData.eta != 0 && block.timestamp >= batchData.eta ) { liquidity64x64 = ABDKMath64x64Token .fromDecimals( batchData.totalPendingDeposits, l.getTokenDecimals(isCall) ) .add(oldLiquidity64x64); cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment( oldCLevel64x64, oldLiquidity64x64, liquidity64x64, isCall ); } else { cLevel64x64 = oldCLevel64x64; liquidity64x64 = oldLiquidity64x64; } } /** * @notice calculate updated C-Level, accounting for unrealized decay * @param l storage layout struct * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for decay * @param isCall whether query is for call or put pool * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after accounting for decay */ function applyCLevelDecayAdjustment( Layout storage l, int128 oldCLevel64x64, bool isCall, int128 utilization64x64 ) internal view returns (int128 cLevel64x64) { uint256 timeElapsed = block.timestamp - (isCall ? l.cLevelUnderlyingUpdatedAt : l.cLevelBaseUpdatedAt); // do not apply C decay if less than 24 hours have elapsed if (timeElapsed > C_DECAY_BUFFER) { timeElapsed -= C_DECAY_BUFFER; } else { return oldCLevel64x64; } int128 timeIntervalsElapsed64x64 = ABDKMath64x64.divu( timeElapsed, C_DECAY_INTERVAL ); return OptionMath.calculateCLevelDecay( OptionMath.CalculateCLevelDecayArgs( timeIntervalsElapsed64x64, oldCLevel64x64, utilization64x64, 0xb333333333333333, // 0.7 0xe666666666666666, // 0.9 0x10000000000000000, // 1.0 0x10000000000000000, // 1.0 0xe666666666666666, // 0.9 0x56fc2a2c515da32ea // 2e ) ); } function getUtilization64x64(Layout storage l, bool isCall) internal view returns (int128 utilization64x64) { uint256 tokenId = formatTokenId( isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ, 0, 0 ); uint256 tvl = l.totalTVL[isCall]; uint256 pendingDeposits = l.totalPendingDeposits(isCall); if (tvl <= pendingDeposits) return 0; uint256 freeLiq = ERC1155EnumerableStorage.layout().totalSupply[ tokenId ]; if (tvl < freeLiq) { // workaround for TVL underflow issue freeLiq = tvl; } utilization64x64 = ABDKMath64x64.divu( tvl - freeLiq, tvl - pendingDeposits ); // Safeguard check require(utilization64x64 <= ONE_64x64, "utilization > 1"); } /** * @notice calculate updated C-Level, accounting for change in liquidity * @param l storage layout struct * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity * @param newLiquidity64x64 64x64 fixed point representation of current liquidity * @param isCallPool whether to update C-Level for call or put pool * @return cLevel64x64 64x64 fixed point representation of C-Level */ function applyCLevelLiquidityChangeAdjustment( Layout storage l, int128 oldCLevel64x64, int128 oldLiquidity64x64, int128 newLiquidity64x64, bool isCallPool ) internal view returns (int128 cLevel64x64) { int128 steepness64x64 = isCallPool ? l.steepnessUnderlying64x64 : l.steepnessBase64x64; // fallback to deprecated storage value if side-specific value is not set if (steepness64x64 == 0) steepness64x64 = l._deprecated_steepness64x64; cLevel64x64 = OptionMath.calculateCLevel( oldCLevel64x64, oldLiquidity64x64, newLiquidity64x64, steepness64x64 ); if (cLevel64x64 < 0xb333333333333333) { cLevel64x64 = int128(0xb333333333333333); // 64x64 fixed point representation of 0.7 } } /** * @notice set C-Level to arbitrary pre-calculated value * @param cLevel64x64 new C-Level of pool * @param isCallPool whether to update C-Level for call or put pool */ function setCLevel( Layout storage l, int128 cLevel64x64, bool isCallPool ) internal { if (isCallPool) { l.cLevelUnderlying64x64 = cLevel64x64; l.cLevelUnderlyingUpdatedAt = block.timestamp; } else { l.cLevelBase64x64 = cLevel64x64; l.cLevelBaseUpdatedAt = block.timestamp; } } function setOracles( Layout storage l, address baseOracle, address underlyingOracle ) internal { require( AggregatorV3Interface(baseOracle).decimals() == AggregatorV3Interface(underlyingOracle).decimals(), "Pool: oracle decimals must match" ); l.baseOracle = baseOracle; l.underlyingOracle = underlyingOracle; } function fetchPriceUpdate(Layout storage l) internal view returns (int128 price64x64) { int256 priceUnderlying = AggregatorInterface(l.underlyingOracle) .latestAnswer(); int256 priceBase = AggregatorInterface(l.baseOracle).latestAnswer(); return ABDKMath64x64.divi(priceUnderlying, priceBase); } /** * @notice set price update for hourly bucket corresponding to given timestamp * @param l storage layout struct * @param timestamp timestamp to update * @param price64x64 64x64 fixed point representation of price */ function setPriceUpdate( Layout storage l, uint256 timestamp, int128 price64x64 ) internal { uint256 bucket = timestamp / (1 hours); l.bucketPrices64x64[bucket] = price64x64; l.priceUpdateSequences[bucket >> 8] += 1 << (255 - (bucket & 255)); } /** * @notice get price update for hourly bucket corresponding to given timestamp * @param l storage layout struct * @param timestamp timestamp to query * @return 64x64 fixed point representation of price */ function getPriceUpdate(Layout storage l, uint256 timestamp) internal view returns (int128) { return l.bucketPrices64x64[timestamp / (1 hours)]; } /** * @notice get first price update available following given timestamp * @param l storage layout struct * @param timestamp timestamp to query * @return 64x64 fixed point representation of price */ function getPriceUpdateAfter(Layout storage l, uint256 timestamp) internal view returns (int128) { // price updates are grouped into hourly buckets uint256 bucket = timestamp / (1 hours); // divide by 256 to get the index of the relevant price update sequence uint256 sequenceId = bucket >> 8; // get position within sequence relevant to current price update uint256 offset = bucket & 255; // shift to skip buckets from earlier in sequence uint256 sequence = (l.priceUpdateSequences[sequenceId] << offset) >> offset; // iterate through future sequences until a price update is found // sequence corresponding to current timestamp used as upper bound uint256 currentPriceUpdateSequenceId = block.timestamp / (256 hours); while (sequence == 0 && sequenceId <= currentPriceUpdateSequenceId) { sequence = l.priceUpdateSequences[++sequenceId]; } // if no price update is found (sequence == 0) function will return 0 // this should never occur, as each relevant external function triggers a price update // the most significant bit of the sequence corresponds to the offset of the relevant bucket uint256 msb; for (uint256 i = 128; i > 0; i >>= 1) { if (sequence >> i > 0) { msb += i; sequence >>= i; } } return l.bucketPrices64x64[((sequenceId + 1) << 8) - msb - 1]; } function totalPendingDeposits(Layout storage l, bool isCallPool) internal view returns (uint256) { return l.nextDeposits[isCallPool].totalPendingDeposits; } function pendingDepositsOf( Layout storage l, address account, bool isCallPool ) internal view returns (uint256) { return l.pendingDeposits[account][l.nextDeposits[isCallPool].eta][ isCallPool ]; } function contractSizeToBaseTokenAmount( Layout storage l, uint256 contractSize, int128 price64x64, bool isCallPool ) internal view returns (uint256 tokenAmount) { if (isCallPool) { tokenAmount = contractSize; } else { uint256 value = price64x64.mulu(contractSize); int128 value64x64 = ABDKMath64x64Token.fromDecimals( value, l.underlyingDecimals ); tokenAmount = ABDKMath64x64Token.toDecimals( value64x64, l.baseDecimals ); } } function setBuybackEnabled( Layout storage l, bool state, bool isCallPool ) internal { l.isBuybackEnabled[msg.sender][isCallPool] = state; } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {PremiaStakingStorage} from "./PremiaStakingStorage.sol"; import {IOFT} from "../layerZero/token/oft/IOFT.sol"; import {IERC2612} from "@solidstate/contracts/token/ERC20/permit/IERC2612.sol"; // IERC20Metadata inheritance not possible due to linearization issue interface IPremiaStaking is IERC2612, IOFT { error PremiaStaking__CantTransfer(); error PremiaStaking__ExcessiveStakePeriod(); error PremiaStaking__NoPendingWithdrawal(); error PremiaStaking__NotEnoughLiquidity(); error PremiaStaking__StakeLocked(); error PremiaStaking__StakeNotLocked(); error PremiaStaking__WithdrawalStillPending(); error PremiaStaking__InsufficientSwapOutput(); event Stake( address indexed user, uint256 amount, uint64 stakePeriod, uint64 lockedUntil ); event Unstake( address indexed user, uint256 amount, uint256 fee, uint256 startDate ); event Harvest(address indexed user, uint256 amount); event EarlyUnstakeRewardCollected(address indexed user, uint256 amount); event Withdraw(address indexed user, uint256 amount); event RewardsAdded(uint256 amount); struct StakeLevel { uint256 amount; // Amount to stake uint256 discountBPS; // Discount when amount is reached } struct SwapArgs { //min amount out to be used to purchase uint256 amountOutMin; // exchange address to call to execute the trade address callee; // address for which to set allowance for the trade address allowanceTarget; // data to execute the trade bytes data; // address to which refund excess tokens address refundAddress; } event BridgeLock( address indexed user, uint64 stakePeriod, uint64 lockedUntil ); /** * @notice Returns the reward token address * @return The reward token address */ function getRewardToken() external view returns (address); /** * @notice add premia tokens as available tokens to be distributed as rewards * @param amount amount of premia tokens to add as rewards */ function addRewards(uint256 amount) external; /** * @notice get amount of tokens that have not yet been distributed as rewards * @return rewards amount of tokens not yet distributed as rewards * @return unstakeRewards amount of PREMIA not yet claimed from early unstake fees */ function getAvailableRewards() external view returns (uint256 rewards, uint256 unstakeRewards); /** * @notice get pending amount of tokens to be distributed as rewards to stakers * @return amount of tokens pending to be distributed as rewards */ function getPendingRewards() external view returns (uint256); /** * @notice get pending withdrawal data of a user * @return amount pending withdrawal amount * @return startDate start timestamp of withdrawal * @return unlockDate timestamp at which withdrawal becomes available */ function getPendingWithdrawal(address user) external view returns ( uint256 amount, uint256 startDate, uint256 unlockDate ); /** * @notice get the amount of PREMIA available for withdrawal * @return amount of PREMIA available for withdrawal */ function getAvailablePremiaAmount() external view returns (uint256); /** * @notice Stake using IERC2612 permit * @param amount The amount of xPremia to stake * @param period The lockup period (in seconds) * @param deadline Deadline after which permit will fail * @param v V * @param r R * @param s S */ function stakeWithPermit( uint256 amount, uint64 period, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @notice Lockup xPremia for protocol fee discounts * Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation * @param amount The amount of xPremia to stake * @param period The lockup period (in seconds) */ function stake(uint256 amount, uint64 period) external; /** * @notice harvest rewards, convert to PREMIA using exchange helper, and stake * @param s swap arguments * @param stakePeriod The lockup period (in seconds) */ function harvestAndStake( IPremiaStaking.SwapArgs memory s, uint64 stakePeriod ) external; /** * @notice Harvest rewards directly to user wallet */ function harvest() external; /** * @notice Get pending rewards amount, including pending pool update * @param user User for which to calculate pending rewards * @return reward amount of pending rewards from protocol fees (in REWARD_TOKEN) * @return unstakeReward amount of pending rewards from early unstake fees (in PREMIA) */ function getPendingUserRewards(address user) external view returns (uint256 reward, uint256 unstakeReward); /** * @notice unstake tokens before end of the lock period, for a fee * @param amount the amount of vxPremia to unstake */ function earlyUnstake(uint256 amount) external; /** * @notice get early unstake fee for given user * @param user address of the user * @return feePercentage % fee to pay for early unstake (1e4 = 100%) */ function getEarlyUnstakeFeeBPS(address user) external view returns (uint256 feePercentage); /** * @notice Initiate the withdrawal process by burning xPremia, starting the delay period * @param amount quantity of xPremia to unstake */ function startWithdraw(uint256 amount) external; /** * @notice Withdraw underlying premia */ function withdraw() external; ////////// // View // ////////// /** * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen * @param user The user from which to query the stake amount * @return The user stake amount after applying the bonus */ function getUserPower(address user) external view returns (uint256); /** * Return the total power across all users (applying the bonus from lockup period chosen) * @return The total power across all users */ function getTotalPower() external view returns (uint256); /** * @notice Calculate the % of fee discount for user, based on his stake * @param user The _user for which the discount is for * @return Percentage of protocol fee discount (in basis point) * Ex : 1000 = 10% fee discount */ function getDiscountBPS(address user) external view returns (uint256); /** * @notice Get stake levels * @return Stake levels * Ex : 2500 = -25% */ function getStakeLevels() external returns (StakeLevel[] memory); /** * @notice Get stake period multiplier * @param period The duration (in seconds) for which tokens are locked * @return The multiplier for this staking period * Ex : 20000 = x2 */ function getStakePeriodMultiplierBPS(uint256 period) external returns (uint256); /** * @notice Get staking infos of a user * @param user The user address for which to get staking infos * @return The staking infos of the user */ function getUserInfo(address user) external view returns (PremiaStakingStorage.UserInfo memory); }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; library PremiaStakingStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.staking.PremiaStaking"); struct Withdrawal { uint256 amount; // Premia amount uint256 startDate; // Will unlock at startDate + withdrawalDelay } struct UserInfo { uint256 reward; // Amount of rewards accrued which havent been claimed yet uint256 rewardDebt; // Debt to subtract from reward calculation uint256 unstakeRewardDebt; // Debt to subtract from reward calculation from early unstake fee uint64 stakePeriod; // Stake period selected by user uint64 lockedUntil; // Timestamp at which the lock ends } struct Layout { uint256 pendingWithdrawal; uint256 _deprecated_withdrawalDelay; mapping(address => Withdrawal) withdrawals; uint256 availableRewards; uint256 lastRewardUpdate; // Timestamp of last reward distribution update uint256 totalPower; // Total power of all staked tokens (underlying amount with multiplier applied) mapping(address => UserInfo) userInfo; uint256 accRewardPerShare; uint256 accUnstakeRewardPerShare; uint256 availableUnstakeRewards; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/libraries/OptionMath.sol": { "OptionMath": "0x0f6e8ef18fb5bb61d545fee60f779d8aed60408f" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"ivolOracle","type":"address"},{"internalType":"address","name":"wrappedNativeToken","type":"address"},{"internalType":"address","name":"premiaMining","type":"address"},{"internalType":"address","name":"feeReceiver","type":"address"},{"internalType":"address","name":"feeDiscountAddress","type":"address"},{"internalType":"int128","name":"feePremium64x64","type":"int128"},{"internalType":"address","name":"exchangeProxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressUtils__NotContract","type":"error"},{"inputs":[],"name":"ERC1155Base__ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"ERC1155Base__BalanceQueryZeroAddress","type":"error"},{"inputs":[],"name":"ERC1155Base__BurnExceedsBalance","type":"error"},{"inputs":[],"name":"ERC1155Base__BurnFromZeroAddress","type":"error"},{"inputs":[],"name":"ERC1155Base__ERC1155ReceiverNotImplemented","type":"error"},{"inputs":[],"name":"ERC1155Base__ERC1155ReceiverRejected","type":"error"},{"inputs":[],"name":"ERC1155Base__MintToZeroAddress","type":"error"},{"inputs":[],"name":"ERC1155Base__NotOwnerOrApproved","type":"error"},{"inputs":[],"name":"ERC1155Base__SelfApproval","type":"error"},{"inputs":[],"name":"ERC1155Base__TransferExceedsBalance","type":"error"},{"inputs":[],"name":"ERC1155Base__TransferToZeroAddress","type":"error"},{"inputs":[],"name":"SafeERC20__OperationFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"APYFeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"APYFeeReserved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Annihilate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervalContractSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"AssignExercise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervalContractSize","type":"uint256"}],"name":"AssignSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"isCallPool","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"longTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"contractSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exerciseValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Exercise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"isCallPool","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeeWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"longTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"contractSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseCost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeCost","type":"uint256"},{"indexed":false,"internalType":"int128","name":"spot64x64","type":"int128"}],"name":"Purchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"longTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"contractSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseCost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeCost","type":"uint256"},{"indexed":false,"internalType":"int128","name":"spot64x64","type":"int128"}],"name":"Sell","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":true,"internalType":"address","name":"longReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"shortTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervalContractSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervalPremium","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isManualUnderwrite","type":"bool"}],"name":"Underwrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"isCall","type":"bool"},{"indexed":false,"internalType":"int128","name":"cLevel64x64","type":"int128"},{"indexed":false,"internalType":"int128","name":"oldLiquidity64x64","type":"int128"},{"indexed":false,"internalType":"int128","name":"newLiquidity64x64","type":"int128"}],"name":"UpdateCLevel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int128","name":"spotOffset64x64","type":"int128"}],"name":"UpdateSpotOffset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int128","name":"steepness64x64","type":"int128"},{"indexed":false,"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"UpdateSteepness","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"isCallPool","type":"bool"},{"indexed":false,"internalType":"uint256","name":"depositedAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"contractSize","type":"uint256"},{"internalType":"bool","name":"divest","type":"bool"}],"name":"annihilate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"contractSize","type":"uint256"},{"internalType":"bool","name":"divest","type":"bool"}],"name":"reassign","outputs":[{"internalType":"uint256","name":"baseCost","type":"uint256"},{"internalType":"uint256","name":"feeCost","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"contractSizes","type":"uint256[]"},{"internalType":"bool","name":"divest","type":"bool"}],"name":"reassignBatch","outputs":[{"internalType":"uint256[]","name":"baseCosts","type":"uint256[]"},{"internalType":"uint256[]","name":"feeCosts","type":"uint256[]"},{"internalType":"uint256","name":"amountOutCall","type":"uint256"},{"internalType":"uint256","name":"amountOutPut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"setDivestmentTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"callee","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"refundAddress","type":"address"}],"internalType":"struct IPoolInternal.SwapArgs","name":"s","type":"tuple"},{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"swapAndDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"updateMiningPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isCallPool","type":"bool"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[{"internalType":"uint256","name":"amountOutCall","type":"uint256"},{"internalType":"uint256","name":"amountOutPut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101e06040523480156200001257600080fd5b506040516200577238038062005772833981016040819052620000359162000174565b6001600160a01b038781166101005286811660805285811660a05284811660c05283811660e052600f83900b61014052811661012052868686868686866200008c600080806200010b602090811b62000a2a17901c565b6101608181525050620000ae60016000806200010b60201b62000a2a1760201c565b6101808181525050620000d060026000806200010b60201b62000a2a1760201c565b6101a08181525050620000f260036000806200010b60201b62000a2a1760201c565b6101c052506200024e9c50505050505050505050505050565b600081600f0b6080846001600160401b0316901b60f886600781111562000136576200013662000210565b6200014392911b62000226565b6200014f919062000226565b949350505050565b80516001600160a01b03811681146200016f57600080fd5b919050565b600080600080600080600060e0888a0312156200019057600080fd5b6200019b8862000157565b9650620001ab6020890162000157565b9550620001bb6040890162000157565b9450620001cb6060890162000157565b9350620001db6080890162000157565b925060a088015180600f0b8114620001f257600080fd5b91506200020260c0890162000157565b905092959891949750929550565b634e487b7160e01b600052602160045260246000fd5b808201808211156200024857634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516153e76200038b60003960008181610ff2015261283b0152600081816110180152818161281101526128b40152600081816111d1015281816127e7015261295a0152600081816111f7015281816127be0152818161288b015281816129310152612983015260006138c4015260008181611b0901528181611bcf0152611bf60152600061367e0152600081816131cf015261321f0152600081816112ae015281816112e00152818161130b01528181611ec401526125b80152600081816105a7015281816108c90152818161098a01528181610d6d01528181612ed601528181613eb60152613f4a015260008181611a0e01528181611a8301528181611b3701528181612cbd0152612ddf01526153e76000f3fe60806040526004361061009c5760003560e01c8063491c011a11610064578063491c011a1461016d5780637365d18d1461018d5780639a408321146101bd578063a2c3c433146101d0578063e4d4517b146101e3578063fbe5f3a11461020357600080fd5b80630178efa0146100a15780630e07ee09146100c35780630e6878a31461010357806338d0743614610123578063476343ee14610143575b600080fd5b3480156100ad57600080fd5b506100c16100bc366004614a48565b610218565b005b3480156100cf57600080fd5b506100e36100de366004614a48565b6102d1565b604080519384526020840192909252908201526060015b60405180910390f35b34801561010f57600080fd5b506100c161011e366004614a81565b6103b7565b34801561012f57600080fd5b506100c161013e366004614a9e565b6103c4565b34801561014f57600080fd5b50610158610539565b604080519283526020830191909152016100fa565b34801561017957600080fd5b506100c1610188366004614ae5565b610558565b34801561019957600080fd5b506101ad6101a8366004614b5c565b610641565b6040516100fa9493929190614c1a565b6100c16101cb366004614a9e565b61079c565b6100c16101de366004614d1d565b6107c6565b3480156101ef57600080fd5b506100c16101fe366004614df3565b610800565b34801561020f57600080fd5b506100c161089f565b60f883901c608084901c846000600584600781111561023957610239614e1d565b14806102565750600484600781111561025457610254614e1d565b145b90506000610262610a6f565b905060006102708284610a93565b9050600061028283338888888e610b86565b90506000610292848b8888610c61565b90506102a23383878c6001610cc7565b6102c48433878c6102bc576102b78587614e49565b6102be565b845b87610d12565b5050505050505050505050565b6000806000806000806102ea6102e5610a6f565b610e3d565b9050608089901c8960f881901c600581600781111561030b5761030b614e1d565b14806103285750600481600781111561032657610326614e1d565b145b95505061033d85610337610a6f565b90610a93565b935061035561034a610a6f565b338484898f89610e9f565b91995097509550610370925033915085905084896001610cc7565b6103ac61037b610a6f565b3384896103915761038c888a614e5c565b6103a6565b8661039c898b614e5c565b6103a69190614e5c565b85610d12565b505093509350939050565b6103c13382610558565b50565b60006103ce610a6f565b336000908152600d8201602090815260408083208615158452909152902054909150426103fe8262015180614e5c565b106104245760405162461bcd60e51b815260040161041b90614e6f565b60405180910390fd5b61042e8284610eef565b600061043a8385610a93565b905060006104488486610f89565b9050600061045586610fe9565b90506000610463338361103d565b905060008882106104745788610476565b815b90506000610484828b614e49565b90508115610497576104973385846110ad565b80156104cd576104b0336104aa8b6111c8565b836110ad565b60006104bc898b610f89565b90506104cb8987838d8b61121c565b505b6104da88338b848a610d12565b6104e8338b8b600180610cc7565b604080518a15158152602081018990529081018b905233907f477b6b4ae4e555826c33dfac094986168321f8f6295ab29cb8109c27e312e50f9060600160405180910390a250505050505050505050565b600080610546600161129b565b9150610552600061129b565b90509091565b6000610562610a6f565b6001600160a01b0384166000908152601782016020908152604080832086151584528252808320546018850190925282205492935091906105a38486610a93565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a12fb66b8730888788886105e589600461137f565b6040518863ffffffff1660e01b81526004016106079796959493929190614e94565b600060405180830381600087803b15801561062157600080fd5b505af1158015610635573d6000803e3d6000fd5b50505050505050505050565b60608060008087861461068a5760405162461bcd60e51b81526020600482015260116024820152700c8d2cccc40c2e4e4c2f240d8cadccee8d607b1b604482015260640161041b565b60006106976102e5610a6f565b905060006106a86001610337610a6f565b905060006106b96000610337610a6f565b905060006107706040518060c001604052808f8f80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505081526020018d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252508b15156020820152600f87810b604083015286810b606083015285900b6080909101526113a1565b805160208201516040830151606090930151919a50985090965094505050505095509550955095915050565b60006107a6610a6f565b90506107b68133858560016117fc565b6107c181848461192a565b505050565b60006107d0610a6f565b905060006107de82846119db565b905060006107ec8583611a04565b90506107f983828661192a565b5050505050565b600061080a610a6f565b90506001600160401b03831615806108565750336000908152600d82016020908152604080832085151584529091529020546108499062015180614e5c565b836001600160401b031610155b6108725760405162461bcd60e51b815260040161041b90614e6f565b336000908152600e909101602090815260408083209315158352929052206001600160401b039091169055565b60006108a9610a6f565b600160008181526018830160205260409020549192506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691637d6d0d399130916109066108ff8784610a93565b600461137f565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152911515602484015260448301526064820152608401600060405180830381600087803b15801561095957600080fd5b505af115801561096d573d6000803e3d6000fd5b505060008080526018840160205260408120546001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169350637d6d0d3992503091906109c36108ff8784610a93565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152911515602484015260448301526064820152608401600060405180830381600087803b158015610a1657600080fd5b505af11580156107f9573d6000803e3d6000fd5b600081600f0b6080846001600160401b0316901b60f8866007811115610a5257610a52614e1d565b610a5d92911b614e5c565b610a679190614e5c565b949350505050565b7fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52eb90565b600080610ab083610aa5576001610aa8565b60005b600080610a2a565b8315156000908152601886016020908152604080832054601389019092529091206001015491925090808211610aec5760009350505050610b80565b6000838152600080516020615392833981519152602052604090205480831015610b135750815b610b2f610b208285614e49565b610b2a8486614e49565b611d08565b9450600160401b600f86900b1315610b7b5760405162461bcd60e51b815260206004820152600f60248201526e7574696c697a6174696f6e203e203160881b604482015260640161041b565b505050505b92915050565b600080610b9e610b97856001611d42565b8787610a2a565b90506000610bb7610bb0866000611d42565b8888610a2a565b90506000610bc78a868989610c61565b90506000610bd78b84848c611d75565b9050610be48a85886110ad565b6000610bf48c8c868a868d611e00565b9050610c018b85896110ad565b81610c0c8285614e5c565b610c169190614e5c565b60408051868152602081018a90529197507f2c36fd3d1ab99ca7bec7ffe3c66b81f331da75eeda6c23fda680a77a7c458979910160405180910390a150505050509695505050505050565b60008115610c70575082610a67565b6000610c80600f85900b86611f45565b90506000610c9f828860030160149054906101000a900460ff16611fb0565b9050610cbc818860030160159054906101000a900460ff1661137f565b979650505050505050565b8115610d07578015610cf457610cef85610ce985610ce3610a6f565b906119db565b86611fc1565b6107f9565b610cef85610d0185610fe9565b86612085565b6107f98585856120a0565b6001600160a01b0384166000908152601786016020908152604080832086151584528252808320546018890190925282205490918085841015610d53578395505b610d5d8685614e49565b9150610d698684614e49565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166313c4d2a589308a888789610dab8d600461137f565b6040518863ffffffff1660e01b8152600401610dcd9796959493929190614e94565b600060405180830381600087803b158015610de757600080fd5b505af1158015610dfb573d6000803e3d6000fd5b505050506001600160a01b0397909716600090815260178901602090815260408083209815158352978152878220929092556018909801905250505092205550565b60004282600c015403610e5457610b808242612171565b610e5d8261219f565b9050610e698242612171565b600f0b600003610e7e57610e7e8242836122a4565b42600c830155610e8f826001610eef565b610e9a826000610eef565b919050565b6000806000610eb38a8a8a8a8a8a8a612322565b90935091506000610ec88b8b8b8b8b8b610b86565b905082610ed58583614e49565b610edf9190614e49565b9150509750975097945050505050565b8015156000908152601383016020526040902080541580610f105750805442105b15610f1a57505050565b6000610f268484610f89565b9050610f6b8482610f5b610f508660010154610f4b898b61265090919063ffffffff16565b611fb0565b600f86900b9061267e565b86610f668982610a93565b61121c565b50501515600090815260139091016020526040812081815560010155565b600080610f9b83610aa5576001610aa8565b8315156000908152601386016020526040902060010154909150610a679060008381526000805160206153928339815191526020526040902054610fdf9190614e49565b610f4b8686612650565b600081611016577f0000000000000000000000000000000000000000000000000000000000000000610b80565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b60006001600160a01b0383166110665760405163db5d879760e01b815260040160405180910390fd5b7f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b6000928352602090815260408084206001600160a01b0395909516845293905250205490565b6001600160a01b0383166110d45760405163baa75df760e01b815260040160405180910390fd5b611102338460006110e4866126b2565b6110ed866126b2565b604051806020016040528060008152506126fd565b60008281527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b0387168452918290529091205482111561116657604051634190713d60e01b815260040160405180910390fd5b6001600160a01b03841660008181526020838152604080832080548790039055805187815291820186905291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a450505050565b6000816111f5577f0000000000000000000000000000000000000000000000000000000000000000610b80565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b6000611229868484612b4c565b9050600061123a8783888888612b66565b9050611247878286612c5c565b60408051600f83810b825288810b602083015287900b818301529051851515917f4e23621c6f591f14bf9505cb8326b45af9dc6c5569fd608de2a7a2ddd6146b2e919081900360600190a250505050505050565b6000806112a783610fe9565b90506112d37f00000000000000000000000000000000000000000000000000000000000000008261103d565b91508115611379576113067f000000000000000000000000000000000000000000000000000000000000000082846110ad565b61133c7f000000000000000000000000000000000000000000000000000000000000000061133685610ce3610a6f565b84611fc1565b8215157f496915fc2759ccee8966a2c2098c4895c608b8decdb351dccea354eb4ca9c4f08360405161137091815260200190565b60405180910390a25b50919050565b600061139a61138f83600a614fb6565b600f85900b90611f45565b9392505050565b6113cc6040518060800160405280606081526020016060815260200160008152602001600081525090565b8151516001600160401b038111156113e6576113e6614c53565b60405190808252806020026020018201604052801561140f578160200160208202803683370190505b5081528151516001600160401b0381111561142c5761142c614c53565b604051908082528060200260200182016040528015611455578160200160208202803683370190505b5060208201528151516000906001600160401b0381111561147857611478614c53565b6040519080825280602002602001820160405280156114a1578160200160208202803683370190505b50905060005b8351518110156116105760008060006114e6876000015185815181106114cf576114cf614fc5565b602002602001015160f881901c91608082901c9190565b919450925090506000600584600781111561150357611503614e1d565b14806115205750600484600781111561151e5761151e614e1d565b145b905060008187878151811061153757611537614fc5565b9115156020928302919091019091015261157e611552610a6f565b338686868e602001518c8151811061156c5761156c614fc5565b60200260200101518f60600151610e9f565b8a5180518a90811061159257611592614fc5565b602002602001018b602001518a815181106115af576115af614fc5565b6020908102919091010192909252919052905081156115e25780886040018181516115da9190614e5c565b9052506115f8565b80886060018181516115f49190614e5c565b9052505b5050505050808061160890614fdb565b9150506114a7565b50604082015115611702576000805b8451518110156116b25782818151811061163b5761163b614fc5565b6020908102919091010151156116a057835180518290811061165f5761165f614fc5565b6020026020010151826116729190614e5c565b91508360200151818151811061168a5761168a614fc5565b60200260200101518261169d9190614e5c565b91505b806116aa81614fdb565b91505061161f565b506116ca338460400151600187604001516001610cc7565b6117006116d5610a6f565b33600187604001516116e757846116f6565b60408701516116f69086614e5c565b8860800151610d12565b505b606082015115611379576000805b8451518110156117a75782818151811061172c5761172c614fc5565b6020026020010151151560011515031561179557835180518290811061175457611754614fc5565b6020026020010151826117679190614e5c565b91508360200151818151811061177f5761177f614fc5565b6020026020010151826117929190614e5c565b91505b8061179f81614fdb565b915050611710565b506117bf338460600151600087604001516001610cc7565b6117f56117ca610a6f565b33600087604001516117dc57846117eb565b60608701516117eb9086614e5c565b8860a00151610d12565b5050919050565b600081156118115761180e8484612cb3565b90505b8084111561183a5761182d856118278387614e49565b85612e57565b6118379082614e5c565b90505b808411156119225761184c86846119db565b6001600160a01b03166323b872dd86306118668589614e49565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156118ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118de9190614ff4565b6119225760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161041b565b505050505050565b336000908152600e84016020908152604080832084151584529091528120556119538382610eef565b336000818152600d8501602090815260408083208515158452909152902042905561198b90849083856119868483610a93565b612e9e565b611999338383600080610cc7565b6040805182151581526020810184905233917f74a132f462598ad738fcdf934a3f668aac78953afdb0c9fb953e16e07cca511c910160405180910390a2505050565b6000816119f25782546001600160a01b031661139a565b5050600101546001600160a01b031690565b60003415611baa577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031683600001516001600160a01b031614611a815760405162461bcd60e51b815260206004820152600d60248201526c3bb937b733903a37b5b2b724b760991b604482015260640161041b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611adc57600080fd5b505af1158015611af0573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301523460248301527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af1158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba89190614ff4565b505b602083015115611bf45760208301518351611bf4916001600160a01b039091169033907f000000000000000000000000000000000000000000000000000000000000000090612fc7565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e25a7ab846000015184348760200151611c399190614e5c565b876060015188608001518960a001518a60c001516040518863ffffffff1660e01b8152600401611c6f9796959493929190615061565b6020604051808303816000875af1158015611c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb291906150b8565b90508260400151811015610b805760405162461bcd60e51b815260206004820152601c60248201527f6e6f7420656e6f756768206f75747075742066726f6d20747261646500000000604482015260640161041b565b600081600003611d1757600080fd5b6000611d238484613027565b905060016001607f1b036001600160801b038216111561139a57600080fd5b60008215611d605781611d56576005611d59565b60045b9050610b80565b81611d6c57600761139a565b50600692915050565b6000816001600160401b0316421015610a67576000611d938561318c565b15611db1576000858152601d87016020526040902054600f0b611dba565b611dba866131ab565b9050611df66301e13380611dd7426001600160401b038716614e49565b611de190876150d1565b611deb91906150fe565b600f83900b90611f45565b9695505050505050565b6001600160a01b0385166000908152601c87016020908152604080832087845290915281205481611e31888861103d565b611e3b87846150d1565b611e4591906150fe565b6001600160a01b0389166000908152601c8b01602090815260408083208b8452909152812080549293508392909190611e7f908490614e49565b90915550611ea49050611e928683614e49565b611e9b8a6131cb565b600f0b90611f45565b9250600083611eb38784614e49565b611ebd9190614e49565b9050611eee7f0000000000000000000000000000000000000000000000000000000000000000828760016000610cc7565b604080516001600160a01b038b168152602081018a90529081018290527fe02f89a472c9f9be95b6093634de6f0967e703e84390d7e00d23e15ea6b997659060600160405180910390a15050509695505050505050565b600081600003611f5757506000610b80565b600083600f0b1215611f6857600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b03811115611f9757600080fd5b60401b8119811115611fa857600080fd5b019392505050565b600061139a83610b2a84600a614fb6565b80600003611fce57505050565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526024820183905283169063a9059cbb906044016020604051808303816000875af115801561201d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120419190614ff4565b6107c15760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161041b565b6107c183838360405180602001604052806000815250613294565b60006120aa610a6f565b905060006120b7836111c8565b905060006120c48261318c565b1115612166576000610104806120da81426150fe565b6120e491906150d1565b6120ee9190614e5c565b6001600160a01b0387166000908152601485016020908152604080832084845282528083208815158452909152812080549293508792909190612132908490614e5c565b9091555050831515600090815260138401602052604081206001810180549192889261215f908490614e5c565b9091555050555b6107f9858286612085565b60006011830181612184610e10856150fe565b8152602081019190915260400160002054600f0b9392505050565b6000808260030160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221b91906150b8565b905060008360020160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229891906150b8565b9050610a678282613376565b60006122b2610e10846150fe565b6000818152601186016020526040902080546001600160801b0319166001600160801b03851617905590506122eb60ff80831690614e49565b6001901b846012016000600884901c815260200190815260200160002060008282546123179190614e5c565b909155505050505050565b60008042876001600160401b0316116123675760405162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015260640161041b565b88600501548410156123a75760405162461bcd60e51b81526020600482015260096024820152681d1bdbc81cdb585b1b60ba1b604482015260640161041b565b60006123b38a87610a93565b905060006123c38b878a8a610c61565b905060006123d0886111c8565b90506123dd8c8c8a613414565b6123e78c8361103d565b6123f19190614e49565b881515600090815260138e0160205260409020600101546124118361318c565b61241b9190614e49565b6124259190614e49565b8211156124605760405162461bcd60e51b8152602060048201526009602482015268696e737566206c697160b81b604482015260640161041b565b505060006124b36040518060c001604052808c6001600160a01b031681526020018b6001600160401b031681526020018a600f0b815260200187600f0b8152602001888152602001891515815250613459565b80519091506124cb906124c68d8a612650565b61137f565b60208201519094506124e1906124c68d8a612650565b925060006124fa6124f3896001611d42565b8b8b610a2a565b6000818152601b8e016020526040902054909150600f0b801580612527575080600f0b8360400151600f0b125b1561256e5782604001518d601b01600084815260200190815260200160002060006101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b5061257a8b8289612085565b60006125868d8a610f89565b90506125988d8d8d8d8c8b8f8b613946565b60006125a48e8b610f89565b90506125b38e83838d8961121c565b6125e27f0000000000000000000000000000000000000000000000000000000000000000878c60016000610cc7565b60408051848152602081018b905290810188905260608101879052600f89900b60808201526001600160a01b038e16907f4719d073a940c087132bffc1aac4f3837ca2c820d95574727828ce38f7fbc8c79060a00160405180910390a2505050505097509795505050505050565b60008161266b576003830154600160a81b900460ff1661139a565b505060030154600160a01b900460ff1690565b6000600f83810b9083900b0160016001607f1b031981128015906126a9575060016001607f1b038113155b61139a57600080fd5b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106126ec576126ec614fc5565b602090810291909101015292915050565b61270b868686868686613b94565b6000612715610a6f565b905060005b8451811015612b4257600085828151811061273757612737614fc5565b60200260200101519050600085838151811061275557612755614fc5565b602002602001015190508060000361276e575050612b30565b6001600160a01b03891661278b576127896015850183613d61565b505b6001600160a01b0388161580156127a857506127a68261318c565b155b156127bc576127ba6015850183613d6d565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061280957507f000000000000000000000000000000000000000000000000000000000000000082145b8061283357507f000000000000000000000000000000000000000000000000000000000000000082145b8061285d57507f000000000000000000000000000000000000000000000000000000000000000082145b1561292f576001600160a01b0389161580159061288257506001600160a01b03881615155b1561292f5760007f00000000000000000000000000000000000000000000000000000000000000008314806128d657507f000000000000000000000000000000000000000000000000000000000000000083145b6001600160a01b038b166000908152600d870160209081526040808320841515845290915290205490915042906129109062015180614e5c565b1061292d5760405162461bcd60e51b815260040161041b90614e6f565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061297c57507f000000000000000000000000000000000000000000000000000000000000000082145b15612abe577f0000000000000000000000000000000000000000000000000000000000000000821460006129b08683613d79565b90506001600160a01b038b1615612a735760006129cd8c8661103d565b905081811180156129e757506129e38285614e5c565b8111155b15612a4b57836129f8888e86613414565b612a029083614e49565b1015612a405760405162461bcd60e51b815260206004820152600d60248201526c496e7375662062616c616e636560981b604482015260640161041b565b612a4b878d85613d93565b6001600160a01b038b1615612a7157612a71878d8d8688612a6c8583610a93565b613e44565b505b6001600160a01b038a1615612abb576000612a8e8b8661103d565b9050818111158015612aa9575081612aa68583614e5c565b10155b15612ab957612ab9878c8561406b565b505b50505b60f882901c6005816007811115612ad757612ad7614e1d565b1480612af457506007816007811115612af257612af2614e1d565b145b15612b2c576000612b1a6005836007811115612b1257612b12614e1d565b889114610a93565b9050612b2a868c8c878786614113565b505b5050505b80612b3a81614fdb565b91505061271a565b5050505050505050565b6000612b58848461428a565b9050610a67848285856142b1565b60008082612b7b576019870154600f0b612b8b565b6019870154600160801b9004600f0b5b905080600f0b600003612ba257506008860154600f0b5b60405163e101a89b60e01b8152600f87810b600483015286810b602483015285810b604483015282900b6064820152730f6e8ef18fb5bb61d545fee60f779d8aed60408f9063e101a89b90608401602060405180830381865af4158015612c0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c319190615132565b915067b33333333333333382600f0b1215612c525767b33333333333333391505b5095945050505050565b8015612c8c576009830180546001600160801b0384166001600160801b031990911617905542600b840155505050565b6008830180546001600160801b03808516600160801b02911617905542600a840155505050565b60003415610b80577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612cf183610ce3610a6f565b6001600160a01b031614612d3a5760405162461bcd60e51b815260206004820152601060248201526f1b9bdd0815d155120819195c1bdcda5d60821b604482015260640161041b565b82341115612dda57604051600090339034869003908381818185875af1925050503d8060008114612d87576040519150601f19603f3d011682016040523d82523d6000602084013e612d8c565b606091505b5050905080612dd15760405162461bcd60e51b8152602060048201526011602482015270115512081c99599d5b990819985a5b1959607a1b604482015260640161041b565b83915050612ddd565b50345b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612e3857600080fd5b505af1158015612e4c573d6000803e3d6000fd5b505050505092915050565b600080612e6383610fe9565b90506000612e71868361103d565b90508015612e9557848111612e865780612e88565b845b9250612e958683856110ad565b50509392505050565b6001600160a01b0380851660009081526017870160209081526040808320871515845282528083205460188a019092529091205490917f0000000000000000000000000000000000000000000000000000000000000000166313c4d2a587308886612f098a82614e5c565b87612f158b600461137f565b6040518863ffffffff1660e01b8152600401612f379796959493929190614e94565b600060405180830381600087803b158015612f5157600080fd5b505af1158015612f65573d6000803e3d6000fd5b505050508382612f759190614e5c565b6001600160a01b038716600090815260178901602090815260408083208915158452909152902055612fa78482614e5c565b941515600090815260189097016020525050604090942091909155505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526130219085906143e6565b50505050565b60008160000361303657600080fd5b60006001600160c01b0384116130615782604085901b81613059576130596150e8565b049050613178565b60c084811c640100000000811061307a576020918201911c5b62010000811061308c576010918201911c5b610100811061309d576008918201911c5b601081106130ad576004918201911c5b600481106130bd576002918201911c5b600281106130cc576001820191505b60bf820360018603901c6001018260ff0387901b816130ed576130ed6150e8565b0492506001600160801b0383111561310457600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015613130576001820391505b608084901b92900382811015613147576001820391505b829003608084901c821461315d5761315d61514d565b88818161316c5761316c6150e8565b04870196505050505050505b6001600160801b0381111561139a57600080fd5b6000908152600080516020615392833981519152602052604090205490565b601e810154600f0b6000819003610e9a5750670666666666666666919050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615610e9a576040516317828a7160e31b81526001600160a01b038381166004830152610b80917f00000000000000000000000000000000000000000000000000000000000000009091169063bc14538890602401602060405180830381865afa158015613268573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328c91906150b8565b612710611d08565b6001600160a01b0384166132bb576040516301c8efbf60e11b815260040160405180910390fd5b6132da336000866132cb876126b2565b6132d4876126b2565b866126fd565b60008381527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b03881684529091528120805484929061332b908490614e5c565b909155505060408051848152602081018490526001600160a01b0386169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291016111ba565b60008160000361338557600080fd5b60008084121561339a57836000039350600190505b60008312156133ac5760009290920391155b60006133b88585613027565b905081156133e6576001607f1b816001600160801b031611156133da57600080fd5b6000039150610b809050565b60016001607f1b03816001600160801b0316111561340357600080fd5b9150610b809050565b505092915050565b6001600160a01b038216600090815260148401602090815260408083208415158085526013880184528285205485529083528184209084529091529020549392505050565b60408051608081018252600080825260208201819052918101829052606081019190915260008260400151600f0b13801561349b575060008260600151600f0b135b80156134b45750600082602001516001600160401b0316115b6134ef5760405162461bcd60e51b815260206004820152600c60248201526b696e76616c6964206172677360a01b604482015260640161041b565b60006134f9610a6f565b90508260a001511561354657601e81015460608401516135399161352b91600f90810b91600160801b9004900b614476565b6060850151600f0b9061267e565b600f0b6060840152613583565b601e810154606084015161357a9161356c91600f90810b91600160801b9004900b614476565b6060850151600f0b906144ac565b600f0b60608401525b608083015160038201546000916135a391600160a01b900460ff16611fb0565b90506000806135bf8660a00151856144df90919063ffffffff16565b91509150600081600f0b136135ff5760405162461bcd60e51b81526020600482015260066024820152656e6f206c697160d01b604482015260640161041b565b60006136274288602001516001600160401b031661361d9190614e49565b6301e13380611d08565b8554600187015460608a01516040808c01519051631af9d50d60e11b81526001600160a01b0394851660048201529284166024840152600f91820b6044840152810b606483015283900b60848201529192506000917f0000000000000000000000000000000000000000000000000000000000000000909116906335f3aa1a9060a401602060405180830381865afa1580156136c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136eb9190615132565b9050600081600f0b1361372a5760405162461bcd60e51b81526020600482015260076024820152660766f6c203d20360cc1b604482015260640161041b565b60008860a001516137545761374f896040015187600f0b61447690919063ffffffff16565b613756565b855b90506000806000730f6e8ef18fb5bb61d545fee60f779d8aed60408f630313686c604051806101400160405280613799898a600f0b61447690919063ffffffff16565b600f0b81526020018f60400151600f0b81526020018f60600151600f0b815260200189600f0b81526020018b600f0b81526020018a600f0b81526020016137ec888c600f0b6144ac90919063ffffffff16565b600f0b8152600160401b60208201526040016138078e614590565b600f0b81526020018f60a0015115158152506040518263ffffffff1660e01b81526004016138359190615163565b606060405180830381865af4158015613852573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613876919061522c565b9250925092508b60a0015161389857613893600f84900b8a614476565b6138b8565b60608c01516138b8906138af600f86900b8c614476565b600f0b906145a8565b600f0b808c526138e8907f0000000000000000000000000000000000000000000000000000000000000000614476565b600f90810b60208d015282810b60408d015281900b60608c01528b5161391f90613911906131cb565b60208d0151600f0b90614476565b8b602001818151613930919061526f565b600f0b905250999b9a5050505050505050505050565b6000613956610bb0846000611d42565b905060006139668a878987610c61565b905060006139768b84848c611d75565b90505b81156102c4578415156000908152600f8c01602090815260408083208380529091528120546001600160a01b0316906139ba826139b5896111c8565b61103d565b90506139c78d83896145ff565b6139fb576139d8826104aa896111c8565b6139e782828960016000610cc7565b6139f48d8389848a610d12565b5050613979565b613a058d88613d79565b811015613a17576139f48d8389613d93565b8b6001600160a01b0316826001600160a01b031603613a4657613a3b8d8389613d93565b6139f48d838961406b565b613a518d8389613414565b613a5b9082614e49565b9050613a886040518060800160405280600081526020016000815260200160008152602001600081525090565b8484613a948b83614e5c565b613a9e9190614e49565b613aa890846150d1565b613ab291906150fe565b60208201819052600003613ac857505050613979565b8481602001511115613adc57602081018590525b8481602001518b613aed91906150d1565b613af791906150fe565b815260208101518590613b0a908b6150d1565b613b1491906150fe565b604082015260208101518590613b2a90866150d1565b613b3491906150fe565b6060820152613b488e848f89858d8d61463c565b6020810151613b579086614e49565b8151909550613b66908b614e49565b9950806040015189613b789190614e49565b9850806060015184613b8a9190614e49565b9350505050613979565b836001600160a01b0316856001600160a01b031614611922576001600160a01b0385811660009081527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424ec602052604080822092871682528120600080516020615392833981519152927fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb929091905b87518110156102c4576000878281518110613c4057613c40614fc5565b602002602001015190506000811115613d58576000898381518110613c6757613c67614fc5565b6020026020010151905060006001600160a01b03168c6001600160a01b031603613cb45760008181526020889052604081208054849290613ca9908490614e5c565b90915550613ce99050565b81613cbf8d8361103d565b03613ce9576000818152602087905260409020613cdc908d614750565b50613ce78582613d6d565b505b6001600160a01b038b16613d205760008181526020889052604081208054849290613d15908490614e49565b90915550613d569050565b613d2a8b8261103d565b600003613d56576000818152602087905260409020613d49908c614765565b50613d548482613d61565b505b505b50600101613c23565b600061139a8383614776565b600061139a83836147c5565b600081613d8a57826004015461139a565b50506005015490565b6001600160a01b038216613da657600080fd5b8015156000908152600f84016020908152604080832060108701909252909120613dd1848383614892565b613ddc575050505050565b6001600160a01b0393841660008181526020838152604080832080549683528184208054978a16808652838620805499909b166001600160a01b0319998a168117909b5599855295909252822080548616909717909655528054821690558254169091555050565b6001600160a01b038086166000908152601788016020818152604080842088151580865290835281852054958a1685529282528084209284529181528183205460188b0190915290822054909185841115613ea657613ea38685614e49565b90505b6000613eb28785614e5c565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166313c4d2a58b308b898789613ef48e600461137f565b6040518863ffffffff1660e01b8152600401613f169796959493929190614e94565b600060405180830381600087803b158015613f3057600080fd5b505af1158015613f44573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166313c4d2a58a308b888689613f888e600461137f565b6040518863ffffffff1660e01b8152600401613faa9796959493929190614e94565b600060405180830381600087803b158015613fc457600080fd5b505af1158015613fd8573d6000803e3d6000fd5b50505050818b60170160008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008a15151515815260200190815260200160002081905550808b60170160008b6001600160a01b03166001600160a01b0316815260200190815260200160002060008a151515158152602001908152602001600020819055505050505050505050505050565b6001600160a01b03821661407e57600080fd5b8015156000908152600f840160209081526040808320601087019092529091206140a9848383614892565b156140b5575050505050565b60008080526020828152604080832080546001600160a01b0390811680865296845282852080546001600160a01b03199081169a909216998a1790558885529490925282208054841690941790935580528154169092179091555050565b6001600160a01b03851615801561413157508161412f8461318c565b145b1561416e5761413f866131ab565b6000848152601d88016020526040902080546001600160801b0319166001600160801b03929092169190911790555b6001600160a01b03841615801561418b57506141898361318c565b155b156141af576000838152601d87016020526040902080546001600160801b03191690555b6001600160a01b038516158015906141cf57506001600160a01b03841615155b15611922576000808060f886901c608087901c8760058360078111156141f7576141f7614e1d565b1495506142068c898389610c61565b94506142148c8a8785611d75565b935050505060006142298a8a89898689611e00565b90506142378a8989856148de565b60006142448b8b876145ff565b159050811561425b5761425b8a8387846000610cc7565b61427d8b8b8784614275576142708689614e49565b614277565b875b8a610d12565b6102c48b8a87878a612e9e565b6000816142a5576008830154600160801b9004600f0b61139a565b505060090154600f0b90565b600080836142c35785600a01546142c9565b85600b01545b6142d39042614e49565b905061a8c08111156142f2576142eb61a8c082614e49565b90506142fb565b84915050610a67565b600061430982613840611d08565b6040805161012081018252600f83810b825289810b602083015287900b8183015267b333333333333333606082015267e66666666666666660808201819052600160401b60a0830181905260c083015260e082015268056fc2a2c515da32ea6101008201529051634916d70d60e01b8152919250730f6e8ef18fb5bb61d545fee60f779d8aed60408f91634916d70d916143a59160040161529c565b602060405180830381865af41580156143c2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbc9190615132565b600061443b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166149689092919063ffffffff16565b8051909150156107c157808060200190518101906144599190614ff4565b6107c1576040516306dacbc160e51b815260040160405180910390fd5b6000600f83810b9083900b0260401d60016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b6000600f82810b9084900b0360016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b8015156000908152601383016020526040812081908161450b856145038882610a93565b889190612b4c565b905060006145198787610f89565b90506000836001015411801561452f5750825415155b801561453c575082544210155b1561457f57614569816145608560010154610f4b8a8c61265090919063ffffffff16565b600f0b9061267e565b9350614578878383878a612b66565b9450614586565b8194508093505b5050509250929050565b6000600361459d836131ab565b600f0b901b92915050565b600081600f0b6000036145ba57600080fd5b600082600f0b604085600f0b901b816145d5576145d56150e8565b05905060016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b6001600160a01b0382166000908152600e840160209081526040808320841515845290915281205480158061463357504281115b95945050505050565b61464c87878686606001516148de565b6000836060015184602001516146629190614e5c565b8460400151149050806146a4576146a48761467c856111c8565b8660400151876060015188602001516146959190614e5c565b61469f9190614e49565b6110ad565b6146b387868660000151612085565b6146d3888885876060015188604001516146cd9190614e49565b86612e9e565b856001600160a01b0316876001600160a01b03167fa30eb1f1bb0892af5e3389941c42bc7f44a8bf7b6071ecfe3e964673c908f2b28787600001518561471d578860400151614720565b60005b60408051938452602084019290925290820152841515606082015260800160405180910390a35050505050505050565b600061139a836001600160a01b0384166147c5565b600061139a836001600160a01b0384165b60008181526001830160205260408120546147bd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610b80565b506000610b80565b60008181526001830160205260408120548015614888578354600090859060001981019081106147f7576147f7614fc5565b906000526020600020015490508085600001600184038154811061481d5761481d614fc5565b600091825260208083209091019290925591825260018601905260409020819055835484908061484f5761484f61534c565b60019003818190600052602060002001600090559055836001016000848152602001908152602001600020600090556001915050610b80565b6000915050610b80565b6001600160a01b03838116600090815260208490526040812054909116151580610a6757506000808052602083905260409020546001600160a01b039081169085161490509392505050565b6001600160a01b0383166000908152601c85016020908152604080832085845290915281208054839290614913908490614e5c565b9091555050604080516001600160a01b0385168152602081018490529081018290527f3a8ab48b47eb6f5307d9b87d560cc92cf89c416cce1d94297a70d1c82d8c84129060600160405180910390a150505050565b6060610a6784846000856060843b61499357604051632270d6bf60e21b815260040160405180910390fd5b600080866001600160a01b031685876040516149af9190615362565b60006040518083038185875af1925050503d80600081146149ec576040519150601f19603f3d011682016040523d82523d6000602084013e6149f1565b606091505b50915091508115614a05579150610a679050565b805115614a155780518082602001fd5b8360405162461bcd60e51b815260040161041b919061537e565b80151581146103c157600080fd5b8035610e9a81614a2f565b600080600060608486031215614a5d57600080fd5b83359250602084013591506040840135614a7681614a2f565b809150509250925092565b600060208284031215614a9357600080fd5b813561139a81614a2f565b60008060408385031215614ab157600080fd5b823591506020830135614ac381614a2f565b809150509250929050565b80356001600160a01b0381168114610e9a57600080fd5b60008060408385031215614af857600080fd5b614b0183614ace565b91506020830135614ac381614a2f565b60008083601f840112614b2357600080fd5b5081356001600160401b03811115614b3a57600080fd5b6020830191508360208260051b8501011115614b5557600080fd5b9250929050565b600080600080600060608688031215614b7457600080fd5b85356001600160401b0380821115614b8b57600080fd5b614b9789838a01614b11565b90975095506020880135915080821115614bb057600080fd5b50614bbd88828901614b11565b9094509250506040860135614bd181614a2f565b809150509295509295909350565b600081518084526020808501945080840160005b83811015614c0f57815187529582019590820190600101614bf3565b509495945050505050565b608081526000614c2d6080830187614bdf565b8281036020840152614c3f8187614bdf565b604084019590955250506060015292915050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715614c8b57614c8b614c53565b60405290565b600082601f830112614ca257600080fd5b81356001600160401b0380821115614cbc57614cbc614c53565b604051601f8301601f19908116603f01168101908282118183101715614ce457614ce4614c53565b81604052838152866020858801011115614cfd57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215614d3057600080fd5b82356001600160401b0380821115614d4757600080fd5b9084019060e08287031215614d5b57600080fd5b614d63614c69565b614d6c83614ace565b81526020830135602082015260408301356040820152614d8e60608401614ace565b6060820152614d9f60808401614ace565b608082015260a083013582811115614db657600080fd5b614dc288828601614c91565b60a083015250614dd460c08401614ace565b60c08201529350614dea91505060208401614a3d565b90509250929050565b60008060408385031215614e0657600080fd5b82356001600160401b0381168114614b0157600080fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610b8057610b80614e33565b80820180821115610b8057610b80614e33565b6020808252600b908201526a1b1a5c481b1bd8dac80c5960aa1b604082015260600190565b6001600160a01b03978816815295909616602086015292151560408501526060840191909152608083015260a082015260c081019190915260e00190565b600181815b80851115614f0d578160001904821115614ef357614ef3614e33565b80851615614f0057918102915b93841c9390800290614ed7565b509250929050565b600082614f2457506001610b80565b81614f3157506000610b80565b8160018114614f475760028114614f5157614f6d565b6001915050610b80565b60ff841115614f6257614f62614e33565b50506001821b610b80565b5060208310610133831016604e8410600b8410161715614f90575081810a610b80565b614f9a8383614ed2565b8060001904821115614fae57614fae614e33565b029392505050565b600061139a60ff841683614f15565b634e487b7160e01b600052603260045260246000fd5b600060018201614fed57614fed614e33565b5060010190565b60006020828403121561500657600080fd5b815161139a81614a2f565b60005b8381101561502c578181015183820152602001615014565b50506000910152565b6000815180845261504d816020860160208601615011565b601f01601f19169290920160200192915050565b600060018060a01b03808a16835280891660208401528760408401528087166060840152808616608084015260e060a08401526150a160e0840186615035565b915080841660c08401525098975050505050505050565b6000602082840312156150ca57600080fd5b5051919050565b8082028115828204841417610b8057610b80614e33565b634e487b7160e01b600052601260045260246000fd5b60008261511b57634e487b7160e01b600052601260045260246000fd5b500490565b8051600f81900b8114610e9a57600080fd5b60006020828403121561514457600080fd5b61139a82615120565b634e487b7160e01b600052600160045260246000fd5b8151600f0b8152610140810160208301516151836020840182600f0b9052565b5060408301516151986040840182600f0b9052565b5060608301516151ad6060840182600f0b9052565b5060808301516151c26080840182600f0b9052565b5060a08301516151d760a0840182600f0b9052565b5060c08301516151ec60c0840182600f0b9052565b5060e083015161520160e0840182600f0b9052565b506101008084015161521782850182600f0b9052565b5050610120838101518015158483015261340c565b60008060006060848603121561524157600080fd5b61524a84615120565b925061525860208501615120565b915061526660408501615120565b90509250925092565b600f82810b9082900b0360016001607f1b0319811260016001607f1b0382131715610b8057610b80614e33565b6000610120820190508251600f0b82526020830151600f0b602083015260408301516152cd6040840182600f0b9052565b5060608301516152e26060840182600f0b9052565b5060808301516152f76080840182600f0b9052565b5060a083015161530c60a0840182600f0b9052565b5060c083015161532160c0840182600f0b9052565b5060e083015161533660e0840182600f0b9052565b506101008084015161340c82850182600f0b9052565b634e487b7160e01b600052603160045260246000fd5b60008251615374818460208701615011565b9190910192915050565b60208152600061139a602083018461503556feb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eaa26469706673582212207a9db0325c09b850d185d3d8462ddf7f665833c1a17134f34fbf1068ef897f5464736f6c634300081100330000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f900000000000000000000000000000000000000000000000007ae147ae147ae14000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
Deployed Bytecode
0x60806040526004361061009c5760003560e01c8063491c011a11610064578063491c011a1461016d5780637365d18d1461018d5780639a408321146101bd578063a2c3c433146101d0578063e4d4517b146101e3578063fbe5f3a11461020357600080fd5b80630178efa0146100a15780630e07ee09146100c35780630e6878a31461010357806338d0743614610123578063476343ee14610143575b600080fd5b3480156100ad57600080fd5b506100c16100bc366004614a48565b610218565b005b3480156100cf57600080fd5b506100e36100de366004614a48565b6102d1565b604080519384526020840192909252908201526060015b60405180910390f35b34801561010f57600080fd5b506100c161011e366004614a81565b6103b7565b34801561012f57600080fd5b506100c161013e366004614a9e565b6103c4565b34801561014f57600080fd5b50610158610539565b604080519283526020830191909152016100fa565b34801561017957600080fd5b506100c1610188366004614ae5565b610558565b34801561019957600080fd5b506101ad6101a8366004614b5c565b610641565b6040516100fa9493929190614c1a565b6100c16101cb366004614a9e565b61079c565b6100c16101de366004614d1d565b6107c6565b3480156101ef57600080fd5b506100c16101fe366004614df3565b610800565b34801561020f57600080fd5b506100c161089f565b60f883901c608084901c846000600584600781111561023957610239614e1d565b14806102565750600484600781111561025457610254614e1d565b145b90506000610262610a6f565b905060006102708284610a93565b9050600061028283338888888e610b86565b90506000610292848b8888610c61565b90506102a23383878c6001610cc7565b6102c48433878c6102bc576102b78587614e49565b6102be565b845b87610d12565b5050505050505050505050565b6000806000806000806102ea6102e5610a6f565b610e3d565b9050608089901c8960f881901c600581600781111561030b5761030b614e1d565b14806103285750600481600781111561032657610326614e1d565b145b95505061033d85610337610a6f565b90610a93565b935061035561034a610a6f565b338484898f89610e9f565b91995097509550610370925033915085905084896001610cc7565b6103ac61037b610a6f565b3384896103915761038c888a614e5c565b6103a6565b8661039c898b614e5c565b6103a69190614e5c565b85610d12565b505093509350939050565b6103c13382610558565b50565b60006103ce610a6f565b336000908152600d8201602090815260408083208615158452909152902054909150426103fe8262015180614e5c565b106104245760405162461bcd60e51b815260040161041b90614e6f565b60405180910390fd5b61042e8284610eef565b600061043a8385610a93565b905060006104488486610f89565b9050600061045586610fe9565b90506000610463338361103d565b905060008882106104745788610476565b815b90506000610484828b614e49565b90508115610497576104973385846110ad565b80156104cd576104b0336104aa8b6111c8565b836110ad565b60006104bc898b610f89565b90506104cb8987838d8b61121c565b505b6104da88338b848a610d12565b6104e8338b8b600180610cc7565b604080518a15158152602081018990529081018b905233907f477b6b4ae4e555826c33dfac094986168321f8f6295ab29cb8109c27e312e50f9060600160405180910390a250505050505050505050565b600080610546600161129b565b9150610552600061129b565b90509091565b6000610562610a6f565b6001600160a01b0384166000908152601782016020908152604080832086151584528252808320546018850190925282205492935091906105a38486610a93565b90507f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e97036001600160a01b031663a12fb66b8730888788886105e589600461137f565b6040518863ffffffff1660e01b81526004016106079796959493929190614e94565b600060405180830381600087803b15801561062157600080fd5b505af1158015610635573d6000803e3d6000fd5b50505050505050505050565b60608060008087861461068a5760405162461bcd60e51b81526020600482015260116024820152700c8d2cccc40c2e4e4c2f240d8cadccee8d607b1b604482015260640161041b565b60006106976102e5610a6f565b905060006106a86001610337610a6f565b905060006106b96000610337610a6f565b905060006107706040518060c001604052808f8f80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505081526020018d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252508b15156020820152600f87810b604083015286810b606083015285900b6080909101526113a1565b805160208201516040830151606090930151919a50985090965094505050505095509550955095915050565b60006107a6610a6f565b90506107b68133858560016117fc565b6107c181848461192a565b505050565b60006107d0610a6f565b905060006107de82846119db565b905060006107ec8583611a04565b90506107f983828661192a565b5050505050565b600061080a610a6f565b90506001600160401b03831615806108565750336000908152600d82016020908152604080832085151584529091529020546108499062015180614e5c565b836001600160401b031610155b6108725760405162461bcd60e51b815260040161041b90614e6f565b336000908152600e909101602090815260408083209315158352929052206001600160401b039091169055565b60006108a9610a6f565b600160008181526018830160205260409020549192506001600160a01b037f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e97031691637d6d0d399130916109066108ff8784610a93565b600461137f565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152911515602484015260448301526064820152608401600060405180830381600087803b15801561095957600080fd5b505af115801561096d573d6000803e3d6000fd5b505060008080526018840160205260408120546001600160a01b037f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703169350637d6d0d3992503091906109c36108ff8784610a93565b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152911515602484015260448301526064820152608401600060405180830381600087803b158015610a1657600080fd5b505af11580156107f9573d6000803e3d6000fd5b600081600f0b6080846001600160401b0316901b60f8866007811115610a5257610a52614e1d565b610a5d92911b614e5c565b610a679190614e5c565b949350505050565b7fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52eb90565b600080610ab083610aa5576001610aa8565b60005b600080610a2a565b8315156000908152601886016020908152604080832054601389019092529091206001015491925090808211610aec5760009350505050610b80565b6000838152600080516020615392833981519152602052604090205480831015610b135750815b610b2f610b208285614e49565b610b2a8486614e49565b611d08565b9450600160401b600f86900b1315610b7b5760405162461bcd60e51b815260206004820152600f60248201526e7574696c697a6174696f6e203e203160881b604482015260640161041b565b505050505b92915050565b600080610b9e610b97856001611d42565b8787610a2a565b90506000610bb7610bb0866000611d42565b8888610a2a565b90506000610bc78a868989610c61565b90506000610bd78b84848c611d75565b9050610be48a85886110ad565b6000610bf48c8c868a868d611e00565b9050610c018b85896110ad565b81610c0c8285614e5c565b610c169190614e5c565b60408051868152602081018a90529197507f2c36fd3d1ab99ca7bec7ffe3c66b81f331da75eeda6c23fda680a77a7c458979910160405180910390a150505050509695505050505050565b60008115610c70575082610a67565b6000610c80600f85900b86611f45565b90506000610c9f828860030160149054906101000a900460ff16611fb0565b9050610cbc818860030160159054906101000a900460ff1661137f565b979650505050505050565b8115610d07578015610cf457610cef85610ce985610ce3610a6f565b906119db565b86611fc1565b6107f9565b610cef85610d0185610fe9565b86612085565b6107f98585856120a0565b6001600160a01b0384166000908152601786016020908152604080832086151584528252808320546018890190925282205490918085841015610d53578395505b610d5d8685614e49565b9150610d698684614e49565b90507f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e97036001600160a01b03166313c4d2a589308a888789610dab8d600461137f565b6040518863ffffffff1660e01b8152600401610dcd9796959493929190614e94565b600060405180830381600087803b158015610de757600080fd5b505af1158015610dfb573d6000803e3d6000fd5b505050506001600160a01b0397909716600090815260178901602090815260408083209815158352978152878220929092556018909801905250505092205550565b60004282600c015403610e5457610b808242612171565b610e5d8261219f565b9050610e698242612171565b600f0b600003610e7e57610e7e8242836122a4565b42600c830155610e8f826001610eef565b610e9a826000610eef565b919050565b6000806000610eb38a8a8a8a8a8a8a612322565b90935091506000610ec88b8b8b8b8b8b610b86565b905082610ed58583614e49565b610edf9190614e49565b9150509750975097945050505050565b8015156000908152601383016020526040902080541580610f105750805442105b15610f1a57505050565b6000610f268484610f89565b9050610f6b8482610f5b610f508660010154610f4b898b61265090919063ffffffff16565b611fb0565b600f86900b9061267e565b86610f668982610a93565b61121c565b50501515600090815260139091016020526040812081815560010155565b600080610f9b83610aa5576001610aa8565b8315156000908152601386016020526040902060010154909150610a679060008381526000805160206153928339815191526020526040902054610fdf9190614e49565b610f4b8686612650565b600081611016577f0300000000000000000000000000000000000000000000000000000000000000610b80565b7f020000000000000000000000000000000000000000000000000000000000000092915050565b60006001600160a01b0383166110665760405163db5d879760e01b815260040160405180910390fd5b7f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b6000928352602090815260408084206001600160a01b0395909516845293905250205490565b6001600160a01b0383166110d45760405163baa75df760e01b815260040160405180910390fd5b611102338460006110e4866126b2565b6110ed866126b2565b604051806020016040528060008152506126fd565b60008281527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b0387168452918290529091205482111561116657604051634190713d60e01b815260040160405180910390fd5b6001600160a01b03841660008181526020838152604080832080548790039055805187815291820186905291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a450505050565b6000816111f5577f0100000000000000000000000000000000000000000000000000000000000000610b80565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b6000611229868484612b4c565b9050600061123a8783888888612b66565b9050611247878286612c5c565b60408051600f83810b825288810b602083015287900b818301529051851515917f4e23621c6f591f14bf9505cb8326b45af9dc6c5569fd608de2a7a2ddd6146b2e919081900360600190a250505050505050565b6000806112a783610fe9565b90506112d37f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf8261103d565b91508115611379576113067f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf82846110ad565b61133c7f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf61133685610ce3610a6f565b84611fc1565b8215157f496915fc2759ccee8966a2c2098c4895c608b8decdb351dccea354eb4ca9c4f08360405161137091815260200190565b60405180910390a25b50919050565b600061139a61138f83600a614fb6565b600f85900b90611f45565b9392505050565b6113cc6040518060800160405280606081526020016060815260200160008152602001600081525090565b8151516001600160401b038111156113e6576113e6614c53565b60405190808252806020026020018201604052801561140f578160200160208202803683370190505b5081528151516001600160401b0381111561142c5761142c614c53565b604051908082528060200260200182016040528015611455578160200160208202803683370190505b5060208201528151516000906001600160401b0381111561147857611478614c53565b6040519080825280602002602001820160405280156114a1578160200160208202803683370190505b50905060005b8351518110156116105760008060006114e6876000015185815181106114cf576114cf614fc5565b602002602001015160f881901c91608082901c9190565b919450925090506000600584600781111561150357611503614e1d565b14806115205750600484600781111561151e5761151e614e1d565b145b905060008187878151811061153757611537614fc5565b9115156020928302919091019091015261157e611552610a6f565b338686868e602001518c8151811061156c5761156c614fc5565b60200260200101518f60600151610e9f565b8a5180518a90811061159257611592614fc5565b602002602001018b602001518a815181106115af576115af614fc5565b6020908102919091010192909252919052905081156115e25780886040018181516115da9190614e5c565b9052506115f8565b80886060018181516115f49190614e5c565b9052505b5050505050808061160890614fdb565b9150506114a7565b50604082015115611702576000805b8451518110156116b25782818151811061163b5761163b614fc5565b6020908102919091010151156116a057835180518290811061165f5761165f614fc5565b6020026020010151826116729190614e5c565b91508360200151818151811061168a5761168a614fc5565b60200260200101518261169d9190614e5c565b91505b806116aa81614fdb565b91505061161f565b506116ca338460400151600187604001516001610cc7565b6117006116d5610a6f565b33600187604001516116e757846116f6565b60408701516116f69086614e5c565b8860800151610d12565b505b606082015115611379576000805b8451518110156117a75782818151811061172c5761172c614fc5565b6020026020010151151560011515031561179557835180518290811061175457611754614fc5565b6020026020010151826117679190614e5c565b91508360200151818151811061177f5761177f614fc5565b6020026020010151826117929190614e5c565b91505b8061179f81614fdb565b915050611710565b506117bf338460600151600087604001516001610cc7565b6117f56117ca610a6f565b33600087604001516117dc57846117eb565b60608701516117eb9086614e5c565b8860a00151610d12565b5050919050565b600081156118115761180e8484612cb3565b90505b8084111561183a5761182d856118278387614e49565b85612e57565b6118379082614e5c565b90505b808411156119225761184c86846119db565b6001600160a01b03166323b872dd86306118668589614e49565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156118ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118de9190614ff4565b6119225760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161041b565b505050505050565b336000908152600e84016020908152604080832084151584529091528120556119538382610eef565b336000818152600d8501602090815260408083208515158452909152902042905561198b90849083856119868483610a93565b612e9e565b611999338383600080610cc7565b6040805182151581526020810184905233917f74a132f462598ad738fcdf934a3f668aac78953afdb0c9fb953e16e07cca511c910160405180910390a2505050565b6000816119f25782546001600160a01b031661139a565b5050600101546001600160a01b031690565b60003415611baa577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031683600001516001600160a01b031614611a815760405162461bcd60e51b815260206004820152600d60248201526c3bb937b733903a37b5b2b724b760991b604482015260640161041b565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611adc57600080fd5b505af1158015611af0573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5811660048301523460248301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb925060440190506020604051808303816000875af1158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba89190614ff4565b505b602083015115611bf45760208301518351611bf4916001600160a01b039091169033907f000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb590612fc7565b7f000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb56001600160a01b0316632e25a7ab846000015184348760200151611c399190614e5c565b876060015188608001518960a001518a60c001516040518863ffffffff1660e01b8152600401611c6f9796959493929190615061565b6020604051808303816000875af1158015611c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb291906150b8565b90508260400151811015610b805760405162461bcd60e51b815260206004820152601c60248201527f6e6f7420656e6f756768206f75747075742066726f6d20747261646500000000604482015260640161041b565b600081600003611d1757600080fd5b6000611d238484613027565b905060016001607f1b036001600160801b038216111561139a57600080fd5b60008215611d605781611d56576005611d59565b60045b9050610b80565b81611d6c57600761139a565b50600692915050565b6000816001600160401b0316421015610a67576000611d938561318c565b15611db1576000858152601d87016020526040902054600f0b611dba565b611dba866131ab565b9050611df66301e13380611dd7426001600160401b038716614e49565b611de190876150d1565b611deb91906150fe565b600f83900b90611f45565b9695505050505050565b6001600160a01b0385166000908152601c87016020908152604080832087845290915281205481611e31888861103d565b611e3b87846150d1565b611e4591906150fe565b6001600160a01b0389166000908152601c8b01602090815260408083208b8452909152812080549293508392909190611e7f908490614e49565b90915550611ea49050611e928683614e49565b611e9b8a6131cb565b600f0b90611f45565b9250600083611eb38784614e49565b611ebd9190614e49565b9050611eee7f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf828760016000610cc7565b604080516001600160a01b038b168152602081018a90529081018290527fe02f89a472c9f9be95b6093634de6f0967e703e84390d7e00d23e15ea6b997659060600160405180910390a15050509695505050505050565b600081600003611f5757506000610b80565b600083600f0b1215611f6857600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b03811115611f9757600080fd5b60401b8119811115611fa857600080fd5b019392505050565b600061139a83610b2a84600a614fb6565b80600003611fce57505050565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526024820183905283169063a9059cbb906044016020604051808303816000875af115801561201d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120419190614ff4565b6107c15760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161041b565b6107c183838360405180602001604052806000815250613294565b60006120aa610a6f565b905060006120b7836111c8565b905060006120c48261318c565b1115612166576000610104806120da81426150fe565b6120e491906150d1565b6120ee9190614e5c565b6001600160a01b0387166000908152601485016020908152604080832084845282528083208815158452909152812080549293508792909190612132908490614e5c565b9091555050831515600090815260138401602052604081206001810180549192889261215f908490614e5c565b9091555050555b6107f9858286612085565b60006011830181612184610e10856150fe565b8152602081019190915260400160002054600f0b9392505050565b6000808260030160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221b91906150b8565b905060008360020160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229891906150b8565b9050610a678282613376565b60006122b2610e10846150fe565b6000818152601186016020526040902080546001600160801b0319166001600160801b03851617905590506122eb60ff80831690614e49565b6001901b846012016000600884901c815260200190815260200160002060008282546123179190614e5c565b909155505050505050565b60008042876001600160401b0316116123675760405162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015260640161041b565b88600501548410156123a75760405162461bcd60e51b81526020600482015260096024820152681d1bdbc81cdb585b1b60ba1b604482015260640161041b565b60006123b38a87610a93565b905060006123c38b878a8a610c61565b905060006123d0886111c8565b90506123dd8c8c8a613414565b6123e78c8361103d565b6123f19190614e49565b881515600090815260138e0160205260409020600101546124118361318c565b61241b9190614e49565b6124259190614e49565b8211156124605760405162461bcd60e51b8152602060048201526009602482015268696e737566206c697160b81b604482015260640161041b565b505060006124b36040518060c001604052808c6001600160a01b031681526020018b6001600160401b031681526020018a600f0b815260200187600f0b8152602001888152602001891515815250613459565b80519091506124cb906124c68d8a612650565b61137f565b60208201519094506124e1906124c68d8a612650565b925060006124fa6124f3896001611d42565b8b8b610a2a565b6000818152601b8e016020526040902054909150600f0b801580612527575080600f0b8360400151600f0b125b1561256e5782604001518d601b01600084815260200190815260200160002060006101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b5061257a8b8289612085565b60006125868d8a610f89565b90506125988d8d8d8d8c8b8f8b613946565b60006125a48e8b610f89565b90506125b38e83838d8961121c565b6125e27f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf878c60016000610cc7565b60408051848152602081018b905290810188905260608101879052600f89900b60808201526001600160a01b038e16907f4719d073a940c087132bffc1aac4f3837ca2c820d95574727828ce38f7fbc8c79060a00160405180910390a2505050505097509795505050505050565b60008161266b576003830154600160a81b900460ff1661139a565b505060030154600160a01b900460ff1690565b6000600f83810b9083900b0160016001607f1b031981128015906126a9575060016001607f1b038113155b61139a57600080fd5b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106126ec576126ec614fc5565b602090810291909101015292915050565b61270b868686868686613b94565b6000612715610a6f565b905060005b8451811015612b4257600085828151811061273757612737614fc5565b60200260200101519050600085838151811061275557612755614fc5565b602002602001015190508060000361276e575050612b30565b6001600160a01b03891661278b576127896015850183613d61565b505b6001600160a01b0388161580156127a857506127a68261318c565b155b156127bc576127ba6015850183613d6d565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061280957507f010000000000000000000000000000000000000000000000000000000000000082145b8061283357507f020000000000000000000000000000000000000000000000000000000000000082145b8061285d57507f030000000000000000000000000000000000000000000000000000000000000082145b1561292f576001600160a01b0389161580159061288257506001600160a01b03881615155b1561292f5760007f00000000000000000000000000000000000000000000000000000000000000008314806128d657507f020000000000000000000000000000000000000000000000000000000000000083145b6001600160a01b038b166000908152600d870160209081526040808320841515845290915290205490915042906129109062015180614e5c565b1061292d5760405162461bcd60e51b815260040161041b90614e6f565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061297c57507f010000000000000000000000000000000000000000000000000000000000000082145b15612abe577f0000000000000000000000000000000000000000000000000000000000000000821460006129b08683613d79565b90506001600160a01b038b1615612a735760006129cd8c8661103d565b905081811180156129e757506129e38285614e5c565b8111155b15612a4b57836129f8888e86613414565b612a029083614e49565b1015612a405760405162461bcd60e51b815260206004820152600d60248201526c496e7375662062616c616e636560981b604482015260640161041b565b612a4b878d85613d93565b6001600160a01b038b1615612a7157612a71878d8d8688612a6c8583610a93565b613e44565b505b6001600160a01b038a1615612abb576000612a8e8b8661103d565b9050818111158015612aa9575081612aa68583614e5c565b10155b15612ab957612ab9878c8561406b565b505b50505b60f882901c6005816007811115612ad757612ad7614e1d565b1480612af457506007816007811115612af257612af2614e1d565b145b15612b2c576000612b1a6005836007811115612b1257612b12614e1d565b889114610a93565b9050612b2a868c8c878786614113565b505b5050505b80612b3a81614fdb565b91505061271a565b5050505050505050565b6000612b58848461428a565b9050610a67848285856142b1565b60008082612b7b576019870154600f0b612b8b565b6019870154600160801b9004600f0b5b905080600f0b600003612ba257506008860154600f0b5b60405163e101a89b60e01b8152600f87810b600483015286810b602483015285810b604483015282900b6064820152730f6e8ef18fb5bb61d545fee60f779d8aed60408f9063e101a89b90608401602060405180830381865af4158015612c0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c319190615132565b915067b33333333333333382600f0b1215612c525767b33333333333333391505b5095945050505050565b8015612c8c576009830180546001600160801b0384166001600160801b031990911617905542600b840155505050565b6008830180546001600160801b03808516600160801b02911617905542600a840155505050565b60003415610b80577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316612cf183610ce3610a6f565b6001600160a01b031614612d3a5760405162461bcd60e51b815260206004820152601060248201526f1b9bdd0815d155120819195c1bdcda5d60821b604482015260640161041b565b82341115612dda57604051600090339034869003908381818185875af1925050503d8060008114612d87576040519150601f19603f3d011682016040523d82523d6000602084013e612d8c565b606091505b5050905080612dd15760405162461bcd60e51b8152602060048201526011602482015270115512081c99599d5b990819985a5b1959607a1b604482015260640161041b565b83915050612ddd565b50345b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612e3857600080fd5b505af1158015612e4c573d6000803e3d6000fd5b505050505092915050565b600080612e6383610fe9565b90506000612e71868361103d565b90508015612e9557848111612e865780612e88565b845b9250612e958683856110ad565b50509392505050565b6001600160a01b0380851660009081526017870160209081526040808320871515845282528083205460188a019092529091205490917f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703166313c4d2a587308886612f098a82614e5c565b87612f158b600461137f565b6040518863ffffffff1660e01b8152600401612f379796959493929190614e94565b600060405180830381600087803b158015612f5157600080fd5b505af1158015612f65573d6000803e3d6000fd5b505050508382612f759190614e5c565b6001600160a01b038716600090815260178901602090815260408083208915158452909152902055612fa78482614e5c565b941515600090815260189097016020525050604090942091909155505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526130219085906143e6565b50505050565b60008160000361303657600080fd5b60006001600160c01b0384116130615782604085901b81613059576130596150e8565b049050613178565b60c084811c640100000000811061307a576020918201911c5b62010000811061308c576010918201911c5b610100811061309d576008918201911c5b601081106130ad576004918201911c5b600481106130bd576002918201911c5b600281106130cc576001820191505b60bf820360018603901c6001018260ff0387901b816130ed576130ed6150e8565b0492506001600160801b0383111561310457600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015613130576001820391505b608084901b92900382811015613147576001820391505b829003608084901c821461315d5761315d61514d565b88818161316c5761316c6150e8565b04870196505050505050505b6001600160801b0381111561139a57600080fd5b6000908152600080516020615392833981519152602052604090205490565b601e810154600f0b6000819003610e9a5750670666666666666666919050565b60007f000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f96001600160a01b031615610e9a576040516317828a7160e31b81526001600160a01b038381166004830152610b80917f000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f99091169063bc14538890602401602060405180830381865afa158015613268573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328c91906150b8565b612710611d08565b6001600160a01b0384166132bb576040516301c8efbf60e11b815260040160405180910390fd5b6132da336000866132cb876126b2565b6132d4876126b2565b866126fd565b60008381527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b03881684529091528120805484929061332b908490614e5c565b909155505060408051848152602081018490526001600160a01b0386169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291016111ba565b60008160000361338557600080fd5b60008084121561339a57836000039350600190505b60008312156133ac5760009290920391155b60006133b88585613027565b905081156133e6576001607f1b816001600160801b031611156133da57600080fd5b6000039150610b809050565b60016001607f1b03816001600160801b0316111561340357600080fd5b9150610b809050565b505092915050565b6001600160a01b038216600090815260148401602090815260408083208415158085526013880184528285205485529083528184209084529091529020549392505050565b60408051608081018252600080825260208201819052918101829052606081019190915260008260400151600f0b13801561349b575060008260600151600f0b135b80156134b45750600082602001516001600160401b0316115b6134ef5760405162461bcd60e51b815260206004820152600c60248201526b696e76616c6964206172677360a01b604482015260640161041b565b60006134f9610a6f565b90508260a001511561354657601e81015460608401516135399161352b91600f90810b91600160801b9004900b614476565b6060850151600f0b9061267e565b600f0b6060840152613583565b601e810154606084015161357a9161356c91600f90810b91600160801b9004900b614476565b6060850151600f0b906144ac565b600f0b60608401525b608083015160038201546000916135a391600160a01b900460ff16611fb0565b90506000806135bf8660a00151856144df90919063ffffffff16565b91509150600081600f0b136135ff5760405162461bcd60e51b81526020600482015260066024820152656e6f206c697160d01b604482015260640161041b565b60006136274288602001516001600160401b031661361d9190614e49565b6301e13380611d08565b8554600187015460608a01516040808c01519051631af9d50d60e11b81526001600160a01b0394851660048201529284166024840152600f91820b6044840152810b606483015283900b60848201529192506000917f0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a909116906335f3aa1a9060a401602060405180830381865afa1580156136c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136eb9190615132565b9050600081600f0b1361372a5760405162461bcd60e51b81526020600482015260076024820152660766f6c203d20360cc1b604482015260640161041b565b60008860a001516137545761374f896040015187600f0b61447690919063ffffffff16565b613756565b855b90506000806000730f6e8ef18fb5bb61d545fee60f779d8aed60408f630313686c604051806101400160405280613799898a600f0b61447690919063ffffffff16565b600f0b81526020018f60400151600f0b81526020018f60600151600f0b815260200189600f0b81526020018b600f0b81526020018a600f0b81526020016137ec888c600f0b6144ac90919063ffffffff16565b600f0b8152600160401b60208201526040016138078e614590565b600f0b81526020018f60a0015115158152506040518263ffffffff1660e01b81526004016138359190615163565b606060405180830381865af4158015613852573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613876919061522c565b9250925092508b60a0015161389857613893600f84900b8a614476565b6138b8565b60608c01516138b8906138af600f86900b8c614476565b600f0b906145a8565b600f0b808c526138e8907f00000000000000000000000000000000000000000000000007ae147ae147ae14614476565b600f90810b60208d015282810b60408d015281900b60608c01528b5161391f90613911906131cb565b60208d0151600f0b90614476565b8b602001818151613930919061526f565b600f0b905250999b9a5050505050505050505050565b6000613956610bb0846000611d42565b905060006139668a878987610c61565b905060006139768b84848c611d75565b90505b81156102c4578415156000908152600f8c01602090815260408083208380529091528120546001600160a01b0316906139ba826139b5896111c8565b61103d565b90506139c78d83896145ff565b6139fb576139d8826104aa896111c8565b6139e782828960016000610cc7565b6139f48d8389848a610d12565b5050613979565b613a058d88613d79565b811015613a17576139f48d8389613d93565b8b6001600160a01b0316826001600160a01b031603613a4657613a3b8d8389613d93565b6139f48d838961406b565b613a518d8389613414565b613a5b9082614e49565b9050613a886040518060800160405280600081526020016000815260200160008152602001600081525090565b8484613a948b83614e5c565b613a9e9190614e49565b613aa890846150d1565b613ab291906150fe565b60208201819052600003613ac857505050613979565b8481602001511115613adc57602081018590525b8481602001518b613aed91906150d1565b613af791906150fe565b815260208101518590613b0a908b6150d1565b613b1491906150fe565b604082015260208101518590613b2a90866150d1565b613b3491906150fe565b6060820152613b488e848f89858d8d61463c565b6020810151613b579086614e49565b8151909550613b66908b614e49565b9950806040015189613b789190614e49565b9850806060015184613b8a9190614e49565b9350505050613979565b836001600160a01b0316856001600160a01b031614611922576001600160a01b0385811660009081527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424ec602052604080822092871682528120600080516020615392833981519152927fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb929091905b87518110156102c4576000878281518110613c4057613c40614fc5565b602002602001015190506000811115613d58576000898381518110613c6757613c67614fc5565b6020026020010151905060006001600160a01b03168c6001600160a01b031603613cb45760008181526020889052604081208054849290613ca9908490614e5c565b90915550613ce99050565b81613cbf8d8361103d565b03613ce9576000818152602087905260409020613cdc908d614750565b50613ce78582613d6d565b505b6001600160a01b038b16613d205760008181526020889052604081208054849290613d15908490614e49565b90915550613d569050565b613d2a8b8261103d565b600003613d56576000818152602087905260409020613d49908c614765565b50613d548482613d61565b505b505b50600101613c23565b600061139a8383614776565b600061139a83836147c5565b600081613d8a57826004015461139a565b50506005015490565b6001600160a01b038216613da657600080fd5b8015156000908152600f84016020908152604080832060108701909252909120613dd1848383614892565b613ddc575050505050565b6001600160a01b0393841660008181526020838152604080832080549683528184208054978a16808652838620805499909b166001600160a01b0319998a168117909b5599855295909252822080548616909717909655528054821690558254169091555050565b6001600160a01b038086166000908152601788016020818152604080842088151580865290835281852054958a1685529282528084209284529181528183205460188b0190915290822054909185841115613ea657613ea38685614e49565b90505b6000613eb28785614e5c565b90507f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e97036001600160a01b03166313c4d2a58b308b898789613ef48e600461137f565b6040518863ffffffff1660e01b8152600401613f169796959493929190614e94565b600060405180830381600087803b158015613f3057600080fd5b505af1158015613f44573d6000803e3d6000fd5b505050507f0000000000000000000000009abb27581c2e46a114f8c367355851e0580e97036001600160a01b03166313c4d2a58a308b888689613f888e600461137f565b6040518863ffffffff1660e01b8152600401613faa9796959493929190614e94565b600060405180830381600087803b158015613fc457600080fd5b505af1158015613fd8573d6000803e3d6000fd5b50505050818b60170160008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008a15151515815260200190815260200160002081905550808b60170160008b6001600160a01b03166001600160a01b0316815260200190815260200160002060008a151515158152602001908152602001600020819055505050505050505050505050565b6001600160a01b03821661407e57600080fd5b8015156000908152600f840160209081526040808320601087019092529091206140a9848383614892565b156140b5575050505050565b60008080526020828152604080832080546001600160a01b0390811680865296845282852080546001600160a01b03199081169a909216998a1790558885529490925282208054841690941790935580528154169092179091555050565b6001600160a01b03851615801561413157508161412f8461318c565b145b1561416e5761413f866131ab565b6000848152601d88016020526040902080546001600160801b0319166001600160801b03929092169190911790555b6001600160a01b03841615801561418b57506141898361318c565b155b156141af576000838152601d87016020526040902080546001600160801b03191690555b6001600160a01b038516158015906141cf57506001600160a01b03841615155b15611922576000808060f886901c608087901c8760058360078111156141f7576141f7614e1d565b1495506142068c898389610c61565b94506142148c8a8785611d75565b935050505060006142298a8a89898689611e00565b90506142378a8989856148de565b60006142448b8b876145ff565b159050811561425b5761425b8a8387846000610cc7565b61427d8b8b8784614275576142708689614e49565b614277565b875b8a610d12565b6102c48b8a87878a612e9e565b6000816142a5576008830154600160801b9004600f0b61139a565b505060090154600f0b90565b600080836142c35785600a01546142c9565b85600b01545b6142d39042614e49565b905061a8c08111156142f2576142eb61a8c082614e49565b90506142fb565b84915050610a67565b600061430982613840611d08565b6040805161012081018252600f83810b825289810b602083015287900b8183015267b333333333333333606082015267e66666666666666660808201819052600160401b60a0830181905260c083015260e082015268056fc2a2c515da32ea6101008201529051634916d70d60e01b8152919250730f6e8ef18fb5bb61d545fee60f779d8aed60408f91634916d70d916143a59160040161529c565b602060405180830381865af41580156143c2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbc9190615132565b600061443b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166149689092919063ffffffff16565b8051909150156107c157808060200190518101906144599190614ff4565b6107c1576040516306dacbc160e51b815260040160405180910390fd5b6000600f83810b9083900b0260401d60016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b6000600f82810b9084900b0360016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b8015156000908152601383016020526040812081908161450b856145038882610a93565b889190612b4c565b905060006145198787610f89565b90506000836001015411801561452f5750825415155b801561453c575082544210155b1561457f57614569816145608560010154610f4b8a8c61265090919063ffffffff16565b600f0b9061267e565b9350614578878383878a612b66565b9450614586565b8194508093505b5050509250929050565b6000600361459d836131ab565b600f0b901b92915050565b600081600f0b6000036145ba57600080fd5b600082600f0b604085600f0b901b816145d5576145d56150e8565b05905060016001607f1b031981128015906126a9575060016001607f1b0381131561139a57600080fd5b6001600160a01b0382166000908152600e840160209081526040808320841515845290915281205480158061463357504281115b95945050505050565b61464c87878686606001516148de565b6000836060015184602001516146629190614e5c565b8460400151149050806146a4576146a48761467c856111c8565b8660400151876060015188602001516146959190614e5c565b61469f9190614e49565b6110ad565b6146b387868660000151612085565b6146d3888885876060015188604001516146cd9190614e49565b86612e9e565b856001600160a01b0316876001600160a01b03167fa30eb1f1bb0892af5e3389941c42bc7f44a8bf7b6071ecfe3e964673c908f2b28787600001518561471d578860400151614720565b60005b60408051938452602084019290925290820152841515606082015260800160405180910390a35050505050505050565b600061139a836001600160a01b0384166147c5565b600061139a836001600160a01b0384165b60008181526001830160205260408120546147bd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610b80565b506000610b80565b60008181526001830160205260408120548015614888578354600090859060001981019081106147f7576147f7614fc5565b906000526020600020015490508085600001600184038154811061481d5761481d614fc5565b600091825260208083209091019290925591825260018601905260409020819055835484908061484f5761484f61534c565b60019003818190600052602060002001600090559055836001016000848152602001908152602001600020600090556001915050610b80565b6000915050610b80565b6001600160a01b03838116600090815260208490526040812054909116151580610a6757506000808052602083905260409020546001600160a01b039081169085161490509392505050565b6001600160a01b0383166000908152601c85016020908152604080832085845290915281208054839290614913908490614e5c565b9091555050604080516001600160a01b0385168152602081018490529081018290527f3a8ab48b47eb6f5307d9b87d560cc92cf89c416cce1d94297a70d1c82d8c84129060600160405180910390a150505050565b6060610a6784846000856060843b61499357604051632270d6bf60e21b815260040160405180910390fd5b600080866001600160a01b031685876040516149af9190615362565b60006040518083038185875af1925050503d80600081146149ec576040519150601f19603f3d011682016040523d82523d6000602084013e6149f1565b606091505b50915091508115614a05579150610a679050565b805115614a155780518082602001fd5b8360405162461bcd60e51b815260040161041b919061537e565b80151581146103c157600080fd5b8035610e9a81614a2f565b600080600060608486031215614a5d57600080fd5b83359250602084013591506040840135614a7681614a2f565b809150509250925092565b600060208284031215614a9357600080fd5b813561139a81614a2f565b60008060408385031215614ab157600080fd5b823591506020830135614ac381614a2f565b809150509250929050565b80356001600160a01b0381168114610e9a57600080fd5b60008060408385031215614af857600080fd5b614b0183614ace565b91506020830135614ac381614a2f565b60008083601f840112614b2357600080fd5b5081356001600160401b03811115614b3a57600080fd5b6020830191508360208260051b8501011115614b5557600080fd5b9250929050565b600080600080600060608688031215614b7457600080fd5b85356001600160401b0380821115614b8b57600080fd5b614b9789838a01614b11565b90975095506020880135915080821115614bb057600080fd5b50614bbd88828901614b11565b9094509250506040860135614bd181614a2f565b809150509295509295909350565b600081518084526020808501945080840160005b83811015614c0f57815187529582019590820190600101614bf3565b509495945050505050565b608081526000614c2d6080830187614bdf565b8281036020840152614c3f8187614bdf565b604084019590955250506060015292915050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715614c8b57614c8b614c53565b60405290565b600082601f830112614ca257600080fd5b81356001600160401b0380821115614cbc57614cbc614c53565b604051601f8301601f19908116603f01168101908282118183101715614ce457614ce4614c53565b81604052838152866020858801011115614cfd57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215614d3057600080fd5b82356001600160401b0380821115614d4757600080fd5b9084019060e08287031215614d5b57600080fd5b614d63614c69565b614d6c83614ace565b81526020830135602082015260408301356040820152614d8e60608401614ace565b6060820152614d9f60808401614ace565b608082015260a083013582811115614db657600080fd5b614dc288828601614c91565b60a083015250614dd460c08401614ace565b60c08201529350614dea91505060208401614a3d565b90509250929050565b60008060408385031215614e0657600080fd5b82356001600160401b0381168114614b0157600080fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610b8057610b80614e33565b80820180821115610b8057610b80614e33565b6020808252600b908201526a1b1a5c481b1bd8dac80c5960aa1b604082015260600190565b6001600160a01b03978816815295909616602086015292151560408501526060840191909152608083015260a082015260c081019190915260e00190565b600181815b80851115614f0d578160001904821115614ef357614ef3614e33565b80851615614f0057918102915b93841c9390800290614ed7565b509250929050565b600082614f2457506001610b80565b81614f3157506000610b80565b8160018114614f475760028114614f5157614f6d565b6001915050610b80565b60ff841115614f6257614f62614e33565b50506001821b610b80565b5060208310610133831016604e8410600b8410161715614f90575081810a610b80565b614f9a8383614ed2565b8060001904821115614fae57614fae614e33565b029392505050565b600061139a60ff841683614f15565b634e487b7160e01b600052603260045260246000fd5b600060018201614fed57614fed614e33565b5060010190565b60006020828403121561500657600080fd5b815161139a81614a2f565b60005b8381101561502c578181015183820152602001615014565b50506000910152565b6000815180845261504d816020860160208601615011565b601f01601f19169290920160200192915050565b600060018060a01b03808a16835280891660208401528760408401528087166060840152808616608084015260e060a08401526150a160e0840186615035565b915080841660c08401525098975050505050505050565b6000602082840312156150ca57600080fd5b5051919050565b8082028115828204841417610b8057610b80614e33565b634e487b7160e01b600052601260045260246000fd5b60008261511b57634e487b7160e01b600052601260045260246000fd5b500490565b8051600f81900b8114610e9a57600080fd5b60006020828403121561514457600080fd5b61139a82615120565b634e487b7160e01b600052600160045260246000fd5b8151600f0b8152610140810160208301516151836020840182600f0b9052565b5060408301516151986040840182600f0b9052565b5060608301516151ad6060840182600f0b9052565b5060808301516151c26080840182600f0b9052565b5060a08301516151d760a0840182600f0b9052565b5060c08301516151ec60c0840182600f0b9052565b5060e083015161520160e0840182600f0b9052565b506101008084015161521782850182600f0b9052565b5050610120838101518015158483015261340c565b60008060006060848603121561524157600080fd5b61524a84615120565b925061525860208501615120565b915061526660408501615120565b90509250925092565b600f82810b9082900b0360016001607f1b0319811260016001607f1b0382131715610b8057610b80614e33565b6000610120820190508251600f0b82526020830151600f0b602083015260408301516152cd6040840182600f0b9052565b5060608301516152e26060840182600f0b9052565b5060808301516152f76080840182600f0b9052565b5060a083015161530c60a0840182600f0b9052565b5060c083015161532160c0840182600f0b9052565b5060e083015161533660e0840182600f0b9052565b506101008084015161340c82850182600f0b9052565b634e487b7160e01b600052603160045260246000fd5b60008251615374818460208701615011565b9190910192915050565b60208152600061139a602083018461503556feb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eaa26469706673582212207a9db0325c09b850d185d3d8462ddf7f665833c1a17134f34fbf1068ef897f5464736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f900000000000000000000000000000000000000000000000007ae147ae147ae14000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
-----Decoded View---------------
Arg [0] : ivolOracle (address): 0x3A87bB29b984d672664Aa1dD2d19D2e8b24f0f2A
Arg [1] : wrappedNativeToken (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [2] : premiaMining (address): 0x9aBB27581c2E46A114F8C367355851e0580e9703
Arg [3] : feeReceiver (address): 0xC4B2C51f969e0713E799De73b7f130Fb7Bb604CF
Arg [4] : feeDiscountAddress (address): 0xF1bB87563A122211d40d393eBf1c633c330377F9
Arg [5] : feePremium64x64 (int128): 553402322211286548
Arg [6] : exchangeProxy (address): 0x380Eb51db6FE77a8876cB0735164cB8AF7f80Cb5
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [2] : 0000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703
Arg [3] : 000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf
Arg [4] : 000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f9
Arg [5] : 00000000000000000000000000000000000000000000000007ae147ae147ae14
Arg [6] : 000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.