Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 19375896 | 1278 days ago | 6.472 ETH | ||||
| 19375461 | 1278 days ago | 0 ETH | ||||
| 19370076 | 1278 days ago | 0 ETH | ||||
| 19370076 | 1278 days ago | 0 ETH | ||||
| 19370076 | 1278 days ago | 0 ETH | ||||
| 19364783 | 1278 days ago | 0 ETH | ||||
| 19364726 | 1278 days ago | 0 ETH | ||||
| 19363751 | 1278 days ago | 0 ETH | ||||
| 19363691 | 1278 days ago | 0 ETH | ||||
| 19363679 | 1278 days ago | 0 ETH | ||||
| 19362225 | 1278 days ago | 0 ETH | ||||
| 19362207 | 1278 days ago | 0 ETH | ||||
| 19355620 | 1278 days ago | 0 ETH | ||||
| 19348255 | 1278 days ago | 0 ETH | ||||
| 19348255 | 1278 days ago | 0 ETH | ||||
| 19307918 | 1279 days ago | 0 ETH | ||||
| 19285649 | 1279 days ago | 0 ETH | ||||
| 19285546 | 1279 days ago | 0 ETH | ||||
| 19285508 | 1279 days ago | 0 ETH | ||||
| 19285426 | 1279 days ago | 0 ETH | ||||
| 19285388 | 1279 days ago | 0 ETH | ||||
| 19278304 | 1279 days ago | 0 ETH | ||||
| 19274196 | 1279 days ago | 0 ETH | ||||
| 19274122 | 1279 days ago | 0 ETH | ||||
| 19273299 | 1279 days ago | 0 ETH |
Cross-Chain Transactions
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.10+commit.fc410830
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 {IPoolIO} from "./IPoolIO.sol";
import {PoolSwap} from "./PoolSwap.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, PoolSwap {
using ABDKMath64x64 for int128;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
using PoolStorage for PoolStorage.Layout;
constructor(
address ivolOracle,
address weth,
address premiaMining,
address feeReceiver,
address feeDiscountAddress,
int128 feePremium64x64,
int128 feeApy64x64,
address uniswapV2Factory,
address sushiswapFactory
)
PoolSwap(
ivolOracle,
weth,
premiaMining,
feeReceiver,
feeDiscountAddress,
feePremium64x64,
feeApy64x64,
uniswapV2Factory,
sushiswapFactory
)
{}
/**
* @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 {
_deposit(amount, isCallPool, true);
}
/**
* @inheritdoc IPoolIO
*/
function swapAndDeposit(
uint256 amount,
bool isCallPool,
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
bool isSushi
) external payable {
// If value is passed, amountInMax must be 0, as the value wont be used
// If amountInMax is not 0, user wants to do a swap from an ERC20, and therefore no value should be attached
require(
msg.value == 0 || amountInMax == 0,
"value and amountInMax passed"
);
// If no amountOut has been passed, we swap the exact deposit amount specified
if (amountOut == 0) {
amountOut = amount;
}
if (msg.value > 0) {
_swapETHForExactTokens(amountOut, path, isSushi);
} else {
_swapTokensForExactTokens(amountOut, amountInMax, path, isSushi);
}
_deposit(amount, isCallPool, false);
}
/**
* @inheritdoc IPoolIO
*/
function withdraw(uint256 amount, bool isCallPool) public {
PoolStorage.Layout storage l = PoolStorage.layout();
uint256 toWithdraw = amount;
_processPendingDeposits(l, isCallPool);
uint256 depositedAt = l.depositedAt[msg.sender][isCallPool];
require(depositedAt + (1 days) < block.timestamp, "liq lock 1d");
int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCallPool);
uint256 reservedLiqToWithdraw;
{
uint256 reservedLiqTokenId = _getReservedLiquidityTokenId(
isCallPool
);
uint256 reservedLiquidity = _balanceOf(
msg.sender,
reservedLiqTokenId
);
if (reservedLiquidity > 0) {
if (reservedLiquidity < toWithdraw) {
reservedLiqToWithdraw = reservedLiquidity;
} else {
reservedLiqToWithdraw = toWithdraw;
}
toWithdraw -= reservedLiqToWithdraw;
// burn reserved liquidity tokens from sender
_burn(msg.sender, reservedLiqTokenId, reservedLiqToWithdraw);
}
}
if (toWithdraw > 0) {
// burn free liquidity tokens from sender
_burn(msg.sender, _getFreeLiquidityTokenId(isCallPool), toWithdraw);
int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(
isCallPool
);
_setCLevel(l, oldLiquidity64x64, newLiquidity64x64, isCallPool);
}
_subUserTVL(l, msg.sender, isCallPool, amount - reservedLiqToWithdraw);
_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
)
{
PoolStorage.Layout storage l = PoolStorage.layout();
int128 newPrice64x64 = _update(l);
uint64 maturity;
int128 strike64x64;
bool isCall;
{
PoolStorage.TokenType tokenType;
(tokenType, maturity, strike64x64) = PoolStorage.parseTokenId(
tokenId
);
isCall =
tokenType == PoolStorage.TokenType.SHORT_CALL ||
tokenType == PoolStorage.TokenType.LONG_CALL;
}
(baseCost, feeCost, amountOut) = _reassign(
l,
msg.sender,
maturity,
strike64x64,
isCall,
contractSize,
newPrice64x64
);
_processAvailableFunds(msg.sender, amountOut, isCall, divest, true);
_subUserTVL(
l,
msg.sender,
isCall,
divest ? baseCost + feeCost + amountOut : baseCost + feeCost
);
}
/**
* @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());
baseCosts = new uint256[](tokenIds.length);
feeCosts = new uint256[](tokenIds.length);
bool[] memory isCallToken = new bool[](tokenIds.length);
for (uint256 i; i < tokenIds.length; i++) {
(
PoolStorage.TokenType tokenType,
uint64 maturity,
int128 strike64x64
) = PoolStorage.parseTokenId(tokenIds[i]);
bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL ||
tokenType == PoolStorage.TokenType.LONG_CALL;
uint256 amountOut;
uint256 contractSize = contractSizes[i];
isCallToken[i] = isCall;
(baseCosts[i], feeCosts[i], amountOut) = _reassign(
PoolStorage.layout(),
msg.sender,
maturity,
strike64x64,
isCall,
contractSize,
newPrice64x64
);
if (isCall) {
amountOutCall += amountOut;
} else {
amountOutPut += amountOut;
}
}
if (amountOutCall > 0) {
uint256 reassignmentCost;
for (uint256 i; i < tokenIds.length; i++) {
if (isCallToken[i] == false) continue;
reassignmentCost += baseCosts[i];
reassignmentCost += feeCosts[i];
}
_processAvailableFunds(
msg.sender,
amountOutCall,
true,
divest,
true
);
_subUserTVL(
PoolStorage.layout(),
msg.sender,
true,
divest ? reassignmentCost + amountOutCall : reassignmentCost
);
}
if (amountOutPut > 0) {
uint256 reassignmentCost;
for (uint256 i; i < tokenIds.length; i++) {
if (isCallToken[i] == true) continue;
reassignmentCost += baseCosts[i];
reassignmentCost += feeCosts[i];
}
_processAvailableFunds(
msg.sender,
amountOutPut,
false,
divest,
true
);
_subUserTVL(
PoolStorage.layout(),
msg.sender,
false,
divest ? reassignmentCost + amountOutPut : reassignmentCost
);
}
}
/**
* @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();
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
);
}
/**
* @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];
IPremiaMining(PREMIA_MINING_ADDRESS).claim(
account,
address(this),
isCallPool,
userTVL,
userTVL,
totalTVL
);
}
/**
* @inheritdoc IPoolIO
*/
function updateMiningPools() external {
PoolStorage.Layout storage l = PoolStorage.layout();
IPremiaMining(PREMIA_MINING_ADDRESS).updatePool(
address(this),
true,
l.totalTVL[true]
);
IPremiaMining(PREMIA_MINING_ADDRESS).updatePool(
address(this),
false,
l.totalTVL[false]
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Set implementation with enumeration functions
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
*/
library EnumerableSet {
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)
{
require(
set._values.length > index,
'EnumerableSet: index out of bounds'
);
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: 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 >= 0x8) { 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;
/**
* @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 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
* @param amountOut amount out of tokens requested. If 0, we will swap exact amount necessary to pay the quote
* @param amountInMax amount in max of tokens
* @param path swap path
* @param isSushi whether we use sushi or uniV2 for the swap
*/
function swapAndDeposit(
uint256 amount,
bool isCallPool,
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
bool isSushi
) 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 {PoolStorage} from "./PoolStorage.sol";
import {IWETH} from "@solidstate/contracts/utils/IWETH.sol";
import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {SafeERC20} from "@solidstate/contracts/utils/SafeERC20.sol";
import {IERC20} from "@solidstate/contracts/token/ERC20/IERC20.sol";
import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol";
import {PoolInternal} from "./PoolInternal.sol";
/**
* @title Premia option pool
* @dev deployed standalone and referenced by PoolProxy
*/
abstract contract PoolSwap is PoolInternal {
using SafeERC20 for IERC20;
using ABDKMath64x64 for int128;
using PoolStorage for PoolStorage.Layout;
address internal immutable UNISWAP_V2_FACTORY;
address internal immutable SUSHISWAP_FACTORY;
constructor(
address ivolOracle,
address weth,
address premiaMining,
address feeReceiver,
address feeDiscountAddress,
int128 feePremium64x64,
int128 feeApy64x64,
address uniswapV2Factory,
address sushiswapFactory
)
PoolInternal(
ivolOracle,
weth,
premiaMining,
feeReceiver,
feeDiscountAddress,
feePremium64x64,
feeApy64x64
)
{
UNISWAP_V2_FACTORY = uniswapV2Factory;
SUSHISWAP_FACTORY = sushiswapFactory;
}
// calculates the CREATE2 address for a pair without making any external calls
function _pairFor(
address factory,
address tokenA,
address tokenB,
bool isSushi
) internal pure returns (address pair) {
(address token0, address token1) = _sortTokens(tokenA, tokenB);
pair = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encodePacked(token0, token1)),
isSushi
? hex"e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303"
: hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
)
)
)
)
);
}
// returns sorted token addresses, used to handle return values from pairs sorted in this order
function _sortTokens(address tokenA, address tokenB)
internal
pure
returns (address token0, address token1)
{
require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
(token0, token1) = tokenA < tokenB
? (tokenA, tokenB)
: (tokenB, tokenA);
require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
}
// performs chained getAmountIn calculations on any number of pairs
function _getAmountsIn(
address factory,
uint256 amountOut,
address[] memory path,
bool isSushi
) internal view returns (uint256[] memory amounts) {
require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
amounts = new uint256[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
(uint256 reserveIn, uint256 reserveOut) = _getReserves(
factory,
path[i - 1],
path[i],
isSushi
);
amounts[i - 1] = _getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
// fetches and sorts the reserves for a pair
function _getReserves(
address factory,
address tokenA,
address tokenB,
bool isSushi
) internal view returns (uint256 reserveA, uint256 reserveB) {
(address token0, ) = _sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
_pairFor(factory, tokenA, tokenB, isSushi)
).getReserves();
(reserveA, reserveB) = tokenA == token0
? (reserve0, reserve1)
: (reserve1, reserve0);
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
function _getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountIn) {
require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
require(
reserveIn > 0 && reserveOut > 0,
"UniswapV2Library: INSUFFICIENT_LIQUIDITY"
);
uint256 numerator = reserveIn * amountOut * 1000;
uint256 denominator = (reserveOut - amountOut) * 997;
amountIn = (numerator / denominator) + 1;
}
// requires the initial amount to have already been sent to the first pair
function _swap(
uint256[] memory amounts,
address[] memory path,
address _to,
bool isSushi
) internal {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = _sortTokens(input, output);
uint256 amountOut = amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amountOut)
: (amountOut, uint256(0));
address to = i < path.length - 2
? _pairFor(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
output,
path[i + 2],
isSushi
)
: _to;
IUniswapV2Pair(
_pairFor(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
input,
output,
isSushi
)
).swap(amount0Out, amount1Out, to, new bytes(0));
}
}
function _swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
bool isSushi
) internal returns (uint256[] memory amounts) {
amounts = _getAmountsIn(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
amountOut,
path,
isSushi
);
require(
amounts[0] <= amountInMax,
"UniswapV2Router: EXCESSIVE_INPUT_AMOUNT"
);
IERC20(path[0]).safeTransferFrom(
msg.sender,
_pairFor(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
path[0],
path[1],
isSushi
),
amounts[0]
);
_swap(amounts, path, msg.sender, isSushi);
}
function _swapETHForExactTokens(
uint256 amountOut,
address[] calldata path,
bool isSushi
) internal returns (uint256[] memory amounts) {
require(path[0] == WETH_ADDRESS, "UniswapV2Router: INVALID_PATH");
amounts = _getAmountsIn(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
amountOut,
path,
isSushi
);
require(
amounts[0] <= msg.value,
"UniswapV2Router: EXCESSIVE_INPUT_AMOUNT"
);
IWETH(WETH_ADDRESS).deposit{value: amounts[0]}();
assert(
IWETH(WETH_ADDRESS).transfer(
_pairFor(
isSushi ? SUSHISWAP_FACTORY : UNISWAP_V2_FACTORY,
path[0],
path[1],
isSushi
),
amounts[0]
)
);
_swap(amounts, path, msg.sender, isSushi);
// refund dust eth, if any
if (msg.value > amounts[0]) {
(bool success, ) = payable(msg.sender).call{
value: msg.value - amounts[0]
}(new bytes(0));
require(
success,
"TransferHelper::safeTransferETH: ETH transfer failed"
);
}
}
}// 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 {EnumerableSet, ERC1155EnumerableStorage} from "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol";
import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol";
import {ABDKMath64x64Token} from "../libraries/ABDKMath64x64Token.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;
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 -> averageC
mapping(uint256 => int128) avgCLevel64x64;
// APY fee tracking
// underwriter -> shortTokenId -> amount
mapping(address => mapping(uint256 => uint256)) feesReserved;
// shortTokenId -> 64x64 fixed point representation of apy fee
mapping(uint256 => int128) feeReserveRates;
}
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 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
* @return cLevel64x64 64x64 fixed point representation of C-Level
*/
function getDecayAdjustedCLevel64x64(Layout storage l, bool isCall)
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);
}
/**
* @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);
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
) 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
);
uint256 tokenId = formatTokenId(
isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,
0,
0
);
uint256 tvl = l.totalTVL[isCall];
int128 utilization = ABDKMath64x64.divu(
tvl -
(ERC1155EnumerableStorage.layout().totalSupply[tokenId] -
l.totalPendingDeposits(isCall)),
tvl
);
return
OptionMath.calculateCLevelDecay(
OptionMath.CalculateCLevelDecayArgs(
timeIntervalsElapsed64x64,
oldCLevel64x64,
utilization,
0xb333333333333333, // 0.7
0xe666666666666666, // 0.9
0x10000000000000000, // 1.0
0x10000000000000000, // 1.0
0xe666666666666666, // 0.9
0x56fc2a2c515da32ea // 2e
)
);
}
/**
* @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 {PremiaMiningStorage} from "./PremiaMiningStorage.sol";
interface IPremiaMining {
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 addPool(address _pool, uint256 _allocPoints) external;
function setPoolAllocPoints(
address[] memory _pools,
uint256[] memory _allocPoints
) external;
function pendingPremia(
address _pool,
bool _isCallPool,
address _user
) external view returns (uint256);
function updatePool(
address _pool,
bool _isCallPool,
uint256 _totalTVL
) external;
function allocatePending(
address _user,
address _pool,
bool _isCallPool,
uint256 _userTVLOld,
uint256 _userTVLNew,
uint256 _totalTVL
) external;
function claim(
address _user,
address _pool,
bool _isCallPool,
uint256 _userTVLOld,
uint256 _userTVLNew,
uint256 _totalTVL
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../token/ERC20/IERC20.sol';
import { IERC20Metadata } from '../token/ERC20/metadata/IERC20Metadata.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;
}pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../token/ERC20/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;
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 {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
'SafeERC20: approve from non-zero to non-zero allowance'
);
_callOptionalReturn(
token,
abi.encodeWithSelector(token.approve.selector, spender, value)
);
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(
token,
abi.encodeWithSelector(
token.approve.selector,
spender,
newAllowance
)
);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(
oldAllowance >= value,
'SafeERC20: decreased allowance below zero'
);
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(
token,
abi.encodeWithSelector(
token.approve.selector,
spender,
newAllowance
)
);
}
}
/**
* @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) {
require(
abi.decode(returndata, (bool)),
'SafeERC20: ERC20 operation did not succeed'
);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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: BUSL-1.1
// For further clarification please see https://license.premia.legal
pragma solidity ^0.8.0;
import {IERC173} from "@solidstate/contracts/access/IERC173.sol";
import {OwnableStorage} from "@solidstate/contracts/access/OwnableStorage.sol";
import {IERC20} from "@solidstate/contracts/token/ERC20/IERC20.sol";
import {ERC1155EnumerableInternal, ERC1155EnumerableStorage, EnumerableSet} from "@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol";
import {IWETH} from "@solidstate/contracts/utils/IWETH.sol";
import {PoolStorage} from "./PoolStorage.sol";
import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol";
import {ABDKMath64x64Token} from "../libraries/ABDKMath64x64Token.sol";
import {OptionMath} from "../libraries/OptionMath.sol";
import {IFeeDiscount} from "../staking/IFeeDiscount.sol";
import {IPoolEvents} from "./IPoolEvents.sol";
import {IPremiaMining} from "../mining/IPremiaMining.sol";
import {IVolatilitySurfaceOracle} from "../oracle/IVolatilitySurfaceOracle.sol";
/**
* @title Premia option pool
* @dev deployed standalone and referenced by PoolProxy
*/
contract PoolInternal is IPoolEvents, ERC1155EnumerableInternal {
using ABDKMath64x64 for int128;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
using PoolStorage for PoolStorage.Layout;
struct Interval {
uint256 contractSize;
uint256 tokenAmount;
uint256 payment;
uint256 apyFee;
}
address internal immutable WETH_ADDRESS;
address internal immutable PREMIA_MINING_ADDRESS;
address internal immutable FEE_RECEIVER_ADDRESS;
address internal immutable FEE_DISCOUNT_ADDRESS;
address internal immutable IVOL_ORACLE_ADDRESS;
int128 internal immutable FEE_PREMIUM_64x64;
int128 internal immutable FEE_APY_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;
// Minimum APY for capital locked up to underwrite options.
// The quote will return a minimum price corresponding to this APY
int128 internal constant MIN_APY_64x64 = 0x4ccccccccccccccd; // 0.3
// Multiply sell quote by this constant
int128 internal constant SELL_COEFFICIENT_64x64 = 0xb333333333333333; // 0.7
constructor(
address ivolOracle,
address weth,
address premiaMining,
address feeReceiver,
address feeDiscountAddress,
int128 feePremium64x64,
int128 feeApy64x64
) {
IVOL_ORACLE_ADDRESS = ivolOracle;
WETH_ADDRESS = weth;
PREMIA_MINING_ADDRESS = premiaMining;
FEE_RECEIVER_ADDRESS = feeReceiver;
// PremiaFeeDiscount contract address
FEE_DISCOUNT_ADDRESS = feeDiscountAddress;
FEE_PREMIUM_64x64 = feePremium64x64;
FEE_APY_64x64 = feeApy64x64;
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(
IFeeDiscount(FEE_DISCOUNT_ADDRESS).getDiscount(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();
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
MIN_APY_64x64,
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
),
l.baseDecimals
);
int128 sellCLevel64x64;
{
uint256 longTokenId = PoolStorage.formatTokenId(
PoolStorage.getTokenType(args.isCall, true),
args.maturity,
args.strike64x64
);
// Initialize to avg value, and replace by current if avg not set or current is lower
sellCLevel64x64 = l.avgCLevel64x64[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
* @param creditMessageValue whether to apply message value as credit towards transfer
*/
function _deposit(
uint256 amount,
bool isCallPool,
bool creditMessageValue
) internal {
PoolStorage.Layout storage l = PoolStorage.layout();
// 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);
_pullFrom(l, msg.sender, amount, isCallPool, creditMessageValue);
_addToDepositQueue(msg.sender, amount, isCallPool);
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");
{
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
);
_updateCLevelAverage(l, longTokenId, contractSize, 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
);
int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);
_setCLevel(l, oldLiquidity64x64, newLiquidity64x64, isCall);
// 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 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
);
}
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),
false
);
}
}
}
function _mintShortTokenLoop(
PoolStorage.Layout storage l,
address buyer,
uint64 maturity,
int128 strike64x64,
uint256 contractSize,
uint256 premium,
bool isCall
) 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);
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
);
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
) 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
);
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
) 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
);
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
) 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);
} else {
if (refundWithRebate > interval.payment) {
_addUserTVL(
l,
underwriter,
isCallPool,
refundWithRebate - interval.payment
);
} else if (interval.payment > refundWithRebate) {
_subUserTVL(
l,
underwriter,
isCallPool,
interval.payment - refundWithRebate
);
}
}
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
? FEE_APY_64x64
: 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) {
if (intervalApyFee == 0) return 0;
// 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();
_mint(account, _getFreeLiquidityTokenId(isCallPool), amount);
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;
}
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
);
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
) internal {
int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64(isCallPool);
int128 cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(
oldCLevel64x64,
oldLiquidity64x64,
newLiquidity64x64,
isCallPool
);
l.setCLevel(cLevel64x64, isCallPool);
emit UpdateCLevel(
isCallPool,
cLevel64x64,
oldLiquidity64x64,
newLiquidity64x64
);
}
function _updateCLevelAverage(
PoolStorage.Layout storage l,
uint256 longTokenId,
uint256 contractSize,
int128 cLevel64x64
) internal {
int128 supply64x64 = ABDKMath64x64Token.fromDecimals(
_totalSupply(longTokenId),
l.underlyingDecimals
);
int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals(
contractSize,
l.underlyingDecimals
);
l.avgCLevel64x64[longTokenId] = l
.avgCLevel64x64[longTokenId]
.mul(supply64x64)
.add(cLevel64x64.mul(contractSize64x64))
.div(supply64x64.add(contractSize64x64));
}
/**
* @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) == WETH_ADDRESS,
"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(WETH_ADDRESS).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);
}
}
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
) 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
);
l.userTVL[user][isCallPool] = userTVL + amount;
l.totalTVL[isCallPool] = totalTVL + amount;
}
function _subUserTVL(
PoolStorage.Layout storage l,
address user,
bool isCallPool,
uint256 amount
) internal {
uint256 userTVL = l.userTVL[user][isCallPool];
uint256 totalTVL = l.totalTVL[isCallPool];
uint256 newUserTVL;
uint256 newTotalTVL;
if (userTVL > amount) {
newUserTVL = userTVL - amount;
}
if (totalTVL > amount) {
newTotalTVL = totalTVL - amount;
}
IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(
user,
address(this),
isCallPool,
userTVL,
newUserTVL,
totalTVL
);
l.userTVL[user][isCallPool] = newUserTVL;
l.totalTVL[isCallPool] = newTotalTVL;
}
/**
* @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)) {
_subUserTVL(l, from, isCallPool, amount);
_addUserTVL(l, to, isCallPool, amount);
}
}
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
) {
_beforeShortTokenTransfer(l, from, to, id, amount);
}
}
}
function _beforeShortTokenTransfer(
PoolStorage.Layout storage l,
address from,
address to,
uint256 id,
uint256 amount
) private {
// total supply has already been updated, so compare to amount rather than 0
if (from == address(0) && _totalSupply(id) == amount) {
l.feeReserveRates[id] = FEE_APY_64x64;
}
if (to == address(0) && _totalSupply(id) == 0) {
delete l.feeReserveRates[id];
}
if (from != address(0) && to != address(0)) {
(
PoolStorage.TokenType tokenType,
uint64 maturity,
int128 strike64x64
) = PoolStorage.parseTokenId(id);
bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL;
uint256 collateral = l.contractSizeToBaseTokenAmount(
amount,
strike64x64,
isCall
);
uint256 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
);
_addUserTVL(l, to, isCall, collateral);
}
}
}// 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);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
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 { 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: 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 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: 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: MIT
pragma solidity ^0.8.0;
/**
* @title ERC20 metadata interface
*/
interface IERC20Metadata {
/**
* @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.0;
/**
* @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.0;
import { UintUtils } from './UintUtils.sol';
library AddressUtils {
using UintUtils for uint256;
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 }('');
require(success, 'AddressUtils: failed to send value');
}
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) {
require(
address(this).balance >= value,
'AddressUtils: insufficient balance for call'
);
return _functionCallWithValue(target, data, value, error);
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory error
) private returns (bytes memory) {
require(
isContract(target),
'AddressUtils: function call to non-contract'
);
(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.0;
/**
* @title utility functions for uint256 operations
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
*/
library UintUtils {
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;
}
}
require(value == 0, 'UintUtils: hex length insufficient');
return string(buffer);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Contract ownership standard interface
* @dev see https://eips.ethereum.org/EIPS/eip-173
*/
interface IERC173 {
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @notice get the ERC173 contract owner
* @return conract 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.0;
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.0;
import { EnumerableSet } from '../../../utils/EnumerableSet.sol';
import { ERC1155Base, ERC1155BaseInternal } from '../base/ERC1155Base.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,
ERC1155Base,
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);
}
/**
* @notice ERC1155 hook: update aggregate values
* @inheritdoc ERC1155EnumerableInternal
*/
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
)
internal
virtual
override(ERC1155BaseInternal, ERC1155EnumerableInternal)
{
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.8.0;
import {FeeDiscountStorage} from "./FeeDiscountStorage.sol";
interface IFeeDiscount {
event Staked(
address indexed user,
uint256 amount,
uint256 stakePeriod,
uint256 lockedUntil
);
event Unstaked(address indexed user, uint256 amount);
struct StakeLevel {
uint256 amount; // Amount to stake
uint256 discount; // Discount when amount is reached
}
/**
* @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,
uint256 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, uint256 period) external;
/**
* @notice Unstake xPremia (If lockup period has ended)
* @param amount The amount of xPremia to unstake
*/
function unstake(uint256 amount) 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 getStakeAmountWithBonus(address user)
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 getDiscount(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 getStakePeriodMultiplier(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 (FeeDiscountStorage.UserInfo memory);
}// 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);
}// 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 strike64x64 Strike, as a64x64 fixed point representation
* @param spot64x64 Spot price, as a 64x64 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 strike64x64,
int128 spot64x64,
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 strike64x64 Strike, as a64x64 fixed point representation
* @param spot64x64 Spot price, as a 64x64 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 strike64x64,
int128 spot64x64,
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: MIT
pragma solidity ^0.8.0;
import { IERC1155 } from '../IERC1155.sol';
import { IERC1155Receiver } from '../IERC1155Receiver.sol';
import { ERC1155BaseInternal, ERC1155BaseStorage } from './ERC1155BaseInternal.sol';
/**
* @title Base ERC1155 contract
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
*/
abstract contract ERC1155Base is IERC1155, ERC1155BaseInternal {
/**
* @inheritdoc IERC1155
*/
function balanceOf(address account, uint256 id)
public
view
virtual
returns (uint256)
{
return _balanceOf(account, id);
}
/**
* @inheritdoc IERC1155
*/
function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
public
view
virtual
returns (uint256[] memory)
{
require(
accounts.length == ids.length,
'ERC1155: accounts and ids length mismatch'
);
mapping(uint256 => mapping(address => uint256))
storage balances = ERC1155BaseStorage.layout().balances;
uint256[] memory batchBalances = new uint256[](accounts.length);
unchecked {
for (uint256 i; i < accounts.length; i++) {
require(
accounts[i] != address(0),
'ERC1155: batch balance query for the zero address'
);
batchBalances[i] = balances[ids[i]][accounts[i]];
}
}
return batchBalances;
}
/**
* @inheritdoc IERC1155
*/
function isApprovedForAll(address account, address operator)
public
view
virtual
returns (bool)
{
return ERC1155BaseStorage.layout().operatorApprovals[account][operator];
}
/**
* @inheritdoc IERC1155
*/
function setApprovalForAll(address operator, bool status) public virtual {
require(
msg.sender != operator,
'ERC1155: setting approval status for self'
);
ERC1155BaseStorage.layout().operatorApprovals[msg.sender][
operator
] = status;
emit ApprovalForAll(msg.sender, operator, status);
}
/**
* @inheritdoc IERC1155
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual {
require(
from == msg.sender || isApprovedForAll(from, msg.sender),
'ERC1155: caller is not owner nor approved'
);
_safeTransfer(msg.sender, from, to, id, amount, data);
}
/**
* @inheritdoc IERC1155
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual {
require(
from == msg.sender || isApprovedForAll(from, msg.sender),
'ERC1155: caller is not owner nor approved'
);
_safeTransferBatch(msg.sender, from, to, ids, amounts, data);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ERC1155 enumerable and aggregate function interface
*/
interface IERC1155Enumerable {
/**
* @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.0;
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.0;
import { IERC1155Internal } from './IERC1155Internal.sol';
import { IERC165 } from '../../introspection/IERC165.sol';
/**
* @notice ERC1155 interface
* @dev see https://github.com/ethereum/EIPs/issues/1155
*/
interface IERC1155 is IERC1155Internal, IERC165 {
/**
* @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)
external
view
returns (uint256);
/**
* @notice query the balances of given tokens held by given addresses
* @param accounts addresss to query
* @param ids tokens to query
* @return token balances
*/
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
/**
* @notice query approval status of given operator with respect to given address
* @param account address to query for approval granted
* @param operator address to query for approval received
* @return whether operator is approved to spend tokens held by account
*/
function isApprovedForAll(address account, address operator)
external
view
returns (bool);
/**
* @notice grant approval to or revoke approval from given operator to spend held tokens
* @param operator address whose approval status to update
* @param status whether operator should be considered approved
*/
function setApprovalForAll(address operator, bool status) external;
/**
* @notice transfer tokens between given addresses, checking for ERC1155Receiver implementation if applicable
* @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 safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
/**
* @notice transfer batch of tokens between given addresses, checking for ERC1155Receiver implementation if applicable
* @param from sender of tokens
* @param to receiver of tokens
* @param ids list of token IDs
* @param amounts list of quantities of tokens to transfer
* @param data data payload
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC165 } from '../../introspection/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.0;
import { AddressUtils } from '../../../utils/AddressUtils.sol';
import { IERC1155Internal } from '../IERC1155Internal.sol';
import { IERC1155Receiver } from '../IERC1155Receiver.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 IERC1155Internal {
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)
{
require(
account != address(0),
'ERC1155: balance query for the zero address'
);
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 {
require(account != address(0), 'ERC1155: mint to the zero address');
_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 {
require(account != address(0), 'ERC1155: mint to the zero address');
require(
ids.length == amounts.length,
'ERC1155: ids and amounts length mismatch'
);
_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 {
require(account != address(0), 'ERC1155: burn from the zero address');
_beforeTokenTransfer(
msg.sender,
account,
address(0),
_asSingletonArray(id),
_asSingletonArray(amount),
''
);
mapping(address => uint256) storage balances = ERC1155BaseStorage
.layout()
.balances[id];
unchecked {
require(
balances[account] >= amount,
'ERC1155: burn amount exceeds balances'
);
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 {
require(account != address(0), 'ERC1155: burn from the zero address');
require(
ids.length == amounts.length,
'ERC1155: ids and amounts length mismatch'
);
_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];
require(
balances[id][account] >= amounts[i],
'ERC1155: burn amount exceeds balance'
);
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 {
require(
recipient != address(0),
'ERC1155: transfer to the zero address'
);
_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];
require(
senderBalance >= amount,
'ERC1155: insufficient balances for transfer'
);
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 {
require(
recipient != address(0),
'ERC1155: transfer to the zero address'
);
require(
ids.length == amounts.length,
'ERC1155: ids and amounts length mismatch'
);
_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];
require(
senderBalance >= amount,
'ERC1155: insufficient balances for transfer'
);
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) {
require(
response == IERC1155Receiver.onERC1155Received.selector,
'ERC1155: ERC1155Receiver rejected tokens'
);
} catch Error(string memory reason) {
revert(reason);
} catch {
revert('ERC1155: transfer to non ERC1155Receiver implementer');
}
}
}
/**
* @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) {
require(
response ==
IERC1155Receiver.onERC1155BatchReceived.selector,
'ERC1155: ERC1155Receiver rejected tokens'
);
} catch Error(string memory reason) {
revert(reason);
} catch {
revert('ERC1155: transfer to non ERC1155Receiver implementer');
}
}
}
/**
* @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.0;
import { IERC165 } from '../../introspection/IERC165.sol';
/**
* @notice 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.0;
/**
* @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.0;
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: BUSL-1.1
// For further clarification please see https://license.premia.legal
pragma solidity ^0.8.0;
library FeeDiscountStorage {
bytes32 internal constant STORAGE_SLOT =
keccak256("premia.contracts.staking.PremiaFeeDiscount");
struct UserInfo {
uint256 balance; // Balance staked by user
uint64 stakePeriod; // Stake period selected by user
uint64 lockedUntil; // Timestamp at which the lock ends
}
struct Layout {
// User data with xPREMIA balance staked and date at which lock ends
mapping(address => UserInfo) userInfo;
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}// 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: 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)
}
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/libraries/OptionMath.sol": {
"OptionMath": "0xc7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f5"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"ivolOracle","type":"address"},{"internalType":"address","name":"weth","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":"int128","name":"feeApy64x64","type":"int128"},{"internalType":"address","name":"uniswapV2Factory","type":"address"},{"internalType":"address","name":"sushiswapFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"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":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isCallPool","type":"bool"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bool","name":"isSushi","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
6102206040523480156200001257600080fd5b50604051620060ba380380620060ba8339810160408190526200003591620001ad565b6001600160a01b038981166101005288811660805287811660a05286811660c052851660e052600f84810b6101205283900b6101405288888888888888888888888888888888620000956000808062000131602090811b62000d2c17901c565b6101608181525050620000b760016000806200013160201b62000d2c1760201c565b6101808181525050620000d960026000806200013160201b62000d2c1760201c565b6101a08181525050620000fb60036000806200013160201b62000d2c1760201c565b6101c0525050506001600160a01b039586166101e05250505050166102005250620002a59e505050505050505050505050505050565b600081600f0b6080846001600160401b0316901b60f88660078111156200015c576200015c62000268565b6200016992911b6200027e565b6200017591906200027e565b949350505050565b80516001600160a01b03811681146200019557600080fd5b919050565b8051600f81900b81146200019557600080fd5b60008060008060008060008060006101208a8c031215620001cd57600080fd5b620001d88a6200017d565b9850620001e860208b016200017d565b9750620001f860408b016200017d565b96506200020860608b016200017d565b95506200021860808b016200017d565b94506200022860a08b016200019a565b93506200023860c08b016200019a565b92506200024860e08b016200017d565b9150620002596101008b016200017d565b90509295985092959850929598565b634e487b7160e01b600052602160045260246000fd5b60008219821115620002a057634e487b7160e01b600052601160045260246000fd5b500190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051615c9862000422600039600081816117100152818161189501528181611b3f0152818161306101526130de0152600081816116ea0152818161186f01528181611b1901528181611be50152818161303b01526130b80152600081816111e4015261287101526000818161120a0152818161284701526128ea01526000818161147e0152818161281d01526129900152600081816114a4015281816127f4015281816128c10152818161296701526129b9015260008181611df701526142ae01526000613b740152600061392b01526000818161347901526134c90152600081816115590152818161158b015281816115b601528181611f3001526125eb0152600081816105a601528181610c3801528181610cc301528181610f7b015261324701526000818161163a015281816117aa015281816118380152818161499a0152614ac00152615c986000f3fe60806040526004361061009c5760003560e01c8063491c011a11610064578063491c011a1461016d5780637365d18d1461018d5780637d09e3bc146101bd5780639a408321146101d0578063e4d4517b146101e3578063fbe5f3a11461020357600080fd5b80630178efa0146100a15780630e07ee09146100c35780630e6878a31461010357806338d0743614610123578063476343ee14610143575b600080fd5b3480156100ad57600080fd5b506100c16100bc3660046152ba565b610218565b005b3480156100cf57600080fd5b506100e36100de3660046152ba565b6102c3565b604080519384526020840192909252908201526060015b60405180910390f35b34801561010f57600080fd5b506100c161011e3660046152f3565b61037f565b34801561012f57600080fd5b506100c161013e366004615310565b61038c565b34801561014f57600080fd5b506101586104f7565b604080519283526020830191909152016100fa565b34801561017957600080fd5b506100c1610188366004615357565b610516565b34801561019957600080fd5b506101ad6101a83660046153c7565b61061e565b6040516100fa9493929190615485565b6100c16101cb3660046154be565b610a5e565b6100c16101de366004615310565b610afa565b3480156101ef57600080fd5b506100c16101fe366004615544565b610b0a565b34801561020f57600080fd5b506100c1610bac565b60f883901c608084901c84600060058460078111156102395761023961556e565b1480610256575060048460078111156102545761025461556e565b145b9050600080516020615c43833981519152600061027782338787878d610d71565b90506000610287838a8787610e4c565b90506102973383868b6001610eb2565b6102b78333868b6102b1576102ac858761559a565b610f05565b84610f05565b50505050505050505050565b60008080600080516020615c43833981519152816102e08261102a565b9050608088901c88600060f882901c60058160078111156103035761030361556e565b14806103205750600481600781111561031e5761031e61556e565b145b91505061033285338585858f8a611090565b919950975095506103473387838c6001610eb2565b6103718533838c61035c576102ac8b8d6155b1565b896103678c8e6155b1565b6102ac91906155b1565b505050505093509350939050565b6103893382610516565b50565b600080516020615c43833981519152826103a682846110e0565b336000908152600d8301602090815260408083208615158452909152902054426103d382620151806155b1565b106103f95760405162461bcd60e51b81526004016103f0906155c9565b60405180910390fd5b60006104058486611170565b9050600080610413876111db565b90506000610421338361122f565b9050801561045457858110156104395780925061043d565b8592505b610447838761559a565b95506104543383856112e2565b5050831561048b5761046f3361046988611475565b866112e2565b600061047b8688611170565b90506104898684838a6114c9565b505b61049b8533886102ac858c61559a565b6104a9338888600180610eb2565b6040805187151581526020810185905290810188905233907f477b6b4ae4e555826c33dfac094986168321f8f6295ab29cb8109c27e312e50f9060600160405180910390a250505050505050565b6000806105046001611546565b91506105106000611546565b90509091565b6001600160a01b0382811660009081527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df53026020908152604080832085151584528252808320547fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df5303909252918290205491516357795f1b60e11b8152600080516020615c43833981519152939192917f0000000000000000000000000000000000000000000000000000000000000000169063aef2be36906105e5908890309089908890819089906004016155ee565b600060405180830381600087803b1580156105ff57600080fd5b505af1158015610613573d6000803e3d6000fd5b505050505050505050565b6060806000808786146106675760405162461bcd60e51b81526020600482015260116024820152700c8d2cccc40c2e4e4c2f240d8cadccee8d607b1b60448201526064016103f0565b6000610680600080516020615c4383398151915261102a565b9050886001600160401b0381111561069a5761069a615624565b6040519080825280602002602001820160405280156106c3578160200160208202803683370190505b509450886001600160401b038111156106de576106de615624565b604051908082528060200260200182016040528015610707578160200160208202803683370190505b5093506000896001600160401b0381111561072457610724615624565b60405190808252806020026020018201604052801561074d578160200160208202803683370190505b50905060005b8a8110156108a057600080600061078b8f8f868181106107755761077561563a565b9050602002013560f881901c91608082901c9190565b91945092509050600060058460078111156107a8576107a861556e565b14806107c5575060048460078111156107c3576107c361556e565b145b90506000808f8f888181106107dc576107dc61563a565b905060200201359050828888815181106107f8576107f861563a565b91151560209283029190910190910152610825600080516020615c4383398151915233878787868f611090565b8f8a815181106108375761083761563a565b602002602001018f8b815181106108505761085061563a565b60209081029190910101929092529190529150821561087a57610873828c6155b1565b9a50610887565b610884828b6155b1565b99505b505050505050808061089890615650565b915050610753565b508315610976576000805b8b81101561093a578281815181106108c5576108c561563a565b60209081029190910101516108d957610928565b8781815181106108eb576108eb61563a565b6020026020010151826108fe91906155b1565b91508681815181106109125761091261563a565b60200260200101518261092591906155b1565b91505b8061093281615650565b9150506108ab565b5061094a338660018b6001610eb2565b610974600080516020615c438339815191523360018b61096a5784610f05565b6102ac89866155b1565b505b8215610a50576000805b8b811015610a145782818151811061099a5761099a61563a565b602002602001015115156001151514156109b357610a02565b8781815181106109c5576109c561563a565b6020026020010151826109d891906155b1565b91508681815181106109ec576109ec61563a565b6020026020010151826109ff91906155b1565b91505b80610a0c81615650565b915050610980565b50610a24338560008b6001610eb2565b610a4e600080516020615c438339815191523360008b610a445784610f05565b6102ac88866155b1565b505b505095509550955095915050565b341580610a69575083155b610ab55760405162461bcd60e51b815260206004820152601c60248201527f76616c756520616e6420616d6f756e74496e4d6178207061737365640000000060448201526064016103f0565b84610abe578694505b3415610ad657610ad085848484611636565b50610ae5565b610ae38585858585611b0d565b505b610af187876000611ca7565b50505050505050565b610b0682826001611ca7565b5050565b600080516020615c438339815191526001600160401b0383161580610b635750336000908152600d8201602090815260408083208515158452909152902054610b5690620151806155b1565b836001600160401b031610155b610b7f5760405162461bcd60e51b81526004016103f0906155c9565b336000908152600e909101602090815260408083209315158352929052206001600160401b039091169055565b600160008181527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df53036020527f574488069b1552032c6ff89bebe1010d4367afb63ca1952e2124c136d7904c3154604080516358097aa360e11b81523060048201526024810194909452604484019190915251600080516020615c43833981519152926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263b012f5469260648084019382900301818387803b158015610c7a57600080fd5b505af1158015610c8e573d6000803e3d6000fd5b505060008080526018840160205260408082205490516358097aa360e11b8152306004820152602481019290925260448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063b012f5469150606401600060405180830381600087803b158015610d1157600080fd5b505af1158015610d25573d6000803e3d6000fd5b5050505050565b600081600f0b6080846001600160401b0316901b60f8866007811115610d5457610d5461556e565b610d5f92911b6155b1565b610d6991906155b1565b949350505050565b600080610d89610d82856001611d85565b8787610d2c565b90506000610da2610d9b866000611d85565b8888610d2c565b90506000610db28a868989610e4c565b90506000610dc28b84848c611db9565b9050610dcf8a85886112e2565b6000610ddf8c8c868a868d611e5d565b9050610dec8b85896112e2565b81610df782856155b1565b610e0191906155b1565b60408051868152602081018a90529197507f2c36fd3d1ab99ca7bec7ffe3c66b81f331da75eeda6c23fda680a77a7c458979910160405180910390a150505050509695505050505050565b60008115610e5b575082610d69565b6000610e6b600f85900b86611fb1565b90506000610e8a828860030160149054906101000a900460ff16612019565b9050610ea7818860030160159054906101000a900460ff1661202f565b979650505050505050565b8115610efa578015610ee757610ee285610edc85600080516020615c438339815191525b9061204a565b86612073565b610d25565b610ee285610ef4856111db565b86612139565b610d25858585612154565b6001600160a01b0383166000908152601785016020908152604080832085151584528252808320546018880190925282205490918084841115610f4f57610f4c858561559a565b91505b84831115610f6457610f61858461559a565b90505b60405163edaf7d5b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063edaf7d5b90610fba908a9030908b908a9089908b906004016155ee565b600060405180830381600087803b158015610fd457600080fd5b505af1158015610fe8573d6000803e3d6000fd5b505050506001600160a01b0396909616600090815260178801602090815260408083209715158352968152868220929092556018909701905250505090912055565b60004282600c01541415611048576110428242612217565b92915050565b61105182612245565b905061105d8242612217565b600f0b61106f5761106f82428361234a565b42600c8301556110808260016110e0565b61108b8260006110e0565b919050565b60008060006110a48a8a8a8a8a8a8a6123c8565b909350915060006110b98b8b8b8b8b8b610d71565b9050826110c6858361559a565b6110d0919061559a565b9150509750975097945050505050565b80151560009081526013830160205260409020805415806111015750805442105b1561110b57505050565b60006111178484611170565b9050611152848261114c611141866001015461113c898b61268290919063ffffffff16565b612019565b600f86900b906126b0565b866114c9565b50501515600090815260139091016020526040812081815560010155565b60008061118d83611182576001611185565b60005b600080610d2c565b8315156000908152601386016020526040902060010154909150610d69906000838152600080516020615c2383398151915260205260409020546111d1919061559a565b61113c8686612682565b600081611208577f0000000000000000000000000000000000000000000000000000000000000000611042565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b60006001600160a01b03831661129b5760405162461bcd60e51b815260206004820152602b60248201527f455243313135353a2062616c616e636520717565727920666f7220746865207a60448201526a65726f206164647265737360a81b60648201526084016103f0565b7f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b6000928352602090815260408084206001600160a01b0395909516845293905250205490565b6001600160a01b0383166113445760405162461bcd60e51b815260206004820152602360248201527f455243313135353a206275726e2066726f6d20746865207a65726f206164647260448201526265737360e81b60648201526084016103f0565b61137233846000611354866126e4565b61135d866126e4565b6040518060200160405280600081525061272f565b60008281527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b038716845291829052909120548211156114135760405162461bcd60e51b815260206004820152602560248201527f455243313135353a206275726e20616d6f756e7420657863656564732062616c604482015264616e63657360d81b60648201526084016103f0565b6001600160a01b03841660008181526020838152604080832080548790039055805187815291820186905291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a450505050565b6000816114a2577f0000000000000000000000000000000000000000000000000000000000000000611042565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b60006114d58583612b5d565b905060006114e68683878787612b76565b90506114f3868285612c6d565b60408051600f83810b825287810b602083015286900b818301529051841515917f4e23621c6f591f14bf9505cb8326b45af9dc6c5569fd608de2a7a2ddd6146b2e919081900360600190a2505050505050565b600080611552836111db565b905061157e7f00000000000000000000000000000000000000000000000000000000000000008261122f565b91508115611630576115b17f000000000000000000000000000000000000000000000000000000000000000082846112e2565b6115f37f00000000000000000000000000000000000000000000000000000000000000006115ed85600080516020615c43833981519152610ed6565b84612073565b8215157f496915fc2759ccee8966a2c2098c4895c608b8decdb351dccea354eb4ca9c4f08360405161162791815260200190565b60405180910390a25b50919050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316848460008181106116755761167561563a565b905060200201602081019061168a919061566b565b6001600160a01b0316146116e05760405162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f5041544800000060448201526064016103f0565b61176c8261170e577f0000000000000000000000000000000000000000000000000000000000000000611730565b7f00000000000000000000000000000000000000000000000000000000000000005b86868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612cc4915050565b905034816000815181106117825761178261563a565b602002602001015111156117a85760405162461bcd60e51b81526004016103f090615686565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826000815181106117ea576117ea61563a565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561181d57600080fd5b505af1158015611831573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb61190b84611893577f00000000000000000000000000000000000000000000000000000000000000006118b5565b7f00000000000000000000000000000000000000000000000000000000000000005b878760008181106118c8576118c861563a565b90506020020160208101906118dd919061566b565b888860018181106118f0576118f061563a565b9050602002016020810190611905919061566b565b87612e52565b8360008151811061191e5761191e61563a565b60200260200101516040518363ffffffff1660e01b81526004016119579291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015611976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199a91906156cd565b6119a6576119a66156ea565b6119e781858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250879150612f539050565b806000815181106119fa576119fa61563a565b6020026020010151341115610d69576000336001600160a01b031682600081518110611a2857611a2861563a565b602002602001015134611a3b919061559a565b60408051600081526020810191829052611a549161572c565b60006040518083038185875af1925050503d8060008114611a91576040519150601f19603f3d011682016040523d82523d6000602084013e611a96565b606091505b5050905080611b045760405162461bcd60e51b815260206004820152603460248201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60448201527308115512081d1c985b9cd9995c8819985a5b195960621b60648201526084016103f0565b50949350505050565b6060611b9b82611b3d577f0000000000000000000000000000000000000000000000000000000000000000611b5f565b7f00000000000000000000000000000000000000000000000000000000000000005b87868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612cc4915050565b90508481600081518110611bb157611bb161563a565b60200260200101511115611bd75760405162461bcd60e51b81526004016103f090615686565b611c5d33611c0984611893577f00000000000000000000000000000000000000000000000000000000000000006118b5565b83600081518110611c1c57611c1c61563a565b602002602001015187876000818110611c3757611c3761563a565b9050602002016020810190611c4c919061566b565b6001600160a01b03169291906131af565b611c9e81858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250879150612f539050565b95945050505050565b3360009081527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52f9602090815260408083208515158452909152812055600080516020615c43833981519152611cfc81846110e0565b336000818152600d83016020908152604080832087151584529091529020429055611d2a908290858761320f565b611d378133868686613328565b611d42338585612154565b6040805184151581526020810186905233917f74a132f462598ad738fcdf934a3f668aac78953afdb0c9fb953e16e07cca511c910160405180910390a250505050565b60008215611da35781611d99576005611d9c565b60045b9050611042565b81611daf576007611db2565b60065b9392505050565b6000816001600160401b0316421015610d69576000611dd785613456565b15611df5576000858152601d87016020526040902054600f0b611e17565b7f00000000000000000000000000000000000000000000000000000000000000005b9050611e536301e13380611e34426001600160401b03871661559a565b611e3e9087615748565b611e48919061577d565b600f83900b90611fb1565b9695505050505050565b600082611e6c57506000611e53565b6001600160a01b0386166000908152601c88016020908152604080832088845290915281205490611e9d888861122f565b611ea78784615748565b611eb1919061577d565b6001600160a01b0389166000908152601c8b01602090815260408083208b8452909152812080549293508392909190611eeb90849061559a565b90915550611f109050611efe868361559a565b611f078a613475565b600f0b90611fb1565b9250600083611f1f878461559a565b611f29919061559a565b9050611f5a7f0000000000000000000000000000000000000000000000000000000000000000828760016000610eb2565b604080516001600160a01b038b168152602081018a90529081018290527fe02f89a472c9f9be95b6093634de6f0967e703e84390d7e00d23e15ea6b997659060600160405180910390a15050509695505050505050565b600081611fc057506000611042565b600083600f0b1215611fd157600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b0381111561200057600080fd5b60401b811981111561201157600080fd5b019392505050565b6000611db28361202a84600a615883565b61353a565b6000611db261203f83600a615883565b600f85900b90611fb1565b6000816120615782546001600160a01b0316611db2565b5050600101546001600160a01b031690565b8061207d57505050565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526024820183905283169063a9059cbb906044016020604051808303816000875af11580156120cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f091906156cd565b6121345760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016103f0565b505050565b61213483838360405180602001604052806000815250613571565b600080516020615c438339815191526121768461217084611475565b85612139565b600061010480612186814261577d565b6121909190615748565b61219a91906155b1565b6001600160a01b03861660009081526014840160209081526040808320848452825280832087151584529091528120805492935086929091906121de9084906155b1565b9091555050821515600090815260138301602052604081206001810180549192879261220b9084906155b1565b90915550505550505050565b6000601183018161222a610e108561577d565b8152602081019190915260400160002054600f0b9392505050565b6000808260030160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c19190615892565b905060008360020160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561231a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233e9190615892565b9050610d69828261368c565b6000612358610e108461577d565b6000818152601186016020526040902080546001600160801b0319166001600160801b038516179055905061239160ff8083169061559a565b6001901b846012016000600884901c815260200190815260200160002060008282546123bd91906155b1565b909155505050505050565b60008042876001600160401b03161161240d5760405162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b60448201526064016103f0565b886005015484101561244d5760405162461bcd60e51b81526020600482015260096024820152681d1bdbc81cdb585b1b60ba1b60448201526064016103f0565b600061245b8a868989610e4c565b9050600061246887611475565b90506124758b8b89613727565b61247f8b8361122f565b612489919061559a565b871515600090815260138d0160205260409020600101546124a983613456565b6124b3919061559a565b6124bd919061559a565b8211156124f85760405162461bcd60e51b8152602060048201526009602482015268696e737566206c697160b81b60448201526064016103f0565b5050600061254b6040518060c001604052808b6001600160a01b031681526020018a6001600160401b0316815260200189600f0b815260200186600f0b815260200187815260200188151581525061376c565b80519091506125639061255e8c89612682565b61202f565b60208201519093506125799061255e8c89612682565b9150600061259261258b886001611d85565b8a8a610d2c565b90506125a48b82888560400151613bf6565b6125af8a8288612139565b60006125bb8c89611170565b90506125cc8c8c8c8c8b8a8e613cb9565b60006125d88d8a611170565b90506125e68d83838c6114c9565b6126157f0000000000000000000000000000000000000000000000000000000000000000868b60016000610eb2565b60408051848152602081018a905290810187905260608101869052600f88900b60808201526001600160a01b038d16907f4719d073a940c087132bffc1aac4f3837ca2c820d95574727828ce38f7fbc8c79060a00160405180910390a25050505097509795505050505050565b60008161269d576003830154600160a81b900460ff16611db2565b505060030154600160a01b900460ff1690565b6000600f83810b9083900b0160016001607f1b031981128015906126db575060016001607f1b038113155b611db257600080fd5b6040805160018082528183019092526060916000919060208083019080368337019050509050828160008151811061271e5761271e61563a565b602090810291909101015292915050565b61273d868686868686613f09565b600080516020615c4383398151915260005b8451811015612b5357600085828151811061276c5761276c61563a565b60200260200101519050600085838151811061278a5761278a61563a565b6020026020010151905080600014156127a4575050612b41565b6001600160a01b0389166127c1576127bf60158501836140e2565b505b6001600160a01b0388161580156127de57506127dc82613456565b155b156127f2576127f060158501836140ee565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061283f57507f000000000000000000000000000000000000000000000000000000000000000082145b8061286957507f000000000000000000000000000000000000000000000000000000000000000082145b8061289357507f000000000000000000000000000000000000000000000000000000000000000082145b15612965576001600160a01b038916158015906128b857506001600160a01b03881615155b156129655760007f000000000000000000000000000000000000000000000000000000000000000083148061290c57507f000000000000000000000000000000000000000000000000000000000000000083145b6001600160a01b038b166000908152600d8701602090815260408083208415158452909152902054909150429061294690620151806155b1565b106129635760405162461bcd60e51b81526004016103f0906155c9565b505b7f00000000000000000000000000000000000000000000000000000000000000008214806129b257507f000000000000000000000000000000000000000000000000000000000000000082145b15612af5577f0000000000000000000000000000000000000000000000000000000000000000821460006129e686836140fa565b90506001600160a01b038b1615612aaa576000612a038c8661122f565b90508181118015612a1d5750612a1982856155b1565b8111155b15612a815783612a2e888e86613727565b612a38908361559a565b1015612a765760405162461bcd60e51b815260206004820152600d60248201526c496e7375662062616c616e636560981b60448201526064016103f0565b612a81878d85614114565b6001600160a01b038b1615612aa857612a9c878d8587610f05565b612aa8878c858761320f565b505b6001600160a01b038a1615612af2576000612ac58b8661122f565b9050818111158015612ae0575081612add85836155b1565b10155b15612af057612af0878c856141c5565b505b50505b60f882901c6005816007811115612b0e57612b0e61556e565b1480612b2b57506007816007811115612b2957612b2961556e565b145b15612b3d57612b3d858b8b868661426d565b5050505b80612b4b81615650565b91505061274f565b5050505050505050565b6000612b6983836143fc565b9050611db2838284614423565b60008082612b8b576019870154600f0b612b9b565b6019870154600160801b9004600f0b5b905080600f0b60001415612bb357506008860154600f0b5b60405163e101a89b60e01b8152600f87810b600483015286810b602483015285810b604483015282900b606482015273c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f59063e101a89b90608401602060405180830381865af4158015612c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c4291906158bd565b915067b33333333333333382600f0b1215612c635767b33333333333333391505b5095945050505050565b8015612c9d576009830180546001600160801b0384166001600160801b031990911617905542600b840155505050565b6008830180546001600160801b03808516600160801b02911617905542600a840155505050565b6060600283511015612d185760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f50415448000060448201526064016103f0565b82516001600160401b03811115612d3157612d31615624565b604051908082528060200260200182016040528015612d5a578160200160208202803683370190505b509050838160018351612d6d919061559a565b81518110612d7d57612d7d61563a565b602002602001018181525050600060018451612d99919061559a565b90505b8015611b0457600080612ded8887612db560018761559a565b81518110612dc557612dc561563a565b6020026020010151888681518110612ddf57612ddf61563a565b6020026020010151886145d6565b91509150612e15848481518110612e0657612e0661563a565b602002602001015183836146a2565b84612e2160018661559a565b81518110612e3157612e3161563a565b60200260200101818152505050508080612e4a906158d8565b915050612d9c565b6000806000612e6186866147be565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b166034820152919350915087906048016040516020818303038152906040528051906020012085612ee7576040518060400160405280602081526020017f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f815250612f1e565b6040518060400160405280602081526020017fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c63038152505b604051602001612f30939291906158ef565b60408051601f198184030181529190528051602090910120979650505050505050565b60005b60018451612f64919061559a565b811015610d2557600080858381518110612f8057612f8061563a565b602002602001015186846001612f9691906155b1565b81518110612fa657612fa661563a565b6020026020010151915091506000612fbe83836147be565b509050600088612fcf8660016155b1565b81518110612fdf57612fdf61563a565b60200260200101519050600080836001600160a01b0316866001600160a01b03161461300d57826000613011565b6000835b91509150600060028b51613025919061559a565b881061303157896130ac565b6130ac8961305f577f0000000000000000000000000000000000000000000000000000000000000000613081565b7f00000000000000000000000000000000000000000000000000000000000000005b878d61308e8c60026155b1565b8151811061309e5761309e61563a565b60200260200101518c612e52565b9050613106896130dc577f00000000000000000000000000000000000000000000000000000000000000006130fe565b7f00000000000000000000000000000000000000000000000000000000000000005b88888c612e52565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613143576020820181803683370190505b506040518563ffffffff1660e01b81526004016131639493929190615964565b600060405180830381600087803b15801561317d57600080fd5b505af1158015613191573d6000803e3d6000fd5b505050505050505050505080806131a790615650565b915050612f56565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526132099085906148b6565b50505050565b6001600160a01b03808416600090815260178601602090815260408083208615158452825280832054601889019092529091205490917f00000000000000000000000000000000000000000000000000000000000000001663edaf7d5b8630878661327a89826155b1565b876040518763ffffffff1660e01b815260040161329c969594939291906155ee565b600060405180830381600087803b1580156132b657600080fd5b505af11580156132ca573d6000803e3d6000fd5b5050505082826132da91906155b1565b6001600160a01b03861660009081526017880160209081526040808320881515845290915290205561330c83826155b1565b9315156000908152601890960160205250506040909320555050565b6000811561333d5761333a8484614988565b90505b808411156133665761335985613353838761559a565b85614b38565b61336390826155b1565b90505b8084111561344e57613378868461204a565b6001600160a01b03166323b872dd8630613392858961559a565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156133e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061340a91906156cd565b61344e5760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016103f0565b505050505050565b6000908152600080516020615c23833981519152602052604090205490565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161561108b576040516303793c8d60e11b81526001600160a01b038381166004830152611042917f0000000000000000000000000000000000000000000000000000000000000000909116906306f2791a90602401602060405180830381865afa158015613512573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135369190615892565b6127105b60008161354657600080fd5b60006135528484614b7f565b905060016001607f1b036001600160801b0382161115611db257600080fd5b6001600160a01b0384166135d15760405162461bcd60e51b815260206004820152602160248201527f455243313135353a206d696e7420746f20746865207a65726f206164647265736044820152607360f81b60648201526084016103f0565b6135f0336000866135e1876126e4565b6135ea876126e4565b8661272f565b60008381527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b0388168452909152812080548492906136419084906155b1565b909155505060408051848152602081018490526001600160a01b0386169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f629101611467565b60008161369857600080fd5b6000808412156136ad57836000039350600190505b60008312156136bf5760009290920391155b60006136cb8585614b7f565b905081156136f9576001607f1b816001600160801b031611156136ed57600080fd5b60000391506110429050565b60016001607f1b03816001600160801b0316111561371657600080fd5b91506110429050565b505092915050565b6001600160a01b038216600090815260148401602090815260408083208415158085526013880184528285205485529083528184209084529091529020549392505050565b60408051608081018252600080825260208201819052918101829052606081019190915260008260400151600f0b1380156137ae575060008260600151600f0b135b80156137c75750600082602001516001600160401b0316115b6138025760405162461bcd60e51b815260206004820152600c60248201526b696e76616c6964206172677360a01b60448201526064016103f0565b60808201517fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52ee54600080516020615c43833981519152916000916138509190600160a01b900460ff16612019565b905060008061386c8660a0015185614ce190919063ffffffff16565b91509150600081600f0b136138ac5760405162461bcd60e51b81526020600482015260066024820152656e6f206c697160d01b60448201526064016103f0565b60006138d44288602001516001600160401b03166138ca919061559a565b6301e1338061353a565b8554600187015460608a01516040808c01519051631af9d50d60e11b81526001600160a01b0394851660048201529284166024840152600f91820b6044840152810b606483015283900b60848201529192506000917f0000000000000000000000000000000000000000000000000000000000000000909116906335f3aa1a9060a401602060405180830381865afa158015613974573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399891906158bd565b9050600081600f0b136139d75760405162461bcd60e51b81526020600482015260076024820152660766f6c203d20360cc1b60448201526064016103f0565b60008860a00151613a01576139fc896040015187600f0b614d7d90919063ffffffff16565b613a03565b855b9050600080600073c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f5630313686c604051806101400160405280613a46898a600f0b614d7d90919063ffffffff16565b600f0b81526020018f60400151600f0b81526020018f60600151600f0b815260200189600f0b81526020018b600f0b81526020018a600f0b8152602001613a99888c600f0b614db390919063ffffffff16565b600f0b8152602001600160401b600f0b8152602001674ccccccccccccccd600f0b81526020018f60a0015115158152506040518263ffffffff1660e01b8152600401613ae59190615991565b606060405180830381865af4158015613b02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b269190615a5a565b9250925092508b60a00151613b4857613b43600f84900b8a614d7d565b613b68565b60608c0151613b6890613b5f600f86900b8c614d7d565b600f0b90614de6565b600f0b808c52613b98907f0000000000000000000000000000000000000000000000000000000000000000614d7d565b600f90810b60208d015282810b60408d015281900b60608c01528b51613bcf90613bc190613475565b60208d0151600f0b90614d7d565b8b602001818151613be09190615a9d565b600f0b905250999b9a5050505050505050505050565b6000613c18613c0485613456565b6003870154600160a01b900460ff16612019565b90506000613c37848760030160149054906101000a900460ff16612019565b9050613c82613c4a600f84900b836126b0565b613b5f613c5b600f87900b85614d7d565b6000898152601b8b016020526040902054613c7990600f0b87614d7d565b600f0b906126b0565b6000958652601b969096016020525050604090922080546001600160801b0319166001600160801b03909416939093179092555050565b6000613cc9610d82836000611d85565b90506000613cd989868886610e4c565b90506000613ce98a84848b611db9565b90505b81156102b7578315156000908152600f8b01602090815260408083208380529091528120546001600160a01b031690613d2d82613d2888611475565b61122f565b9050613d3a8c8388614e3e565b613d7357613d5182613d4b88611475565b836112e2565b613d6082828860016000610eb2565b613d6c8c838884610f05565b5050613cec565b613d7d8c876140fa565b811015613d8f57613d6c8c8388614114565b8a6001600160a01b0316826001600160a01b03161415613dbf57613db48c8388614114565b613d6c8c83886141c5565b613dca8c8388613727565b613dd4908261559a565b9050613e016040518060800160405280600081526020016000815260200160008152602001600081525090565b8484613e0d8a836155b1565b613e17919061559a565b613e219084615748565b613e2b919061577d565b60208201819052613e3e57505050613cec565b8481602001511115613e5257602081018590525b8481602001518a613e639190615748565b613e6d919061577d565b815260208101518590613e80908a615748565b613e8a919061577d565b604082015260208101518590613ea09086615748565b613eaa919061577d565b6060820152613ebd8d848e89858c614e78565b6020810151613ecc908661559a565b8151909550613edb908a61559a565b9850806040015188613eed919061559a565b9750806060015184613eff919061559a565b9350505050613cec565b836001600160a01b0316856001600160a01b03161461344e576001600160a01b0385811660009081527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424ec602052604080822092871682528120600080516020615c23833981519152927fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb929091905b87518110156140d5576000878281518110613fb557613fb561563a565b6020026020010151905060008111156140cc576000898381518110613fdc57613fdc61563a565b6020026020010151905060006001600160a01b03168c6001600160a01b0316141561402a576000818152602088905260408120805484929061401f9084906155b1565b909155506140609050565b816140358d8361122f565b1415614060576000818152602087905260409020614053908d614f8a565b5061405e85826140ee565b505b6001600160a01b038b16614097576000818152602088905260408120805484929061408c90849061559a565b909155506140ca9050565b6140a18b8261122f565b6140ca5760008181526020879052604090206140bd908c614f9f565b506140c884826140e2565b505b505b50600101613f98565b5050505050505050505050565b6000611db28383614fb0565b6000611db28383614fff565b60008161410b578260040154611db2565b50506005015490565b6001600160a01b03821661412757600080fd5b8015156000908152600f840160209081526040808320601087019092529091206141528483836150cc565b61415d575050505050565b6001600160a01b0393841660008181526020838152604080832080549683528184208054978a16808652838620805499909b166001600160a01b0319998a168117909b5599855295909252822080548616909717909655528054821690558254169091555050565b6001600160a01b0382166141d857600080fd5b8015156000908152600f840160209081526040808320601087019092529091206142038483836150cc565b1561420f575050505050565b60008080526020828152604080832080546001600160a01b0390811680865296845282852080546001600160a01b03199081169a909216998a1790558885529490925282208054841690941790935580528154169092179091555050565b6001600160a01b03841615801561428b57508061428983613456565b145b156142da576000828152601d86016020526040902080546001600160801b0319167f00000000000000000000000000000000000000000000000000000000000000006001600160801b03161790555b6001600160a01b0383161580156142f757506142f582613456565b155b1561431b576000828152601d86016020526040902080546001600160801b03191690555b6001600160a01b0384161580159061433b57506001600160a01b03831615155b15610d255760f882901c608083901c83600060058460078111156143615761436161556e565b14905060006143728a878585610e4c565b905060006143828b898488611db9565b905060006143948c8c8b8b8689611e5d565b90506143a28c8b8b85615118565b60006143af8d8d87614e3e565b15905081156143c6576143c68c8387846000610eb2565b6143e18d8d87846143db576102ac868961559a565b87610f05565b6143ed8d8c878761320f565b50505050505050505050505050565b600081614417576008830154600160801b9004600f0b611db2565b505060090154600f0b90565b600080826144355784600a015461443b565b84600b01545b614445904261559a565b905061a8c08111156144645761445d61a8c08261559a565b905061446d565b83915050611db2565b600061447b8261384061353a565b9050600061448e85611182576001611185565b851515600090815260188901602090815260408083205460138c01835281842060010154858552600080516020615c2383398151915290935290832054939450926144ec916144dc9161559a565b6144e6908461559a565b8361353a565b6040805161012081018252600f87810b82528b810b602083015283900b8183015267b333333333333333606082015267e66666666666666660808201819052600160401b60a0830181905260c083015260e082015268056fc2a2c515da32ea6101008201529051634916d70d60e01b815291925073c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f591634916d70d9161458891600401615aed565b602060405180830381865af41580156145a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c991906158bd565b9998505050505050505050565b60008060006145e586866147be565b5090506000806145f789898989612e52565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015614634573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146589190615bb4565b506001600160701b031691506001600160701b03169150826001600160a01b0316886001600160a01b03161461468f578082614692565b81815b909a909950975050505050505050565b60008084116147085760405162461bcd60e51b815260206004820152602c60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4f60448201526b155514155517d05353d5539560a21b60648201526084016103f0565b6000831180156147185750600082115b6147755760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c604482015267495155494449545960c01b60648201526084016103f0565b60006147818585615748565b61478d906103e8615748565b9050600061479b868561559a565b6147a7906103e5615748565b90506147b3818361577d565b611e539060016155b1565b600080826001600160a01b0316846001600160a01b031614156148315760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f41444452604482015264455353455360d81b60648201526084016103f0565b826001600160a01b0316846001600160a01b031610614851578284614854565b83835b90925090506001600160a01b0382166148af5760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f41444452455353000060448201526064016103f0565b9250929050565b600061490b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166151a29092919063ffffffff16565b805190915015612134578080602001905181019061492991906156cd565b6121345760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103f0565b60003415611042576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166149d283600080516020615c43833981519152610ed6565b6001600160a01b031614614a1b5760405162461bcd60e51b815260206004820152601060248201526f1b9bdd0815d155120819195c1bdcda5d60821b60448201526064016103f0565b82341115614abb57604051600090339034869003908381818185875af1925050503d8060008114614a68576040519150601f19603f3d011682016040523d82523d6000602084013e614a6d565b606091505b5050905080614ab25760405162461bcd60e51b8152602060048201526011602482015270115512081c99599d5b990819985a5b1959607a1b60448201526064016103f0565b83915050614abe565b50345b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015614b1957600080fd5b505af1158015614b2d573d6000803e3d6000fd5b505050505092915050565b600080614b44836111db565b90506000614b52868361122f565b90508015614b7657848111614b675780614b69565b845b9250614b768683856112e2565b50509392505050565b600081614b8b57600080fd5b60006001600160c01b038411614bb65782604085901b81614bae57614bae615767565b049050614ccd565b60c084811c6401000000008110614bcf576020918201911c5b620100008110614be1576010918201911c5b6101008110614bf2576008918201911c5b60108110614c02576004918201911c5b60048110614c12576002918201911c5b60028110614c21576001820191505b60bf820360018603901c6001018260ff0387901b81614c4257614c42615767565b0492506001600160801b03831115614c5957600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015614c85576001820391505b608084901b92900382811015614c9c576001820391505b829003608084901c8214614cb257614cb26156ea565b888181614cc157614cc1615767565b04870196505050505050505b6001600160801b03811115611db257600080fd5b80151560009081526013830160205260408120819081614d018686612b5d565b90506000614d0f8787611170565b905060008360010154118015614d255750825415155b8015614d32575082544210155b15614d6c57614d5681613c79856001015461113c8a8c61268290919063ffffffff16565b9350614d65878383878a612b76565b9450614d73565b8194508093505b5050509250929050565b6000600f83810b9083900b0260401d60016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b6000600f82810b9084900b0360016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b600081600f0b60001415614df957600080fd5b600082600f0b604085600f0b901b81614e1457614e14615767565b05905060016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b6001600160a01b0382166000908152600e8401602090815260408083208415158452909152812054801580611c9e57504210949350505050565b614e888686858560600151615118565b600082606001518360200151614e9e91906155b1565b836040015114905080614ee057614ee086614eb884611475565b856040015186606001518760200151614ed191906155b1565b614edb919061559a565b6112e2565b614eef86858560000151612139565b614f0e87878486606001518760400151614f09919061559a565b61320f565b846001600160a01b0316866001600160a01b03167fa30eb1f1bb0892af5e3389941c42bc7f44a8bf7b6071ecfe3e964673c908f2b286866000015185614f58578760400151614f5b565b60005b60408051938452602084019290925290820152841515606082015260800160405180910390a350505050505050565b6000611db2836001600160a01b038416614fff565b6000611db2836001600160a01b0384165b6000818152600183016020526040812054614ff757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611042565b506000611042565b600081815260018301602052604081205480156150c2578354600090859060001981019081106150315761503161563a565b90600052602060002001549050808560000160018403815481106150575761505761563a565b600091825260208083209091019290925591825260018601905260409020819055835484908061508957615089615bf9565b60019003818190600052602060002001600090559055836001016000848152602001908152602001600020600090556001915050611042565b6000915050611042565b6001600160a01b03838116600090815260208490526040812054909116151580610d6957506000808052602083905260409020546001600160a01b039081169085161490509392505050565b6001600160a01b0383166000908152601c8501602090815260408083208584529091528120805483929061514d9084906155b1565b9091555050604080516001600160a01b0385168152602081018490529081018290527f3a8ab48b47eb6f5307d9b87d560cc92cf89c416cce1d94297a70d1c82d8c84129060600160405180910390a150505050565b6060610d6984846000856060843b6152105760405162461bcd60e51b815260206004820152602b60248201527f416464726573735574696c733a2066756e6374696f6e2063616c6c20746f206e60448201526a1bdb8b58dbdb9d1c9858dd60aa1b60648201526084016103f0565b600080866001600160a01b0316858760405161522c919061572c565b60006040518083038185875af1925050503d8060008114615269576040519150601f19603f3d011682016040523d82523d6000602084013e61526e565b606091505b50915091508115615282579150610d699050565b8051156152925780518082602001fd5b8360405162461bcd60e51b81526004016103f09190615c0f565b801515811461038957600080fd5b6000806000606084860312156152cf57600080fd5b833592506020840135915060408401356152e8816152ac565b809150509250925092565b60006020828403121561530557600080fd5b8135611db2816152ac565b6000806040838503121561532357600080fd5b823591506020830135615335816152ac565b809150509250929050565b80356001600160a01b038116811461108b57600080fd5b6000806040838503121561536a57600080fd5b61537383615340565b91506020830135615335816152ac565b60008083601f84011261539557600080fd5b5081356001600160401b038111156153ac57600080fd5b6020830191508360208260051b85010111156148af57600080fd5b6000806000806000606086880312156153df57600080fd5b85356001600160401b03808211156153f657600080fd5b61540289838a01615383565b9097509550602088013591508082111561541b57600080fd5b5061542888828901615383565b909450925050604086013561543c816152ac565b809150509295509295909350565b600081518084526020808501945080840160005b8381101561547a5781518752958201959082019060010161545e565b509495945050505050565b608081526000615498608083018761544a565b82810360208401526154aa818761544a565b604084019590955250506060015292915050565b600080600080600080600060c0888a0312156154d957600080fd5b8735965060208801356154eb816152ac565b9550604088013594506060880135935060808801356001600160401b0381111561551457600080fd5b6155208a828b01615383565b90945092505060a0880135615534816152ac565b8091505092959891949750929550565b6000806040838503121561555757600080fd5b82356001600160401b038116811461537357600080fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000828210156155ac576155ac615584565b500390565b600082198211156155c4576155c4615584565b500190565b6020808252600b908201526a1b1a5c481b1bd8dac80c5960aa1b604082015260600190565b6001600160a01b03968716815294909516602085015291151560408401526060830152608082015260a081019190915260c00190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600060001982141561566457615664615584565b5060010190565b60006020828403121561567d57600080fd5b611db282615340565b60208082526027908201527f556e69737761705632526f757465723a204558434553534956455f494e50555460408201526617d05353d5539560ca1b606082015260800190565b6000602082840312156156df57600080fd5b8151611db2816152ac565b634e487b7160e01b600052600160045260246000fd5b60005b8381101561571b578181015183820152602001615703565b838111156132095750506000910152565b6000825161573e818460208701615700565b9190910192915050565b600081600019048311821515161561576257615762615584565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261579a57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b808511156157da5781600019048211156157c0576157c0615584565b808516156157cd57918102915b93841c93908002906157a4565b509250929050565b6000826157f157506001611042565b816157fe57506000611042565b8160018114615814576002811461581e5761583a565b6001915050611042565b60ff84111561582f5761582f615584565b50506001821b611042565b5060208310610133831016604e8410600b841016171561585d575081810a611042565b615867838361579f565b806000190482111561587b5761587b615584565b029392505050565b6000611db260ff8416836157e2565b6000602082840312156158a457600080fd5b5051919050565b8051600f81900b811461108b57600080fd5b6000602082840312156158cf57600080fd5b611db2826158ab565b6000816158e7576158e7615584565b506000190190565b60ff60f81b81526bffffffffffffffffffffffff198460601b16600182015282601582015260008251615929816035850160208701615700565b91909101603501949350505050565b60008151808452615950816020860160208601615700565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000611e536080830184615938565b8151600f0b8152610140810160208301516159b16020840182600f0b9052565b5060408301516159c66040840182600f0b9052565b5060608301516159db6060840182600f0b9052565b5060808301516159f06080840182600f0b9052565b5060a0830151615a0560a0840182600f0b9052565b5060c0830151615a1a60c0840182600f0b9052565b5060e0830151615a2f60e0840182600f0b9052565b5061010080840151615a4582850182600f0b9052565b5050610120838101518015158483015261371f565b600080600060608486031215615a6f57600080fd5b615a78846158ab565b9250615a86602085016158ab565b9150615a94604085016158ab565b90509250925092565b600081600f0b83600f0b600081128160016001607f1b031901831281151615615ac857615ac8615584565b8160016001607f1b03018313811615615ae357615ae3615584565b5090039392505050565b6000610120820190508251600f0b82526020830151600f0b60208301526040830151615b1e6040840182600f0b9052565b506060830151615b336060840182600f0b9052565b506080830151615b486080840182600f0b9052565b5060a0830151615b5d60a0840182600f0b9052565b5060c0830151615b7260c0840182600f0b9052565b5060e0830151615b8760e0840182600f0b9052565b506101008084015161371f82850182600f0b9052565b80516001600160701b038116811461108b57600080fd5b600080600060608486031215615bc957600080fd5b615bd284615b9d565b9250615be060208501615b9d565b9150604084015163ffffffff811681146152e857600080fd5b634e487b7160e01b600052603160045260246000fd5b602081526000611db2602083018461593856feb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eabbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52eba26469706673582212206681e40d3f3ef727a7669ebe437645a130798516b846167e75f71ef69be51b0964736f6c634300080a0033000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be230000000000000000000000007fa86681a7c19416950bae6c04a5116f3b07116d00000000000000000000000000000000000000000000000007ae147ae147ae1400000000000000000000000000000000000000000000000007ae147ae147ae140000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c4
Deployed Bytecode
0x60806040526004361061009c5760003560e01c8063491c011a11610064578063491c011a1461016d5780637365d18d1461018d5780637d09e3bc146101bd5780639a408321146101d0578063e4d4517b146101e3578063fbe5f3a11461020357600080fd5b80630178efa0146100a15780630e07ee09146100c35780630e6878a31461010357806338d0743614610123578063476343ee14610143575b600080fd5b3480156100ad57600080fd5b506100c16100bc3660046152ba565b610218565b005b3480156100cf57600080fd5b506100e36100de3660046152ba565b6102c3565b604080519384526020840192909252908201526060015b60405180910390f35b34801561010f57600080fd5b506100c161011e3660046152f3565b61037f565b34801561012f57600080fd5b506100c161013e366004615310565b61038c565b34801561014f57600080fd5b506101586104f7565b604080519283526020830191909152016100fa565b34801561017957600080fd5b506100c1610188366004615357565b610516565b34801561019957600080fd5b506101ad6101a83660046153c7565b61061e565b6040516100fa9493929190615485565b6100c16101cb3660046154be565b610a5e565b6100c16101de366004615310565b610afa565b3480156101ef57600080fd5b506100c16101fe366004615544565b610b0a565b34801561020f57600080fd5b506100c1610bac565b60f883901c608084901c84600060058460078111156102395761023961556e565b1480610256575060048460078111156102545761025461556e565b145b9050600080516020615c43833981519152600061027782338787878d610d71565b90506000610287838a8787610e4c565b90506102973383868b6001610eb2565b6102b78333868b6102b1576102ac858761559a565b610f05565b84610f05565b50505050505050505050565b60008080600080516020615c43833981519152816102e08261102a565b9050608088901c88600060f882901c60058160078111156103035761030361556e565b14806103205750600481600781111561031e5761031e61556e565b145b91505061033285338585858f8a611090565b919950975095506103473387838c6001610eb2565b6103718533838c61035c576102ac8b8d6155b1565b896103678c8e6155b1565b6102ac91906155b1565b505050505093509350939050565b6103893382610516565b50565b600080516020615c43833981519152826103a682846110e0565b336000908152600d8301602090815260408083208615158452909152902054426103d382620151806155b1565b106103f95760405162461bcd60e51b81526004016103f0906155c9565b60405180910390fd5b60006104058486611170565b9050600080610413876111db565b90506000610421338361122f565b9050801561045457858110156104395780925061043d565b8592505b610447838761559a565b95506104543383856112e2565b5050831561048b5761046f3361046988611475565b866112e2565b600061047b8688611170565b90506104898684838a6114c9565b505b61049b8533886102ac858c61559a565b6104a9338888600180610eb2565b6040805187151581526020810185905290810188905233907f477b6b4ae4e555826c33dfac094986168321f8f6295ab29cb8109c27e312e50f9060600160405180910390a250505050505050565b6000806105046001611546565b91506105106000611546565b90509091565b6001600160a01b0382811660009081527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df53026020908152604080832085151584528252808320547fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df5303909252918290205491516357795f1b60e11b8152600080516020615c43833981519152939192917f000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a169063aef2be36906105e5908890309089908890819089906004016155ee565b600060405180830381600087803b1580156105ff57600080fd5b505af1158015610613573d6000803e3d6000fd5b505050505050505050565b6060806000808786146106675760405162461bcd60e51b81526020600482015260116024820152700c8d2cccc40c2e4e4c2f240d8cadccee8d607b1b60448201526064016103f0565b6000610680600080516020615c4383398151915261102a565b9050886001600160401b0381111561069a5761069a615624565b6040519080825280602002602001820160405280156106c3578160200160208202803683370190505b509450886001600160401b038111156106de576106de615624565b604051908082528060200260200182016040528015610707578160200160208202803683370190505b5093506000896001600160401b0381111561072457610724615624565b60405190808252806020026020018201604052801561074d578160200160208202803683370190505b50905060005b8a8110156108a057600080600061078b8f8f868181106107755761077561563a565b9050602002013560f881901c91608082901c9190565b91945092509050600060058460078111156107a8576107a861556e565b14806107c5575060048460078111156107c3576107c361556e565b145b90506000808f8f888181106107dc576107dc61563a565b905060200201359050828888815181106107f8576107f861563a565b91151560209283029190910190910152610825600080516020615c4383398151915233878787868f611090565b8f8a815181106108375761083761563a565b602002602001018f8b815181106108505761085061563a565b60209081029190910101929092529190529150821561087a57610873828c6155b1565b9a50610887565b610884828b6155b1565b99505b505050505050808061089890615650565b915050610753565b508315610976576000805b8b81101561093a578281815181106108c5576108c561563a565b60209081029190910101516108d957610928565b8781815181106108eb576108eb61563a565b6020026020010151826108fe91906155b1565b91508681815181106109125761091261563a565b60200260200101518261092591906155b1565b91505b8061093281615650565b9150506108ab565b5061094a338660018b6001610eb2565b610974600080516020615c438339815191523360018b61096a5784610f05565b6102ac89866155b1565b505b8215610a50576000805b8b811015610a145782818151811061099a5761099a61563a565b602002602001015115156001151514156109b357610a02565b8781815181106109c5576109c561563a565b6020026020010151826109d891906155b1565b91508681815181106109ec576109ec61563a565b6020026020010151826109ff91906155b1565b91505b80610a0c81615650565b915050610980565b50610a24338560008b6001610eb2565b610a4e600080516020615c438339815191523360008b610a445784610f05565b6102ac88866155b1565b505b505095509550955095915050565b341580610a69575083155b610ab55760405162461bcd60e51b815260206004820152601c60248201527f76616c756520616e6420616d6f756e74496e4d6178207061737365640000000060448201526064016103f0565b84610abe578694505b3415610ad657610ad085848484611636565b50610ae5565b610ae38585858585611b0d565b505b610af187876000611ca7565b50505050505050565b610b0682826001611ca7565b5050565b600080516020615c438339815191526001600160401b0383161580610b635750336000908152600d8201602090815260408083208515158452909152902054610b5690620151806155b1565b836001600160401b031610155b610b7f5760405162461bcd60e51b81526004016103f0906155c9565b336000908152600e909101602090815260408083209315158352929052206001600160401b039091169055565b600160008181527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df53036020527f574488069b1552032c6ff89bebe1010d4367afb63ca1952e2124c136d7904c3154604080516358097aa360e11b81523060048201526024810194909452604484019190915251600080516020615c43833981519152926001600160a01b037f000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a169263b012f5469260648084019382900301818387803b158015610c7a57600080fd5b505af1158015610c8e573d6000803e3d6000fd5b505060008080526018840160205260408082205490516358097aa360e11b8152306004820152602481019290925260448201527f000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a6001600160a01b0316925063b012f5469150606401600060405180830381600087803b158015610d1157600080fd5b505af1158015610d25573d6000803e3d6000fd5b5050505050565b600081600f0b6080846001600160401b0316901b60f8866007811115610d5457610d5461556e565b610d5f92911b6155b1565b610d6991906155b1565b949350505050565b600080610d89610d82856001611d85565b8787610d2c565b90506000610da2610d9b866000611d85565b8888610d2c565b90506000610db28a868989610e4c565b90506000610dc28b84848c611db9565b9050610dcf8a85886112e2565b6000610ddf8c8c868a868d611e5d565b9050610dec8b85896112e2565b81610df782856155b1565b610e0191906155b1565b60408051868152602081018a90529197507f2c36fd3d1ab99ca7bec7ffe3c66b81f331da75eeda6c23fda680a77a7c458979910160405180910390a150505050509695505050505050565b60008115610e5b575082610d69565b6000610e6b600f85900b86611fb1565b90506000610e8a828860030160149054906101000a900460ff16612019565b9050610ea7818860030160159054906101000a900460ff1661202f565b979650505050505050565b8115610efa578015610ee757610ee285610edc85600080516020615c438339815191525b9061204a565b86612073565b610d25565b610ee285610ef4856111db565b86612139565b610d25858585612154565b6001600160a01b0383166000908152601785016020908152604080832085151584528252808320546018880190925282205490918084841115610f4f57610f4c858561559a565b91505b84831115610f6457610f61858461559a565b90505b60405163edaf7d5b60e01b81526001600160a01b037f000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a169063edaf7d5b90610fba908a9030908b908a9089908b906004016155ee565b600060405180830381600087803b158015610fd457600080fd5b505af1158015610fe8573d6000803e3d6000fd5b505050506001600160a01b0396909616600090815260178801602090815260408083209715158352968152868220929092556018909701905250505090912055565b60004282600c01541415611048576110428242612217565b92915050565b61105182612245565b905061105d8242612217565b600f0b61106f5761106f82428361234a565b42600c8301556110808260016110e0565b61108b8260006110e0565b919050565b60008060006110a48a8a8a8a8a8a8a6123c8565b909350915060006110b98b8b8b8b8b8b610d71565b9050826110c6858361559a565b6110d0919061559a565b9150509750975097945050505050565b80151560009081526013830160205260409020805415806111015750805442105b1561110b57505050565b60006111178484611170565b9050611152848261114c611141866001015461113c898b61268290919063ffffffff16565b612019565b600f86900b906126b0565b866114c9565b50501515600090815260139091016020526040812081815560010155565b60008061118d83611182576001611185565b60005b600080610d2c565b8315156000908152601386016020526040902060010154909150610d69906000838152600080516020615c2383398151915260205260409020546111d1919061559a565b61113c8686612682565b600081611208577f0300000000000000000000000000000000000000000000000000000000000000611042565b7f020000000000000000000000000000000000000000000000000000000000000092915050565b60006001600160a01b03831661129b5760405162461bcd60e51b815260206004820152602b60248201527f455243313135353a2062616c616e636520717565727920666f7220746865207a60448201526a65726f206164647265737360a81b60648201526084016103f0565b7f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b6000928352602090815260408084206001600160a01b0395909516845293905250205490565b6001600160a01b0383166113445760405162461bcd60e51b815260206004820152602360248201527f455243313135353a206275726e2066726f6d20746865207a65726f206164647260448201526265737360e81b60648201526084016103f0565b61137233846000611354866126e4565b61135d866126e4565b6040518060200160405280600081525061272f565b60008281527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b038716845291829052909120548211156114135760405162461bcd60e51b815260206004820152602560248201527f455243313135353a206275726e20616d6f756e7420657863656564732062616c604482015264616e63657360d81b60648201526084016103f0565b6001600160a01b03841660008181526020838152604080832080548790039055805187815291820186905291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a450505050565b6000816114a2577f0100000000000000000000000000000000000000000000000000000000000000611042565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b60006114d58583612b5d565b905060006114e68683878787612b76565b90506114f3868285612c6d565b60408051600f83810b825287810b602083015286900b818301529051841515917f4e23621c6f591f14bf9505cb8326b45af9dc6c5569fd608de2a7a2ddd6146b2e919081900360600190a2505050505050565b600080611552836111db565b905061157e7f0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be238261122f565b91508115611630576115b17f0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be2382846112e2565b6115f37f0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be236115ed85600080516020615c43833981519152610ed6565b84612073565b8215157f496915fc2759ccee8966a2c2098c4895c608b8decdb351dccea354eb4ca9c4f08360405161162791815260200190565b60405180910390a25b50919050565b60607f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316848460008181106116755761167561563a565b905060200201602081019061168a919061566b565b6001600160a01b0316146116e05760405162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f5041544800000060448201526064016103f0565b61176c8261170e577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f611730565b7f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c45b86868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612cc4915050565b905034816000815181106117825761178261563a565b602002602001015111156117a85760405162461bcd60e51b81526004016103f090615686565b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663d0e30db0826000815181106117ea576117ea61563a565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561181d57600080fd5b505af1158015611831573d6000803e3d6000fd5b50505050507f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663a9059cbb61190b84611893577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f6118b5565b7f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c45b878760008181106118c8576118c861563a565b90506020020160208101906118dd919061566b565b888860018181106118f0576118f061563a565b9050602002016020810190611905919061566b565b87612e52565b8360008151811061191e5761191e61563a565b60200260200101516040518363ffffffff1660e01b81526004016119579291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015611976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199a91906156cd565b6119a6576119a66156ea565b6119e781858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250879150612f539050565b806000815181106119fa576119fa61563a565b6020026020010151341115610d69576000336001600160a01b031682600081518110611a2857611a2861563a565b602002602001015134611a3b919061559a565b60408051600081526020810191829052611a549161572c565b60006040518083038185875af1925050503d8060008114611a91576040519150601f19603f3d011682016040523d82523d6000602084013e611a96565b606091505b5050905080611b045760405162461bcd60e51b815260206004820152603460248201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60448201527308115512081d1c985b9cd9995c8819985a5b195960621b60648201526084016103f0565b50949350505050565b6060611b9b82611b3d577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f611b5f565b7f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c45b87868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612cc4915050565b90508481600081518110611bb157611bb161563a565b60200260200101511115611bd75760405162461bcd60e51b81526004016103f090615686565b611c5d33611c0984611893577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f6118b5565b83600081518110611c1c57611c1c61563a565b602002602001015187876000818110611c3757611c3761563a565b9050602002016020810190611c4c919061566b565b6001600160a01b03169291906131af565b611c9e81858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250879150612f539050565b95945050505050565b3360009081527fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52f9602090815260408083208515158452909152812055600080516020615c43833981519152611cfc81846110e0565b336000818152600d83016020908152604080832087151584529091529020429055611d2a908290858761320f565b611d378133868686613328565b611d42338585612154565b6040805184151581526020810186905233917f74a132f462598ad738fcdf934a3f668aac78953afdb0c9fb953e16e07cca511c910160405180910390a250505050565b60008215611da35781611d99576005611d9c565b60045b9050611042565b81611daf576007611db2565b60065b9392505050565b6000816001600160401b0316421015610d69576000611dd785613456565b15611df5576000858152601d87016020526040902054600f0b611e17565b7f00000000000000000000000000000000000000000000000007ae147ae147ae145b9050611e536301e13380611e34426001600160401b03871661559a565b611e3e9087615748565b611e48919061577d565b600f83900b90611fb1565b9695505050505050565b600082611e6c57506000611e53565b6001600160a01b0386166000908152601c88016020908152604080832088845290915281205490611e9d888861122f565b611ea78784615748565b611eb1919061577d565b6001600160a01b0389166000908152601c8b01602090815260408083208b8452909152812080549293508392909190611eeb90849061559a565b90915550611f109050611efe868361559a565b611f078a613475565b600f0b90611fb1565b9250600083611f1f878461559a565b611f29919061559a565b9050611f5a7f0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be23828760016000610eb2565b604080516001600160a01b038b168152602081018a90529081018290527fe02f89a472c9f9be95b6093634de6f0967e703e84390d7e00d23e15ea6b997659060600160405180910390a15050509695505050505050565b600081611fc057506000611042565b600083600f0b1215611fd157600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b0381111561200057600080fd5b60401b811981111561201157600080fd5b019392505050565b6000611db28361202a84600a615883565b61353a565b6000611db261203f83600a615883565b600f85900b90611fb1565b6000816120615782546001600160a01b0316611db2565b5050600101546001600160a01b031690565b8061207d57505050565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526024820183905283169063a9059cbb906044016020604051808303816000875af11580156120cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f091906156cd565b6121345760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016103f0565b505050565b61213483838360405180602001604052806000815250613571565b600080516020615c438339815191526121768461217084611475565b85612139565b600061010480612186814261577d565b6121909190615748565b61219a91906155b1565b6001600160a01b03861660009081526014840160209081526040808320848452825280832087151584529091528120805492935086929091906121de9084906155b1565b9091555050821515600090815260138301602052604081206001810180549192879261220b9084906155b1565b90915550505550505050565b6000601183018161222a610e108561577d565b8152602081019190915260400160002054600f0b9392505050565b6000808260030160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c19190615892565b905060008360020160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561231a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233e9190615892565b9050610d69828261368c565b6000612358610e108461577d565b6000818152601186016020526040902080546001600160801b0319166001600160801b038516179055905061239160ff8083169061559a565b6001901b846012016000600884901c815260200190815260200160002060008282546123bd91906155b1565b909155505050505050565b60008042876001600160401b03161161240d5760405162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b60448201526064016103f0565b886005015484101561244d5760405162461bcd60e51b81526020600482015260096024820152681d1bdbc81cdb585b1b60ba1b60448201526064016103f0565b600061245b8a868989610e4c565b9050600061246887611475565b90506124758b8b89613727565b61247f8b8361122f565b612489919061559a565b871515600090815260138d0160205260409020600101546124a983613456565b6124b3919061559a565b6124bd919061559a565b8211156124f85760405162461bcd60e51b8152602060048201526009602482015268696e737566206c697160b81b60448201526064016103f0565b5050600061254b6040518060c001604052808b6001600160a01b031681526020018a6001600160401b0316815260200189600f0b815260200186600f0b815260200187815260200188151581525061376c565b80519091506125639061255e8c89612682565b61202f565b60208201519093506125799061255e8c89612682565b9150600061259261258b886001611d85565b8a8a610d2c565b90506125a48b82888560400151613bf6565b6125af8a8288612139565b60006125bb8c89611170565b90506125cc8c8c8c8c8b8a8e613cb9565b60006125d88d8a611170565b90506125e68d83838c6114c9565b6126157f0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be23868b60016000610eb2565b60408051848152602081018a905290810187905260608101869052600f88900b60808201526001600160a01b038d16907f4719d073a940c087132bffc1aac4f3837ca2c820d95574727828ce38f7fbc8c79060a00160405180910390a25050505097509795505050505050565b60008161269d576003830154600160a81b900460ff16611db2565b505060030154600160a01b900460ff1690565b6000600f83810b9083900b0160016001607f1b031981128015906126db575060016001607f1b038113155b611db257600080fd5b6040805160018082528183019092526060916000919060208083019080368337019050509050828160008151811061271e5761271e61563a565b602090810291909101015292915050565b61273d868686868686613f09565b600080516020615c4383398151915260005b8451811015612b5357600085828151811061276c5761276c61563a565b60200260200101519050600085838151811061278a5761278a61563a565b6020026020010151905080600014156127a4575050612b41565b6001600160a01b0389166127c1576127bf60158501836140e2565b505b6001600160a01b0388161580156127de57506127dc82613456565b155b156127f2576127f060158501836140ee565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061283f57507f010000000000000000000000000000000000000000000000000000000000000082145b8061286957507f020000000000000000000000000000000000000000000000000000000000000082145b8061289357507f030000000000000000000000000000000000000000000000000000000000000082145b15612965576001600160a01b038916158015906128b857506001600160a01b03881615155b156129655760007f000000000000000000000000000000000000000000000000000000000000000083148061290c57507f020000000000000000000000000000000000000000000000000000000000000083145b6001600160a01b038b166000908152600d8701602090815260408083208415158452909152902054909150429061294690620151806155b1565b106129635760405162461bcd60e51b81526004016103f0906155c9565b505b7f00000000000000000000000000000000000000000000000000000000000000008214806129b257507f010000000000000000000000000000000000000000000000000000000000000082145b15612af5577f0000000000000000000000000000000000000000000000000000000000000000821460006129e686836140fa565b90506001600160a01b038b1615612aaa576000612a038c8661122f565b90508181118015612a1d5750612a1982856155b1565b8111155b15612a815783612a2e888e86613727565b612a38908361559a565b1015612a765760405162461bcd60e51b815260206004820152600d60248201526c496e7375662062616c616e636560981b60448201526064016103f0565b612a81878d85614114565b6001600160a01b038b1615612aa857612a9c878d8587610f05565b612aa8878c858761320f565b505b6001600160a01b038a1615612af2576000612ac58b8661122f565b9050818111158015612ae0575081612add85836155b1565b10155b15612af057612af0878c856141c5565b505b50505b60f882901c6005816007811115612b0e57612b0e61556e565b1480612b2b57506007816007811115612b2957612b2961556e565b145b15612b3d57612b3d858b8b868661426d565b5050505b80612b4b81615650565b91505061274f565b5050505050505050565b6000612b6983836143fc565b9050611db2838284614423565b60008082612b8b576019870154600f0b612b9b565b6019870154600160801b9004600f0b5b905080600f0b60001415612bb357506008860154600f0b5b60405163e101a89b60e01b8152600f87810b600483015286810b602483015285810b604483015282900b606482015273c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f59063e101a89b90608401602060405180830381865af4158015612c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c4291906158bd565b915067b33333333333333382600f0b1215612c635767b33333333333333391505b5095945050505050565b8015612c9d576009830180546001600160801b0384166001600160801b031990911617905542600b840155505050565b6008830180546001600160801b03808516600160801b02911617905542600a840155505050565b6060600283511015612d185760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f50415448000060448201526064016103f0565b82516001600160401b03811115612d3157612d31615624565b604051908082528060200260200182016040528015612d5a578160200160208202803683370190505b509050838160018351612d6d919061559a565b81518110612d7d57612d7d61563a565b602002602001018181525050600060018451612d99919061559a565b90505b8015611b0457600080612ded8887612db560018761559a565b81518110612dc557612dc561563a565b6020026020010151888681518110612ddf57612ddf61563a565b6020026020010151886145d6565b91509150612e15848481518110612e0657612e0661563a565b602002602001015183836146a2565b84612e2160018661559a565b81518110612e3157612e3161563a565b60200260200101818152505050508080612e4a906158d8565b915050612d9c565b6000806000612e6186866147be565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b166034820152919350915087906048016040516020818303038152906040528051906020012085612ee7576040518060400160405280602081526020017f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f815250612f1e565b6040518060400160405280602081526020017fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c63038152505b604051602001612f30939291906158ef565b60408051601f198184030181529190528051602090910120979650505050505050565b60005b60018451612f64919061559a565b811015610d2557600080858381518110612f8057612f8061563a565b602002602001015186846001612f9691906155b1565b81518110612fa657612fa661563a565b6020026020010151915091506000612fbe83836147be565b509050600088612fcf8660016155b1565b81518110612fdf57612fdf61563a565b60200260200101519050600080836001600160a01b0316866001600160a01b03161461300d57826000613011565b6000835b91509150600060028b51613025919061559a565b881061303157896130ac565b6130ac8961305f577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f613081565b7f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c45b878d61308e8c60026155b1565b8151811061309e5761309e61563a565b60200260200101518c612e52565b9050613106896130dc577f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f6130fe565b7f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c45b88888c612e52565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613143576020820181803683370190505b506040518563ffffffff1660e01b81526004016131639493929190615964565b600060405180830381600087803b15801561317d57600080fd5b505af1158015613191573d6000803e3d6000fd5b505050505050505050505080806131a790615650565b915050612f56565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526132099085906148b6565b50505050565b6001600160a01b03808416600090815260178601602090815260408083208615158452825280832054601889019092529091205490917f000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a1663edaf7d5b8630878661327a89826155b1565b876040518763ffffffff1660e01b815260040161329c969594939291906155ee565b600060405180830381600087803b1580156132b657600080fd5b505af11580156132ca573d6000803e3d6000fd5b5050505082826132da91906155b1565b6001600160a01b03861660009081526017880160209081526040808320881515845290915290205561330c83826155b1565b9315156000908152601890960160205250506040909320555050565b6000811561333d5761333a8484614988565b90505b808411156133665761335985613353838761559a565b85614b38565b61336390826155b1565b90505b8084111561344e57613378868461204a565b6001600160a01b03166323b872dd8630613392858961559a565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156133e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061340a91906156cd565b61344e5760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016103f0565b505050505050565b6000908152600080516020615c23833981519152602052604090205490565b60007f0000000000000000000000007fa86681a7c19416950bae6c04a5116f3b07116d6001600160a01b03161561108b576040516303793c8d60e11b81526001600160a01b038381166004830152611042917f0000000000000000000000007fa86681a7c19416950bae6c04a5116f3b07116d909116906306f2791a90602401602060405180830381865afa158015613512573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135369190615892565b6127105b60008161354657600080fd5b60006135528484614b7f565b905060016001607f1b036001600160801b0382161115611db257600080fd5b6001600160a01b0384166135d15760405162461bcd60e51b815260206004820152602160248201527f455243313135353a206d696e7420746f20746865207a65726f206164647265736044820152607360f81b60648201526084016103f0565b6135f0336000866135e1876126e4565b6135ea876126e4565b8661272f565b60008381527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b0388168452909152812080548492906136419084906155b1565b909155505060408051848152602081018490526001600160a01b0386169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f629101611467565b60008161369857600080fd5b6000808412156136ad57836000039350600190505b60008312156136bf5760009290920391155b60006136cb8585614b7f565b905081156136f9576001607f1b816001600160801b031611156136ed57600080fd5b60000391506110429050565b60016001607f1b03816001600160801b0316111561371657600080fd5b91506110429050565b505092915050565b6001600160a01b038216600090815260148401602090815260408083208415158085526013880184528285205485529083528184209084529091529020549392505050565b60408051608081018252600080825260208201819052918101829052606081019190915260008260400151600f0b1380156137ae575060008260600151600f0b135b80156137c75750600082602001516001600160401b0316115b6138025760405162461bcd60e51b815260206004820152600c60248201526b696e76616c6964206172677360a01b60448201526064016103f0565b60808201517fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52ee54600080516020615c43833981519152916000916138509190600160a01b900460ff16612019565b905060008061386c8660a0015185614ce190919063ffffffff16565b91509150600081600f0b136138ac5760405162461bcd60e51b81526020600482015260066024820152656e6f206c697160d01b60448201526064016103f0565b60006138d44288602001516001600160401b03166138ca919061559a565b6301e1338061353a565b8554600187015460608a01516040808c01519051631af9d50d60e11b81526001600160a01b0394851660048201529284166024840152600f91820b6044840152810b606483015283900b60848201529192506000917f000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf909116906335f3aa1a9060a401602060405180830381865afa158015613974573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399891906158bd565b9050600081600f0b136139d75760405162461bcd60e51b81526020600482015260076024820152660766f6c203d20360cc1b60448201526064016103f0565b60008860a00151613a01576139fc896040015187600f0b614d7d90919063ffffffff16565b613a03565b855b9050600080600073c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f5630313686c604051806101400160405280613a46898a600f0b614d7d90919063ffffffff16565b600f0b81526020018f60400151600f0b81526020018f60600151600f0b815260200189600f0b81526020018b600f0b81526020018a600f0b8152602001613a99888c600f0b614db390919063ffffffff16565b600f0b8152602001600160401b600f0b8152602001674ccccccccccccccd600f0b81526020018f60a0015115158152506040518263ffffffff1660e01b8152600401613ae59190615991565b606060405180830381865af4158015613b02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b269190615a5a565b9250925092508b60a00151613b4857613b43600f84900b8a614d7d565b613b68565b60608c0151613b6890613b5f600f86900b8c614d7d565b600f0b90614de6565b600f0b808c52613b98907f00000000000000000000000000000000000000000000000007ae147ae147ae14614d7d565b600f90810b60208d015282810b60408d015281900b60608c01528b51613bcf90613bc190613475565b60208d0151600f0b90614d7d565b8b602001818151613be09190615a9d565b600f0b905250999b9a5050505050505050505050565b6000613c18613c0485613456565b6003870154600160a01b900460ff16612019565b90506000613c37848760030160149054906101000a900460ff16612019565b9050613c82613c4a600f84900b836126b0565b613b5f613c5b600f87900b85614d7d565b6000898152601b8b016020526040902054613c7990600f0b87614d7d565b600f0b906126b0565b6000958652601b969096016020525050604090922080546001600160801b0319166001600160801b03909416939093179092555050565b6000613cc9610d82836000611d85565b90506000613cd989868886610e4c565b90506000613ce98a84848b611db9565b90505b81156102b7578315156000908152600f8b01602090815260408083208380529091528120546001600160a01b031690613d2d82613d2888611475565b61122f565b9050613d3a8c8388614e3e565b613d7357613d5182613d4b88611475565b836112e2565b613d6082828860016000610eb2565b613d6c8c838884610f05565b5050613cec565b613d7d8c876140fa565b811015613d8f57613d6c8c8388614114565b8a6001600160a01b0316826001600160a01b03161415613dbf57613db48c8388614114565b613d6c8c83886141c5565b613dca8c8388613727565b613dd4908261559a565b9050613e016040518060800160405280600081526020016000815260200160008152602001600081525090565b8484613e0d8a836155b1565b613e17919061559a565b613e219084615748565b613e2b919061577d565b60208201819052613e3e57505050613cec565b8481602001511115613e5257602081018590525b8481602001518a613e639190615748565b613e6d919061577d565b815260208101518590613e80908a615748565b613e8a919061577d565b604082015260208101518590613ea09086615748565b613eaa919061577d565b6060820152613ebd8d848e89858c614e78565b6020810151613ecc908661559a565b8151909550613edb908a61559a565b9850806040015188613eed919061559a565b9750806060015184613eff919061559a565b9350505050613cec565b836001600160a01b0316856001600160a01b03161461344e576001600160a01b0385811660009081527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424ec602052604080822092871682528120600080516020615c23833981519152927fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb929091905b87518110156140d5576000878281518110613fb557613fb561563a565b6020026020010151905060008111156140cc576000898381518110613fdc57613fdc61563a565b6020026020010151905060006001600160a01b03168c6001600160a01b0316141561402a576000818152602088905260408120805484929061401f9084906155b1565b909155506140609050565b816140358d8361122f565b1415614060576000818152602087905260409020614053908d614f8a565b5061405e85826140ee565b505b6001600160a01b038b16614097576000818152602088905260408120805484929061408c90849061559a565b909155506140ca9050565b6140a18b8261122f565b6140ca5760008181526020879052604090206140bd908c614f9f565b506140c884826140e2565b505b505b50600101613f98565b5050505050505050505050565b6000611db28383614fb0565b6000611db28383614fff565b60008161410b578260040154611db2565b50506005015490565b6001600160a01b03821661412757600080fd5b8015156000908152600f840160209081526040808320601087019092529091206141528483836150cc565b61415d575050505050565b6001600160a01b0393841660008181526020838152604080832080549683528184208054978a16808652838620805499909b166001600160a01b0319998a168117909b5599855295909252822080548616909717909655528054821690558254169091555050565b6001600160a01b0382166141d857600080fd5b8015156000908152600f840160209081526040808320601087019092529091206142038483836150cc565b1561420f575050505050565b60008080526020828152604080832080546001600160a01b0390811680865296845282852080546001600160a01b03199081169a909216998a1790558885529490925282208054841690941790935580528154169092179091555050565b6001600160a01b03841615801561428b57508061428983613456565b145b156142da576000828152601d86016020526040902080546001600160801b0319167f00000000000000000000000000000000000000000000000007ae147ae147ae146001600160801b03161790555b6001600160a01b0383161580156142f757506142f582613456565b155b1561431b576000828152601d86016020526040902080546001600160801b03191690555b6001600160a01b0384161580159061433b57506001600160a01b03831615155b15610d255760f882901c608083901c83600060058460078111156143615761436161556e565b14905060006143728a878585610e4c565b905060006143828b898488611db9565b905060006143948c8c8b8b8689611e5d565b90506143a28c8b8b85615118565b60006143af8d8d87614e3e565b15905081156143c6576143c68c8387846000610eb2565b6143e18d8d87846143db576102ac868961559a565b87610f05565b6143ed8d8c878761320f565b50505050505050505050505050565b600081614417576008830154600160801b9004600f0b611db2565b505060090154600f0b90565b600080826144355784600a015461443b565b84600b01545b614445904261559a565b905061a8c08111156144645761445d61a8c08261559a565b905061446d565b83915050611db2565b600061447b8261384061353a565b9050600061448e85611182576001611185565b851515600090815260188901602090815260408083205460138c01835281842060010154858552600080516020615c2383398151915290935290832054939450926144ec916144dc9161559a565b6144e6908461559a565b8361353a565b6040805161012081018252600f87810b82528b810b602083015283900b8183015267b333333333333333606082015267e66666666666666660808201819052600160401b60a0830181905260c083015260e082015268056fc2a2c515da32ea6101008201529051634916d70d60e01b815291925073c7a7275bc25a7bf07c6d0c2f8784c5450cb9b8f591634916d70d9161458891600401615aed565b602060405180830381865af41580156145a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c991906158bd565b9998505050505050505050565b60008060006145e586866147be565b5090506000806145f789898989612e52565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015614634573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146589190615bb4565b506001600160701b031691506001600160701b03169150826001600160a01b0316886001600160a01b03161461468f578082614692565b81815b909a909950975050505050505050565b60008084116147085760405162461bcd60e51b815260206004820152602c60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4f60448201526b155514155517d05353d5539560a21b60648201526084016103f0565b6000831180156147185750600082115b6147755760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c604482015267495155494449545960c01b60648201526084016103f0565b60006147818585615748565b61478d906103e8615748565b9050600061479b868561559a565b6147a7906103e5615748565b90506147b3818361577d565b611e539060016155b1565b600080826001600160a01b0316846001600160a01b031614156148315760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f41444452604482015264455353455360d81b60648201526084016103f0565b826001600160a01b0316846001600160a01b031610614851578284614854565b83835b90925090506001600160a01b0382166148af5760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f41444452455353000060448201526064016103f0565b9250929050565b600061490b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166151a29092919063ffffffff16565b805190915015612134578080602001905181019061492991906156cd565b6121345760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103f0565b60003415611042576001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1166149d283600080516020615c43833981519152610ed6565b6001600160a01b031614614a1b5760405162461bcd60e51b815260206004820152601060248201526f1b9bdd0815d155120819195c1bdcda5d60821b60448201526064016103f0565b82341115614abb57604051600090339034869003908381818185875af1925050503d8060008114614a68576040519150601f19603f3d011682016040523d82523d6000602084013e614a6d565b606091505b5050905080614ab25760405162461bcd60e51b8152602060048201526011602482015270115512081c99599d5b990819985a5b1959607a1b60448201526064016103f0565b83915050614abe565b50345b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015614b1957600080fd5b505af1158015614b2d573d6000803e3d6000fd5b505050505092915050565b600080614b44836111db565b90506000614b52868361122f565b90508015614b7657848111614b675780614b69565b845b9250614b768683856112e2565b50509392505050565b600081614b8b57600080fd5b60006001600160c01b038411614bb65782604085901b81614bae57614bae615767565b049050614ccd565b60c084811c6401000000008110614bcf576020918201911c5b620100008110614be1576010918201911c5b6101008110614bf2576008918201911c5b60108110614c02576004918201911c5b60048110614c12576002918201911c5b60028110614c21576001820191505b60bf820360018603901c6001018260ff0387901b81614c4257614c42615767565b0492506001600160801b03831115614c5957600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015614c85576001820391505b608084901b92900382811015614c9c576001820391505b829003608084901c8214614cb257614cb26156ea565b888181614cc157614cc1615767565b04870196505050505050505b6001600160801b03811115611db257600080fd5b80151560009081526013830160205260408120819081614d018686612b5d565b90506000614d0f8787611170565b905060008360010154118015614d255750825415155b8015614d32575082544210155b15614d6c57614d5681613c79856001015461113c8a8c61268290919063ffffffff16565b9350614d65878383878a612b76565b9450614d73565b8194508093505b5050509250929050565b6000600f83810b9083900b0260401d60016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b6000600f82810b9084900b0360016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b600081600f0b60001415614df957600080fd5b600082600f0b604085600f0b901b81614e1457614e14615767565b05905060016001607f1b031981128015906126db575060016001607f1b03811315611db257600080fd5b6001600160a01b0382166000908152600e8401602090815260408083208415158452909152812054801580611c9e57504210949350505050565b614e888686858560600151615118565b600082606001518360200151614e9e91906155b1565b836040015114905080614ee057614ee086614eb884611475565b856040015186606001518760200151614ed191906155b1565b614edb919061559a565b6112e2565b614eef86858560000151612139565b614f0e87878486606001518760400151614f09919061559a565b61320f565b846001600160a01b0316866001600160a01b03167fa30eb1f1bb0892af5e3389941c42bc7f44a8bf7b6071ecfe3e964673c908f2b286866000015185614f58578760400151614f5b565b60005b60408051938452602084019290925290820152841515606082015260800160405180910390a350505050505050565b6000611db2836001600160a01b038416614fff565b6000611db2836001600160a01b0384165b6000818152600183016020526040812054614ff757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611042565b506000611042565b600081815260018301602052604081205480156150c2578354600090859060001981019081106150315761503161563a565b90600052602060002001549050808560000160018403815481106150575761505761563a565b600091825260208083209091019290925591825260018601905260409020819055835484908061508957615089615bf9565b60019003818190600052602060002001600090559055836001016000848152602001908152602001600020600090556001915050611042565b6000915050611042565b6001600160a01b03838116600090815260208490526040812054909116151580610d6957506000808052602083905260409020546001600160a01b039081169085161490509392505050565b6001600160a01b0383166000908152601c8501602090815260408083208584529091528120805483929061514d9084906155b1565b9091555050604080516001600160a01b0385168152602081018490529081018290527f3a8ab48b47eb6f5307d9b87d560cc92cf89c416cce1d94297a70d1c82d8c84129060600160405180910390a150505050565b6060610d6984846000856060843b6152105760405162461bcd60e51b815260206004820152602b60248201527f416464726573735574696c733a2066756e6374696f6e2063616c6c20746f206e60448201526a1bdb8b58dbdb9d1c9858dd60aa1b60648201526084016103f0565b600080866001600160a01b0316858760405161522c919061572c565b60006040518083038185875af1925050503d8060008114615269576040519150601f19603f3d011682016040523d82523d6000602084013e61526e565b606091505b50915091508115615282579150610d699050565b8051156152925780518082602001fd5b8360405162461bcd60e51b81526004016103f09190615c0f565b801515811461038957600080fd5b6000806000606084860312156152cf57600080fd5b833592506020840135915060408401356152e8816152ac565b809150509250925092565b60006020828403121561530557600080fd5b8135611db2816152ac565b6000806040838503121561532357600080fd5b823591506020830135615335816152ac565b809150509250929050565b80356001600160a01b038116811461108b57600080fd5b6000806040838503121561536a57600080fd5b61537383615340565b91506020830135615335816152ac565b60008083601f84011261539557600080fd5b5081356001600160401b038111156153ac57600080fd5b6020830191508360208260051b85010111156148af57600080fd5b6000806000806000606086880312156153df57600080fd5b85356001600160401b03808211156153f657600080fd5b61540289838a01615383565b9097509550602088013591508082111561541b57600080fd5b5061542888828901615383565b909450925050604086013561543c816152ac565b809150509295509295909350565b600081518084526020808501945080840160005b8381101561547a5781518752958201959082019060010161545e565b509495945050505050565b608081526000615498608083018761544a565b82810360208401526154aa818761544a565b604084019590955250506060015292915050565b600080600080600080600060c0888a0312156154d957600080fd5b8735965060208801356154eb816152ac565b9550604088013594506060880135935060808801356001600160401b0381111561551457600080fd5b6155208a828b01615383565b90945092505060a0880135615534816152ac565b8091505092959891949750929550565b6000806040838503121561555757600080fd5b82356001600160401b038116811461537357600080fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000828210156155ac576155ac615584565b500390565b600082198211156155c4576155c4615584565b500190565b6020808252600b908201526a1b1a5c481b1bd8dac80c5960aa1b604082015260600190565b6001600160a01b03968716815294909516602085015291151560408401526060830152608082015260a081019190915260c00190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600060001982141561566457615664615584565b5060010190565b60006020828403121561567d57600080fd5b611db282615340565b60208082526027908201527f556e69737761705632526f757465723a204558434553534956455f494e50555460408201526617d05353d5539560ca1b606082015260800190565b6000602082840312156156df57600080fd5b8151611db2816152ac565b634e487b7160e01b600052600160045260246000fd5b60005b8381101561571b578181015183820152602001615703565b838111156132095750506000910152565b6000825161573e818460208701615700565b9190910192915050565b600081600019048311821515161561576257615762615584565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261579a57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b808511156157da5781600019048211156157c0576157c0615584565b808516156157cd57918102915b93841c93908002906157a4565b509250929050565b6000826157f157506001611042565b816157fe57506000611042565b8160018114615814576002811461581e5761583a565b6001915050611042565b60ff84111561582f5761582f615584565b50506001821b611042565b5060208310610133831016604e8410600b841016171561585d575081810a611042565b615867838361579f565b806000190482111561587b5761587b615584565b029392505050565b6000611db260ff8416836157e2565b6000602082840312156158a457600080fd5b5051919050565b8051600f81900b811461108b57600080fd5b6000602082840312156158cf57600080fd5b611db2826158ab565b6000816158e7576158e7615584565b506000190190565b60ff60f81b81526bffffffffffffffffffffffff198460601b16600182015282601582015260008251615929816035850160208701615700565b91909101603501949350505050565b60008151808452615950816020860160208601615700565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000611e536080830184615938565b8151600f0b8152610140810160208301516159b16020840182600f0b9052565b5060408301516159c66040840182600f0b9052565b5060608301516159db6060840182600f0b9052565b5060808301516159f06080840182600f0b9052565b5060a0830151615a0560a0840182600f0b9052565b5060c0830151615a1a60c0840182600f0b9052565b5060e0830151615a2f60e0840182600f0b9052565b5061010080840151615a4582850182600f0b9052565b5050610120838101518015158483015261371f565b600080600060608486031215615a6f57600080fd5b615a78846158ab565b9250615a86602085016158ab565b9150615a94604085016158ab565b90509250925092565b600081600f0b83600f0b600081128160016001607f1b031901831281151615615ac857615ac8615584565b8160016001607f1b03018313811615615ae357615ae3615584565b5090039392505050565b6000610120820190508251600f0b82526020830151600f0b60208301526040830151615b1e6040840182600f0b9052565b506060830151615b336060840182600f0b9052565b506080830151615b486080840182600f0b9052565b5060a0830151615b5d60a0840182600f0b9052565b5060c0830151615b7260c0840182600f0b9052565b5060e0830151615b8760e0840182600f0b9052565b506101008084015161371f82850182600f0b9052565b80516001600160701b038116811461108b57600080fd5b600080600060608486031215615bc957600080fd5b615bd284615b9d565b9250615be060208501615b9d565b9150604084015163ffffffff811681146152e857600080fd5b634e487b7160e01b600052603160045260246000fd5b602081526000611db2602083018461593856feb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eabbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52eba26469706673582212206681e40d3f3ef727a7669ebe437645a130798516b846167e75f71ef69be51b0964736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be230000000000000000000000007fa86681a7c19416950bae6c04a5116f3b07116d00000000000000000000000000000000000000000000000007ae147ae147ae1400000000000000000000000000000000000000000000000007ae147ae147ae140000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c4
-----Decoded View---------------
Arg [0] : ivolOracle (address): 0xC4B2C51f969e0713E799De73b7f130Fb7Bb604CF
Arg [1] : weth (address): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
Arg [2] : premiaMining (address): 0xbC3c01D954282eEd8433da4359C1ac1443a7d09A
Arg [3] : feeReceiver (address): 0x7bf2392bd078C8353069CffeAcc67c094079be23
Arg [4] : feeDiscountAddress (address): 0x7Fa86681A7c19416950bAE6c04A5116f3b07116D
Arg [5] : feePremium64x64 (int128): 553402322211286548
Arg [6] : feeApy64x64 (int128): 553402322211286548
Arg [7] : uniswapV2Factory (address): 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
Arg [8] : sushiswapFactory (address): 0xc35DADB65012eC5796536bD9864eD8773aBc74C4
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf
Arg [1] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Arg [2] : 000000000000000000000000bc3c01d954282eed8433da4359c1ac1443a7d09a
Arg [3] : 0000000000000000000000007bf2392bd078c8353069cffeacc67c094079be23
Arg [4] : 0000000000000000000000007fa86681a7c19416950bae6c04a5116f3b07116d
Arg [5] : 00000000000000000000000000000000000000000000000007ae147ae147ae14
Arg [6] : 00000000000000000000000000000000000000000000000007ae147ae147ae14
Arg [7] : 0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f
Arg [8] : 000000000000000000000000c35dadb65012ec5796536bd9864ed8773abc74c4
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.