Users that set ERC-20 allowance via this contract should revoke all approvals from this contract based on Primitive Finance's post mortem announcement.
Latest 25 from a total of 64 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Add Short Liquid... | 11889044 | 1836 days ago | IN | 0 ETH | 0.04698465 | ||||
| Add Short Liquid... | 11883102 | 1837 days ago | IN | 0 ETH | 0.04317948 | ||||
| Add Short Liquid... | 11878858 | 1838 days ago | IN | 0 ETH | 0.03348456 | ||||
| Add Short Liquid... | 11869498 | 1839 days ago | IN | 0 ETH | 0.04239385 | ||||
| Add Short Liquid... | 11869401 | 1839 days ago | IN | 0 ETH | 0.02734382 | ||||
| Open Flash Long | 11868119 | 1839 days ago | IN | 0 ETH | 0.03095579 | ||||
| Mint Options The... | 11867603 | 1840 days ago | IN | 0 ETH | 0.03595255 | ||||
| Open Flash Long | 11862454 | 1840 days ago | IN | 0 ETH | 0.05416992 | ||||
| Add Short Liquid... | 11857185 | 1841 days ago | IN | 0 ETH | 0.03888175 | ||||
| Add Short Liquid... | 11856230 | 1841 days ago | IN | 0 ETH | 0.03641682 | ||||
| Add Short Liquid... | 11841895 | 1843 days ago | IN | 0 ETH | 0.03541769 | ||||
| Open Flash Long | 11840733 | 1844 days ago | IN | 0 ETH | 0.03103221 | ||||
| Add Short Liquid... | 11840199 | 1844 days ago | IN | 0 ETH | 0.02849491 | ||||
| Open Flash Long | 11839872 | 1844 days ago | IN | 0 ETH | 0.02486717 | ||||
| Add Short Liquid... | 11838106 | 1844 days ago | IN | 0 ETH | 0.04887094 | ||||
| Open Flash Long | 11838093 | 1844 days ago | IN | 0 ETH | 0.03724182 | ||||
| Add Short Liquid... | 11837257 | 1844 days ago | IN | 0 ETH | 0.07886788 | ||||
| Open Flash Long | 11837205 | 1844 days ago | IN | 0 ETH | 0.06350249 | ||||
| Add Short Liquid... | 11837052 | 1844 days ago | IN | 0 ETH | 0.04420098 | ||||
| Remove Short Liq... | 11837033 | 1844 days ago | IN | 0 ETH | 0.06020932 | ||||
| Remove Short Liq... | 11836888 | 1844 days ago | IN | 0 ETH | 0.09465788 | ||||
| Add Short Liquid... | 11832100 | 1845 days ago | IN | 0 ETH | 0.05287932 | ||||
| Add Short Liquid... | 11830282 | 1845 days ago | IN | 0 ETH | 0.08250459 | ||||
| Add Short Liquid... | 11828942 | 1845 days ago | IN | 0 ETH | 0.07248177 | ||||
| Open Flash Long | 11827845 | 1846 days ago | IN | 0 ETH | 0.05983824 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
UniswapConnector03
Compiler Version
v0.6.2+commit.bacdbe57
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
///
/// @title Combines Uniswap V2 Protocol functions with Primitive V1.
/// @notice Primitive V1 UniswapConnector03 - @primitivefi/v1-connectors@v1.2.2
/// @author Primitive
///
// Uniswap V2 & Primitive V1
import {
IUniswapV2Callee
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol";
import {
IUniswapV2Pair
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {
IUniswapConnector03,
IUniswapV2Router02,
IUniswapV2Factory,
IOption,
ITrader,
IERC20
} from "./interfaces/IUniswapConnector03.sol";
import {
TraderLib
} from "@primitivefi/contracts/contracts/option/libraries/TraderLib.sol";
import {UniswapConnectorLib03} from "./libraries/UniswapConnectorLib03.sol";
// Open Zeppelin
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {
ReentrancyGuard
} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "hardhat/console.sol";
contract UniswapConnector03 is
IUniswapConnector03,
IUniswapV2Callee,
ReentrancyGuard
{
using SafeERC20 for IERC20; // Reverts when `transfer` or `transferFrom` erc20 calls don't return proper data
using SafeMath for uint256; // Reverts on math underflows/overflows
ITrader public override trader; // The Primitive contract used to interact with the protocol
IUniswapV2Factory public override factory; // The Uniswap V2 factory contract to get pair addresses from
IUniswapV2Router02 public override router; // The Uniswap contract used to interact with the protocol
event Initialized(address indexed from); // Emmitted on deployment
event FlashOpened(address indexed from, uint256 quantity, uint256 premium); // Emmitted on flash opening a long position
event FlashClosed(address indexed from, uint256 quantity, uint256 payout);
event WroteOption(address indexed from, uint256 quantity);
// ==== Constructor ====
constructor(
address router_,
address factory_,
address trader_
) public {
require(address(router) == address(0x0), "ERR_INITIALIZED");
require(address(factory) == address(0x0), "ERR_INITIALIZED");
require(address(trader) == address(0x0), "ERR_INITIALIZED");
router = IUniswapV2Router02(router_);
factory = IUniswapV2Factory(factory_);
trader = ITrader(trader_);
emit Initialized(msg.sender);
}
// ==== Combo Operations ====
///
/// @dev Mints long + short option tokens, then swaps the shortOptionTokens (redeem) for tokens.
/// @notice If the first address in the path is not the shortOptionToken address, the tx will fail.
/// underlyingToken -> shortOptionToken -> quoteToken.
/// IMPORTANT: redeemTokens = shortOptionTokens
/// @param optionToken The address of the Option contract.
/// @param amountIn The quantity of options to mint.
/// @param amountOutMin The minimum quantity of tokens to receive in exchange for the shortOptionTokens.
/// @param path The token addresses to trade through using their Uniswap V2 pools. Assumes path[0] = shortOptionToken.
/// @param to The address to send the shortOptionToken proceeds and longOptionTokens to.
/// @param deadline The timestamp for a trade to fail at if not successful.
/// @return bool Whether the transaction was successful or not.
///
function mintShortOptionsThenSwapToTokens(
IOption optionToken,
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external override nonReentrant returns (bool) {
bool success =
UniswapConnectorLib03.mintShortOptionsThenSwapToTokens(
router,
optionToken,
amountIn,
amountOutMin,
path,
to,
deadline
);
return success;
}
/// @dev Write options by minting option tokens and selling the long option tokens for premium.
/// @notice IMPORTANT: if `minPayout` is 0, this function can cost the caller `underlyingToken`s.
/// @param optionToken The option contract to underwrite.
/// @param writeQuantity The quantity of option tokens to write and equally, the quantity of underlyings to deposit.
/// @param minPayout The minimum amount of underlyingTokens to receive from selling long option tokens.
function mintOptionsThenFlashCloseLong(
IOption optionToken,
uint256 writeQuantity,
uint256 minPayout
) external returns (bool) {
// Pulls underlyingTokens from `msg.sender` using `transferFrom`. Mints option tokens to `msg.sender`.
(, uint256 outputRedeems) =
TraderLib.safeMint(optionToken, writeQuantity, msg.sender);
// Sell the long option tokens for underlyingToken premium.
bool success = closeFlashLong(optionToken, outputRedeems, minPayout);
require(success, "ERR_FLASH_CLOSE");
emit WroteOption(msg.sender, writeQuantity);
return success;
}
// ==== Flash Functions ====
///
/// @dev Receives underlyingTokens from a UniswapV2Pair.swap() call from a pair with
/// reserve0 = shortOptionTokens and reserve1 = underlyingTokens.
/// Uses underlyingTokens to mint long (option) + short (redeem) tokens.
/// Sends longOptionTokens to msg.sender, and pays back the UniswapV2Pair the shortOptionTokens,
/// AND any remainder quantity of underlyingTokens (paid by msg.sender).
/// @notice If the first address in the path is not the shortOptionToken address, the tx will fail.
/// @param optionAddress The address of the Option contract.
/// @param flashLoanQuantity The quantity of options to mint using borrowed underlyingTokens.
/// @param maxPremium The maximum quantity of underlyingTokens to pay for the optionTokens.
/// @param path The token addresses to trade through using their Uniswap V2 pools. Assumes path[0] = shortOptionToken.
/// @param to The address to send the shortOptionToken proceeds and longOptionTokens to.
/// @return success bool Whether the transaction was successful or not.
///
function flashMintShortOptionsThenSwap(
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 maxPremium,
address[] memory path,
address to
) public override returns (uint256, uint256) {
(uint256 outputOptions, uint256 loanRemainder) =
UniswapConnectorLib03.flashMintShortOptionsThenSwap(
router,
pairAddress,
optionAddress,
flashLoanQuantity,
maxPremium,
path,
to
);
emit FlashOpened(msg.sender, outputOptions, loanRemainder);
return (outputOptions, loanRemainder);
}
/// @dev Sends shortOptionTokens to msg.sender, and pays back the UniswapV2Pair in underlyingTokens.
/// @notice IMPORTANT: If minPayout is 0, the `to` address is liable for negative payouts *if* that occurs.
/// @param pairAddress The address of the redeemToken<>underlyingToken UniswapV2Pair contract.
/// @param optionAddress The address of the longOptionTokes to close.
/// @param flashLoanQuantity The quantity of shortOptionTokens borrowed to use to close longOptionTokens.
/// @param minPayout The minimum payout of underlyingTokens sent to the `to` address.
/// @param path underlyingTokens -> shortOptionTokens, because we are paying the input of underlyingTokens.
/// @param to The address which is sent the underlyingToken payout, or liable to pay for a negative payout.
function flashCloseLongOptionsThenSwap(
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 minPayout,
address[] memory path,
address to
) public override returns (uint256, uint256) {
(uint256 outputUnderlyings, uint256 underlyingPayout) =
UniswapConnectorLib03.flashCloseLongOptionsThenSwap(
router,
pairAddress,
optionAddress,
flashLoanQuantity,
minPayout,
path,
to
);
emit FlashClosed(msg.sender, outputUnderlyings, underlyingPayout);
return (outputUnderlyings, underlyingPayout);
}
///
/// @dev Opens a longOptionToken position by minting long + short tokens, then selling the short tokens.
/// @notice IMPORTANT: amountOutMin parameter is the price to swap shortOptionTokens to underlyingTokens.
/// IMPORTANT: If the ratio between shortOptionTokens and underlyingTokens is 1:1, then only the swap fee (0.30%) has to be paid.
/// @param optionToken The option address.
/// @param amountOptions The quantity of longOptionTokens to purchase.
/// @param maxPremium The maximum quantity of underlyingTokens to pay for the optionTokens.
///
function openFlashLong(
IOption optionToken,
uint256 amountOptions,
uint256 maxPremium
) external override nonReentrant returns (bool) {
address redeemToken = optionToken.redeemToken();
address underlyingToken = optionToken.getUnderlyingTokenAddress();
address pairAddress = factory.getPair(redeemToken, underlyingToken);
// Build the path to get the appropriate reserves to borrow from, and then pay back.
// We are borrowing from reserve1 then paying it back mostly in reserve0.
// Borrowing underlyingTokens, paying back in shortOptionTokens (normal swap). Pay any remainder in underlyingTokens.
address[] memory path = new address[](2);
path[0] = redeemToken;
path[1] = underlyingToken;
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
bytes4 selector =
bytes4(
keccak256(
bytes(
"flashMintShortOptionsThenSwap(address,address,uint256,uint256,address[],address)"
)
)
);
bytes memory params =
abi.encodeWithSelector(
selector, // function to call in this contract
pairAddress, // pair contract we are borrowing from
optionToken, // option token to mint with flash loaned tokens
amountOptions, // quantity of underlyingTokens from flash loan to use to mint options
maxPremium, // total price paid (in underlyingTokens) for selling shortOptionTokens
path, // redeemToken -> underlyingToken
msg.sender // address to pull the remainder loan amount to pay, and send longOptionTokens to.
);
// Receives 0 quoteTokens and `amountOptions` of underlyingTokens to `this` contract address.
// Then executes `flashMintShortOptionsThenSwap`.
uint256 amount0Out =
pair.token0() == underlyingToken ? amountOptions : 0;
uint256 amount1Out =
pair.token0() == underlyingToken ? 0 : amountOptions;
// Borrow the amountOptions quantity of underlyingTokens and execute the callback function using params.
pair.swap(amount0Out, amount1Out, address(this), params);
return true;
}
///
/// @dev Closes a longOptionToken position by flash swapping in redeemTokens,
/// closing the option, and paying back in underlyingTokens.
/// @notice IMPORTANT: If minPayout is 0, this function will cost the caller to close the option, for no gain.
/// @param optionToken The address of the longOptionTokens to close.
/// @param amountRedeems The quantity of redeemTokens to borrow to close the options.
/// @param minPayout The minimum payout of underlyingTokens sent out to the user.
///
function closeFlashLong(
IOption optionToken,
uint256 amountRedeems,
uint256 minPayout
) public override nonReentrant returns (bool) {
address redeemToken = optionToken.redeemToken();
address underlyingToken = optionToken.getUnderlyingTokenAddress();
address pairAddress = factory.getPair(redeemToken, underlyingToken);
// Build the path to get the appropriate reserves to borrow from, and then pay back.
// We are borrowing from reserve1 then paying it back mostly in reserve0.
// Borrowing redeemTokens, paying back in underlyingTokens (normal swap).
// Pay any remainder in underlyingTokens.
address[] memory path = new address[](2);
path[0] = underlyingToken;
path[1] = redeemToken;
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
bytes4 selector =
bytes4(
keccak256(
bytes(
"flashCloseLongOptionsThenSwap(address,address,uint256,uint256,address[],address)"
)
)
);
bytes memory params =
abi.encodeWithSelector(
selector, // function to call in this contract
pairAddress, // pair contract we are borrowing from
optionToken, // option token to close with flash loaned redeemTokens
amountRedeems, // quantity of redeemTokens from flash loan to use to close options
minPayout, // total remaining underlyingTokens after flash loan is paid
path, // underlyingToken -> redeemToken
msg.sender // address to send payout of underlyingTokens to. Will pull underlyingTokens if negative payout and minPayout <= 0.
);
// Receives 0 underlyingTokens and `amountRedeems` of redeemTokens to `this` contract address.
// Then executes `flashCloseLongOptionsThenSwap`.
uint256 amount0Out = pair.token0() == redeemToken ? amountRedeems : 0;
uint256 amount1Out = pair.token0() == redeemToken ? 0 : amountRedeems;
// Borrow the amountRedeems quantity of redeemTokens and execute the callback function using params.
pair.swap(amount0Out, amount1Out, address(this), params);
return true;
}
// ==== Liquidity Functions ====
///
/// @dev Adds redeemToken liquidity to a redeem<>token pair by minting shortOptionTokens with underlyingTokens.
/// @notice Pulls underlying tokens from msg.sender and pushes UNI-V2 liquidity tokens to the "to" address.
/// underlyingToken -> redeemToken -> UNI-V2.
/// @param optionAddress The address of the optionToken to get the redeemToken to mint then provide liquidity for.
/// @param quantityOptions The quantity of underlyingTokens to use to mint option + redeem tokens.
/// @param amountBMax The minimum quantity of shortOptionTokens expected to provide liquidity with.
/// @param amountBMin The minimum quantity of otherTokens expected to provide liquidity with.
/// @param to The address that receives UNI-V2 shares.
/// @param deadline The timestamp to expire a pending transaction.
///
function addShortLiquidityWithUnderlying(
address optionAddress,
uint256 quantityOptions,
uint256 amountBMax,
uint256 amountBMin,
address to,
uint256 deadline
)
external
override
nonReentrant
returns (
uint256,
uint256,
uint256
)
{
return
UniswapConnectorLib03.addShortLiquidityWithUnderlying(
router,
optionAddress,
quantityOptions,
amountBMax,
amountBMin,
to,
deadline
);
}
///
/// @dev Combines Uniswap V2 Router "removeLiquidity" function with Primitive "closeOptions" function.
/// @notice Pulls UNI-V2 liquidity shares with shortOption<>underlying token, and optionTokens from msg.sender.
/// Then closes the longOptionTokens and withdraws underlyingTokens to the "to" address.
/// Sends underlyingTokens from the burned UNI-V2 liquidity shares to the "to" address.
/// UNI-V2 -> optionToken -> underlyingToken.
/// @param optionAddress The address of the option that will be closed from burned UNI-V2 liquidity shares.
/// @param liquidity The quantity of liquidity tokens to pull from msg.sender and burn.
/// @param amountAMin The minimum quantity of shortOptionTokens to receive from removing liquidity.
/// @param amountBMin The minimum quantity of underlyingTokens to receive from removing liquidity.
/// @param to The address that receives underlyingTokens from burned UNI-V2, and underlyingTokens from closed options.
/// @param deadline The timestamp to expire a pending transaction.
///
function removeShortLiquidityThenCloseOptions(
address optionAddress,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external override nonReentrant returns (uint256, uint256) {
address redeemToken = IOption(optionAddress).redeemToken();
address underlyingTokenAddress =
IOption(optionAddress).getUnderlyingTokenAddress();
// Check the short option tokens before and after, there could be dust.
uint256 redeemBalance = IERC20(redeemToken).balanceOf(address(this));
// Remove liquidity by burning lp tokens from msg.sender, withdraw tokens to this contract.
// Notice: the `to` address is not passed into this function, because address(this) receives the withrawn tokens.
(uint256 shortTokensWithdrawn, uint256 underlyingTokensWithdrawn) =
UniswapConnectorLib03.removeLiquidity(
router, // UniswapV2Router02
redeemToken, // tokenA
underlyingTokenAddress, // tokenB
liquidity,
amountAMin,
amountBMin,
deadline
);
// Burn option and redeem tokens from this contract then send underlyingTokens to the `to` address.
(, , uint256 underlyingTokensFromClosedOptions) =
UniswapConnectorLib03.closeOptionsWithShortTokens(
trader, // Primitive V1 Trader
IOption(optionAddress),
shortTokensWithdrawn,
to
);
// After the options were closed, calculate the dust by checking after balance against the before balance.
redeemBalance = IERC20(redeemToken).balanceOf(address(this)).sub(
redeemBalance
);
// If there is dust, send it out
if (redeemBalance > 0) {
IERC20(redeemToken).safeTransfer(to, redeemBalance);
}
// Send the UnderlyingTokens received from burning liquidity shares to the "to" address.
IERC20(underlyingTokenAddress).safeTransfer(
to,
underlyingTokensWithdrawn
);
return (
underlyingTokensWithdrawn.add(underlyingTokensFromClosedOptions),
redeemBalance
);
}
// ==== Callback Implementation ====
///
/// @dev The callback function triggered in a UniswapV2Pair.swap() call when the `data` parameter has data.
/// @param sender The original msg.sender of the UniswapV2Pair.swap() call.
/// @param amount0 The quantity of token0 received to the `to` address in the swap() call.
/// @param amount1 The quantity of token1 received to the `to` address in the swap() call.
/// @param data The payload passed in the `data` parameter of the swap() call.
///
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external override {
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
assert(msg.sender == factory.getPair(token0, token1)); /// ensure that msg.sender is actually a V2 pair
(bool success, bytes memory returnData) = address(this).call(data);
require(
success &&
(returnData.length == 0 || abi.decode(returnData, (bool))),
"ERR_UNISWAPV2_CALL_FAIL"
);
}
// ==== Management Functions ====
/// @dev Creates a UniswapV2Pair by calling `createPair` on the UniswapV2Factory.
function deployUniswapMarket(address optionAddress, address otherToken)
external
override
returns (address)
{
address uniswapPair = factory.createPair(optionAddress, otherToken);
return uniswapPair;
}
// ==== View ====
/// @dev Gets a UniswapV2Pair address for two tokens by calling the UniswapV2Factory.
function getUniswapMarketForTokens(address token0, address token1)
public
view
override
returns (address)
{
address uniswapPair = factory.getPair(token0, token1);
require(uniswapPair != address(0x0), "ERR_PAIR_DOES_NOT_EXIST");
return uniswapPair;
}
/// @dev Gets the name of the contract.
function getName() external pure override returns (string memory) {
return "PrimitiveV1UniswapConnector03";
}
/// @dev Gets the version of the contract.
function getVersion() external pure override returns (uint8) {
return uint8(3);
}
/// @dev Gets the total premium cost to buy `quantity` of `optionToken`s.
/// @notice Also returns the negative premium, which will be 0 in most cases.
/// @param optionToken The option to get the close premium of.
/// @param quantityLong The quantity of long option tokens that will be closed.
/// @return premiumCost, premiumPayout
function getOpenPremium(IOption optionToken, uint256 quantityLong)
external
view
returns (uint256, uint256)
{
return
UniswapConnectorLib03.getOpenPremium(
router,
optionToken,
quantityLong
);
}
/// @dev Gets the total premium payout to sell long option tokens proportional to `quantity` of `shortOptionToken`s.
/// @notice Also gets the cost, a negative payout, which will be 0 unless the reserve ratio is incorrectly set.
/// @param optionToken The option to get the close premium of.
/// @param quantityShort The quantity of short option tokens that will be closed.
/// @return premiumPayout, premumCost
function getClosePremium(IOption optionToken, uint256 quantityShort)
external
view
returns (uint256, uint256)
{
return
UniswapConnectorLib03.getClosePremium(
router,
optionToken,
quantityShort
);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
import {
IUniswapV2Router02
} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {
IUniswapV2Factory
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import { ITrader } from "@primitivefi/contracts/contracts/option/interfaces/ITrader.sol";
import { IOption, IERC20 } from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol";
interface IUniswapConnector03 {
// ==== Combo Operations ====
function mintShortOptionsThenSwapToTokens(
IOption optionToken,
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (bool);
// ==== Flash Functions ====
function flashCloseLongOptionsThenSwap(
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 minPayout,
address[] calldata path,
address to
) external returns (uint256, uint256);
function flashMintShortOptionsThenSwap(
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 maxPremium,
address[] calldata path,
address to
) external returns (uint256, uint256);
function openFlashLong(
IOption optionToken,
uint256 amountOptions,
uint256 amountOutMin
) external returns (bool);
function closeFlashLong(
IOption optionToken,
uint256 amountRedeems,
uint256 minPayout
) external returns (bool);
// ==== Liquidity Functions ====
function addShortLiquidityWithUnderlying(
address optionAddress,
uint256 quantityOptions,
uint256 amountBMax,
uint256 amountBMin,
address to,
uint256 deadline
)
external
returns (
uint256,
uint256,
uint256
);
function removeShortLiquidityThenCloseOptions(
address optionAddress,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256, uint256);
// ==== Management Functions ====
function deployUniswapMarket(address optionAddress, address otherToken)
external
returns (address);
// ==== View ====
function getUniswapMarketForTokens(address token0, address token1)
external
view
returns (address);
function router() external view returns (IUniswapV2Router02);
function factory() external view returns (IUniswapV2Factory);
function trader() external view returns (ITrader);
function getName() external pure returns (string memory);
function getVersion() external pure returns (uint8);
}pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
import { IOption } from "./IOption.sol";
interface ITrader {
function safeMint(
IOption optionToken,
uint256 mintQuantity,
address receiver
) external returns (uint256, uint256);
function safeExercise(
IOption optionToken,
uint256 exerciseQuantity,
address receiver
) external returns (uint256, uint256);
function safeRedeem(
IOption optionToken,
uint256 redeemQuantity,
address receiver
) external returns (uint256);
function safeClose(
IOption optionToken,
uint256 closeQuantity,
address receiver
)
external
returns (
uint256,
uint256,
uint256
);
function safeUnwind(
IOption optionToken,
uint256 unwindQuantity,
address receiver
)
external
returns (
uint256,
uint256,
uint256
);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
interface IOption is IERC20 {
function mintOptions(address receiver) external returns (uint256, uint256);
function exerciseOptions(
address receiver,
uint256 outUnderlyings,
bytes calldata data
) external returns (uint256, uint256);
function redeemStrikeTokens(address receiver) external returns (uint256);
function closeOptions(address receiver)
external
returns (
uint256,
uint256,
uint256
);
function redeemToken() external view returns (address);
function getStrikeTokenAddress() external view returns (address);
function getUnderlyingTokenAddress() external view returns (address);
function getBaseValue() external view returns (uint256);
function getQuoteValue() external view returns (uint256);
function getExpiryTime() external view returns (uint256);
function underlyingCache() external view returns (uint256);
function strikeCache() external view returns (uint256);
function factory() external view returns (address);
function getCacheBalances() external view returns (uint256, uint256);
function getAssetAddresses()
external
view
returns (
address,
address,
address
);
function getParameters()
external
view
returns (
address _underlyingToken,
address _strikeToken,
address _redeemToken,
uint256 _base,
uint256 _quote,
uint256 _expiry
);
function initRedeemToken(address _redeemToken) external;
function updateCacheBalances() external;
}pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}pragma solidity >=0.5.0;
interface IUniswapV2Callee {
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) 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.6.2;
/**
* @title Trader Library
* @notice Internal functions that can be used to safeTransfer
* tokens into the option contract then call respective option contract functions.
* @author Primitive
*/
import { IOption } from "../interfaces/IOption.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
library TraderLib {
using SafeMath for uint256;
using SafeERC20 for IERC20;
/**
* @dev Conducts important safety checks to safely mint option tokens.
* @param optionToken The address of the option token to mint.
* @param mintQuantity The quantity of option tokens to mint.
* @param receiver The address which receives the minted option tokens.
*/
function safeMint(
IOption optionToken,
uint256 mintQuantity,
address receiver
) internal returns (uint256, uint256) {
require(mintQuantity > 0, "ERR_ZERO");
IERC20(optionToken.getUnderlyingTokenAddress()).safeTransferFrom(
msg.sender,
address(optionToken),
mintQuantity
);
(uint256 outputOptions, uint256 outputRedeems) = optionToken
.mintOptions(receiver);
return (outputOptions, outputRedeems);
}
/**
* @dev Swaps strikeTokens to underlyingTokens using the strike ratio as the exchange rate.
* @notice Burns optionTokens, option contract receives strikeTokens, user receives underlyingTokens.
* @param optionToken The address of the option contract.
* @param exerciseQuantity Quantity of optionTokens to exercise.
* @param receiver The underlyingTokens are sent to the receiver address.
*/
function safeExercise(
IOption optionToken,
uint256 exerciseQuantity,
address receiver
) internal returns (uint256, uint256) {
require(exerciseQuantity > 0, "ERR_ZERO");
require(
IERC20(address(optionToken)).balanceOf(msg.sender) >=
exerciseQuantity,
"ERR_BAL_OPTIONS"
);
// Calculate quantity of strikeTokens needed to exercise quantity of optionTokens.
uint256 inputStrikes = exerciseQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
require(
IERC20(optionToken.getStrikeTokenAddress()).balanceOf(msg.sender) >=
inputStrikes,
"ERR_BAL_STRIKE"
);
IERC20(optionToken.getStrikeTokenAddress()).safeTransferFrom(
msg.sender,
address(optionToken),
inputStrikes
);
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(optionToken),
exerciseQuantity
);
uint256 inputOptions;
(inputStrikes, inputOptions) = optionToken.exerciseOptions(
receiver,
exerciseQuantity,
new bytes(0)
);
return (inputStrikes, inputOptions);
}
/**
* @dev Burns redeemTokens to withdraw available strikeTokens.
* @notice inputRedeems = outputStrikes.
* @param optionToken The address of the option contract.
* @param redeemQuantity redeemQuantity of redeemTokens to burn.
* @param receiver The strikeTokens are sent to the receiver address.
*/
function safeRedeem(
IOption optionToken,
uint256 redeemQuantity,
address receiver
) internal returns (uint256) {
require(redeemQuantity > 0, "ERR_ZERO");
require(
IERC20(optionToken.redeemToken()).balanceOf(msg.sender) >=
redeemQuantity,
"ERR_BAL_REDEEM"
);
// There can be the case there is no available strikes to redeem, causing a revert.
IERC20(optionToken.redeemToken()).safeTransferFrom(
msg.sender,
address(optionToken),
redeemQuantity
);
uint256 inputRedeems = optionToken.redeemStrikeTokens(receiver);
return inputRedeems;
}
/**
* @dev Burn optionTokens and redeemTokens to withdraw underlyingTokens.
* @notice The redeemTokens to burn is equal to the optionTokens * strike ratio.
* inputOptions = inputRedeems / strike ratio = outUnderlyings
* @param optionToken The address of the option contract.
* @param closeQuantity Quantity of optionTokens to burn.
* (Implictly will burn the strike ratio quantity of redeemTokens).
* @param receiver The underlyingTokens are sent to the receiver address.
*/
function safeClose(
IOption optionToken,
uint256 closeQuantity,
address receiver
)
internal
returns (
uint256,
uint256,
uint256
)
{
require(closeQuantity > 0, "ERR_ZERO");
require(
IERC20(address(optionToken)).balanceOf(msg.sender) >= closeQuantity,
"ERR_BAL_OPTIONS"
);
// Calculate the quantity of redeemTokens that need to be burned. (What we mean by Implicit).
uint256 inputRedeems = closeQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
require(
IERC20(optionToken.redeemToken()).balanceOf(msg.sender) >=
inputRedeems,
"ERR_BAL_REDEEM"
);
IERC20(optionToken.redeemToken()).safeTransferFrom(
msg.sender,
address(optionToken),
inputRedeems
);
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(optionToken),
closeQuantity
);
uint256 inputOptions;
uint256 outUnderlyings;
(inputRedeems, inputOptions, outUnderlyings) = optionToken.closeOptions(
receiver
);
return (inputRedeems, inputOptions, outUnderlyings);
}
/**
* @dev Burn redeemTokens to withdraw underlyingTokens and strikeTokens from expired options.
* @param optionToken The address of the option contract.
* @param unwindQuantity Quantity of option tokens used to calculate the amount of redeem tokens to burn.
* @param receiver The underlyingTokens are sent to the receiver address and the redeemTokens are burned.
*/
function safeUnwind(
IOption optionToken,
uint256 unwindQuantity,
address receiver
)
internal
returns (
uint256,
uint256,
uint256
)
{
// Checks
require(unwindQuantity > 0, "ERR_ZERO");
// solhint-disable-next-line not-rely-on-time
require(
optionToken.getExpiryTime() < block.timestamp,
"ERR_NOT_EXPIRED"
);
// Calculate amount of redeems required
uint256 inputRedeems = unwindQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
require(
IERC20(optionToken.redeemToken()).balanceOf(msg.sender) >=
inputRedeems,
"ERR_BAL_REDEEM"
);
IERC20(optionToken.redeemToken()).safeTransferFrom(
msg.sender,
address(optionToken),
inputRedeems
);
uint256 inputOptions;
uint256 outUnderlyings;
(inputRedeems, inputOptions, outUnderlyings) = optionToken.closeOptions(
receiver
);
return (inputRedeems, inputOptions, outUnderlyings);
}
}pragma solidity 0.6.2;
///
/// @title Library for business logic for connecting Uniswap V2 Protocol functions with Primitive V1.
/// @notice Primitive V1 UniswapConnectorLib03 - @primitivefi/v1-connectors@v1.2.2
/// @author Primitive
///
// Uniswap
import {
IUniswapV2Callee
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol";
import {
IUniswapV2Router02
} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {
IUniswapV2Factory
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {
IUniswapV2Pair
} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
// Primitive
import {
ITrader,
IOption
} from "@primitivefi/contracts/contracts/option/interfaces/ITrader.sol";
import {
TraderLib,
IERC20
} from "@primitivefi/contracts/contracts/option/libraries/TraderLib.sol";
import {IWethConnector01, IWETH} from "../interfaces/IWethConnector01.sol";
import {WethConnectorLib01} from "./WethConnectorLib01.sol";
// Open Zeppelin
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
library UniswapConnectorLib03 {
using SafeERC20 for IERC20; // Reverts when `transfer` or `transferFrom` erc20 calls don't return proper data
using SafeMath for uint256; // Reverts on math underflows/overflows
/// ==== Combo Operations ====
///
/// @dev Mints long + short option tokens, then swaps the shortOptionTokens (redeem) for tokens.
/// @notice If the first address in the path is not the shortOptionToken address, the tx will fail.
/// underlyingToken -> shortOptionToken -> quoteToken.
/// IMPORTANT: redeemTokens = shortOptionTokens
/// @param optionToken The address of the Option contract.
/// @param amountIn The quantity of options to mint.
/// @param amountOutMin The minimum quantity of tokens to receive in exchange for the shortOptionTokens.
/// @param path The token addresses to trade through using their Uniswap V2 pools. Assumes path[0] = shortOptionToken.
/// @param to The address to send the shortOptionToken proceeds and longOptionTokens to.
/// @param deadline The timestamp for a trade to fail at if not successful.
/// @return bool Whether the transaction was successful or not.
///
function mintShortOptionsThenSwapToTokens(
IUniswapV2Router02 router,
IOption optionToken,
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
address to,
uint256 deadline
) internal returns (bool) {
// Pulls underlyingTokens from msg.sender, then pushes underlyingTokens to option contract.
// Mints long + short tokens to this contract.
(uint256 outputOptions, uint256 outputRedeems) =
mintOptionsKeepShortOptions(optionToken, amountIn);
// Swaps shortOptionTokens to the token specified at the end of the path, then sends to msg.sender.
// Reverts if the first address in the path is not the shortOptionToken address.
address redeemToken = optionToken.redeemToken();
(, bool success) =
swapExactOptionsForTokens(
router,
redeemToken,
outputRedeems, // shortOptionTokens = redeemTokens
amountOutMin,
path,
to,
deadline
);
// Fail early if the swap failed.
require(success, "ERR_SWAP_FAILED");
return success;
}
// ==== Flash Functions ====
///
/// @dev Receives underlyingTokens from a UniswapV2Pair.swap() call from a pair with
/// shortOptionTokens and underlyingTokens.
/// Uses underlyingTokens to mint long (option) + short (redeem) tokens.
/// Sends longOptionTokens to msg.sender, and pays back the UniswapV2Pair with shortOptionTokens,
/// AND any remainder quantity of underlyingTokens (paid by msg.sender).
/// @notice If the first address in the path is not the shortOptionToken address, the tx will fail.
/// IMPORTANT: UniswapV2 adds a fee of 0.301% to the option premium cost.
/// @param router The address of the UniswapV2Router02 contract.
/// @param pairAddress The address of the redeemToken<>underlyingToken UniswapV2Pair contract.
/// @param optionAddress The address of the Option contract.
/// @param flashLoanQuantity The quantity of options to mint using borrowed underlyingTokens.
/// @param maxPremium The maximum quantity of underlyingTokens to pay for the optionTokens.
/// @param path The token addresses to trade through using their Uniswap V2 pools. Assumes path[0] = shortOptionToken.
/// @param to The address to send the shortOptionToken proceeds and longOptionTokens to.
/// @return success bool Whether the transaction was successful or not.
///
function flashMintShortOptionsThenSwap(
IUniswapV2Router02 router,
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 maxPremium,
address[] memory path,
address to
) internal returns (uint256, uint256) {
require(msg.sender == address(this), "ERR_NOT_SELF");
require(to != address(0x0), "ERR_TO_ADDRESS_ZERO");
require(to != msg.sender, "ERR_TO_MSG_SENDER");
require(
pairFor(router.factory(), path[0], path[1]) == pairAddress,
"ERR_INVALID_PAIR"
);
// IMPORTANT: Assume this contract has already received `flashLoanQuantity` of underlyingTokens.
address underlyingToken =
IOption(optionAddress).getUnderlyingTokenAddress();
address redeemToken = IOption(optionAddress).redeemToken();
require(path[1] == underlyingToken, "ERR_END_PATH_NOT_UNDERLYING");
// Mint longOptionTokens using the underlyingTokens received from UniswapV2 flash swap to this contract.
// Send underlyingTokens from this contract to the optionToken contract, then call mintOptions.
(uint256 mintedOptions, uint256 mintedRedeems) =
mintOptionsWithUnderlyingBalance(
IOption(optionAddress),
flashLoanQuantity
);
// The loanRemainder will be the amount of underlyingTokens that are needed from the original
// transaction caller in order to pay the flash swap.
// IMPORTANT: THIS IS EFFECTIVELY THE PREMIUM PAID IN UNDERLYINGTOKENS TO PURCHASE THE OPTIONTOKEN.
uint256 loanRemainder;
// Economically, negativePremiumPaymentInRedeems value should always be 0.
// In the case that we minted more redeemTokens than are needed to pay back the flash swap,
// (short -> underlying is a positive trade), there is an effective negative premium.
// In that case, this function will send out `negativePremiumAmount` of redeemTokens to the original caller.
// This means the user gets to keep the extra redeemTokens for free.
// Negative premium amount is the opposite difference of the loan remainder: (paid - flash loan amount)
uint256 negativePremiumPaymentInRedeems;
(loanRemainder, negativePremiumPaymentInRedeems) = getOpenPremium(
router,
IOption(optionAddress),
flashLoanQuantity
);
// In the case that more redeemTokens were minted than need to be sent back as payment,
// calculate the new mintedRedeems value to send to the pair
// (don't send all the minted redeemTokens).
if (negativePremiumPaymentInRedeems > 0) {
mintedRedeems = mintedRedeems.sub(negativePremiumPaymentInRedeems);
}
// In most cases, all of the minted redeemTokens will be sent to the pair as payment for the flash swap.
if (mintedRedeems > 0) {
IERC20(redeemToken).safeTransfer(pairAddress, mintedRedeems);
}
// If loanRemainder is non-zero and non-negative (most cases), send underlyingTokens to the pair as payment (premium).
if (loanRemainder > 0) {
// Pull underlyingTokens from the original msg.sender to pay the remainder of the flash swap.
require(maxPremium >= loanRemainder, "ERR_PREMIUM_OVER_MAX"); // check for users to not pay over their max desired value.
IERC20(underlyingToken).safeTransferFrom(
to,
pairAddress,
loanRemainder
);
}
// If negativePremiumAmount is non-zero and non-negative, send redeemTokens to the `to` address.
if (negativePremiumPaymentInRedeems > 0) {
IERC20(redeemToken).safeTransfer(
to,
negativePremiumPaymentInRedeems
);
}
// Send minted longOptionTokens (option) to the original msg.sender.
IERC20(optionAddress).safeTransfer(to, mintedOptions);
return (mintedOptions, loanRemainder);
}
/// @dev Sends shortOptionTokens to msg.sender, and pays back the UniswapV2Pair in underlyingTokens.
/// @notice IMPORTANT: If minPayout is 0, the `to` address is liable for negative payouts *if* that occurs.
/// @param router The UniswapV2Router02 contract.
/// @param pairAddress The address of the redeemToken<>underlyingToken UniswapV2Pair contract.
/// @param optionAddress The address of the longOptionTokes to close.
/// @param flashLoanQuantity The quantity of shortOptionTokens borrowed to use to close longOptionTokens.
/// @param minPayout The minimum payout of underlyingTokens sent to the `to` address.
/// @param path underlyingTokens -> shortOptionTokens, because we are paying the input of underlyingTokens.
/// @param to The address which is sent the underlyingToken payout, or liable to pay for a negative payout.
function flashCloseLongOptionsThenSwap(
IUniswapV2Router02 router,
address pairAddress,
address optionAddress,
uint256 flashLoanQuantity,
uint256 minPayout,
address[] memory path,
address to
) internal returns (uint256, uint256) {
require(msg.sender == address(this), "ERR_NOT_SELF");
require(to != address(0x0), "ERR_TO_ADDRESS_ZERO");
require(to != msg.sender, "ERR_TO_MSG_SENDER");
require(
pairFor(router.factory(), path[0], path[1]) == pairAddress,
"ERR_INVALID_PAIR"
);
// IMPORTANT: Assume this contract has already received `flashLoanQuantity` of redeemTokens.
// We are flash swapping from an underlying <> shortOptionToken pair,
// paying back a portion using underlyingTokens received from closing options.
// In the flash open, we did redeemTokens to underlyingTokens.
// In the flash close, we are doing underlyingTokens to redeemTokens and keeping the remainder.
address underlyingToken =
IOption(optionAddress).getUnderlyingTokenAddress();
address redeemToken = IOption(optionAddress).redeemToken();
require(path[1] == redeemToken, "ERR_END_PATH_NOT_REDEEM");
// Quantity of underlyingTokens this contract receives from burning option + redeem tokens.
uint256 outputUnderlyings =
closeOptionsWithShortBalance(
to,
IOption(optionAddress),
flashLoanQuantity
);
// Loan Remainder is the cost to pay out, should be 0 in most cases.
// Underlying Payout is the `premium` that the original caller receives in underlyingTokens.
// It's the remainder of underlyingTokens after the pair has been paid back underlyingTokens for the
// flash swapped shortOptionTokens.
(uint256 underlyingPayout, uint256 loanRemainder) =
getClosePremium(router, IOption(optionAddress), flashLoanQuantity);
// In most cases there will be an underlying payout, which is subtracted from the outputUnderlyings.
if (underlyingPayout > 0) {
outputUnderlyings = outputUnderlyings.sub(underlyingPayout);
}
// Pay back the pair in underlyingTokens.
if (outputUnderlyings > 0) {
IERC20(underlyingToken).safeTransfer(
pairAddress,
outputUnderlyings
);
}
// If loanRemainder is non-zero and non-negative, send underlyingTokens to the pair as payment (premium).
if (loanRemainder > 0) {
// Pull underlyingTokens from the original msg.sender to pay the remainder of the flash swap.
// Revert if the minPayout is less than or equal to the underlyingPayment of 0.
// There is 0 underlyingPayment in the case that loanRemainder > 0.
// This code branch can be successful by setting `minPayout` to 0.
// This means the user is willing to pay to close the position.
require(minPayout <= underlyingPayout, "ERR_NEGATIVE_PAYOUT");
IERC20(underlyingToken).safeTransferFrom(
to,
pairAddress,
loanRemainder
);
}
// If underlyingPayout is non-zero and non-negative, send it to the `to` address.
if (underlyingPayout > 0) {
// Revert if minPayout is greater than the actual payout.
require(underlyingPayout >= minPayout, "ERR_PREMIUM_UNDER_MIN");
IERC20(underlyingToken).safeTransfer(to, underlyingPayout);
}
return (outputUnderlyings, underlyingPayout);
}
// ==== Liquidity Functions ====
///
/// @dev Adds redeemToken liquidity to a redeem<>underlyingToken pair by minting shortOptionTokens with underlyingTokens.
/// @notice Pulls underlying tokens from msg.sender and pushes UNI-V2 liquidity tokens to the "to" address.
/// underlyingToken -> redeemToken -> UNI-V2.
/// @param optionAddress The address of the optionToken to get the redeemToken to mint then provide liquidity for.
/// @param quantityOptions The quantity of underlyingTokens to use to mint option + redeem tokens.
/// @param amountBMax The quantity of underlyingTokens to add with shortOptionTokens to the Uniswap V2 Pair.
/// @param amountBMin The minimum quantity of underlyingTokens expected to provide liquidity with.
/// @param to The address that receives UNI-V2 shares.
/// @param deadline The timestamp to expire a pending transaction.
///
function addShortLiquidityWithUnderlying(
IUniswapV2Router02 router,
address optionAddress,
uint256 quantityOptions,
uint256 amountBMax,
uint256 amountBMin,
address to,
uint256 deadline
)
internal
returns (
uint256,
uint256,
uint256
)
{
uint256 amountA;
uint256 amountB;
uint256 liquidity;
(, uint256 outputRedeems) =
mintOptionsKeepShortOptions(
IOption(optionAddress),
quantityOptions
);
{
// scope for adding exact liquidity, avoids stack too deep errors
IOption optionToken = IOption(optionAddress);
IUniswapV2Router02 router_ = router;
address underlyingToken = optionToken.getUnderlyingTokenAddress();
uint256 outputRedeems_ = outputRedeems;
uint256 amountBMax_ = amountBMax;
uint256 amountBMin_ = amountBMin;
address to_ = to;
uint256 deadline_ = deadline;
// Adds liquidity to Uniswap V2 Pair and returns liquidity shares to the "to" address.
(amountA, amountB, liquidity) = addExactShortLiquidity(
router_,
optionToken.redeemToken(),
underlyingToken,
outputRedeems_,
amountBMax_,
amountBMin_,
to_,
deadline_
);
// check for exact liquidity provided
assert(amountA == outputRedeems);
uint256 remainder =
amountBMax_ > amountB ? amountBMax_.sub(amountB) : 0;
if (remainder > 0) {
IERC20(underlyingToken).safeTransfer(msg.sender, remainder);
}
}
return (amountA, amountB, liquidity);
}
///
/// @dev Calls the "swapExactTokensForTokens" function on the Uniswap V2 Router 02 Contract.
/// @notice Fails early if the address in the beginning of the path is not the token address.
/// @param tokenAddress The address of the token to swap from.
/// @param amountIn The quantity of longOptionTokens to swap with.
/// @param amountOutMin The minimum quantity of tokens to receive in exchange for the tokens swapped.
/// @param path The token addresses to trade through using their Uniswap V2 pairs.
/// @param to The address to send the token proceeds to.
/// @param deadline The timestamp for a trade to fail at if not successful.
///
function swapExactOptionsForTokens(
IUniswapV2Router02 router,
address tokenAddress,
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
address to,
uint256 deadline
) internal returns (uint256[] memory amounts, bool success) {
// Fails early if the token being swapped from is not the optionToken.
require(path[0] == tokenAddress, "ERR_PATH_OPTION_START");
// Approve the uniswap router to be able to transfer longOptionTokens from this contract.
IERC20(tokenAddress).approve(address(router), uint256(-1));
// Call the Uniswap V2 function to swap longOptionTokens to quoteTokens.
(amounts) = router.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
to,
deadline
);
success = true;
}
/// @dev Calls UniswapV2Router02 function addLiquidity, provides exact amount of `tokenA`.
/// @notice Assumes this contract has a current balance of `tokenA`, pulls required underlyingTokens from `msg.sender`.
function addExactShortLiquidity(
IUniswapV2Router02 router,
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountBMin,
address to,
uint256 deadline
)
internal
returns (
uint256,
uint256,
uint256
)
{
// Pull `tokenB` from msg.sender to add to Uniswap V2 Pair.
// Warning: calls into msg.sender using `safeTransferFrom`. Msg.sender is not trusted.
IERC20(tokenB).safeTransferFrom(
msg.sender,
address(this),
amountBDesired
);
// Approves Uniswap V2 Pair pull tokens from this contract.
IERC20(tokenA).approve(address(router), uint256(-1));
IERC20(tokenB).approve(address(router), uint256(-1));
// Adds liquidity to Uniswap V2 Pair and returns liquidity shares to the "to" address.
return
router.addLiquidity(
tokenA,
tokenB,
amountADesired,
amountBDesired,
amountADesired, // notice how amountAMin === amountADesired
amountBMin,
to,
deadline
);
}
/// @dev Mints long + short option tokens by pulling underlyingTokens from `msg.sender`.
/// @notice Pushes minted longOptionTokens to `msg.sender`. Keeps shortOptionTokens in this contract.
/// IMPORTANT: Must be used in conjuction with a function that uses the shortOptionTokens blanace.
/// @param optionToken The option token to mint.
/// @param quantity The amount of longOptionTokens to mint.
function mintOptionsKeepShortOptions(IOption optionToken, uint256 quantity)
internal
returns (uint256, uint256)
{
// Pulls underlyingTokens from msg.sender to this contract.
// Pushes underlyingTokens to option contract and mints option + redeem tokens to this contract.
// Warning: calls into msg.sender using `safeTransferFrom`. Msg.sender is not trusted.
(uint256 outputOptions, uint256 outputRedeems) =
TraderLib.safeMint(optionToken, quantity, address(this));
// Send longOptionTokens from minting option operation to msg.sender.
IERC20(address(optionToken)).safeTransfer(msg.sender, quantity);
return (outputOptions, outputRedeems);
}
/// @dev Mints long + short option tokens using this contract's underlyingToken balance.
/// @notice Keeps minted tokens in this contract.
/// @param optionToken The option token to mint.
/// @param quantity The amount of longOptionTokens to mint.
function mintOptionsWithUnderlyingBalance(
IOption optionToken,
uint256 quantity
) internal returns (uint256, uint256) {
address underlyingToken = optionToken.getUnderlyingTokenAddress();
// Mint longOptionTokens using the underlyingTokens received from UniswapV2 flash swap to this contract.
// Send underlyingTokens from this contract to the optionToken contract, then call mintOptions.
IERC20(underlyingToken).safeTransfer(address(optionToken), quantity);
return optionToken.mintOptions(address(this));
}
/// @dev Closes options using this contract's balance of shortOptionTokens (redeem), and pulls optionTokens from `from`.
/// @notice IMPORTANT: pulls optionTokens from `from`, an untrusted address.
/// @param from The address to pull optionTokens from which will be burned to release underlyingTokens.
/// @param optionToken The options that will be closed.
/// @param quantity The quantity of optionTokens to burn.
/// @return The quantity of underlyingTokens released.
function closeOptionsWithShortBalance(
address from,
IOption optionToken,
uint256 quantity
) internal returns (uint256) {
// Close longOptionTokens using the redeemToken balance of this contract.
IERC20(optionToken.redeemToken()).safeTransfer(
address(optionToken),
quantity
);
uint256 requiredLongOptions =
getProportionalLongOptions(optionToken, quantity);
// Send out the required amount of options from the `from` address.
// WARNING: CALLS TO UNTRUSTED ADDRESS.
IERC20(address(optionToken)).safeTransferFrom(
from,
address(optionToken),
requiredLongOptions
);
// Close the options.
(, , uint256 outputUnderlyings) =
optionToken.closeOptions(address(this));
return outputUnderlyings;
}
/// @dev Removes liquidity from a uniswap pair, using this contract's balance of LP tokens.
/// Withdrawn tokens are sent to this contract.
/// @notice `tokenA` is the redeemToken and `tokenB` is the underlyingToken.
function removeLiquidity(
IUniswapV2Router02 router,
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
uint256 deadline
) internal returns (uint256, uint256) {
// Gets the Uniswap V2 Pair address for shortOptionToken (redeem) and underlyingTokens.
// Transfers the LP tokens of the pair to this contract.
address pair =
IUniswapV2Factory(router.factory()).getPair(tokenA, tokenB);
// Warning: internal call to a non-trusted address `msg.sender`.
IERC20(pair).safeTransferFrom(msg.sender, address(this), liquidity);
IERC20(pair).approve(address(router), uint256(-1));
// Remove liquidity from Uniswap V2 pool to receive the reserve tokens (shortOptionTokens + UnderlyingTokens).
(uint256 amountShortOptions, uint256 amountUnderlyingTokens) =
router.removeLiquidity(
tokenA,
tokenB,
liquidity,
amountAMin,
amountBMin,
address(this),
deadline
);
return (amountShortOptions, amountUnderlyingTokens);
}
/// @dev Closes option tokens by buring option and redeem tokens.
/// @notice Pulls option tokens from `msg.sender`, uses this contract's balance of redeemTokens.
/// @param trader The Primitive V1 trader contract to handle the option closing operation.
/// @param optionToken The option to close.
/// @param amountShortOptions The quantity of short option tokens that will be burned to close the options.
/// @param receiver The address that will be sent the underlyingTokens from closed options.
function closeOptionsWithShortTokens(
ITrader trader,
IOption optionToken,
uint256 amountShortOptions,
address receiver
)
internal
returns (
uint256,
uint256,
uint256
)
{
// Approves trader to pull longOptionTokens and shortOptionTokens from this contract to close options.
IERC20(address(optionToken)).approve(address(trader), uint256(-1));
IERC20(optionToken.redeemToken()).approve(address(trader), uint256(-1));
// Calculate equivalent quantity of redeem (short option) tokens to close the long option position.
// longOptions = shortOptions / strikeRatio
uint256 requiredLongOptions =
getProportionalLongOptions(optionToken, amountShortOptions);
// Pull the required longOptionTokens from `msg.sender` to this contract.
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(this),
requiredLongOptions
);
// Trader pulls option and redeem tokens from this contract and sends them to the option contract.
// Option and redeem tokens are then burned to release underlyingTokens.
// UnderlyingTokens are sent to the "receiver" address.
return trader.safeClose(optionToken, requiredLongOptions, receiver);
}
// ====== View ======
/// @dev Calculates the effective premium, denominated in underlyingTokens, to "buy" `quantity` of optionTokens.
/// @notice UniswapV2 adds a 0.3009027% fee which is applied to the premium as 0.301%.
/// IMPORTANT: If the pair's reserve ratio is incorrect, there could be a 'negative' premium.
/// Buying negative premium options will pay out redeemTokens.
/// An 'incorrect' ratio occurs when the (reserves of redeemTokens / strike ratio) >= reserves of underlyingTokens.
/// Implicitly uses the `optionToken`'s underlying and redeem tokens for the pair.
/// @param router The UniswapV2Router02 contract.
/// @param optionToken The optionToken to get the premium cost of purchasing.
/// @param quantity The quantity of long option tokens that will be purchased.
function getOpenPremium(
IUniswapV2Router02 router,
IOption optionToken,
uint256 quantity
) internal view returns (uint256, uint256) {
// longOptionTokens are opened by doing a swap from redeemTokens to underlyingTokens effectively.
address[] memory path = new address[](2);
path[0] = optionToken.redeemToken();
path[1] = optionToken.getUnderlyingTokenAddress();
// `quantity` of underlyingTokens are output from the swap.
// They are used to mint options, which will mint `quantity` * quoteValue / baseValue amount of redeemTokens.
uint256 redeemsMinted =
getProportionalShortOptions(optionToken, quantity);
// The loanRemainderInUnderlyings will be the amount of underlyingTokens that are needed from the original
// transaction caller in order to pay the flash swap.
// IMPORTANT: THIS IS EFFECTIVELY THE PREMIUM PAID IN UNDERLYINGTOKENS TO PURCHASE THE OPTIONTOKEN.
uint256 loanRemainderInUnderlyings;
// Economically, negativePremiumPaymentInRedeems value should always be 0.
// In the case that we minted more redeemTokens than are needed to pay back the flash swap,
// (short -> underlying is a positive trade), there is an effective negative premium.
// In that case, this function will send out `negativePremiumAmount` of redeemTokens to the original caller.
// This means the user gets to keep the extra redeemTokens for free.
// Negative premium amount is the opposite difference of the loan remainder: (paid - flash loan amount)
uint256 negativePremiumPaymentInRedeems;
// Need to return tokens from the flash swap by returning shortOptionTokens and any remainder of underlyingTokens.
// Since the borrowed amount is underlyingTokens, and we are paying back in redeemTokens,
// we need to see how much redeemTokens must be returned for the borrowed amount.
// We can find that value by doing the normal swap math, getAmountsIn will give us the amount
// of redeemTokens are needed for the output amount of the flash loan.
// IMPORTANT: amountsIn[0] is how many short tokens we need to pay back.
// This value is most likely greater than the amount of redeemTokens minted.
uint256[] memory amountsIn = router.getAmountsIn(quantity, path);
uint256 redeemsRequired = amountsIn[0]; // the amountIn of redeemTokens based on the amountOut of `quantity`.
// If redeemsMinted is greater than redeems required, there is a cost of 0, implying a negative premium.
uint256 redeemCostRemaining =
redeemsRequired > redeemsMinted
? redeemsRequired.sub(redeemsMinted)
: 0;
// If there is a negative premium, calculate the quantity of remaining redeemTokens after the `redeemsMinted` is spent.
negativePremiumPaymentInRedeems = redeemsMinted > redeemsRequired
? redeemsMinted.sub(redeemsRequired)
: 0;
// In most cases, there will be an outstanding cost (assuming we minted less redeemTokens than the
// required amountIn of redeemTokens for the swap).
if (redeemCostRemaining > 0) {
// The user won't want to pay back the remaining cost in redeemTokens,
// because they borrowed underlyingTokens to mint them in the first place.
// So instead, we get the quantity of underlyingTokens that could be paid instead.
// We can calculate this using normal swap math.
// getAmountsOut will return the quantity of underlyingTokens that are output,
// based on some input of redeemTokens.
// The input redeemTokens is the remaining redeemToken cost, and the output
// underlyingTokens is the proportional amount of underlyingTokens.
// amountsOut[1] is then the outstanding flash loan value denominated in underlyingTokens.
uint256[] memory amountsOut =
router.getAmountsOut(redeemCostRemaining, path);
// Returning withdrawn tokens to the pair has a fee of .003 / .997 = 0.3009027% which must be applied.
loanRemainderInUnderlyings = (
amountsOut[1].mul(100000).add(amountsOut[1].mul(301))
)
.div(100000);
}
return (loanRemainderInUnderlyings, negativePremiumPaymentInRedeems);
}
/// @dev Calculates the effective premium, denominated in underlyingTokens, to "sell" option tokens.
/// @param router The UniswapV2Router02 contract.
/// @param optionToken The optionToken to get the premium cost of purchasing.
/// @param quantity The quantity of short option tokens that will be closed.
function getClosePremium(
IUniswapV2Router02 router,
IOption optionToken,
uint256 quantity
) internal view returns (uint256, uint256) {
// longOptionTokens are closed by doing a swap from underlyingTokens to redeemTokens.
address[] memory path = new address[](2);
path[0] = optionToken.getUnderlyingTokenAddress();
path[1] = optionToken.redeemToken();
uint256 outputUnderlyings =
getProportionalLongOptions(optionToken, quantity);
// The loanRemainder will be the amount of underlyingTokens that are needed from the original
// transaction caller in order to pay the flash swap.
uint256 loanRemainder;
// Economically, underlyingPayout value should always be greater than 0, or this trade shouldn't be made.
// If an underlyingPayout is greater than 0, it means that the redeemTokens borrowed are worth less than the
// underlyingTokens received from closing the redeemToken<>optionTokens.
// If the redeemTokens are worth more than the underlyingTokens they are entitled to,
// then closing the redeemTokens will cost additional underlyingTokens. In this case,
// the transaction should be reverted. Or else, the user is paying extra at the expense of
// rebalancing the pool.
uint256 underlyingPayout;
// Need to return tokens from the flash swap by returning underlyingTokens.
// Since the borrowed amount is redeemTokens, and we are paying back in underlyingTokens,
// we need to see how much underlyingTokens must be returned for the borrowed amount.
// We can find that value by doing the normal swap math, getAmountsIn will give us the amount
// of underlyingTokens are needed for the output amount of the flash loan.
// IMPORTANT: amountsIn 0 is how many underlyingTokens we need to pay back.
// This value is most likely greater than the amount of underlyingTokens received from closing.
uint256[] memory amountsIn = router.getAmountsIn(quantity, path);
uint256 underlyingsRequired = amountsIn[0]; // the amountIn required of underlyingTokens based on the amountOut of flashloanQuantity
// If outputUnderlyings (received from closing) is greater than underlyings required,
// there is a positive payout.
underlyingPayout = outputUnderlyings > underlyingsRequired
? outputUnderlyings.sub(underlyingsRequired)
: 0;
// If there is a negative payout, calculate the remaining cost of underlyingTokens.
uint256 underlyingCostRemaining =
underlyingsRequired > outputUnderlyings
? underlyingsRequired.sub(outputUnderlyings)
: 0;
// In the case that there is a negative payout (additional underlyingTokens are required),
// get the remaining cost into the `loanRemainder` variable and also check to see
// if a user is willing to pay the negative cost. There is no rational economic incentive for this.
if (underlyingCostRemaining > 0) {
loanRemainder = underlyingCostRemaining;
}
return (underlyingPayout, loanRemainder);
}
// ==== Primitive V1 =====
/// @dev Calculates the proportional quantity of long option tokens per short option token.
/// @notice For each long option token, there is quoteValue / baseValue quantity of short option tokens.
function getProportionalLongOptions(
IOption optionToken,
uint256 quantityShort
) internal view returns (uint256) {
uint256 quantityLong =
quantityShort.mul(optionToken.getBaseValue()).div(
optionToken.getQuoteValue()
);
return quantityLong;
}
/// @dev Calculates the proportional quantity of short option tokens per long option token.
/// @notice For each short option token, there is baseValue / quoteValue quantity of long option tokens.
function getProportionalShortOptions(
IOption optionToken,
uint256 quantityLong
) internal view returns (uint256) {
uint256 quantityShort =
quantityLong.mul(optionToken.getQuoteValue()).div(
optionToken.getBaseValue()
);
return quantityShort;
}
// ==== Uniswap V2 Library =====
// 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");
}
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(
address factory,
address tokenA,
address tokenB
) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encodePacked(token0, token1)),
hex"e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" // init code hash
)
)
)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
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).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.8.0;
library console {
address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
function _sendLogPayload(bytes memory payload) private view {
uint256 payloadLength = payload.length;
address consoleAddress = CONSOLE_ADDRESS;
assembly {
let payloadStart := add(payload, 32)
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
}
}
function log() internal view {
_sendLogPayload(abi.encodeWithSignature("log()"));
}
function logInt(int p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
}
function logUint(uint p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
}
function logString(string memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function logBool(bool p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function logAddress(address p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function logBytes(bytes memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
}
function logByte(byte p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(byte)", p0));
}
function logBytes1(bytes1 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
}
function logBytes2(bytes2 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
}
function logBytes3(bytes3 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
}
function logBytes4(bytes4 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
}
function logBytes5(bytes5 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
}
function logBytes6(bytes6 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
}
function logBytes7(bytes7 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
}
function logBytes8(bytes8 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
}
function logBytes9(bytes9 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
}
function logBytes10(bytes10 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
}
function logBytes11(bytes11 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
}
function logBytes12(bytes12 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
}
function logBytes13(bytes13 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
}
function logBytes14(bytes14 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
}
function logBytes15(bytes15 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
}
function logBytes16(bytes16 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
}
function logBytes17(bytes17 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
}
function logBytes18(bytes18 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
}
function logBytes19(bytes19 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
}
function logBytes20(bytes20 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
}
function logBytes21(bytes21 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
}
function logBytes22(bytes22 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
}
function logBytes23(bytes23 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
}
function logBytes24(bytes24 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
}
function logBytes25(bytes25 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
}
function logBytes26(bytes26 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
}
function logBytes27(bytes27 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
}
function logBytes28(bytes28 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
}
function logBytes29(bytes29 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
}
function logBytes30(bytes30 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
}
function logBytes31(bytes31 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
}
function logBytes32(bytes32 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
}
function log(uint p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
}
function log(string memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(bool p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function log(address p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function log(uint p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
}
function log(uint p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
}
function log(uint p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
}
function log(uint p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
}
function log(string memory p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
}
function log(string memory p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
}
function log(string memory p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
}
function log(string memory p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
}
function log(bool p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
}
function log(bool p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
}
function log(bool p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
}
function log(bool p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
}
function log(address p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
}
function log(address p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
}
function log(address p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
}
function log(address p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
}
function log(uint p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
}
function log(uint p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
}
function log(uint p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
}
function log(uint p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
}
function log(uint p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
}
function log(uint p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
}
function log(uint p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
}
function log(uint p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
}
function log(uint p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
}
function log(uint p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
}
function log(uint p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
}
function log(uint p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
}
function log(uint p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
}
function log(uint p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
}
function log(uint p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
}
function log(uint p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
}
function log(string memory p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
}
function log(string memory p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
}
function log(string memory p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
}
function log(string memory p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
}
function log(string memory p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
}
function log(string memory p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
}
function log(string memory p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
}
function log(string memory p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
}
function log(string memory p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
}
function log(string memory p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
}
function log(string memory p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
}
function log(string memory p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
}
function log(string memory p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
}
function log(string memory p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
}
function log(string memory p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
}
function log(string memory p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
}
function log(bool p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
}
function log(bool p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
}
function log(bool p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
}
function log(bool p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
}
function log(bool p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
}
function log(bool p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
}
function log(bool p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
}
function log(bool p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
}
function log(bool p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
}
function log(bool p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
}
function log(bool p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
}
function log(bool p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
}
function log(bool p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
}
function log(bool p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
}
function log(bool p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
}
function log(bool p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
}
function log(address p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
}
function log(address p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
}
function log(address p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
}
function log(address p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
}
function log(address p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
}
function log(address p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
}
function log(address p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
}
function log(address p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
}
function log(address p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
}
function log(address p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
}
function log(address p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
}
function log(address p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
}
function log(address p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
}
function log(address p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
}
function log(address p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
}
function log(address p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
}
function log(uint p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
// Primitive
import { IOption } from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol";
import { IWETH } from "./IWETH.sol";
interface IWethConnector01 {
function weth() external view returns (IWETH);
function safeMintWithETH(IOption optionToken, address receiver)
external
payable
returns (uint256, uint256);
function safeExerciseWithETH(IOption optionToken, address receiver)
external
payable
returns (uint256, uint256);
function safeExerciseForETH(
IOption optionToken,
uint256 exerciseQuantity,
address receiver
) external returns (uint256, uint256);
function safeRedeemForETH(
IOption optionToken,
uint256 redeemQuantity,
address receiver
) external returns (uint256);
function safeCloseForETH(
IOption optionToken,
uint256 closeQuantity,
address receiver
)
external
returns (
uint256,
uint256,
uint256
);
function safeUnwindForETH(
IOption optionToken,
uint256 unwindQuantity,
address receiver
)
external
returns (
uint256,
uint256,
uint256
);
function getName() external pure returns (string memory);
function getVersion() external pure returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
///
/// @title Weth Connector for bridging ether to WETH Primitive options.
/// @notice Abstracts the interfacing with the protocol's option contract for ease-of-use.
/// Manages operations involving options with WETH as the underlying or strike asset.
/// Accepts deposits in ethers and withdraws ethers.
/// Primitive V1 WethConnectorLib01 - @primitivefi/contracts@v0.4.1
/// @author Primitive
///
// WETH Interface
import { IWETH } from "../interfaces/IWETH.sol";
// Primitive
import { IOption, IERC20 } from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol";
// Open Zeppelin
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
library WethConnectorLib01 {
using SafeERC20 for IERC20; // Reverts when `transfer` or `transferFrom` erc20 calls don't return proper data
using SafeMath for uint256; // Reverts on math underflows/overflows
///
/// @dev Checks the quantity of an operation to make sure its not zero. Fails early.
///
modifier nonZero(uint256 quantity) {
require(quantity > 0, "ERR_ZERO");
_;
}
// ==== Operation Functions ====
///
///@dev Mints msg.value quantity of options and "quote" (option parameter) quantity of redeem tokens.
///@notice This function is for options that have WETH as the underlying asset.
///@param optionToken The address of the option token to mint.
///@param receiver The address which receives the minted option and redeem tokens.
///
function safeMintWithETH(
IWETH weth,
IOption optionToken,
address receiver
) internal nonZero(msg.value) returns (uint256, uint256) {
// Check to make sure we are minting a WETH call option.
address underlyingAddress = optionToken.getUnderlyingTokenAddress();
require(address(weth) == underlyingAddress, "ERR_NOT_WETH");
// Convert ethers into WETH, then send WETH to option contract in preparation of calling mintOptions().
_depositEthSendWeth(weth, address(optionToken));
// Mint the option and redeem tokens.
(uint256 outputOptions, uint256 outputRedeems) = optionToken
.mintOptions(receiver);
return (outputOptions, outputRedeems);
}
///
/// @dev Swaps msg.value of strikeTokens (ethers) to underlyingTokens.
/// Uses the strike ratio as the exchange rate. Strike ratio = base / quote.
/// Msg.value (quote units) * base / quote = base units (underlyingTokens) to withdraw.
/// @notice This function is for options with WETH as the strike asset.
/// Burns option tokens, accepts ethers, and pushes out underlyingTokens.
/// @param optionToken The address of the option contract.
/// @param receiver The underlyingTokens are sent to the receiver address.
///
function safeExerciseWithETH(
IWETH weth,
IOption optionToken,
address receiver
) internal nonZero(msg.value) returns (uint256, uint256) {
// Require one of the option's assets to be WETH.
address strikeAddress = optionToken.getStrikeTokenAddress();
require(strikeAddress == address(weth), "ERR_NOT_WETH");
uint256 inputStrikes = msg.value;
// Calculate quantity of optionTokens needed to burn.
// An ether put option with strike price $300 has a "base" value of 300, and a "quote" value of 1.
// To calculate how many options are needed to be burned, we need to cancel out the "quote" units.
// The input strike quantity can be multiplied by the strike ratio to cancel out "quote" units.
// 1 ether (quote units) * 300 (base units) / 1 (quote units) = 300 inputOptions
uint256 inputOptions = inputStrikes.mul(optionToken.getBaseValue()).div(
optionToken.getQuoteValue()
);
// Fail early if msg.sender does not have enough optionTokens to burn.
require(
IERC20(address(optionToken)).balanceOf(msg.sender) >= inputOptions,
"ERR_BAL_OPTIONS"
);
// Wrap the ethers into WETH, and send the WETH to the option contract to prepare for calling exerciseOptions().
_depositEthSendWeth(weth, address(optionToken));
// Send the option tokens required to prepare for calling exerciseOptions().
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(optionToken),
inputOptions
);
// Burns the transferred option tokens, stores the strike asset (ether), and pushes underlyingTokens
// to the receiver address.
(inputStrikes, inputOptions) = optionToken.exerciseOptions(
receiver,
inputOptions,
new bytes(0)
);
return (inputStrikes, inputOptions);
}
///
/// @dev Swaps strikeTokens to underlyingTokens, WETH, which is converted to ethers before withdrawn.
/// Uses the strike ratio as the exchange rate. Strike ratio = base / quote.
/// @notice This function is for options with WETH as the underlying asset.
/// Burns option tokens, pulls strikeTokens, and pushes out ethers.
/// @param optionToken The address of the option contract.
/// @param exerciseQuantity Quantity of optionTokens to exercise.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeExerciseForETH(
IWETH weth,
IOption optionToken,
uint256 exerciseQuantity,
address receiver
) internal nonZero(exerciseQuantity) returns (uint256, uint256) {
// Require one of the option's assets to be WETH.
address underlyingAddress = optionToken.getUnderlyingTokenAddress();
address strikeAddress = optionToken.getStrikeTokenAddress();
require(underlyingAddress == address(weth), "ERR_NOT_WETH");
// Fails early if msg.sender does not have enough optionTokens.
require(
IERC20(address(optionToken)).balanceOf(msg.sender) >=
exerciseQuantity,
"ERR_BAL_OPTIONS"
);
// Calculate quantity of strikeTokens needed to exercise quantity of optionTokens.
uint256 inputStrikes = exerciseQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
// Fails early if msg.sender does not have enough strikeTokens.
require(
IERC20(strikeAddress).balanceOf(msg.sender) >= inputStrikes,
"ERR_BAL_STRIKE"
);
// Send strikeTokens to option contract to prepare for calling exerciseOptions().
IERC20(strikeAddress).safeTransferFrom(
msg.sender,
address(optionToken),
inputStrikes
);
// Send the option tokens to prepare for calling exerciseOptions().
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(optionToken),
exerciseQuantity
);
// Burns the optionTokens sent, stores the strikeTokens sent, and pushes underlyingTokens
// to this contract.
uint256 inputOptions;
(inputStrikes, inputOptions) = optionToken.exerciseOptions(
address(this),
exerciseQuantity,
new bytes(0)
);
// Converts the withdrawn WETH to ethers, then sends the ethers to the receiver address.
_withdrawEthAndSend(weth, receiver, exerciseQuantity);
return (inputStrikes, inputOptions);
}
///
/// @dev Burns redeem tokens to withdraw strike tokens (ethers) at a 1:1 ratio.
/// @notice This function is for options that have WETH as the strike asset.
/// Converts WETH to ethers, and withdraws ethers to the receiver address.
/// @param optionToken The address of the option contract.
/// @param redeemQuantity The quantity of redeemTokens to burn.
/// @param receiver The strikeTokens (ethers) are sent to the receiver address.
///
function safeRedeemForETH(
IWETH weth,
IOption optionToken,
uint256 redeemQuantity,
address receiver
) internal nonZero(redeemQuantity) returns (uint256) {
// Require strikeToken to be WETH.
address strikeAddress = optionToken.getStrikeTokenAddress();
require(strikeAddress == address(weth), "ERR_NOT_WETH");
// Fail early if msg.sender does not have enough redeemTokens.
address redeemAddress = optionToken.redeemToken();
require(
IERC20(redeemAddress).balanceOf(msg.sender) >= redeemQuantity,
"ERR_BAL_REDEEM"
);
// Send redeemTokens to option contract in preparation for calling redeemStrikeTokens().
IERC20(redeemAddress).safeTransferFrom(
msg.sender,
address(optionToken),
redeemQuantity
);
// If options have not been exercised, there will be no strikeTokens to redeem, causing a revert.
// Burns the redeem tokens that were sent to the contract, and withdraws the same quantity of WETH.
// Sends the withdrawn WETH to this contract, so that it can be unwrapped prior to being sent to receiver.
uint256 inputRedeems = optionToken.redeemStrikeTokens(address(this));
// Unwrap the redeemed WETH and then send the ethers to the receiver.
_withdrawEthAndSend(weth, receiver, redeemQuantity);
return inputRedeems;
}
///
/// @dev Burn optionTokens and redeemTokens to withdraw underlyingTokens (ethers).
/// @notice This function is for options with WETH as the underlying asset.
/// WETH underlyingTokens are converted to ethers before being sent to receiver.
/// The redeemTokens to burn is equal to the optionTokens * strike ratio.
/// inputOptions = inputRedeems / strike ratio = outUnderlyings
/// @param optionToken The address of the option contract.
/// @param closeQuantity Quantity of optionTokens to burn and an input to calculate how many redeems to burn.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeCloseForETH(
IWETH weth,
IOption optionToken,
uint256 closeQuantity,
address receiver
)
internal
nonZero(closeQuantity)
returns (
uint256,
uint256,
uint256
)
{
// Require the optionToken to have WETH as the underlying asset.
address underlyingAddress = optionToken.getUnderlyingTokenAddress();
require(address(weth) == underlyingAddress, "ERR_NOT_WETH");
// Fail early if msg.sender does not have enough optionTokens to burn.
require(
IERC20(address(optionToken)).balanceOf(msg.sender) >= closeQuantity,
"ERR_BAL_OPTIONS"
);
// Calculate the quantity of redeemTokens that need to be burned.
uint256 inputRedeems = closeQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
// Fail early is msg.sender does not have enough redeemTokens to burn.
require(
IERC20(optionToken.redeemToken()).balanceOf(msg.sender) >=
inputRedeems,
"ERR_BAL_REDEEM"
);
// Send redeem and option tokens in preparation of calling closeOptions().
IERC20(optionToken.redeemToken()).safeTransferFrom(
msg.sender,
address(optionToken),
inputRedeems
);
IERC20(address(optionToken)).safeTransferFrom(
msg.sender,
address(optionToken),
closeQuantity
);
// Call the closeOptions() function to burn option and redeem tokens and withdraw underlyingTokens.
uint256 inputOptions;
uint256 outUnderlyings;
(inputRedeems, inputOptions, outUnderlyings) = optionToken.closeOptions(
address(this)
);
// Since underlyngTokens are WETH, unwrap them then send the ethers to the receiver.
_withdrawEthAndSend(weth, receiver, closeQuantity);
return (inputRedeems, inputOptions, outUnderlyings);
}
///
/// @dev Burn redeemTokens to withdraw underlyingTokens (ethers) from expired options.
/// This function is for options with WETH as the underlying asset.
/// The underlyingTokens are WETH, which are converted to ethers prior to being sent to receiver.
/// @param optionToken The address of the option contract.
/// @param unwindQuantity Quantity of underlyingTokens (ethers) to withdraw.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeUnwindForETH(
IWETH weth,
IOption optionToken,
uint256 unwindQuantity,
address receiver
)
internal
nonZero(unwindQuantity)
returns (
uint256,
uint256,
uint256
)
{
// Require the optionToken to have WETH as the underlying asset.
address underlyingAddress = optionToken.getUnderlyingTokenAddress();
require(address(weth) == underlyingAddress, "ERR_NOT_WETH");
// If the option is not expired, fail early.
// solhint-disable-next-line not-rely-on-time
require(optionToken.getExpiryTime() < now, "ERR_NOT_EXPIRED");
// Calculate the quantity of redeemTokens that need to be burned.
uint256 inputRedeems = unwindQuantity
.mul(optionToken.getQuoteValue())
.div(optionToken.getBaseValue());
// Fail early if msg.sender does not have enough redeemTokens to burn.
require(
IERC20(optionToken.redeemToken()).balanceOf(msg.sender) >=
inputRedeems,
"ERR_BAL_REDEEM"
);
// Send redeem in preparation of calling closeOptions().
IERC20(optionToken.redeemToken()).safeTransferFrom(
msg.sender,
address(optionToken),
inputRedeems
);
// Call the closeOptions() function to burn redeem tokens and withdraw underlyingTokens.
uint256 inputOptions;
uint256 outUnderlyings;
(inputRedeems, inputOptions, outUnderlyings) = optionToken.closeOptions(
address(this)
);
// Since underlyngTokens are WETH, unwrap them to ethers then send the ethers to the receiver.
_withdrawEthAndSend(weth, receiver, unwindQuantity);
return (inputRedeems, inputOptions, outUnderlyings);
}
// ==== WETH Operations ====
///
/// @dev Deposits msg.value of ethers into WETH contract. Then sends WETH to "to".
/// @param to The address to send WETH ERC-20 tokens to.
///
function _depositEthSendWeth(IWETH weth, address to) internal {
// Deposit the ethers received from msg.value into the WETH contract.
weth.deposit.value(msg.value)();
// Send WETH.
weth.transfer(to, msg.value);
}
///
/// @dev Unwraps WETH to withrdaw ethers, which are then sent to the "to" address.
/// @param to The address to send withdrawn ethers to.
/// @param quantity The quantity of WETH to unwrap.
///
function _withdrawEthAndSend(
IWETH weth,
address to,
uint256 quantity
) internal {
// Withdraw ethers with weth.
weth.withdraw(quantity);
// Send ether.
(bool success, ) = to.call.value(quantity)("");
// Revert is call is unsuccessful.
require(success, "ERR_SENDING_ETHER");
}
}pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.2;
///
/// @title Weth Connector for bridging ether to WETH Primitive options.
/// @notice Abstracts the interfacing with the protocol's option contract for ease-of-use.
/// Manages operations involving options with WETH as the underlying or strike asset.
/// Accepts deposits in ethers and withdraws ethers.
/// Primitive V1 WethConnector01 - @primitivefi/contracts@v0.4.1
/// @author Primitive
///
// Primitive
import { IWethConnector01, IOption, IWETH } from "./interfaces/IWethConnector01.sol";
import { WethConnectorLib01, IERC20 } from "./libraries/WethConnectorLib01.sol";
// Open Zeppelin
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {
ReentrancyGuard
} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract WethConnector01 is IWethConnector01, ReentrancyGuard {
using SafeERC20 for IERC20; // Reverts when `transfer` or `transferFrom` erc20 calls don't return proper data
using SafeMath for uint256; // Reverts on math underflows/overflows
IWETH public override weth;
event WethConnectorMint(
address indexed from,
address indexed option,
uint256 outputOptions,
uint256 outputRedeems
);
event WethConnectorExercise(
address indexed from,
address indexed option,
uint256 outUnderlyings,
uint256 inStrikes
);
event WethConnectorRedeem(
address indexed from,
address indexed option,
uint256 inRedeems
);
event WethConnectorClose(
address indexed from,
address indexed option,
uint256 inOptions
);
event WethConnectorUnwind(
address indexed from,
address indexed option,
uint256 inOptions
);
///
/// @dev Checks the quantity of an operation to make sure its not zero. Fails early.
///
modifier nonZero(uint256 quantity) {
require(quantity > 0, "ERR_ZERO");
_;
}
///
/// @dev Since the WethConnector contract is responsible for converting between ethers and WETH,
/// the contract is initialized with the address for WETH.
///
constructor(address payable _weth) public {
weth = IWETH(_weth);
}
///
/// @dev If ether is sent to this contract through a normal transaction, it will fail, unless
/// it was the WETH contract who sent it.
///
receive() external payable {
assert(msg.sender == address(weth));
}
// ==== Operation Functions ====
///
/// @dev Mints msg.value quantity of options and "quote" (option parameter) quantity of redeem tokens.
/// @notice This function is for options that have WETH as the underlying asset.
/// @param optionToken The address of the option token to mint.
/// @param receiver The address which receives the minted option and redeem tokens.
///
function safeMintWithETH(IOption optionToken, address receiver)
external
override
payable
nonReentrant
nonZero(msg.value)
returns (uint256, uint256)
{
(uint256 outputOptions, uint256 outputRedeems) = WethConnectorLib01
.safeMintWithETH(weth, optionToken, receiver);
emit WethConnectorMint(
msg.sender,
address(optionToken),
outputOptions,
outputRedeems
);
return (outputOptions, outputRedeems);
}
///
/// @dev Swaps msg.value of strikeTokens (ethers) to underlyingTokens.
/// Uses the strike ratio as the exchange rate. Strike ratio = base / quote.
/// Msg.value (quote units) * base / quote = base units (underlyingTokens) to withdraw.
/// @notice This function is for options with WETH as the strike asset.
/// Burns option tokens, accepts ethers, and pushes out underlyingTokens.
/// @param optionToken The address of the option contract.
/// @param receiver The underlyingTokens are sent to the receiver address.
///
function safeExerciseWithETH(IOption optionToken, address receiver)
external
override
payable
nonReentrant
nonZero(msg.value)
returns (uint256, uint256)
{
(uint256 inputStrikes, uint256 inputOptions) = WethConnectorLib01
.safeExerciseWithETH(weth, optionToken, receiver);
emit WethConnectorExercise(
msg.sender,
address(optionToken),
inputOptions,
inputStrikes
);
return (inputStrikes, inputOptions);
}
///
/// @dev Swaps strikeTokens to underlyingTokens, WETH, which is converted to ethers before withdrawn.
/// Uses the strike ratio as the exchange rate. Strike ratio = base / quote.
/// @notice This function is for options with WETH as the underlying asset.
/// Burns option tokens, pulls strikeTokens, and pushes out ethers.
/// @param optionToken The address of the option contract.
/// @param exerciseQuantity Quantity of optionTokens to exercise.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeExerciseForETH(
IOption optionToken,
uint256 exerciseQuantity,
address receiver
)
external
override
nonReentrant
nonZero(exerciseQuantity)
returns (uint256, uint256)
{
(uint256 inputStrikes, uint256 inputOptions) = WethConnectorLib01
.safeExerciseForETH(weth, optionToken, exerciseQuantity, receiver);
emit WethConnectorExercise(
msg.sender,
address(optionToken),
exerciseQuantity,
inputStrikes
);
return (inputStrikes, inputOptions);
}
///
/// @dev Burns redeem tokens to withdraw strike tokens (ethers) at a 1:1 ratio.
/// @notice This function is for options that have WETH as the strike asset.
/// Converts WETH to ethers, and withdraws ethers to the receiver address.
/// @param optionToken The address of the option contract.
/// @param redeemQuantity The quantity of redeemTokens to burn.
/// @param receiver The strikeTokens (ethers) are sent to the receiver address.
///
function safeRedeemForETH(
IOption optionToken,
uint256 redeemQuantity,
address receiver
) external override nonReentrant nonZero(redeemQuantity) returns (uint256) {
uint256 inputRedeems = WethConnectorLib01.safeRedeemForETH(
weth,
optionToken,
redeemQuantity,
receiver
);
emit WethConnectorRedeem(
msg.sender,
address(optionToken),
inputRedeems
);
return inputRedeems;
}
///
/// @dev Burn optionTokens and redeemTokens to withdraw underlyingTokens (ethers).
/// @notice This function is for options with WETH as the underlying asset.
/// WETH underlyingTokens are converted to ethers before being sent to receiver.
/// The redeemTokens to burn is equal to the optionTokens * strike ratio.
/// inputOptions = inputRedeems / strike ratio = outUnderlyings
/// @param optionToken The address of the option contract.
/// @param closeQuantity Quantity of optionTokens to burn and an input to calculate how many redeems to burn.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeCloseForETH(
IOption optionToken,
uint256 closeQuantity,
address receiver
)
external
override
nonReentrant
nonZero(closeQuantity)
returns (
uint256,
uint256,
uint256
)
{
(
uint256 inputRedeems,
uint256 inputOptions,
uint256 outUnderlyings
) = WethConnectorLib01.safeCloseForETH(
weth,
optionToken,
closeQuantity,
receiver
);
emit WethConnectorClose(msg.sender, address(optionToken), inputOptions);
return (inputRedeems, inputOptions, outUnderlyings);
}
///
/// @dev Burn redeemTokens to withdraw underlyingTokens (ethers) from expired options.
/// This function is for options with WETH as the underlying asset.
/// The underlyingTokens are WETH, which are converted to ethers prior to being sent to receiver.
/// @param optionToken The address of the option contract.
/// @param unwindQuantity Quantity of underlyingTokens (ethers) to withdraw.
/// @param receiver The underlyingTokens (ethers) are sent to the receiver address.
///
function safeUnwindForETH(
IOption optionToken,
uint256 unwindQuantity,
address receiver
)
external
override
nonReentrant
nonZero(unwindQuantity)
returns (
uint256,
uint256,
uint256
)
{
(
uint256 inputRedeems,
uint256 inputOptions,
uint256 outUnderlyings
) = WethConnectorLib01.safeUnwindForETH(
weth,
optionToken,
unwindQuantity,
receiver
);
emit WethConnectorUnwind(
msg.sender,
address(optionToken),
inputOptions
);
return (inputRedeems, inputOptions, outUnderlyings);
}
// ==== View ====
/// @dev Gets the name of the contract.
function getName() external override pure returns (string memory) {
return "PrimitiveV1WethConnector01";
}
/// @dev Gets the version of the contract.
function getVersion() external override pure returns (uint8) {
return uint8(1);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestERC20 is ERC20 {
constructor(
string memory name,
string memory symbol,
uint256 initialSupply
) public ERC20(name, symbol) {
_mint(msg.sender, initialSupply);
}
/**
* @dev Function to mint tokens
* @param to The address that will receive the minted tokens.
* @param value The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address to, uint256 value) public returns (bool) {
_mint(to, value);
return true;
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"router_","type":"address"},{"internalType":"address","name":"factory_","type":"address"},{"internalType":"address","name":"trader_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"}],"name":"FlashClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"}],"name":"FlashOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"WroteOption","type":"event"},{"inputs":[{"internalType":"address","name":"optionAddress","type":"address"},{"internalType":"uint256","name":"quantityOptions","type":"uint256"},{"internalType":"uint256","name":"amountBMax","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addShortLiquidityWithUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"amountRedeems","type":"uint256"},{"internalType":"uint256","name":"minPayout","type":"uint256"}],"name":"closeFlashLong","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"optionAddress","type":"address"},{"internalType":"address","name":"otherToken","type":"address"}],"name":"deployUniswapMarket","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pairAddress","type":"address"},{"internalType":"address","name":"optionAddress","type":"address"},{"internalType":"uint256","name":"flashLoanQuantity","type":"uint256"},{"internalType":"uint256","name":"minPayout","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"flashCloseLongOptionsThenSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pairAddress","type":"address"},{"internalType":"address","name":"optionAddress","type":"address"},{"internalType":"uint256","name":"flashLoanQuantity","type":"uint256"},{"internalType":"uint256","name":"maxPremium","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"flashMintShortOptionsThenSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"quantityShort","type":"uint256"}],"name":"getClosePremium","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"quantityLong","type":"uint256"}],"name":"getOpenPremium","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"name":"getUniswapMarketForTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"writeQuantity","type":"uint256"},{"internalType":"uint256","name":"minPayout","type":"uint256"}],"name":"mintOptionsThenFlashCloseLong","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"mintShortOptionsThenSwapToTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"optionToken","type":"address"},{"internalType":"uint256","name":"amountOptions","type":"uint256"},{"internalType":"uint256","name":"maxPremium","type":"uint256"}],"name":"openFlashLong","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"optionAddress","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeShortLiquidityThenCloseOptions","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trader","outputs":[{"internalType":"contract ITrader","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV2Call","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506040516200456138038062004561833981810160405260608110156200003757600080fd5b508051602082015160409092015160016000556003549192916001600160a01b0316156200009e576040805162461bcd60e51b815260206004820152600f60248201526e11549497d253925512505312569151608a1b604482015290519081900360640190fd5b6002546001600160a01b031615620000ef576040805162461bcd60e51b815260206004820152600f60248201526e11549497d253925512505312569151608a1b604482015290519081900360640190fd5b6001546001600160a01b03161562000140576040805162461bcd60e51b815260206004820152600f60248201526e11549497d253925512505312569151608a1b604482015290519081900360640190fd5b600380546001600160a01b038086166001600160a01b03199283161790925560028054858416908316179055600180549284169290911691909117905560405133907f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e690600090a25050506143a680620001bb6000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063904f91dc116100a2578063c45a015511610071578063c45a015514610636578063cd0455241461063e578063dd2c704e14610670578063f887ea401461069c578063fd52c6bb146106a457610116565b8063904f91dc1461046e5780639245d5831461049a57806395f0ae76146104cc578063b17d05701461056557610116565b80631758078b116100e95780631758078b1461026a57806317d7de7c1461028e5780632e16cab31461030b57806360060ccd1461036f57806369bd26cd1461044057610116565b80630398a6131461011b57806307f24906146101615780630d8e6e2c146101c057806310d1e85c146101de575b600080fd5b61014d6004803603606081101561013157600080fd5b506001600160a01b0381351690602081013590604001356106d2565b604080519115158252519081900360200190f35b6101a7600480360360c081101561017757600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610775565b6040805192835260208301919091528051918290030190f35b6101c8610a55565b6040805160ff9092168252519081900360200190f35b610268600480360360808110156101f457600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561022a57600080fd5b82018360208201111561023c57600080fd5b803590602001918460018302840111600160201b8311171561025d57600080fd5b509092509050610a5a565b005b610272610cad565b604080516001600160a01b039092168252519081900360200190f35b610296610cbc565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102d05781810151838201526020016102b8565b50505050905090810190601f1680156102fd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610351600480360360c081101561032157600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610cf3565b60408051938452602084019290925282820152519081900360600190f35b6101a7600480360360c081101561038557600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156103c457600080fd5b8201836020820111156103d657600080fd5b803590602001918460208302840111600160201b831117156103f757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b03169150610d759050565b6102726004803603604081101561045657600080fd5b506001600160a01b0381358116916020013516610dea565b6101a76004803603604081101561048457600080fd5b506001600160a01b038135169060200135610e78565b61014d600480360360608110156104b057600080fd5b506001600160a01b038135169060208101359060400135610ea0565b61014d600480360360c08110156104e257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561051857600080fd5b82018360208201111561052a57600080fd5b803590602001918460208302840111600160201b8311171561054b57600080fd5b91935091506001600160a01b0381351690602001356113f5565b6101a7600480360360c081101561057b57600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156105ba57600080fd5b8201836020820111156105cc57600080fd5b803590602001918460208302840111600160201b831117156105ed57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b031691506114a69050565b61027261151b565b61014d6004803603606081101561065457600080fd5b506001600160a01b03813516906020810135906040013561152a565b6101a76004803603604081101561068657600080fd5b506001600160a01b038135169060200135611937565b610272611953565b610272600480360360408110156106ba57600080fd5b506001600160a01b0381358116916020013516611962565b6000806106e0858533611a49565b91505060006106f0868386610ea0565b905080610736576040805162461bcd60e51b815260206004820152600f60248201526e4552525f464c4153485f434c4f534560881b604482015290519081900360640190fd5b60408051868152905133917f4150f3753a230033657cb8a559a7d2a6a6110a7326d898ac1f07de6ae5c6785b919081900360200190a295945050505050565b600080600260005414156107be576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000886001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561080157600080fd5b505afa158015610815573d6000803e3d6000fd5b505050506040513d602081101561082b57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b038c169163440f5fd2916004808301926020929190829003018186803b15801561087357600080fd5b505afa158015610887573d6000803e3d6000fd5b505050506040513d602081101561089d57600080fd5b5051604080516370a0823160e01b815230600482015290519192506000916001600160a01b038516916370a08231916024808301926020929190829003018186803b1580156108eb57600080fd5b505afa1580156108ff573d6000803e3d6000fd5b505050506040513d602081101561091557600080fd5b5051600354909150600090819061093a906001600160a01b031686868f8f8f8e611b9a565b600154919350915060009061095a906001600160a01b03168f858d611dda565b925050506109f084876001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156109b857600080fd5b505afa1580156109cc573d6000803e3d6000fd5b505050506040513d60208110156109e257600080fd5b50519063ffffffff61202616565b93508315610a1257610a126001600160a01b0387168b8663ffffffff61206816565b610a2c6001600160a01b0386168b8463ffffffff61206816565b610a3c828263ffffffff6120bf16565b60016000559e939d50929b505050505050505050505050565b600390565b6000336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9557600080fd5b505afa158015610aa9573d6000803e3d6000fd5b505050506040513d6020811015610abf57600080fd5b50516040805163d21220a760e01b81529051919250600091339163d21220a7916004808301926020929190829003018186803b158015610afe57600080fd5b505afa158015610b12573d6000803e3d6000fd5b505050506040513d6020811015610b2857600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b0386811660048301528085166024830152915193945091169163e6a4390591604480820192602092909190829003018186803b158015610b8157600080fd5b505afa158015610b95573d6000803e3d6000fd5b505050506040513d6020811015610bab57600080fd5b50516001600160a01b03163314610bbe57fe5b60006060306001600160a01b03168686604051808383808284376040519201945060009350909150508083038183865af19150503d8060008114610c1e576040519150601f19603f3d011682016040523d82523d6000602084013e610c23565b606091505b5091509150818015610c51575080511580610c515750808060200190516020811015610c4e57600080fd5b50515b610ca2576040805162461bcd60e51b815260206004820152601760248201527f4552525f554e495357415056325f43414c4c5f4641494c000000000000000000604482015290519081900360640190fd5b505050505050505050565b6001546001600160a01b031681565b60408051808201909152601d81527f5072696d69746976655631556e6973776170436f6e6e6563746f723033000000602082015290565b600080600060026000541415610d3e576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b6002600055600354610d5e906001600160a01b03168a8a8a8a8a8a612119565b6001600055919b909a509098509650505050505050565b600354600090819081908190610d99906001600160a01b03168b8b8b8b8b8b6122b2565b6040805183815260208101839052815193955091935033927f3725145ca6a6d192a761e3b6dbcf42cdc13147dd45a65fd1b29737ac584b557e9281900390910190a290999098509650505050505050565b600254604080516364e329cb60e11b81526001600160a01b038581166004830152848116602483015291516000938493169163c9c6539691604480830192602092919082900301818787803b158015610e4257600080fd5b505af1158015610e56573d6000803e3d6000fd5b505050506040513d6020811015610e6c57600080fd5b50519150505b92915050565b6003546000908190610e94906001600160a01b0316858561272a565b915091505b9250929050565b600060026000541415610ee8576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f2b57600080fd5b505afa158015610f3f573d6000803e3d6000fd5b505050506040513d6020811015610f5557600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b158015610f9d57600080fd5b505afa158015610fb1573d6000803e3d6000fd5b505050506040513d6020811015610fc757600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b03868116600483015280851660248301529151939450600093919092169163e6a43905916044808301926020929190829003018186803b15801561102457600080fd5b505afa158015611038573d6000803e3d6000fd5b505050506040513d602081101561104e57600080fd5b5051604080516002808252606080830184529394509091602083019080388339019050509050828160008151811061108257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083816001815181106110b057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506000829050600060405180608001604052806050815260200161428660509139805190602001209050606081858c8c8c883360405160240180876001600160a01b03166001600160a01b03168152602001866001600160a01b03166001600160a01b0316815260200185815260200184815260200180602001836001600160a01b03166001600160a01b03168152602001828103825284818151815260200191508051906020019060200280838360005b83811015611199578181015183820152602001611181565b50505050905001975050505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090506000876001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561122357600080fd5b505afa158015611237573d6000803e3d6000fd5b505050506040513d602081101561124d57600080fd5b50516001600160a01b031614611264576000611266565b8a5b90506000886001600160a01b0316856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ad57600080fd5b505afa1580156112c1573d6000803e3d6000fd5b505050506040513d60208110156112d757600080fd5b50516001600160a01b0316146112ed578b6112f0565b60005b9050846001600160a01b031663022c0d9f838330876040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b03166001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561137557818101518382015260200161135d565b50505050905090810190601f1680156113a25780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b1580156113c457600080fd5b505af11580156113d8573d6000803e3d6000fd5b505050506001995050505050505050505060016000559392505050565b60006002600054141561143d576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b600260009081556003546040805160208089028281018201909352888252611494936001600160a01b0316928d928d928d928d918d9182918501908490808284376000920191909152508c92508b9150612a549050565b60016000559998505050505050505050565b6003546000908190819081906114ca906001600160a01b03168b8b8b8b8b8b612b37565b6040805183815260208101839052815193955091935033927ff17aa7749e04732ce7bde5ee88376144e6c5669bd4d8c80358119eada62a1d009281900390910190a290999098509650505050505050565b6002546001600160a01b031681565b600060026000541415611572576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115b557600080fd5b505afa1580156115c9573d6000803e3d6000fd5b505050506040513d60208110156115df57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b15801561162757600080fd5b505afa15801561163b573d6000803e3d6000fd5b505050506040513d602081101561165157600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b03868116600483015280851660248301529151939450600093919092169163e6a43905916044808301926020929190829003018186803b1580156116ae57600080fd5b505afa1580156116c2573d6000803e3d6000fd5b505050506040513d60208110156116d857600080fd5b5051604080516002808252606080830184529394509091602083019080388339019050509050838160008151811061170c57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160018151811061173a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600082905060006040518060800160405280605081526020016142f760509139805190602001209050606081858c8c8c883360405160240180876001600160a01b03166001600160a01b03168152602001866001600160a01b03166001600160a01b0316815260200185815260200184815260200180602001836001600160a01b03166001600160a01b03168152602001828103825284818151815260200191508051906020019060200280838360005b8381101561182357818101518382015260200161180b565b50505050905001975050505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090506000866001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156118ad57600080fd5b505afa1580156118c1573d6000803e3d6000fd5b505050506040513d60208110156118d757600080fd5b50516001600160a01b0316146118ee5760006118f0565b8a5b90506000876001600160a01b0316856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ad57600080fd5b6003546000908190610e94906001600160a01b03168585612f2b565b6003546001600160a01b031681565b6002546040805163e6a4390560e01b81526001600160a01b038581166004830152848116602483015291516000938493169163e6a43905916044808301926020929190829003018186803b1580156119b957600080fd5b505afa1580156119cd573d6000803e3d6000fd5b505050506040513d60208110156119e357600080fd5b505190506001600160a01b038116611a42576040805162461bcd60e51b815260206004820152601760248201527f4552525f504149525f444f45535f4e4f545f4558495354000000000000000000604482015290519081900360640190fd5b9392505050565b60008060008411611a8c576040805162461bcd60e51b81526020600482015260086024820152674552525f5a45524f60c01b604482015290519081900360640190fd5b611b0e338686886001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015611acb57600080fd5b505afa158015611adf573d6000803e3d6000fd5b505050506040513d6020811015611af557600080fd5b50516001600160a01b031692919063ffffffff61340916565b60408051633ea6b5f160e21b81526001600160a01b03858116600483015282516000938493928a169263fa9ad7c4926024808301939282900301818787803b158015611b5957600080fd5b505af1158015611b6d573d6000803e3d6000fd5b505050506040513d6040811015611b8357600080fd5b508051602090910151909890975095505050505050565b6000806000896001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b158015611bd857600080fd5b505afa158015611bec573d6000803e3d6000fd5b505050506040513d6020811015611c0257600080fd5b50516040805163e6a4390560e01b81526001600160a01b038c811660048301528b811660248301529151919092169163e6a43905916044808301926020929190829003018186803b158015611c5657600080fd5b505afa158015611c6a573d6000803e3d6000fd5b505050506040513d6020811015611c8057600080fd5b50519050611c9f6001600160a01b03821633308a63ffffffff61340916565b6040805163095ea7b360e01b81526001600160a01b038c81166004830152600019602483015291519183169163095ea7b3916044808201926020929091908290030181600087803b158015611cf357600080fd5b505af1158015611d07573d6000803e3d6000fd5b505050506040513d6020811015611d1d57600080fd5b505060408051635d5155ef60e11b81526001600160a01b038b811660048301528a81166024830152604482018a905260648201899052608482018890523060a483015260c4820187905282516000938493928f169263baa2abde9260e4808301939282900301818787803b158015611d9457600080fd5b505af1158015611da8573d6000803e3d6000fd5b505050506040513d6040811015611dbe57600080fd5b508051602090910151909d909c509a5050505050505050505050565b6000806000856001600160a01b031663095ea7b3886000196040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015611e4157600080fd5b505af1158015611e55573d6000803e3d6000fd5b505050506040513d6020811015611e6b57600080fd5b505060408051632f310bad60e01b815290516001600160a01b03881691632f310bad916004808301926020929190829003018186803b158015611ead57600080fd5b505afa158015611ec1573d6000803e3d6000fd5b505050506040513d6020811015611ed757600080fd5b50516040805163095ea7b360e01b81526001600160a01b038a8116600483015260001960248301529151919092169163095ea7b39160448083019260209291908290030181600087803b158015611f2d57600080fd5b505af1158015611f41573d6000803e3d6000fd5b505050506040513d6020811015611f5757600080fd5b5060009050611f668787613469565b9050611f836001600160a01b03881633308463ffffffff61340916565b6040805163b8a6bb3b60e01b81526001600160a01b0389811660048301526024820184905287811660448301529151918a169163b8a6bb3b916064808201926060929091908290030181600087803b158015611fde57600080fd5b505af1158015611ff2573d6000803e3d6000fd5b505050506040513d606081101561200857600080fd5b5080516020820151604090920151909a919950975095505050505050565b6000611a4283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613559565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526120ba9084906135f0565b505050565b600082820183811015611a42576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080600080600080600061212e8d8d6136a1565b91505060008d905060008f90506000826001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60208110156121a057600080fd5b81019080805190602001909291905050509050600084905060008f905060008f905060008f905060008f905061224087896001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561220957600080fd5b505afa15801561221d573d6000803e3d6000fd5b505050506040513d602081101561223357600080fd5b50518888888888886136dc565b919d509b509950888c1461225057fe5b60008b8511612260576000612270565b612270858d63ffffffff61202616565b90508015612292576122926001600160a01b038816338363ffffffff61206816565b509a9d50989b509699505050505050505050509750975097945050505050565b6000803330146122f8576040805162461bcd60e51b815260206004820152600c60248201526b22a9292fa727aa2fa9a2a62360a11b604482015290519081900360640190fd5b6001600160a01b038316612349576040805162461bcd60e51b81526020600482015260136024820152724552525f544f5f414444524553535f5a45524f60681b604482015290519081900360640190fd5b6001600160a01b03831633141561239b576040805162461bcd60e51b815260206004820152601160248201527022a9292faa27afa6a9a3afa9a2a72222a960791b604482015290519081900360640190fd5b876001600160a01b031661243c8a6001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e157600080fd5b505afa1580156123f5573d6000803e3d6000fd5b505050506040513d602081101561240b57600080fd5b50518651879060009061241a57fe5b60200260200101518760018151811061242f57fe5b60200260200101516138c5565b6001600160a01b03161461248a576040805162461bcd60e51b815260206004820152601060248201526f22a9292fa4a72b20a624a22fa820a4a960811b604482015290519081900360640190fd5b6000876001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b1580156124c557600080fd5b505afa1580156124d9573d6000803e3d6000fd5b505050506040513d60208110156124ef57600080fd5b505160408051632f310bad60e01b815290519192506000916001600160a01b038b1691632f310bad916004808301926020929190829003018186803b15801561253757600080fd5b505afa15801561254b573d6000803e3d6000fd5b505050506040513d602081101561256157600080fd5b505186519091506001600160a01b038216908790600190811061258057fe5b60200260200101516001600160a01b0316146125e3576040805162461bcd60e51b815260206004820152601760248201527f4552525f454e445f504154485f4e4f545f52454445454d000000000000000000604482015290519081900360640190fd5b60006125f0868b8b613985565b90506000806126008e8d8d61272a565b9092509050811561261e5761261b838363ffffffff61202616565b92505b821561263e5761263e6001600160a01b0386168e8563ffffffff61206816565b80156126aa57818a111561268f576040805162461bcd60e51b815260206004820152601360248201527211549497d39151d05512559157d4105653d555606a1b604482015290519081900360640190fd5b6126aa6001600160a01b038616898f8463ffffffff61340916565b811561271757898210156126fd576040805162461bcd60e51b815260206004820152601560248201527422a9292fa82922a6a4aaa6afaaa72222a92fa6a4a760591b604482015290519081900360640190fd5b6127176001600160a01b038616898463ffffffff61206816565b50909c909b509950505050505050505050565b604080516002808252606080830184526000938493919290602083019080388339019050509050846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561278a57600080fd5b505afa15801561279e573d6000803e3d6000fd5b505050506040513d60208110156127b457600080fd5b5051815182906000906127c357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561281c57600080fd5b505afa158015612830573d6000803e3d6000fd5b505050506040513d602081101561284657600080fd5b505181518290600190811061285757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060006128838686613469565b90506000806060896001600160a01b0316631f00ca7489876040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156128f05781810151838201526020016128d8565b50505050905001935050505060006040518083038186803b15801561291457600080fd5b505afa158015612928573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561295157600080fd5b8101908080516040519392919084600160201b82111561297057600080fd5b90830190602082018581111561298557600080fd5b82518660208202830111600160201b821117156129a157600080fd5b82525081516020918201928201910280838360005b838110156129ce5781810151838201526020016129b6565b5050505090500160405250505090506000816000815181106129ec57fe5b60200260200101519050808511612a04576000612a14565b612a14858263ffffffff61202616565b92506000858211612a26576000612a36565b612a36828763ffffffff61202616565b90508015612a42578094505b50919a92995091975050505050505050565b6000806000612a6389896136a1565b915091506000896001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015612aa257600080fd5b505afa158015612ab6573d6000803e3d6000fd5b505050506040513d6020811015612acc57600080fd5b505190506000612ae18c83858c8c8c8c613ab4565b91505080612b28576040805162461bcd60e51b815260206004820152600f60248201526e11549497d4d5d05417d19052531151608a1b604482015290519081900360640190fd5b9b9a5050505050505050505050565b600080333014612b7d576040805162461bcd60e51b815260206004820152600c60248201526b22a9292fa727aa2fa9a2a62360a11b604482015290519081900360640190fd5b6001600160a01b038316612bce576040805162461bcd60e51b81526020600482015260136024820152724552525f544f5f414444524553535f5a45524f60681b604482015290519081900360640190fd5b6001600160a01b038316331415612c20576040805162461bcd60e51b815260206004820152601160248201527022a9292faa27afa6a9a3afa9a2a72222a960791b604482015290519081900360640190fd5b876001600160a01b0316612c668a6001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e157600080fd5b6001600160a01b031614612cb4576040805162461bcd60e51b815260206004820152601060248201526f22a9292fa4a72b20a624a22fa820a4a960811b604482015290519081900360640190fd5b6000876001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015612cef57600080fd5b505afa158015612d03573d6000803e3d6000fd5b505050506040513d6020811015612d1957600080fd5b505160408051632f310bad60e01b815290519192506000916001600160a01b038b1691632f310bad916004808301926020929190829003018186803b158015612d6157600080fd5b505afa158015612d75573d6000803e3d6000fd5b505050506040513d6020811015612d8b57600080fd5b505186519091506001600160a01b0383169087906001908110612daa57fe5b60200260200101516001600160a01b031614612e0d576040805162461bcd60e51b815260206004820152601b60248201527f4552525f454e445f504154485f4e4f545f554e4445524c59494e470000000000604482015290519081900360640190fd5b600080612e1a8b8b613d30565b91509150600080612e2c8f8e8e612f2b565b90925090508015612e4a57612e47838263ffffffff61202616565b92505b8215612e6a57612e6a6001600160a01b0386168f8563ffffffff61206816565b8115612edc57818b1015612ebc576040805162461bcd60e51b815260206004820152601460248201527308aa4a4bea0a48a9a92aa9abe9eac8aa4be9a82b60631b604482015290519081900360640190fd5b612edc898f84896001600160a01b0316613409909392919063ffffffff16565b8015612efc57612efc6001600160a01b0386168a8363ffffffff61206816565b612f166001600160a01b038e168a8663ffffffff61206816565b50919d919c50909a5050505050505050505050565b604080516002808252606080830184526000938493919290602083019080388339019050509050846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015612f8b57600080fd5b505afa158015612f9f573d6000803e3d6000fd5b505050506040513d6020811015612fb557600080fd5b505181518290600090612fc457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561301d57600080fd5b505afa158015613031573d6000803e3d6000fd5b505050506040513d602081101561304757600080fd5b505181518290600190811061305857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060006130848686613e39565b90506000806060896001600160a01b0316631f00ca7489876040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156130f15781810151838201526020016130d9565b50505050905001935050505060006040518083038186803b15801561311557600080fd5b505afa158015613129573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561315257600080fd5b8101908080516040519392919084600160201b82111561317157600080fd5b90830190602082018581111561318657600080fd5b82518660208202830111600160201b821117156131a257600080fd5b82525081516020918201928201910280838360005b838110156131cf5781810151838201526020016131b7565b5050505090500160405250505090506000816000815181106131ed57fe5b602002602001015190506000858211613207576000613217565b613217828763ffffffff61202616565b9050818611613227576000613237565b613237868363ffffffff61202616565b935080156133f75760608c6001600160a01b031663d06ca61f838a6040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156132a757818101518382015260200161328f565b50505050905001935050505060006040518083038186803b1580156132cb57600080fd5b505afa1580156132df573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561330857600080fd5b8101908080516040519392919084600160201b82111561332757600080fd5b90830190602082018581111561333c57600080fd5b82518660208202830111600160201b8211171561335857600080fd5b82525081516020918201928201910280838360005b8381101561338557818101518382015260200161336d565b5050505090500160405250505090506133f3620186a06133e76133c761012d856001815181106133b157fe5b6020026020010151613ee790919063ffffffff16565b6133db620186a0866001815181106133b157fe5b9063ffffffff6120bf16565b9063ffffffff613f4016565b9550505b50929a91995090975050505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526134639085906135f0565b50505050565b600080613551846001600160a01b031662fe19fa6040518163ffffffff1660e01b815260040160206040518083038186803b1580156134a757600080fd5b505afa1580156134bb573d6000803e3d6000fd5b505050506040513d60208110156134d157600080fd5b5051604080516316b2542760e31b815290516133e7916001600160a01b0389169163b592a13891600480820192602092909190829003018186803b15801561351857600080fd5b505afa15801561352c573d6000803e3d6000fd5b505050506040513d602081101561354257600080fd5b5051869063ffffffff613ee716565b949350505050565b600081848411156135e85760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156135ad578181015183820152602001613595565b50505050905090810190601f1680156135da5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6060613645826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613f829092919063ffffffff16565b8051909150156120ba5780806020019051602081101561366457600080fd5b50516120ba5760405162461bcd60e51b815260040180806020018281038252602a815260200180614347602a913960400191505060405180910390fd5b6000806000806136b2868630611a49565b90925090506136d16001600160a01b038716338763ffffffff61206816565b909590945092505050565b600080806136fb6001600160a01b038a1633308a63ffffffff61340916565b6040805163095ea7b360e01b81526001600160a01b038d8116600483015260001960248301529151918c169163095ea7b3916044808201926020929091908290030181600087803b15801561374f57600080fd5b505af1158015613763573d6000803e3d6000fd5b505050506040513d602081101561377957600080fd5b50506040805163095ea7b360e01b81526001600160a01b038d8116600483015260001960248301529151918b169163095ea7b3916044808201926020929091908290030181600087803b1580156137cf57600080fd5b505af11580156137e3573d6000803e3d6000fd5b505050506040513d60208110156137f957600080fd5b50506040805162e8e33760e81b81526001600160a01b038c811660048301528b81166024830152604482018b9052606482018a9052608482018b905260a4820189905287811660c483015260e482018790529151918d169163e8e3370091610104808201926060929091908290030181600087803b15801561387a57600080fd5b505af115801561388e573d6000803e3d6000fd5b505050506040513d60608110156138a457600080fd5b5080516020820151604090920151909d919c509a5098505050505050505050565b60008060006138d48585613f91565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501206001600160f81b031960688401529a90941b9093166069840152607d8301989098527fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b6000613a078383856001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156139c557600080fd5b505afa1580156139d9573d6000803e3d6000fd5b505050506040513d60208110156139ef57600080fd5b50516001600160a01b0316919063ffffffff61206816565b6000613a138484613469565b9050613a306001600160a01b03851686868463ffffffff61340916565b60408051638349980560e01b815230600482015290516000916001600160a01b0387169163834998059160248082019260609290919082900301818787803b158015613a7b57600080fd5b505af1158015613a8f573d6000803e3d6000fd5b505050506040513d6060811015613aa557600080fd5b50604001519695505050505050565b60606000876001600160a01b031685600081518110613acf57fe5b60200260200101516001600160a01b031614613b2a576040805162461bcd60e51b815260206004820152601560248201527411549497d410551217d3d4151253d397d4d5105495605a1b604482015290519081900360640190fd5b6040805163095ea7b360e01b81526001600160a01b038b8116600483015260001960248301529151918a169163095ea7b3916044808201926020929091908290030181600087803b158015613b7e57600080fd5b505af1158015613b92573d6000803e3d6000fd5b505050506040513d6020811015613ba857600080fd5b50506040516338ed173960e01b815260048101888152602482018890526001600160a01b0386811660648401526084830186905260a060448401908152885160a48501528851918d16936338ed1739938c938c938c938c938c9390929160c401906020878101910280838360005b83811015613c2e578181015183820152602001613c16565b505050509050019650505050505050600060405180830381600087803b158015613c5757600080fd5b505af1158015613c6b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613c9457600080fd5b8101908080516040519392919084600160201b821115613cb357600080fd5b908301906020820185811115613cc857600080fd5b82518660208202830111600160201b82111715613ce457600080fd5b82525081516020918201928201910280838360005b83811015613d11578181015183820152602001613cf9565b5050505090500160405250505091506001905097509795505050505050565b6000806000846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015613d6e57600080fd5b505afa158015613d82573d6000803e3d6000fd5b505050506040513d6020811015613d9857600080fd5b50519050613db66001600160a01b038216868663ffffffff61206816565b60408051633ea6b5f160e21b815230600482015281516001600160a01b0388169263fa9ad7c492602480820193918290030181600087803b158015613dfa57600080fd5b505af1158015613e0e573d6000803e3d6000fd5b505050506040513d6040811015613e2457600080fd5b50805160209091015190969095509350505050565b600080613551846001600160a01b031663b592a1386040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7857600080fd5b505afa158015613e8c573d6000803e3d6000fd5b505050506040513d6020811015613ea257600080fd5b505160408051627f0cfd60e11b815290516133e7916001600160a01b0389169162fe19fa91600480820192602092909190829003018186803b15801561351857600080fd5b600082613ef657506000610e72565b82820282848281613f0357fe5b0414611a425760405162461bcd60e51b81526004018080602001828103825260218152602001806142d66021913960400191505060405180910390fd5b6000611a4283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614068565b606061355184846000856140cd565b600080826001600160a01b0316846001600160a01b03161415613fe55760405162461bcd60e51b81526004018080602001828103825260258152602001806142616025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b031610614005578284614008565b83835b90925090506001600160a01b038216610e99576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b600081836140b75760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156135ad578181015183820152602001613595565b5060008385816140c357fe5b0495945050505050565b60606140d88561423a565b614129576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106141685780518252601f199092019160209182019101614149565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146141ca576040519150601f19603f3d011682016040523d82523d6000602084013e6141cf565b606091505b509150915081156141e35791506135519050565b8051156141f35780518082602001fd5b60405162461bcd60e51b81526020600482018181528651602484015286518793919283926044019190850190808383600083156135ad578181015183820152602001613595565b3b15159056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553666c617368436c6f73654c6f6e674f7074696f6e735468656e5377617028616464726573732c616464726573732c75696e743235362c75696e743235362c616464726573735b5d2c6164647265737329536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77666c6173684d696e7453686f72744f7074696f6e735468656e5377617028616464726573732c616464726573732c75696e743235362c75696e743235362c616464726573735b5d2c61646472657373295361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220051394a9e94f5fc08a5386ae76b01641aaccf3163fe37fec5417c31a702a75bc64736f6c63430006020033000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000c1de48e9577a7cf18594323a73cdcf1ee19962e8
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101165760003560e01c8063904f91dc116100a2578063c45a015511610071578063c45a015514610636578063cd0455241461063e578063dd2c704e14610670578063f887ea401461069c578063fd52c6bb146106a457610116565b8063904f91dc1461046e5780639245d5831461049a57806395f0ae76146104cc578063b17d05701461056557610116565b80631758078b116100e95780631758078b1461026a57806317d7de7c1461028e5780632e16cab31461030b57806360060ccd1461036f57806369bd26cd1461044057610116565b80630398a6131461011b57806307f24906146101615780630d8e6e2c146101c057806310d1e85c146101de575b600080fd5b61014d6004803603606081101561013157600080fd5b506001600160a01b0381351690602081013590604001356106d2565b604080519115158252519081900360200190f35b6101a7600480360360c081101561017757600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610775565b6040805192835260208301919091528051918290030190f35b6101c8610a55565b6040805160ff9092168252519081900360200190f35b610268600480360360808110156101f457600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561022a57600080fd5b82018360208201111561023c57600080fd5b803590602001918460018302840111600160201b8311171561025d57600080fd5b509092509050610a5a565b005b610272610cad565b604080516001600160a01b039092168252519081900360200190f35b610296610cbc565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102d05781810151838201526020016102b8565b50505050905090810190601f1680156102fd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610351600480360360c081101561032157600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610cf3565b60408051938452602084019290925282820152519081900360600190f35b6101a7600480360360c081101561038557600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156103c457600080fd5b8201836020820111156103d657600080fd5b803590602001918460208302840111600160201b831117156103f757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b03169150610d759050565b6102726004803603604081101561045657600080fd5b506001600160a01b0381358116916020013516610dea565b6101a76004803603604081101561048457600080fd5b506001600160a01b038135169060200135610e78565b61014d600480360360608110156104b057600080fd5b506001600160a01b038135169060208101359060400135610ea0565b61014d600480360360c08110156104e257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b81111561051857600080fd5b82018360208201111561052a57600080fd5b803590602001918460208302840111600160201b8311171561054b57600080fd5b91935091506001600160a01b0381351690602001356113f5565b6101a7600480360360c081101561057b57600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156105ba57600080fd5b8201836020820111156105cc57600080fd5b803590602001918460208302840111600160201b831117156105ed57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b031691506114a69050565b61027261151b565b61014d6004803603606081101561065457600080fd5b506001600160a01b03813516906020810135906040013561152a565b6101a76004803603604081101561068657600080fd5b506001600160a01b038135169060200135611937565b610272611953565b610272600480360360408110156106ba57600080fd5b506001600160a01b0381358116916020013516611962565b6000806106e0858533611a49565b91505060006106f0868386610ea0565b905080610736576040805162461bcd60e51b815260206004820152600f60248201526e4552525f464c4153485f434c4f534560881b604482015290519081900360640190fd5b60408051868152905133917f4150f3753a230033657cb8a559a7d2a6a6110a7326d898ac1f07de6ae5c6785b919081900360200190a295945050505050565b600080600260005414156107be576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000886001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561080157600080fd5b505afa158015610815573d6000803e3d6000fd5b505050506040513d602081101561082b57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b038c169163440f5fd2916004808301926020929190829003018186803b15801561087357600080fd5b505afa158015610887573d6000803e3d6000fd5b505050506040513d602081101561089d57600080fd5b5051604080516370a0823160e01b815230600482015290519192506000916001600160a01b038516916370a08231916024808301926020929190829003018186803b1580156108eb57600080fd5b505afa1580156108ff573d6000803e3d6000fd5b505050506040513d602081101561091557600080fd5b5051600354909150600090819061093a906001600160a01b031686868f8f8f8e611b9a565b600154919350915060009061095a906001600160a01b03168f858d611dda565b925050506109f084876001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156109b857600080fd5b505afa1580156109cc573d6000803e3d6000fd5b505050506040513d60208110156109e257600080fd5b50519063ffffffff61202616565b93508315610a1257610a126001600160a01b0387168b8663ffffffff61206816565b610a2c6001600160a01b0386168b8463ffffffff61206816565b610a3c828263ffffffff6120bf16565b60016000559e939d50929b505050505050505050505050565b600390565b6000336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9557600080fd5b505afa158015610aa9573d6000803e3d6000fd5b505050506040513d6020811015610abf57600080fd5b50516040805163d21220a760e01b81529051919250600091339163d21220a7916004808301926020929190829003018186803b158015610afe57600080fd5b505afa158015610b12573d6000803e3d6000fd5b505050506040513d6020811015610b2857600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b0386811660048301528085166024830152915193945091169163e6a4390591604480820192602092909190829003018186803b158015610b8157600080fd5b505afa158015610b95573d6000803e3d6000fd5b505050506040513d6020811015610bab57600080fd5b50516001600160a01b03163314610bbe57fe5b60006060306001600160a01b03168686604051808383808284376040519201945060009350909150508083038183865af19150503d8060008114610c1e576040519150601f19603f3d011682016040523d82523d6000602084013e610c23565b606091505b5091509150818015610c51575080511580610c515750808060200190516020811015610c4e57600080fd5b50515b610ca2576040805162461bcd60e51b815260206004820152601760248201527f4552525f554e495357415056325f43414c4c5f4641494c000000000000000000604482015290519081900360640190fd5b505050505050505050565b6001546001600160a01b031681565b60408051808201909152601d81527f5072696d69746976655631556e6973776170436f6e6e6563746f723033000000602082015290565b600080600060026000541415610d3e576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b6002600055600354610d5e906001600160a01b03168a8a8a8a8a8a612119565b6001600055919b909a509098509650505050505050565b600354600090819081908190610d99906001600160a01b03168b8b8b8b8b8b6122b2565b6040805183815260208101839052815193955091935033927f3725145ca6a6d192a761e3b6dbcf42cdc13147dd45a65fd1b29737ac584b557e9281900390910190a290999098509650505050505050565b600254604080516364e329cb60e11b81526001600160a01b038581166004830152848116602483015291516000938493169163c9c6539691604480830192602092919082900301818787803b158015610e4257600080fd5b505af1158015610e56573d6000803e3d6000fd5b505050506040513d6020811015610e6c57600080fd5b50519150505b92915050565b6003546000908190610e94906001600160a01b0316858561272a565b915091505b9250929050565b600060026000541415610ee8576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f2b57600080fd5b505afa158015610f3f573d6000803e3d6000fd5b505050506040513d6020811015610f5557600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b158015610f9d57600080fd5b505afa158015610fb1573d6000803e3d6000fd5b505050506040513d6020811015610fc757600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b03868116600483015280851660248301529151939450600093919092169163e6a43905916044808301926020929190829003018186803b15801561102457600080fd5b505afa158015611038573d6000803e3d6000fd5b505050506040513d602081101561104e57600080fd5b5051604080516002808252606080830184529394509091602083019080388339019050509050828160008151811061108257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083816001815181106110b057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506000829050600060405180608001604052806050815260200161428660509139805190602001209050606081858c8c8c883360405160240180876001600160a01b03166001600160a01b03168152602001866001600160a01b03166001600160a01b0316815260200185815260200184815260200180602001836001600160a01b03166001600160a01b03168152602001828103825284818151815260200191508051906020019060200280838360005b83811015611199578181015183820152602001611181565b50505050905001975050505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090506000876001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561122357600080fd5b505afa158015611237573d6000803e3d6000fd5b505050506040513d602081101561124d57600080fd5b50516001600160a01b031614611264576000611266565b8a5b90506000886001600160a01b0316856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ad57600080fd5b505afa1580156112c1573d6000803e3d6000fd5b505050506040513d60208110156112d757600080fd5b50516001600160a01b0316146112ed578b6112f0565b60005b9050846001600160a01b031663022c0d9f838330876040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b03166001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561137557818101518382015260200161135d565b50505050905090810190601f1680156113a25780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b1580156113c457600080fd5b505af11580156113d8573d6000803e3d6000fd5b505050506001995050505050505050505060016000559392505050565b60006002600054141561143d576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b600260009081556003546040805160208089028281018201909352888252611494936001600160a01b0316928d928d928d928d918d9182918501908490808284376000920191909152508c92508b9150612a549050565b60016000559998505050505050505050565b6003546000908190819081906114ca906001600160a01b03168b8b8b8b8b8b612b37565b6040805183815260208101839052815193955091935033927ff17aa7749e04732ce7bde5ee88376144e6c5669bd4d8c80358119eada62a1d009281900390910190a290999098509650505050505050565b6002546001600160a01b031681565b600060026000541415611572576040805162461bcd60e51b815260206004820152601f6024820152600080516020614241833981519152604482015290519081900360640190fd5b60026000819055506000846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115b557600080fd5b505afa1580156115c9573d6000803e3d6000fd5b505050506040513d60208110156115df57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b15801561162757600080fd5b505afa15801561163b573d6000803e3d6000fd5b505050506040513d602081101561165157600080fd5b50516002546040805163e6a4390560e01b81526001600160a01b03868116600483015280851660248301529151939450600093919092169163e6a43905916044808301926020929190829003018186803b1580156116ae57600080fd5b505afa1580156116c2573d6000803e3d6000fd5b505050506040513d60208110156116d857600080fd5b5051604080516002808252606080830184529394509091602083019080388339019050509050838160008151811061170c57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160018151811061173a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600082905060006040518060800160405280605081526020016142f760509139805190602001209050606081858c8c8c883360405160240180876001600160a01b03166001600160a01b03168152602001866001600160a01b03166001600160a01b0316815260200185815260200184815260200180602001836001600160a01b03166001600160a01b03168152602001828103825284818151815260200191508051906020019060200280838360005b8381101561182357818101518382015260200161180b565b50505050905001975050505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090506000866001600160a01b0316846001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156118ad57600080fd5b505afa1580156118c1573d6000803e3d6000fd5b505050506040513d60208110156118d757600080fd5b50516001600160a01b0316146118ee5760006118f0565b8a5b90506000876001600160a01b0316856001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156112ad57600080fd5b6003546000908190610e94906001600160a01b03168585612f2b565b6003546001600160a01b031681565b6002546040805163e6a4390560e01b81526001600160a01b038581166004830152848116602483015291516000938493169163e6a43905916044808301926020929190829003018186803b1580156119b957600080fd5b505afa1580156119cd573d6000803e3d6000fd5b505050506040513d60208110156119e357600080fd5b505190506001600160a01b038116611a42576040805162461bcd60e51b815260206004820152601760248201527f4552525f504149525f444f45535f4e4f545f4558495354000000000000000000604482015290519081900360640190fd5b9392505050565b60008060008411611a8c576040805162461bcd60e51b81526020600482015260086024820152674552525f5a45524f60c01b604482015290519081900360640190fd5b611b0e338686886001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015611acb57600080fd5b505afa158015611adf573d6000803e3d6000fd5b505050506040513d6020811015611af557600080fd5b50516001600160a01b031692919063ffffffff61340916565b60408051633ea6b5f160e21b81526001600160a01b03858116600483015282516000938493928a169263fa9ad7c4926024808301939282900301818787803b158015611b5957600080fd5b505af1158015611b6d573d6000803e3d6000fd5b505050506040513d6040811015611b8357600080fd5b508051602090910151909890975095505050505050565b6000806000896001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b158015611bd857600080fd5b505afa158015611bec573d6000803e3d6000fd5b505050506040513d6020811015611c0257600080fd5b50516040805163e6a4390560e01b81526001600160a01b038c811660048301528b811660248301529151919092169163e6a43905916044808301926020929190829003018186803b158015611c5657600080fd5b505afa158015611c6a573d6000803e3d6000fd5b505050506040513d6020811015611c8057600080fd5b50519050611c9f6001600160a01b03821633308a63ffffffff61340916565b6040805163095ea7b360e01b81526001600160a01b038c81166004830152600019602483015291519183169163095ea7b3916044808201926020929091908290030181600087803b158015611cf357600080fd5b505af1158015611d07573d6000803e3d6000fd5b505050506040513d6020811015611d1d57600080fd5b505060408051635d5155ef60e11b81526001600160a01b038b811660048301528a81166024830152604482018a905260648201899052608482018890523060a483015260c4820187905282516000938493928f169263baa2abde9260e4808301939282900301818787803b158015611d9457600080fd5b505af1158015611da8573d6000803e3d6000fd5b505050506040513d6040811015611dbe57600080fd5b508051602090910151909d909c509a5050505050505050505050565b6000806000856001600160a01b031663095ea7b3886000196040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015611e4157600080fd5b505af1158015611e55573d6000803e3d6000fd5b505050506040513d6020811015611e6b57600080fd5b505060408051632f310bad60e01b815290516001600160a01b03881691632f310bad916004808301926020929190829003018186803b158015611ead57600080fd5b505afa158015611ec1573d6000803e3d6000fd5b505050506040513d6020811015611ed757600080fd5b50516040805163095ea7b360e01b81526001600160a01b038a8116600483015260001960248301529151919092169163095ea7b39160448083019260209291908290030181600087803b158015611f2d57600080fd5b505af1158015611f41573d6000803e3d6000fd5b505050506040513d6020811015611f5757600080fd5b5060009050611f668787613469565b9050611f836001600160a01b03881633308463ffffffff61340916565b6040805163b8a6bb3b60e01b81526001600160a01b0389811660048301526024820184905287811660448301529151918a169163b8a6bb3b916064808201926060929091908290030181600087803b158015611fde57600080fd5b505af1158015611ff2573d6000803e3d6000fd5b505050506040513d606081101561200857600080fd5b5080516020820151604090920151909a919950975095505050505050565b6000611a4283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613559565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526120ba9084906135f0565b505050565b600082820183811015611a42576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080600080600080600061212e8d8d6136a1565b91505060008d905060008f90506000826001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60208110156121a057600080fd5b81019080805190602001909291905050509050600084905060008f905060008f905060008f905060008f905061224087896001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561220957600080fd5b505afa15801561221d573d6000803e3d6000fd5b505050506040513d602081101561223357600080fd5b50518888888888886136dc565b919d509b509950888c1461225057fe5b60008b8511612260576000612270565b612270858d63ffffffff61202616565b90508015612292576122926001600160a01b038816338363ffffffff61206816565b509a9d50989b509699505050505050505050509750975097945050505050565b6000803330146122f8576040805162461bcd60e51b815260206004820152600c60248201526b22a9292fa727aa2fa9a2a62360a11b604482015290519081900360640190fd5b6001600160a01b038316612349576040805162461bcd60e51b81526020600482015260136024820152724552525f544f5f414444524553535f5a45524f60681b604482015290519081900360640190fd5b6001600160a01b03831633141561239b576040805162461bcd60e51b815260206004820152601160248201527022a9292faa27afa6a9a3afa9a2a72222a960791b604482015290519081900360640190fd5b876001600160a01b031661243c8a6001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e157600080fd5b505afa1580156123f5573d6000803e3d6000fd5b505050506040513d602081101561240b57600080fd5b50518651879060009061241a57fe5b60200260200101518760018151811061242f57fe5b60200260200101516138c5565b6001600160a01b03161461248a576040805162461bcd60e51b815260206004820152601060248201526f22a9292fa4a72b20a624a22fa820a4a960811b604482015290519081900360640190fd5b6000876001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b1580156124c557600080fd5b505afa1580156124d9573d6000803e3d6000fd5b505050506040513d60208110156124ef57600080fd5b505160408051632f310bad60e01b815290519192506000916001600160a01b038b1691632f310bad916004808301926020929190829003018186803b15801561253757600080fd5b505afa15801561254b573d6000803e3d6000fd5b505050506040513d602081101561256157600080fd5b505186519091506001600160a01b038216908790600190811061258057fe5b60200260200101516001600160a01b0316146125e3576040805162461bcd60e51b815260206004820152601760248201527f4552525f454e445f504154485f4e4f545f52454445454d000000000000000000604482015290519081900360640190fd5b60006125f0868b8b613985565b90506000806126008e8d8d61272a565b9092509050811561261e5761261b838363ffffffff61202616565b92505b821561263e5761263e6001600160a01b0386168e8563ffffffff61206816565b80156126aa57818a111561268f576040805162461bcd60e51b815260206004820152601360248201527211549497d39151d05512559157d4105653d555606a1b604482015290519081900360640190fd5b6126aa6001600160a01b038616898f8463ffffffff61340916565b811561271757898210156126fd576040805162461bcd60e51b815260206004820152601560248201527422a9292fa82922a6a4aaa6afaaa72222a92fa6a4a760591b604482015290519081900360640190fd5b6127176001600160a01b038616898463ffffffff61206816565b50909c909b509950505050505050505050565b604080516002808252606080830184526000938493919290602083019080388339019050509050846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561278a57600080fd5b505afa15801561279e573d6000803e3d6000fd5b505050506040513d60208110156127b457600080fd5b5051815182906000906127c357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561281c57600080fd5b505afa158015612830573d6000803e3d6000fd5b505050506040513d602081101561284657600080fd5b505181518290600190811061285757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060006128838686613469565b90506000806060896001600160a01b0316631f00ca7489876040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156128f05781810151838201526020016128d8565b50505050905001935050505060006040518083038186803b15801561291457600080fd5b505afa158015612928573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561295157600080fd5b8101908080516040519392919084600160201b82111561297057600080fd5b90830190602082018581111561298557600080fd5b82518660208202830111600160201b821117156129a157600080fd5b82525081516020918201928201910280838360005b838110156129ce5781810151838201526020016129b6565b5050505090500160405250505090506000816000815181106129ec57fe5b60200260200101519050808511612a04576000612a14565b612a14858263ffffffff61202616565b92506000858211612a26576000612a36565b612a36828763ffffffff61202616565b90508015612a42578094505b50919a92995091975050505050505050565b6000806000612a6389896136a1565b915091506000896001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015612aa257600080fd5b505afa158015612ab6573d6000803e3d6000fd5b505050506040513d6020811015612acc57600080fd5b505190506000612ae18c83858c8c8c8c613ab4565b91505080612b28576040805162461bcd60e51b815260206004820152600f60248201526e11549497d4d5d05417d19052531151608a1b604482015290519081900360640190fd5b9b9a5050505050505050505050565b600080333014612b7d576040805162461bcd60e51b815260206004820152600c60248201526b22a9292fa727aa2fa9a2a62360a11b604482015290519081900360640190fd5b6001600160a01b038316612bce576040805162461bcd60e51b81526020600482015260136024820152724552525f544f5f414444524553535f5a45524f60681b604482015290519081900360640190fd5b6001600160a01b038316331415612c20576040805162461bcd60e51b815260206004820152601160248201527022a9292faa27afa6a9a3afa9a2a72222a960791b604482015290519081900360640190fd5b876001600160a01b0316612c668a6001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e157600080fd5b6001600160a01b031614612cb4576040805162461bcd60e51b815260206004820152601060248201526f22a9292fa4a72b20a624a22fa820a4a960811b604482015290519081900360640190fd5b6000876001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015612cef57600080fd5b505afa158015612d03573d6000803e3d6000fd5b505050506040513d6020811015612d1957600080fd5b505160408051632f310bad60e01b815290519192506000916001600160a01b038b1691632f310bad916004808301926020929190829003018186803b158015612d6157600080fd5b505afa158015612d75573d6000803e3d6000fd5b505050506040513d6020811015612d8b57600080fd5b505186519091506001600160a01b0383169087906001908110612daa57fe5b60200260200101516001600160a01b031614612e0d576040805162461bcd60e51b815260206004820152601b60248201527f4552525f454e445f504154485f4e4f545f554e4445524c59494e470000000000604482015290519081900360640190fd5b600080612e1a8b8b613d30565b91509150600080612e2c8f8e8e612f2b565b90925090508015612e4a57612e47838263ffffffff61202616565b92505b8215612e6a57612e6a6001600160a01b0386168f8563ffffffff61206816565b8115612edc57818b1015612ebc576040805162461bcd60e51b815260206004820152601460248201527308aa4a4bea0a48a9a92aa9abe9eac8aa4be9a82b60631b604482015290519081900360640190fd5b612edc898f84896001600160a01b0316613409909392919063ffffffff16565b8015612efc57612efc6001600160a01b0386168a8363ffffffff61206816565b612f166001600160a01b038e168a8663ffffffff61206816565b50919d919c50909a5050505050505050505050565b604080516002808252606080830184526000938493919290602083019080388339019050509050846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015612f8b57600080fd5b505afa158015612f9f573d6000803e3d6000fd5b505050506040513d6020811015612fb557600080fd5b505181518290600090612fc457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561301d57600080fd5b505afa158015613031573d6000803e3d6000fd5b505050506040513d602081101561304757600080fd5b505181518290600190811061305857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060006130848686613e39565b90506000806060896001600160a01b0316631f00ca7489876040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156130f15781810151838201526020016130d9565b50505050905001935050505060006040518083038186803b15801561311557600080fd5b505afa158015613129573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561315257600080fd5b8101908080516040519392919084600160201b82111561317157600080fd5b90830190602082018581111561318657600080fd5b82518660208202830111600160201b821117156131a257600080fd5b82525081516020918201928201910280838360005b838110156131cf5781810151838201526020016131b7565b5050505090500160405250505090506000816000815181106131ed57fe5b602002602001015190506000858211613207576000613217565b613217828763ffffffff61202616565b9050818611613227576000613237565b613237868363ffffffff61202616565b935080156133f75760608c6001600160a01b031663d06ca61f838a6040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156132a757818101518382015260200161328f565b50505050905001935050505060006040518083038186803b1580156132cb57600080fd5b505afa1580156132df573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561330857600080fd5b8101908080516040519392919084600160201b82111561332757600080fd5b90830190602082018581111561333c57600080fd5b82518660208202830111600160201b8211171561335857600080fd5b82525081516020918201928201910280838360005b8381101561338557818101518382015260200161336d565b5050505090500160405250505090506133f3620186a06133e76133c761012d856001815181106133b157fe5b6020026020010151613ee790919063ffffffff16565b6133db620186a0866001815181106133b157fe5b9063ffffffff6120bf16565b9063ffffffff613f4016565b9550505b50929a91995090975050505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526134639085906135f0565b50505050565b600080613551846001600160a01b031662fe19fa6040518163ffffffff1660e01b815260040160206040518083038186803b1580156134a757600080fd5b505afa1580156134bb573d6000803e3d6000fd5b505050506040513d60208110156134d157600080fd5b5051604080516316b2542760e31b815290516133e7916001600160a01b0389169163b592a13891600480820192602092909190829003018186803b15801561351857600080fd5b505afa15801561352c573d6000803e3d6000fd5b505050506040513d602081101561354257600080fd5b5051869063ffffffff613ee716565b949350505050565b600081848411156135e85760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156135ad578181015183820152602001613595565b50505050905090810190601f1680156135da5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6060613645826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613f829092919063ffffffff16565b8051909150156120ba5780806020019051602081101561366457600080fd5b50516120ba5760405162461bcd60e51b815260040180806020018281038252602a815260200180614347602a913960400191505060405180910390fd5b6000806000806136b2868630611a49565b90925090506136d16001600160a01b038716338763ffffffff61206816565b909590945092505050565b600080806136fb6001600160a01b038a1633308a63ffffffff61340916565b6040805163095ea7b360e01b81526001600160a01b038d8116600483015260001960248301529151918c169163095ea7b3916044808201926020929091908290030181600087803b15801561374f57600080fd5b505af1158015613763573d6000803e3d6000fd5b505050506040513d602081101561377957600080fd5b50506040805163095ea7b360e01b81526001600160a01b038d8116600483015260001960248301529151918b169163095ea7b3916044808201926020929091908290030181600087803b1580156137cf57600080fd5b505af11580156137e3573d6000803e3d6000fd5b505050506040513d60208110156137f957600080fd5b50506040805162e8e33760e81b81526001600160a01b038c811660048301528b81166024830152604482018b9052606482018a9052608482018b905260a4820189905287811660c483015260e482018790529151918d169163e8e3370091610104808201926060929091908290030181600087803b15801561387a57600080fd5b505af115801561388e573d6000803e3d6000fd5b505050506040513d60608110156138a457600080fd5b5080516020820151604090920151909d919c509a5098505050505050505050565b60008060006138d48585613f91565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501206001600160f81b031960688401529a90941b9093166069840152607d8301989098527fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b6000613a078383856001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156139c557600080fd5b505afa1580156139d9573d6000803e3d6000fd5b505050506040513d60208110156139ef57600080fd5b50516001600160a01b0316919063ffffffff61206816565b6000613a138484613469565b9050613a306001600160a01b03851686868463ffffffff61340916565b60408051638349980560e01b815230600482015290516000916001600160a01b0387169163834998059160248082019260609290919082900301818787803b158015613a7b57600080fd5b505af1158015613a8f573d6000803e3d6000fd5b505050506040513d6060811015613aa557600080fd5b50604001519695505050505050565b60606000876001600160a01b031685600081518110613acf57fe5b60200260200101516001600160a01b031614613b2a576040805162461bcd60e51b815260206004820152601560248201527411549497d410551217d3d4151253d397d4d5105495605a1b604482015290519081900360640190fd5b6040805163095ea7b360e01b81526001600160a01b038b8116600483015260001960248301529151918a169163095ea7b3916044808201926020929091908290030181600087803b158015613b7e57600080fd5b505af1158015613b92573d6000803e3d6000fd5b505050506040513d6020811015613ba857600080fd5b50506040516338ed173960e01b815260048101888152602482018890526001600160a01b0386811660648401526084830186905260a060448401908152885160a48501528851918d16936338ed1739938c938c938c938c938c9390929160c401906020878101910280838360005b83811015613c2e578181015183820152602001613c16565b505050509050019650505050505050600060405180830381600087803b158015613c5757600080fd5b505af1158015613c6b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613c9457600080fd5b8101908080516040519392919084600160201b821115613cb357600080fd5b908301906020820185811115613cc857600080fd5b82518660208202830111600160201b82111715613ce457600080fd5b82525081516020918201928201910280838360005b83811015613d11578181015183820152602001613cf9565b5050505090500160405250505091506001905097509795505050505050565b6000806000846001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015613d6e57600080fd5b505afa158015613d82573d6000803e3d6000fd5b505050506040513d6020811015613d9857600080fd5b50519050613db66001600160a01b038216868663ffffffff61206816565b60408051633ea6b5f160e21b815230600482015281516001600160a01b0388169263fa9ad7c492602480820193918290030181600087803b158015613dfa57600080fd5b505af1158015613e0e573d6000803e3d6000fd5b505050506040513d6040811015613e2457600080fd5b50805160209091015190969095509350505050565b600080613551846001600160a01b031663b592a1386040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7857600080fd5b505afa158015613e8c573d6000803e3d6000fd5b505050506040513d6020811015613ea257600080fd5b505160408051627f0cfd60e11b815290516133e7916001600160a01b0389169162fe19fa91600480820192602092909190829003018186803b15801561351857600080fd5b600082613ef657506000610e72565b82820282848281613f0357fe5b0414611a425760405162461bcd60e51b81526004018080602001828103825260218152602001806142d66021913960400191505060405180910390fd5b6000611a4283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614068565b606061355184846000856140cd565b600080826001600160a01b0316846001600160a01b03161415613fe55760405162461bcd60e51b81526004018080602001828103825260258152602001806142616025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b031610614005578284614008565b83835b90925090506001600160a01b038216610e99576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b600081836140b75760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156135ad578181015183820152602001613595565b5060008385816140c357fe5b0495945050505050565b60606140d88561423a565b614129576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106141685780518252601f199092019160209182019101614149565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146141ca576040519150601f19603f3d011682016040523d82523d6000602084013e6141cf565b606091505b509150915081156141e35791506135519050565b8051156141f35780518082602001fd5b60405162461bcd60e51b81526020600482018181528651602484015286518793919283926044019190850190808383600083156135ad578181015183820152602001613595565b3b15159056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553666c617368436c6f73654c6f6e674f7074696f6e735468656e5377617028616464726573732c616464726573732c75696e743235362c75696e743235362c616464726573735b5d2c6164647265737329536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77666c6173684d696e7453686f72744f7074696f6e735468656e5377617028616464726573732c616464726573732c75696e743235362c75696e743235362c616464726573735b5d2c61646472657373295361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220051394a9e94f5fc08a5386ae76b01641aaccf3163fe37fec5417c31a702a75bc64736f6c63430006020033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000c1de48e9577a7cf18594323a73cdcf1ee19962e8
-----Decoded View---------------
Arg [0] : router_ (address): 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
Arg [1] : factory_ (address): 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac
Arg [2] : trader_ (address): 0xc1de48E9577A7CF18594323A73CDcF1EE19962E8
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
Arg [1] : 000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac
Arg [2] : 000000000000000000000000c1de48e9577a7cf18594323a73cdcf1ee19962e8
Loading...
Loading
Loading...
Loading
OVERVIEW
The proxy contract that handles the trade logic for Primitive options to be traded on SushiSwap.Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 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.