Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 12007259 | 1233 days ago | IN | Create: PrimitiveLiquidity | 0 ETH | 0.21862431 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
14606770 | 828 days ago | 0.10000261 ETH | ||||
14606770 | 828 days ago | 0.10000261 ETH | ||||
14606509 | 828 days ago | 0.10020049 ETH | ||||
14606509 | 828 days ago | 0.10020049 ETH | ||||
14606491 | 828 days ago | 0.15030073 ETH | ||||
14606491 | 828 days ago | 0.15030073 ETH | ||||
14151039 | 899 days ago | 1.00007055 ETH | ||||
14151039 | 899 days ago | 1.00007055 ETH | ||||
13760858 | 960 days ago | 29.02972708 ETH | ||||
13760858 | 960 days ago | 29.02972708 ETH | ||||
13755406 | 960 days ago | 10.03547624 ETH | ||||
13755406 | 960 days ago | 10.03547624 ETH | ||||
13294730 | 1033 days ago | 0.20022025 ETH | ||||
13294730 | 1033 days ago | 0.20022025 ETH | ||||
13294716 | 1033 days ago | 0.20022025 ETH | ||||
13294714 | 1033 days ago | 0.25036754 ETH | ||||
13294714 | 1033 days ago | 0.25036754 ETH | ||||
13294710 | 1033 days ago | 0.50073508 ETH | ||||
13294710 | 1033 days ago | 0.50073508 ETH | ||||
13274783 | 1036 days ago | 0.05499999 ETH | ||||
13274783 | 1036 days ago | 0.05499999 ETH | ||||
13235217 | 1042 days ago | 0.01999999 ETH | ||||
13235217 | 1042 days ago | 0.01999999 ETH | ||||
13221933 | 1044 days ago | 0.09999999 ETH | ||||
13221933 | 1044 days ago | 0.09999999 ETH |
Loading...
Loading
Contract Name:
PrimitiveLiquidity
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 // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Liquidity * @author Primitive * @notice Manage liquidity on Uniswap & Sushiswap Venues. * @dev @primitivefi/[email protected] */ // Open Zeppelin import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // Interfaces import { IPrimitiveLiquidity, IUniswapV2Router02, IUniswapV2Factory, IUniswapV2Pair, IERC20Permit, IOption } from "../interfaces/IPrimitiveLiquidity.sol"; // Primitive import {PrimitiveConnector} from "./PrimitiveConnector.sol"; import {CoreLib, SafeMath} from "../libraries/CoreLib.sol"; interface DaiPermit { function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external; } contract PrimitiveLiquidity is PrimitiveConnector, IPrimitiveLiquidity, 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 event Initialized(address indexed from); // Emitted on deployment. event AddLiquidity(address indexed from, address indexed option, uint256 sum); event RemoveLiquidity(address indexed from, address indexed option, uint256 sum); IUniswapV2Factory private _factory; // The Uniswap V2 factory contract to get pair addresses from. IUniswapV2Router02 private _router; // The Uniswap Router contract used to interact with the protocol. // ===== Constructor ===== constructor( address weth_, address primitiveRouter_, address factory_, address router_ ) public PrimitiveConnector(weth_, primitiveRouter_) { _factory = IUniswapV2Factory(factory_); _router = IUniswapV2Router02(router_); emit Initialized(_msgSender()); } // ===== Liquidity Operations ===== /** * @dev Adds redeemToken liquidity to a redeem<>underlyingToken pair by minting redeemTokens with underlyingTokens. * @notice Pulls underlying tokens from `getCaller()` and pushes UNI-V2 liquidity tokens to the "getCaller()" 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 redeemTokens to the Uniswap V2 Pair. * @param amountBMin The minimum quantity of underlyingTokens expected to provide liquidity with. * @param deadline The timestamp to expire a pending transaction. * @return Returns (amountA, amountB, liquidity) amounts. */ function addShortLiquidityWithUnderlying( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline ) public override nonReentrant onlyRegistered(IOption(optionAddress)) returns ( uint256, uint256, uint256 ) { uint256 amountA; uint256 amountB; uint256 liquidity; address underlying = IOption(optionAddress).getUnderlyingTokenAddress(); // Pulls total = (quantityOptions + amountBMax) of underlyingTokens from `getCaller()` to this contract. { uint256 sum = quantityOptions.add(amountBMax); _transferFromCaller(underlying, sum); } // Pushes underlyingTokens to option contract and mints option + redeem tokens to this contract. IERC20(underlying).safeTransfer(optionAddress, quantityOptions); (, uint256 outputRedeems) = IOption(optionAddress).mintOptions(address(this)); { // scope for adding exact liquidity, avoids stack too deep errors IOption optionToken = IOption(optionAddress); address redeem = optionToken.redeemToken(); AddAmounts memory params; params.amountAMax = outputRedeems; params.amountBMax = amountBMax; params.amountAMin = outputRedeems; params.amountBMin = amountBMin; params.deadline = deadline; // Approves Uniswap V2 Pair pull tokens from this contract. checkApproval(redeem, address(_router)); checkApproval(underlying, address(_router)); // Adds liquidity to Uniswap V2 Pair and returns liquidity shares to the "getCaller()" address. (amountA, amountB, liquidity) = _addLiquidity(redeem, underlying, params); // Check for exact liquidity provided. assert(amountA == outputRedeems); // Return remaining tokens _transferToCaller(underlying); _transferToCaller(redeem); _transferToCaller(address(optionToken)); } { // scope for event, avoids stack too deep errors address a0 = optionAddress; uint256 q0 = quantityOptions; uint256 q1 = amountBMax; emit AddLiquidity(getCaller(), a0, q0.add(q1)); } return (amountA, amountB, liquidity); } /** * @dev Adds redeemToken liquidity to a redeem<>underlyingToken pair by minting shortOptionTokens with underlyingTokens. * Doesn't check for registered optionAddress because the returned function does. * @notice Pulls underlying tokens from `getCaller()` and pushes UNI-V2 liquidity tokens to the "getCaller()" address. * underlyingToken -> redeemToken -> UNI-V2. Uses permit so user does not need to `approve()` our contracts. * @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 deadline The timestamp to expire a pending transaction. * @return Returns (amountA, amountB, liquidity) amounts. */ function addShortLiquidityWithUnderlyingWithPermit( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override returns ( uint256, uint256, uint256 ) { IERC20Permit underlying = IERC20Permit(IOption(optionAddress).getUnderlyingTokenAddress()); uint256 sum = quantityOptions.add(amountBMax); underlying.permit(getCaller(), address(_primitiveRouter), sum, deadline, v, r, s); return addShortLiquidityWithUnderlying( optionAddress, quantityOptions, amountBMax, amountBMin, deadline ); } /** * @dev Doesn't check for registered optionAddress because the returned function does. * @notice Specialized function for `permit` calling on Put options (DAI). */ function addShortLiquidityDAIWithPermit( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override returns ( uint256, uint256, uint256 ) { DaiPermit dai = DaiPermit(IOption(optionAddress).getUnderlyingTokenAddress()); address caller = getCaller(); dai.permit( caller, address(_primitiveRouter), IERC20Permit(address(dai)).nonces(caller), deadline, true, v, r, s ); return addShortLiquidityWithUnderlying( optionAddress, quantityOptions, amountBMax, amountBMin, deadline ); } /** * @dev Adds redeemToken liquidity to a redeem<>underlyingToken pair by minting shortOptionTokens with underlyingTokens. * @notice Pulls underlying tokens from `getCaller()` and pushes UNI-V2 liquidity tokens to the `getCaller()` 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 deadline The timestamp to expire a pending transaction. * @return Returns (amountA, amountB, liquidity) amounts. */ function addShortLiquidityWithETH( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline ) external payable override nonReentrant onlyRegistered(IOption(optionAddress)) returns ( uint256, uint256, uint256 ) { require( msg.value >= quantityOptions.add(amountBMax), "PrimitiveLiquidity: INSUFFICIENT" ); uint256 amountA; uint256 amountB; uint256 liquidity; address underlying = IOption(optionAddress).getUnderlyingTokenAddress(); require(underlying == address(_weth), "PrimitiveLiquidity: NOT_WETH"); _depositETH(); // Wraps `msg.value` to Weth. // Pushes Weth to option contract and mints option + redeem tokens to this contract. IERC20(underlying).safeTransfer(optionAddress, quantityOptions); (, uint256 outputRedeems) = IOption(optionAddress).mintOptions(address(this)); { // scope for adding exact liquidity, avoids stack too deep errors IOption optionToken = IOption(optionAddress); address redeem = optionToken.redeemToken(); AddAmounts memory params; params.amountAMax = outputRedeems; params.amountBMax = amountBMax; params.amountAMin = outputRedeems; params.amountBMin = amountBMin; params.deadline = deadline; // Approves Uniswap V2 Pair pull tokens from this contract. checkApproval(redeem, address(_router)); checkApproval(underlying, address(_router)); // Adds liquidity to Uniswap V2 Pair. (amountA, amountB, liquidity) = _addLiquidity(redeem, underlying, params); assert(amountA == outputRedeems); // Check for exact liquidity provided. // Return remaining tokens and ether. _withdrawETH(); _transferToCaller(redeem); _transferToCaller(address(optionToken)); } { // scope for event, avoids stack too deep errors address a0 = optionAddress; uint256 q0 = quantityOptions; uint256 q1 = amountBMax; emit AddLiquidity(getCaller(), a0, q0.add(q1)); } return (amountA, amountB, liquidity); } struct AddAmounts { uint256 amountAMax; uint256 amountBMax; uint256 amountAMin; uint256 amountBMin; uint256 deadline; } /** * @notice Calls UniswapV2Router02.addLiquidity() function using this contract's tokens. * @param tokenA The first token of the Uniswap Pair to add as liquidity. * @param tokenB The second token of the Uniswap Pair to add as liquidity. * @param params The amounts specified to be added as liquidity. Adds exact short options. * @return Returns (amountTokenA, amountTokenB, liquidity) amounts. */ function _addLiquidity( address tokenA, address tokenB, AddAmounts memory params ) internal returns ( uint256, uint256, uint256 ) { return _router.addLiquidity( tokenA, tokenB, params.amountAMax, params.amountBMax, params.amountAMin, params.amountBMin, getCaller(), params.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 `getCaller()`. * Then closes the longOptionTokens and withdraws underlyingTokens to the `getCaller()` address. * Sends underlyingTokens from the burned UNI-V2 liquidity shares to the `getCaller()` 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 `getCaller()` 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. * @return Returns the sum of the removed underlying tokens. */ function removeShortLiquidityThenCloseOptions( address optionAddress, uint256 liquidity, uint256 amountAMin, uint256 amountBMin ) public override nonReentrant onlyRegistered(IOption(optionAddress)) returns (uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, address underlying, address redeem) = getOptionPair(optionToken); // Gets amounts struct. RemoveAmounts memory params; params.liquidity = liquidity; params.amountAMin = amountAMin; params.amountBMin = amountBMin; // Pulls lp tokens from `getCaller()` and pushes them to the pair in preparation to invoke `burn()`. _transferFromCallerToReceiver(address(pair), liquidity, address(pair)); // Calls `burn` on the `pair`, returning amounts to this contract. (, uint256 underlyingAmount) = _removeLiquidity(pair, redeem, underlying, params); uint256 underlyingProceeds = _closeOptions(optionToken); // Returns amount of underlying tokens released. // Return remaining tokens/ether. _withdrawETH(); // Unwraps Weth and sends ether to `getCaller()`. _transferToCaller(redeem); // Push any remaining redeemTokens from removing liquidity (dust). _transferToCaller(underlying); // Pushes underlying token to `getCaller()`. uint256 sum = underlyingProceeds.add(underlyingAmount); // Total underlyings sent to `getCaller()`. emit RemoveLiquidity(getCaller(), address(optionToken), sum); return sum; } /** * @notice Pulls LP tokens, burns them, removes liquidity, pull option token, burns then, pushes all underlying tokens. * @dev Uses permit to pull LP tokens. * @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 _msgSender() 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 deadline The timestamp to expire a pending transaction and `permit` call. * @return Returns the sum of the removed underlying tokens. */ function removeShortLiquidityThenCloseOptionsWithPermit( address optionAddress, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override returns (uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, , ) = getOptionPair(optionToken); pair.permit(getCaller(), address(_primitiveRouter), liquidity, deadline, v, r, s); return removeShortLiquidityThenCloseOptions( address(optionToken), liquidity, amountAMin, amountBMin ); } struct RemoveAmounts { uint256 liquidity; uint256 amountAMin; uint256 amountBMin; } /** * @notice Calls `UniswapV2Pair.burn(address(this))` to burn LP tokens for pair tokens. * @param pair The UniswapV2Pair contract to burn LP tokens of. * @param tokenA The first token of the pair. * @param tokenB The second token of the pair. * @param params The amounts to specify the amount to remove and minAmounts to withdraw. * @return Returns (amountTokenA, amountTokenB) which is (redeem, underlying) amounts. */ function _removeLiquidity( IUniswapV2Pair pair, address tokenA, address tokenB, RemoveAmounts memory params ) internal returns (uint256, uint256) { (uint256 amount0, uint256 amount1) = pair.burn(address(this)); (address token0, ) = CoreLib.sortTokens(tokenA, tokenB); (uint256 amountA, uint256 amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require(amountA >= params.amountAMin, "PrimitiveLiquidity: INSUFFICIENT_A"); require(amountB >= params.amountBMin, "PrimitiveLiquidity: INSUFFICIENT_B"); return (amountA, amountB); } // ===== View ===== /** * @notice Gets the UniswapV2Router02 contract address. */ function getRouter() public view override returns (IUniswapV2Router02) { return _router; } /** * @notice Gets the UniswapV2Factory contract address. */ function getFactory() public view override returns (IUniswapV2Factory) { return _factory; } /** * @notice Fetchs the Uniswap Pair for an option's redeemToken and underlyingToken params. * @param option The option token to get the corresponding UniswapV2Pair market. * @return The pair address, as well as the tokens of the pair. */ function getOptionPair(IOption option) public view override returns ( IUniswapV2Pair, address, address ) { address redeem = option.redeemToken(); address underlying = option.getUnderlyingTokenAddress(); IUniswapV2Pair pair = IUniswapV2Pair(_factory.getPair(redeem, underlying)); return (pair, underlying, redeem); } }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Connector * @author Primitive * @notice Low-level abstract contract for Primitive Connectors to inherit from. * @dev @primitivefi/[email protected] */ // Open Zeppelin import {Context} from "@openzeppelin/contracts/GSN/Context.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; // Primitive import {CoreLib, IOption} from "../libraries/CoreLib.sol"; import { IPrimitiveConnector, IPrimitiveRouter, IWETH } from "../interfaces/IPrimitiveConnector.sol"; abstract contract PrimitiveConnector is IPrimitiveConnector, Context { using SafeERC20 for IERC20; // Reverts when `transfer` or `transferFrom` erc20 calls don't return proper data IWETH internal _weth; // Canonical WETH9 IPrimitiveRouter internal _primitiveRouter; // The PrimitiveRouter contract which executes calls. mapping(address => mapping(address => bool)) internal _approved; // Stores approvals for future checks. // ===== Constructor ===== constructor(address weth_, address primitiveRouter_) public { _weth = IWETH(weth_); _primitiveRouter = IPrimitiveRouter(primitiveRouter_); checkApproval(weth_, primitiveRouter_); // Approves this contract's weth to be spent by router. } /** * @notice Reverts if the `option` is not registered in the PrimitiveRouter contract. * @dev Any `option` which is deployed from the Primitive Registry can be registered with the Router. * @param option The Primitive Option to check if registered. */ modifier onlyRegistered(IOption option) { require( _primitiveRouter.getRegisteredOption(address(option)), "PrimitiveSwaps: EVIL_OPTION" ); _; } // ===== External ===== /** * @notice Approves the `spender` to pull `token` from this contract. * @dev This contract does not hold funds, infinite approvals cannot be exploited for profit. * @param token The token to approve spending for. * @param spender The address to allow to spend `token`. */ function checkApproval(address token, address spender) public override returns (bool) { if (!_approved[token][spender]) { IERC20(token).safeApprove(spender, uint256(-1)); _approved[token][spender] = true; } return true; } // ===== Internal ===== /** * @notice Deposits `msg.value` into the Weth contract for Weth tokens. * @return Whether or not ether was deposited into Weth. */ function _depositETH() internal returns (bool) { if (msg.value > 0) { _weth.deposit.value(msg.value)(); return true; } return false; } /** * @notice Uses this contract's balance of Weth to withdraw Ether and send it to `getCaller()`. */ function _withdrawETH() internal returns (bool) { uint256 quantity = IERC20(address(_weth)).balanceOf(address(this)); if (quantity > 0) { // Withdraw ethers with weth. _weth.withdraw(quantity); // Send ether. (bool success, ) = getCaller().call.value(quantity)(""); // Revert is call is unsuccessful. require(success, "Connector: ERR_SENDING_ETHER"); return success; } return true; } /** * @notice Calls the Router to pull `token` from the getCaller() and send them to this contract. * @dev This eliminates the need for users to approve the Router and each connector. * @param token The token to pull from `getCaller()` into this contract. * @param quantity The amount of `token` to pull into this contract. * @return Whether or not the `token` was transferred into this contract. */ function _transferFromCaller(address token, uint256 quantity) internal returns (bool) { if (quantity > 0) { _primitiveRouter.transferFromCaller(token, quantity); return true; } return false; } /** * @notice Pushes this contract's balance of `token` to `getCaller()`. * @dev getCaller() is the original `msg.sender` of the Router's `execute` fn. * @param token The token to transfer to `getCaller()`. * @return Whether or not the `token` was transferred to `getCaller()`. */ function _transferToCaller(address token) internal returns (bool) { uint256 quantity = IERC20(token).balanceOf(address(this)); if (quantity > 0) { IERC20(token).safeTransfer(getCaller(), quantity); return true; } return false; } /** * @notice Calls the Router to pull `token` from the getCaller() and send them to this contract. * @dev This eliminates the need for users to approve the Router and each connector. * @param token The token to pull from `getCaller()`. * @param quantity The amount of `token` to pull. * @param receiver The `to` address to send `quantity` of `token` to. * @return Whether or not `token` was transferred to `receiver`. */ function _transferFromCallerToReceiver( address token, uint256 quantity, address receiver ) internal returns (bool) { if (quantity > 0) { _primitiveRouter.transferFromCallerToReceiver(token, quantity, receiver); return true; } return false; } /** * @notice Uses this contract's balance of underlyingTokens to mint optionTokens to this contract. * @param optionToken The Primitive Option to mint. * @return (uint, uint) (longOptions, shortOptions) */ function _mintOptions(IOption optionToken) internal returns (uint256, uint256) { address underlying = optionToken.getUnderlyingTokenAddress(); _transferBalanceToReceiver(underlying, address(optionToken)); // Sends to option contract return optionToken.mintOptions(address(this)); } /** * @notice Uses this contract's balance of underlyingTokens to mint optionTokens to `receiver`. * @param optionToken The Primitive Option to mint. * @param receiver The address that will received the minted long and short optionTokens. * @return (uint, uint) Returns the (long, short) option tokens minted */ function _mintOptionsToReceiver(IOption optionToken, address receiver) internal returns (uint256, uint256) { address underlying = optionToken.getUnderlyingTokenAddress(); _transferBalanceToReceiver(underlying, address(optionToken)); // Sends to option contract return optionToken.mintOptions(receiver); } /** * @notice Pulls underlying tokens from `getCaller()` to option contract, then invokes mintOptions(). * @param optionToken The option token to mint. * @param quantity The amount of option tokens to mint. * @return (uint, uint) Returns the (long, short) option tokens minted */ function _mintOptionsFromCaller(IOption optionToken, uint256 quantity) internal returns (uint256, uint256) { require(quantity > 0, "ERR_ZERO"); _transferFromCallerToReceiver( optionToken.getUnderlyingTokenAddress(), quantity, address(optionToken) ); return optionToken.mintOptions(address(this)); } /** * @notice Multi-step operation to close options. * 1. Transfer balanceOf `redeem` option token to the option contract. * 2. If NOT expired, pull `option` tokens from `getCaller()` and send to option contract. * 3. Invoke `closeOptions()` to burn the options and release underlyings to this contract. * @return The amount of underlyingTokens released to this contract. */ function _closeOptions(IOption optionToken) internal returns (uint256) { address redeem = optionToken.redeemToken(); uint256 short = IERC20(redeem).balanceOf(address(this)); uint256 long = IERC20(address(optionToken)).balanceOf(getCaller()); uint256 proportional = CoreLib.getProportionalShortOptions(optionToken, long); // IF user has more longs than proportional shorts, close the `short` amount. if (proportional > short) { proportional = short; } // If option is expired, transfer the amt of proportional thats larger. if (optionToken.getExpiryTime() >= now) { // Transfers the max proportional amount of short options to option contract. IERC20(redeem).safeTransfer(address(optionToken), proportional); // Pulls the max amount of long options and sends to option contract. _transferFromCallerToReceiver( address(optionToken), CoreLib.getProportionalLongOptions(optionToken, proportional), address(optionToken) ); } else { // If not expired, transfer all redeem in balance. IERC20(redeem).safeTransfer(address(optionToken), short); } uint outputUnderlyings; if(proportional > 0) { (, , outputUnderlyings) = optionToken.closeOptions(address(this)); } return outputUnderlyings; } /** * @notice Multi-step operation to exercise options. * 1. Transfer balanceOf `strike` token to option contract. * 2. Transfer `amount` of options to exercise to option contract. * 3. Invoke `exerciseOptions()` and specify `getCaller()` as the receiver. * @dev If the balanceOf `strike` and `amount` of options are not in correct proportions, call will fail. * @param optionToken The option to exercise. * @param amount The quantity of options to exercise. */ function _exerciseOptions(IOption optionToken, uint256 amount) internal returns (uint256, uint256) { address strike = optionToken.getStrikeTokenAddress(); _transferBalanceToReceiver(strike, address(optionToken)); IERC20(address(optionToken)).safeTransfer(address(optionToken), amount); return optionToken.exerciseOptions(getCaller(), amount, new bytes(0)); } /** * @notice Transfers this contract's balance of Redeem tokens and invokes the redemption function. * @param optionToken The optionToken to redeem, not the redeem token itself. */ function _redeemOptions(IOption optionToken) internal returns (uint256) { address redeem = optionToken.redeemToken(); _transferBalanceToReceiver(redeem, address(optionToken)); return optionToken.redeemStrikeTokens(getCaller()); } /** * @notice Utility function to transfer this contract's balance of `token` to `receiver`. * @param token The token to transfer. * @param receiver The address that receives the token. * @return Returns the quantity of `token` transferred. */ function _transferBalanceToReceiver(address token, address receiver) internal returns (uint256) { uint256 quantity = IERC20(token).balanceOf(address(this)); IERC20(token).safeTransfer(receiver, quantity); return quantity; } // ===== Fallback ===== receive() external payable { assert(_msgSender() == address(_weth)); // only accept ETH via fallback from the WETH contract } // ===== View ===== /** * @notice Returns the Weth contract address. */ function getWeth() public view override returns (IWETH) { return _weth; } /** * @notice Returns the state variable `_CALLER` in the Primitive Router. */ function getCaller() public view override returns (address) { return _primitiveRouter.getCaller(); } /** * @notice Returns the Primitive Router contract address. */ function getPrimitiveRouter() public view override returns (IPrimitiveRouter) { return _primitiveRouter; } /** * @notice Returns whether or not `spender` is approved to spend `token`, from this contract. */ function isApproved(address token, address spender) public view override returns (bool) { return _approved[token][spender]; } }
// 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; 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 // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Swaps Lib * @author Primitive * @notice Library for calculating different proportions of long and short option tokens. * @dev @primitivefi/[email protected] */ import {IOption} from "@primitivefi/contracts/contracts/option/interfaces/ITrader.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; library CoreLib { using SafeMath for uint256; // Reverts on math underflows/overflows /** * @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. * @param optionToken The Option to use to calculate proportional amounts. Each option has different proportions. * @param short The amount of short options used to calculate the proportional amount of long option tokens. * @return The proportional amount of long option tokens based on `short`. */ function getProportionalLongOptions(IOption optionToken, uint256 short) internal view returns (uint256) { return short.mul(optionToken.getBaseValue()).div(optionToken.getQuoteValue()); } /** * @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. * @param optionToken The Option to use to calculate proportional amounts. Each option has different proportions. * @param long The amount of long options used to calculate the proportional amount of short option tokens. * @return The proportional amount of short option tokens based on `long`. */ function getProportionalShortOptions(IOption optionToken, uint256 long) internal view returns (uint256) { return long.mul(optionToken.getQuoteValue()).div(optionToken.getBaseValue()); } // 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"); } }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; import {IPrimitiveRouter} from "../interfaces/IPrimitiveRouter.sol"; import {IWETH} from "../interfaces/IWETH.sol"; interface IPrimitiveConnector { // ===== External ===== function checkApproval(address token, address spender) external returns (bool); // ===== View ===== function getWeth() external view returns (IWETH); function getCaller() external view returns (address); function getPrimitiveRouter() external view returns (IPrimitiveRouter); function isApproved(address token, address spender) external view returns (bool); }
// 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); } } } }
// 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; }
// 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 // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; import { IOption, IERC20 } from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol"; import { IRegistry } from "@primitivefi/contracts/contracts/option/interfaces/IRegistry.sol"; import {IWETH} from "./IWETH.sol"; interface IPrimitiveRouter { // ===== Admin ===== function halt() external; // ===== Registration ===== function setRegisteredOptions(address[] calldata optionAddresses) external returns (bool); function setRegisteredConnectors( address[] calldata connectors, bool[] calldata isValid ) external returns (bool); // ===== Operations ===== function transferFromCaller(address token, uint256 amount) external returns (bool); function transferFromCallerToReceiver( address token, uint256 amount, address receiver ) external returns (bool); // ===== Execution ===== function executeCall(address connector, bytes calldata params) external payable; // ==== View ==== function getWeth() external view returns (IWETH); function getRoute() external view returns (address); function getCaller() external view returns (address); function getRegistry() external view returns (IRegistry); function getRegisteredOption(address option) external view returns (bool); function getRegisteredConnector(address connector) external view returns (bool); function apiVersion() external pure returns (string memory); }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. 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; interface IRegistry { function pauseDeployments() external; function unpauseDeployments() external; function deployOption( address underlyingToken, address strikeToken, uint256 base, uint256 quote, uint256 expiry ) external returns (address); function setOptionFactory(address optionFactory_) external; function setRedeemFactory(address redeemFactory_) external; function optionFactory() external returns (address); function redeemFactory() external returns (address); function verifyToken(address tokenAddress) external; function verifyExpiry(uint256 expiry) external; function unverifyToken(address tokenAddress) external; function unverifyExpiry(uint256 expiry) external; function calculateOptionAddress( address underlyingToken, address strikeToken, uint256 base, uint256 quote, uint256 expiry ) external view returns (address); function getOptionAddress( address underlyingToken, address strikeToken, uint256 base, uint256 quote, uint256 expiry ) external view returns (address); function isVerifiedOption(address optionAddress) external view returns (bool); }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Connector TEST * @author Primitive * @notice Low-level abstract contract for Primitive Connectors to inherit from. * @dev @primitivefi/[email protected] */ import {PrimitiveConnector, IOption} from "../connectors/PrimitiveConnector.sol"; contract ConnectorTest is PrimitiveConnector { event Log(address indexed caller); constructor(address weth_, address primitiveRouter_) public PrimitiveConnector(weth_, primitiveRouter_) {} function depositETH() external payable returns (bool) { emit Log(getCaller()); return _depositETH(); } function withdrawETH() external returns (bool) { emit Log(getCaller()); return _withdrawETH(); } function transferFromCaller(address token, uint256 quantity) external returns (bool) { emit Log(getCaller()); return _transferFromCaller(token, quantity); } function transferToCaller(address token) external returns (bool) { emit Log(getCaller()); return _transferToCaller(token); } function transferFromCallerToReceiver( address token, uint256 quantity, address receiver ) external returns (bool) { emit Log(getCaller()); return _transferFromCallerToReceiver(token, quantity, receiver); } function mintOptions(IOption optionToken, uint256 quantity) external returns (uint256, uint256) { emit Log(getCaller()); _transferFromCaller(optionToken.getUnderlyingTokenAddress(), quantity); return _mintOptions(optionToken); } function mintOptionsToReceiver( IOption optionToken, uint256 quantity, address receiver ) external returns (uint256, uint256) { emit Log(getCaller()); _transferFromCaller(optionToken.getUnderlyingTokenAddress(), quantity); return _mintOptionsToReceiver(optionToken, receiver); } function mintOptionsFromCaller(IOption optionToken, uint256 quantity) external returns (uint256, uint256) { emit Log(getCaller()); return _mintOptionsFromCaller(optionToken, quantity); } function closeOptions(IOption optionToken, uint256 short) external returns (uint256) { emit Log(getCaller()); _transferFromCaller(optionToken.redeemToken(), short); return _closeOptions(optionToken); } function exerciseOptions( IOption optionToken, uint256 amount, uint256 strikeAmount ) external returns (uint256, uint256) { _transferFromCaller(optionToken.getStrikeTokenAddress(), strikeAmount); _transferFromCaller(address(optionToken), amount); emit Log(getCaller()); return _exerciseOptions(optionToken, amount); } function redeemOptions(IOption optionToken, uint256 short) external returns (uint256) { _transferFromCaller(optionToken.redeemToken(), short); emit Log(getCaller()); return _redeemOptions(optionToken); } function transferBalanceToReceiver(address token, address receiver) external returns (uint256) { emit Log(getCaller()); return _transferBalanceToReceiver(token, receiver); } }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Router * @author Primitive * @notice Swap option tokens on Uniswap & Sushiswap venues. * @dev @primitivefi/[email protected] */ // Open Zeppelin import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // Uniswap import { IUniswapV2Callee } from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol"; // Primitive import { IPrimitiveSwaps, IUniswapV2Router02, IUniswapV2Factory, IUniswapV2Pair, IOption, IERC20Permit } from "../interfaces/IPrimitiveSwaps.sol"; import {PrimitiveConnector} from "./PrimitiveConnector.sol"; import {SwapsLib, SafeMath} from "../libraries/SwapsLib.sol"; interface DaiPermit { function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external; } contract PrimitiveSwaps is PrimitiveConnector, IPrimitiveSwaps, 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 event Initialized(address indexed from); // Emitted on deployment. event Buy( address indexed from, address indexed option, uint256 quantity, uint256 premium ); event Sell( address indexed from, address indexed option, uint256 quantity, uint256 payout ); IUniswapV2Factory private _factory; // The Uniswap V2 _factory contract to get pair addresses from IUniswapV2Router02 private _router; // The Uniswap contract used to interact with the protocol modifier onlySelf() { require(_msgSender() == address(this), "PrimitiveSwaps: NOT_SELF"); _; } // ===== Constructor ===== constructor( address weth_, address primitiveRouter_, address factory_, address router_ ) public PrimitiveConnector(weth_, primitiveRouter_) { _factory = IUniswapV2Factory(factory_); _router = IUniswapV2Router02(router_); emit Initialized(_msgSender()); } // ===== Swap Operations ===== /** * @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. * @dev Opens a longOptionToken position by minting long + short tokens, then selling the short tokens. * @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. * @return Whether or not the call succeeded. */ function openFlashLong( IOption optionToken, uint256 amountOptions, uint256 maxPremium ) public override nonReentrant onlyRegistered(optionToken) returns (bool) { // Calls pair.swap(), and executes `flashMintShortOptionsThenSwap` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, address underlying, ) = getOptionPair(optionToken); SwapsLib._flashSwap( pair, // Pair to flash swap from. underlying, // Token to swap to, i.e. receive optimistically. amountOptions, // Amount of underlying to optimistically receive to mint options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes("flashMintShortOptionsThenSwap(address,uint256,uint256)") ) ), 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. ) // End: Function to call in the callback. ); return true; } /** * @notice Executes the same as `openFlashLong`, but calls `permit` to pull underlying tokens. */ function openFlashLongWithPermit( IOption optionToken, uint256 amountOptions, uint256 maxPremium, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public override nonReentrant onlyRegistered(optionToken) returns (bool) { // Calls pair.swap(), and executes `flashMintShortOptionsThenSwap` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, address underlying, ) = getOptionPair(optionToken); IERC20Permit(underlying).permit( getCaller(), address(_primitiveRouter), maxPremium, deadline, v, r, s ); SwapsLib._flashSwap( pair, // Pair to flash swap from. underlying, // Token to swap to, i.e. receive optimistically. amountOptions, // Amount of underlying to optimistically receive to mint options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes("flashMintShortOptionsThenSwap(address,uint256,uint256)") ) ), 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. ) // End: Function to call in the callback. ); return true; } /** * @notice Executes the same as `openFlashLongWithPermit`, but for DAI. */ function openFlashLongWithDAIPermit( IOption optionToken, uint256 amountOptions, uint256 maxPremium, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public override nonReentrant onlyRegistered(optionToken) returns (bool) { // Calls pair.swap(), and executes `flashMintShortOptionsThenSwap` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, address underlying, ) = getOptionPair(optionToken); DaiPermit(underlying).permit( getCaller(), address(_primitiveRouter), IERC20Permit(underlying).nonces(getCaller()), deadline, true, v, r, s ); SwapsLib._flashSwap( pair, // Pair to flash swap from. underlying, // Token to swap to, i.e. receive optimistically. amountOptions, // Amount of underlying to optimistically receive to mint options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes("flashMintShortOptionsThenSwap(address,uint256,uint256)") ) ), 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. ) // End: Function to call in the callback. ); return true; } /** * @notice Uses Ether to pay to purchase the option tokens. * 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. * @dev Opens a longOptionToken position by minting long + short tokens, then selling the short tokens. * @param optionToken The option address. * @param amountOptions The quantity of longOptionTokens to purchase. */ function openFlashLongWithETH(IOption optionToken, uint256 amountOptions) external payable override nonReentrant onlyRegistered(optionToken) returns (bool) { require(msg.value > 0, "PrimitiveSwaps: ZERO"); // Fail early if no Ether was sent. // Calls pair.swap(), and executes `flashMintShortOptionsThenSwap` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, address underlying, ) = getOptionPair(optionToken); SwapsLib._flashSwap( pair, // Pair to flash swap from. underlying, // Token to swap to, i.e. receive optimistically. amountOptions, // Amount of underlying to optimistically receive to mint options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes( "flashMintShortOptionsThenSwapWithETH(address,uint256,uint256)" ) ) ), optionToken, // Option token to mint with flash loaned tokens amountOptions, // Quantity of underlyingTokens from flash loan to use to mint options. msg.value // total price paid (in underlyingTokens) for selling shortOptionTokens. ) // End: Function to call in the callback. ); 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 ) external override nonReentrant onlyRegistered(optionToken) returns (bool) { // Calls pair.swap(), and executes `flashCloseLongOptionsThenSwap` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, , address redeem) = getOptionPair(optionToken); SwapsLib._flashSwap( pair, // Pair to flash swap from. redeem, // Token to swap to, i.e. receive optimistically. amountRedeems, // Amount of underlying to optimistically receive to close options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes("flashCloseLongOptionsThenSwap(address,uint256,uint256)") ) ), 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. ) // End: Function to call in the callback. ); 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 closeFlashLongForETH( IOption optionToken, uint256 amountRedeems, uint256 minPayout ) external override nonReentrant onlyRegistered(optionToken) returns (bool) { // Calls pair.swap(), and executes `flashCloseLongOptionsThenSwapForETH` in the `uniswapV2Callee` callback. (IUniswapV2Pair pair, , address redeem) = getOptionPair(optionToken); SwapsLib._flashSwap( pair, // Pair to flash swap from. redeem, // Token to swap to, i.e. receive optimistically. amountRedeems, // Amount of underlying to optimistically receive to close options with. abi.encodeWithSelector( // Start: Function to call in the callback. bytes4( keccak256( bytes( "flashCloseLongOptionsThenSwapForETH(address,uint256,uint256)" ) ) ), 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. ) // End: Function to call in the callback. ); return true; } // ===== Flash Callback Functions ===== /** * @notice Callback function executed in a UniswapV2Pair.swap() call for `openFlashLong`. * @dev Pays underlying token `premium` for `quantity` of `optionAddress` tokens. * @param optionAddress The address of the Option contract. * @param quantity The quantity of options to mint using borrowed underlyingTokens. * @param maxPremium The maximum quantity of underlyingTokens to pay for the optionTokens. * @return Returns (amount, premium) of options purchased for total premium price. */ function flashMintShortOptionsThenSwap( address optionAddress, uint256 quantity, uint256 maxPremium ) public onlySelf onlyRegistered(IOption(optionAddress)) returns (uint256, uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, address underlying, address redeem) = getOptionPair(optionToken); // Mint option and redeem tokens to this contract. _mintOptions(optionToken); // Get the repayment amounts. (uint256 premium, uint256 redeemPremium) = SwapsLib.repayOpen(_router, optionToken, quantity); // If premium is non-zero and non-negative (most cases), send underlyingTokens to the pair as payment (premium). if (premium > 0) { // Check for users to not pay over their max desired value. require(maxPremium >= premium, "PrimitiveSwaps: MAX_PREMIUM"); // Pull underlyingTokens from the `getCaller()` to pay the remainder of the flash swap. // Push underlying tokens back to the pair as repayment. _transferFromCallerToReceiver(underlying, premium, address(pair)); } // Pay pair in redeem tokens. if (redeemPremium > 0) { IERC20(redeem).safeTransfer(address(pair), redeemPremium); } // Return tokens to `getCaller()`. _transferToCaller(redeem); _transferToCaller(optionAddress); emit Buy(getCaller(), optionAddress, quantity, premium); return (quantity, premium); } /** * @notice Callback function executed in a UniswapV2Pair.swap() call for `openFlashLongWithETH`. * @dev Pays `premium` in ether for `quantity` of `optionAddress` tokens. * @param optionAddress The address of the Option contract. * @param quantity The quantity of options to mint using borrowed underlyingTokens. * @param maxPremium The maximum quantity of underlyingTokens to pay for the optionTokens. * @return Returns (amount, premium) of options purchased for total premium price. */ function flashMintShortOptionsThenSwapWithETH( address optionAddress, uint256 quantity, uint256 maxPremium ) public onlySelf onlyRegistered(IOption(optionAddress)) returns (uint256, uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, address underlying, address redeem) = getOptionPair(optionToken); require(underlying == address(_weth), "PrimitiveSwaps: NOT_WETH"); // Ensure Weth Call. // Mint option and redeem tokens to this contract. _mintOptions(optionToken); // Get the repayment amounts. (uint256 premium, uint256 redeemPremium) = SwapsLib.repayOpen(_router, optionToken, quantity); // If premium is non-zero and non-negative (most cases), send underlyingTokens to the pair as payment (premium). if (premium > 0) { // Check for users to not pay over their max desired value. require(maxPremium >= premium, "PrimitiveSwaps: MAX_PREMIUM"); // Wrap exact Ether amount of `premium`. _weth.deposit.value(premium)(); // Transfer Weth to pair to pay for premium. IERC20(address(_weth)).safeTransfer(address(pair), premium); // Return remaining Ether to caller. _withdrawETH(); } // Pay pair in redeem. if (redeemPremium > 0) { IERC20(redeem).safeTransfer(address(pair), redeemPremium); } // Return tokens to `getCaller()`. _transferToCaller(redeem); _transferToCaller(optionAddress); emit Buy(getCaller(), optionAddress, quantity, premium); return (quantity, premium); } /** * @dev Sends shortOptionTokens to _msgSender(), 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 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. */ function flashCloseLongOptionsThenSwap( address optionAddress, uint256 flashLoanQuantity, uint256 minPayout ) public onlySelf onlyRegistered(IOption(optionAddress)) returns (uint256, uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, address underlying, address redeem) = getOptionPair(optionToken); // Close the options, releasing underlying tokens to this contract. uint256 outputUnderlyings = _closeOptions(optionToken); // Get repay amounts. (uint256 payout, uint256 cost, uint256 outstanding) = SwapsLib.repayClose(_router, optionToken, flashLoanQuantity); if (payout > 0) { cost = outputUnderlyings.sub(payout); } // Pay back the pair in underlyingTokens. if (cost > 0) { IERC20(underlying).safeTransfer(address(pair), cost); } if (outstanding > 0) { // Pull underlyingTokens from the `getCaller()` 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 outstanding > 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 <= payout, "PrimitiveSwaps: NEGATIVE_PAYOUT"); _transferFromCallerToReceiver(underlying, outstanding, address(pair)); } // If payout is non-zero and non-negative, send it to the `getCaller()` address. if (payout > 0) { // Revert if minPayout is greater than the actual payout. require(payout >= minPayout, "PrimitiveSwaps: MIN_PREMIUM"); _transferToCaller(underlying); } emit Sell(getCaller(), optionAddress, flashLoanQuantity, payout); return (payout, cost); } /** * @dev Sends shortOptionTokens to _msgSender(), and pays back the UniswapV2Pair in underlyingTokens. * @notice IMPORTANT: If minPayout is 0, the `getCaller()` address is liable for negative payouts *if* that occurs. * @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. */ function flashCloseLongOptionsThenSwapForETH( address optionAddress, uint256 flashLoanQuantity, uint256 minPayout ) public onlySelf onlyRegistered(IOption(optionAddress)) returns (uint256, uint256) { IOption optionToken = IOption(optionAddress); (IUniswapV2Pair pair, address underlying, address redeem) = getOptionPair(optionToken); require(underlying == address(_weth), "PrimitiveSwaps: NOT_WETH"); // Close the options, releasing underlying tokens to this contract. _closeOptions(optionToken); // Get repay amounts. (uint256 payout, uint256 cost, uint256 outstanding) = SwapsLib.repayClose(_router, optionToken, flashLoanQuantity); // Pay back the pair in underlyingTokens. if (cost > 0) { IERC20(underlying).safeTransfer(address(pair), cost); } if (outstanding > 0) { // Pull underlyingTokens from the `getCaller()` 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 outstanding > 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 <= payout, "PrimitiveSwaps: NEGATIVE_PAYOUT"); _transferFromCallerToReceiver(underlying, outstanding, address(pair)); } // If payout is non-zero and non-negative, send it to the `getCaller()` address. if (payout > 0) { // Revert if minPayout is greater than the actual payout. require(payout >= minPayout, "PrimitiveSwaps: MIN_PREMIUM"); _withdrawETH(); // Unwrap's this contract's balance of Weth and sends Ether to `getCaller()`. } emit Sell(getCaller(), optionAddress, flashLoanQuantity, payout); return (payout, cost); } // ===== Flash Loans ===== /** * @dev The callback function triggered in a UniswapV2Pair.swap() call when the `data` parameter has data. * @param sender The original _msgSender() 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(IPrimitiveSwaps, IUniswapV2Callee) { assert( _msgSender() == _factory.getPair( IUniswapV2Pair(_msgSender()).token0(), IUniswapV2Pair(_msgSender()).token1() ) ); // Ensure that _msgSender() is actually a V2 pair. require(sender == address(this), "PrimitiveSwaps: NOT_SENDER"); // Ensure called by this contract. (bool success, bytes memory returnData) = address(this).call(data); // Execute the callback. (uint256 amountA, uint256 amountB) = abi.decode(returnData, (uint256, uint256)); require( success && (returnData.length == 0 || amountA > 0 || amountB > 0), "PrimitiveSwaps: CALLBACK" ); } // ===== View ===== /** * @notice Gets the UniswapV2Router02 contract address. */ function getRouter() public view override returns (IUniswapV2Router02) { return _router; } /** * @notice Gets the UniswapV2Factory contract address. */ function getFactory() public view override returns (IUniswapV2Factory) { return _factory; } /** * @notice Fetchs the Uniswap Pair for an option's redeemToken and underlyingToken params. * @param option The option token to get the corresponding UniswapV2Pair market. * @return The pair address, as well as the tokens of the pair. */ function getOptionPair(IOption option) public view override returns ( IUniswapV2Pair, address, address ) { address redeem = option.redeemToken(); address underlying = option.getUnderlyingTokenAddress(); IUniswapV2Pair pair = IUniswapV2Pair(_factory.getPair(redeem, underlying)); return (pair, underlying, redeem); } /** * @dev Calculates the effective premium, denominated in underlyingTokens, to buy `quantity` of `optionToken`s. * @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 optionToken The optionToken to get the premium cost of purchasing. * @param quantity The quantity of long option tokens that will be purchased. * @return (uint, uint) Returns the `premium` to buy `quantity` of `optionToken` and the `negativePremium`. */ function getOpenPremium(IOption optionToken, uint256 quantity) public view override returns (uint256, uint256) { return SwapsLib.getOpenPremium(_router, optionToken, quantity); } /** * @dev Calculates the effective premium, denominated in underlyingTokens, to sell `optionToken`s. * @param optionToken The optionToken to get the premium cost of purchasing. * @param quantity The quantity of short option tokens that will be closed. * @return (uint, uint) Returns the `premium` to sell `quantity` of `optionToken` and the `negativePremium`. */ function getClosePremium(IOption optionToken, uint256 quantity) public view override returns (uint256, uint256) { return SwapsLib.getClosePremium(_router, optionToken, quantity); } }
// 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; } }
pragma solidity >=0.5.0; interface IUniswapV2Callee { function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. 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 {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; import {IOption} from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol"; import {IERC20Permit} from "./IERC20Permit.sol"; interface IPrimitiveSwaps { // ==== External Functions ==== function openFlashLong( IOption optionToken, uint256 amountOptions, uint256 maxPremium ) external returns (bool); function openFlashLongWithPermit( IOption optionToken, uint256 amountOptions, uint256 maxPremium, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (bool); function openFlashLongWithDAIPermit( IOption optionToken, uint256 amountOptions, uint256 maxPremium, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (bool); function openFlashLongWithETH(IOption optionToken, uint256 amountOptions) external payable returns (bool); function closeFlashLong( IOption optionToken, uint256 amountRedeems, uint256 minPayout ) external returns (bool); function closeFlashLongForETH( IOption optionToken, uint256 amountRedeems, uint256 minPayout ) external returns (bool); // ===== Callback ===== function uniswapV2Call( address sender, uint256 amount0, uint256 amount1, bytes calldata data ) external; // ==== View ==== function getRouter() external view returns (IUniswapV2Router02); function getFactory() external view returns (IUniswapV2Factory); function getOptionPair(IOption option) external view returns ( IUniswapV2Pair, address, address ); function getOpenPremium(IOption optionToken, uint256 quantity) external view returns (uint256, uint256); function getClosePremium(IOption optionToken, uint256 quantity) external view returns (uint256, uint256); }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Swaps Lib * @author Primitive * @notice Library for Swap Logic for Uniswap AMM. * @dev @primitivefi/[email protected] */ import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; import {CoreLib, IOption, SafeMath} from "./CoreLib.sol"; library SwapsLib { using SafeMath for uint256; // Reverts on math underflows/overflows /** * @notice Passes in `params` to the UniswapV2Pair.swap() function to trigger the callback. * @param pair The Uniswap Pair to call. * @param token The token in the Pair to swap to, and thus optimistically receive. * @param amount The quantity of `token`s to optimistically receive first. * @param params The data to call from this contract, using the `uniswapV2Callee` callback. * @return Whether or not the swap() call suceeded. */ function _flashSwap( IUniswapV2Pair pair, address token, uint256 amount, bytes memory params ) internal returns (bool) { // Receives `amount` of `token` to this contract address. uint256 amount0Out = pair.token0() == token ? amount : 0; uint256 amount1Out = pair.token0() == token ? 0 : amount; // Execute the callback function in params. pair.swap(amount0Out, amount1Out, address(this), params); return true; } /** * @notice Gets the amounts to pay out, pay back, and outstanding cost. * @param router The UniswapV2Router02 to use for calculating `amountsOut`. * @param optionToken The option token to use for fetching its corresponding Uniswap Pair. * @param redeemAmount The quantity of REDEEM tokens, with `quoteValue` units, needed to close the options. */ function repayClose( IUniswapV2Router02 router, IOption optionToken, uint256 redeemAmount ) internal view returns ( uint256, uint256, uint256 ) { // Outstanding is the cost remaining, should be 0 in most cases. // Payout is the `premium` that the original caller receives in underlyingTokens. (uint256 payout, uint256 outstanding) = getClosePremium(router, optionToken, redeemAmount); // In most cases there will be an underlying payout, which is subtracted from the redeemAmount. uint256 cost = CoreLib.getProportionalLongOptions(optionToken, redeemAmount); if (payout > 0) { cost = cost.sub(payout); } return (payout, cost, outstanding); } /** * @notice Returns the swap amounts required to return to repay the flash loan used to open a long position. * @param router The UniswapV2Router02 to use for calculating `amountsOut`. * @param optionToken The option token to use for fetching its corresponding Uniswap Pair. * @param underlyingAmount The quantity of UNDERLYING tokens, with `baseValue` units, needed to open the options. */ function repayOpen( IUniswapV2Router02 router, IOption optionToken, uint256 underlyingAmount ) internal view returns (uint256, uint256) { // Premium is the `underlyingTokens` required to buy the `optionToken`. // ExtraRedeems is the `redeemTokens` that are remaining. // If `premium` is not 0, `extraRedeems` should be 0, else `extraRedeems` is the payout (a negative premium). (uint256 premium, uint256 extraRedeems) = getOpenPremium(router, optionToken, underlyingAmount); uint256 redeemPremium = CoreLib.getProportionalShortOptions(optionToken, underlyingAmount); if (extraRedeems > 0) { redeemPremium = redeemPremium.sub(extraRedeems); } return (premium, redeemPremium); } /** * @dev Calculates the effective premium, denominated in underlyingTokens, to buy `quantity` of `optionToken`s. * @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 ( /* override */ 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 = CoreLib.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; // 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 `optionToken`s. * @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 ( /* override */ 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 = CoreLib.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; // 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); } }
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; }
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 // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity >=0.5.0; interface IERC20Permit { function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); function DOMAIN_SEPARATOR() external view returns (bytes32); }
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 // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. 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 {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; import {IOption} from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol"; import {IERC20Permit} from "./IERC20Permit.sol"; interface IPrimitiveLiquidity { // ==== External ==== function addShortLiquidityWithUnderlying( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline ) external returns ( uint256, uint256, uint256 ); function addShortLiquidityWithETH( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline ) external payable returns ( uint256, uint256, uint256 ); function addShortLiquidityWithUnderlyingWithPermit( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns ( uint256, uint256, uint256 ); function addShortLiquidityDAIWithPermit( address optionAddress, uint256 quantityOptions, uint256 amountBMax, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns ( uint256, uint256, uint256 ); function removeShortLiquidityThenCloseOptions( address optionAddress, uint256 liquidity, uint256 amountAMin, uint256 amountBMin ) external returns (uint256); function removeShortLiquidityThenCloseOptionsWithPermit( address optionAddress, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (uint256); // ==== View ==== function getRouter() external view returns (IUniswapV2Router02); function getFactory() external view returns (IUniswapV2Factory); function getOptionPair(IOption option) external view returns ( IUniswapV2Pair, address, address ); }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title TestERC20 * @author Primitive * @notice An opinionated ERC20 with `permit` to use ONLY for testing. * @dev @primitivefi/[email protected] */ import {IERC20Permit} from "../interfaces/IERC20Permit.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; contract TestERC20 { using SafeMath for uint256; string public name = "Test Token"; string public symbol; uint8 public constant decimals = 18; uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; mapping(address => uint256) public nonces; bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); constructor( string memory name_, string memory symbol_, uint256 initialSupply ) public { name = name_; symbol = symbol_; _mint(msg.sender, initialSupply); uint256 chainId; assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name_)), keccak256(bytes("1")), chainId, address(this) ) ); } /** * @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; } function _mint(address to, uint256 value) internal { totalSupply = totalSupply.add(value); balanceOf[to] = balanceOf[to].add(value); emit Transfer(address(0), to, value); } function _burn(address from, uint256 value) internal { balanceOf[from] = balanceOf[from].sub(value); totalSupply = totalSupply.sub(value); emit Transfer(from, address(0), value); } function _approve( address owner, address spender, uint256 value ) private { allowance[owner][spender] = value; emit Approval(owner, spender, value); } function _transfer( address from, address to, uint256 value ) private { balanceOf[from] = balanceOf[from].sub(value); balanceOf[to] = balanceOf[to].add(value); emit Transfer(from, to, value); } function approve(address spender, uint256 value) external returns (bool) { _approve(msg.sender, spender, value); return true; } function transfer(address to, uint256 value) external returns (bool) { _transfer(msg.sender, to, value); return true; } function transferFrom( address from, address to, uint256 value ) external returns (bool) { if (allowance[from][msg.sender] != uint256(-1)) { allowance[from][msg.sender] = allowance[from][msg.sender].sub(value); } _transfer(from, to, value); return true; } function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(deadline >= block.timestamp, "Primitive: EXPIRED"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline ) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, "Primitive: INVALID_SIGNATURE" ); _approve(owner, spender, value); } }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Router * @author Primitive * @notice Contract to execute Primitive Connector functions. * @dev @primitivefi/[email protected] */ // Open Zeppelin import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IPrimitiveRouter, IRegistry, IOption, IERC20, IWETH } from "./interfaces/IPrimitiveRouter.sol"; /** * @notice Used to execute calls on behalf of the Router contract. * @dev Changes `msg.sender` context so the Router is not `msg.sender`. */ contract Route { function executeCall(address target, bytes calldata params) external payable { (bool success, bytes memory returnData) = target.call.value(msg.value)(params); require(success, "Route: EXECUTION_FAIL"); } } contract PrimitiveRouter is IPrimitiveRouter, Ownable, Pausable, 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. // Constants address private constant _NO_CALLER = address(0x0); // Default state for `_CALLER`. // Events event Initialized(address indexed from); // Emmitted on deployment event Executed(address indexed from, address indexed to, bytes params); event RegisteredOptions(address[] indexed options); event RegisteredConnectors(address[] indexed connectors, bool[] registered); // State variables IRegistry private _registry; // The Primitive Registry which deploys Option clones. IWETH private _weth; // Canonical WETH9 Route private _route; // Intermediary to do connector.call() from. address private _CONNECTOR = _NO_CALLER; // If _EXECUTING, the `connector` of the execute call param. address private _CALLER = _NO_CALLER; // If _EXECUTING, the orginal `_msgSender()` of the execute call. bool private _EXECUTING; // True if the `executeCall` function was called. // Whitelisted mappings mapping(address => bool) private _registeredConnectors; mapping(address => bool) private _registeredOptions; /** * @notice A mutex to use during an `execute` call. * @dev Checks to make sure the `_CONNECTOR` in state is the `msg.sender`. * Checks to make sure a `_CALLER` was set. * Fails if this modifier is triggered by an external call. * Fails if this modifier is triggered by calling a function without going through `executeCall`. */ modifier isExec() { require(_CONNECTOR == _msgSender(), "Router: NOT_CONNECTOR"); require(_CALLER != _NO_CALLER, "Router: NO_CALLER"); require(!_EXECUTING, "Router: IN_EXECUTION"); _EXECUTING = true; _; _EXECUTING = false; } // ===== Constructor ===== constructor(address weth_, address registry_) public { require(address(_weth) == address(0x0), "Router: INITIALIZED"); _route = new Route(); _weth = IWETH(weth_); _registry = IRegistry(registry_); emit Initialized(_msgSender()); } // ===== Pausability ===== /** * @notice Halts use of `executeCall`, and other functions that change state. */ function halt() external override onlyOwner { if (paused()) { _unpause(); } else { _pause(); } } // ===== Registration ===== /** * @notice Checks option against Primitive Registry. If from Registry, registers as true. * NOTE: Purposefully does not have `onlyOwner` modifier. * @dev Sets `optionAddresses` to true in the whitelisted options mapping, if from Registry. * @param optionAddresses The array of option addresses to update. */ function setRegisteredOptions(address[] calldata optionAddresses) external override returns (bool) { uint256 len = optionAddresses.length; for (uint256 i = 0; i < len; i++) { address option = optionAddresses[i]; require(isFromPrimitiveRegistry(IOption(option)), "Router: EVIL_OPTION"); _registeredOptions[option] = true; } emit RegisteredOptions(optionAddresses); return true; } /** * @notice Allows the `owner` to set whitelisted connector contracts. * @dev Sets `connectors` to `isValid` in the whitelisted connectors mapping. * @param connectors The array of option addresses to update. * @param isValid Whether or not the optionAddress is registered. */ function setRegisteredConnectors(address[] memory connectors, bool[] memory isValid) public override onlyOwner returns (bool) { uint256 len = connectors.length; require(len == isValid.length, "Router: LENGTHS"); for (uint256 i = 0; i < len; i++) { address connector = connectors[i]; bool status = isValid[i]; _registeredConnectors[connector] = status; } emit RegisteredConnectors(connectors, isValid); return true; } /** * @notice Checks an option against the Primitive Registry. * @param option The IOption token to check. * @return Whether or not the option was deployed from the Primitive Registry. */ function isFromPrimitiveRegistry(IOption option) internal view returns (bool) { return (address(option) == _registry.getOptionAddress( option.getUnderlyingTokenAddress(), option.getStrikeTokenAddress(), option.getBaseValue(), option.getQuoteValue(), option.getExpiryTime() ) && address(option) != address(0)); } // ===== Operations ===== /** * @notice Transfers ERC20 tokens from the executing `_CALLER` to the executing `_CONNECTOR`. * @param token The address of the ERC20. * @param amount The amount of ERC20 to transfer. * @return Whether or not the transfer succeeded. */ function transferFromCaller(address token, uint256 amount) public override isExec whenNotPaused returns (bool) { IERC20(token).safeTransferFrom( getCaller(), // Account to pull from _msgSender(), // The connector amount ); return true; } /** * @notice Transfers ERC20 tokens from the executing `_CALLER` to an arbitrary address. * @param token The address of the ERC20. * @param amount The amount of ERC20 to transfer. * @return Whether or not the transfer succeeded. */ function transferFromCallerToReceiver( address token, uint256 amount, address receiver ) public override isExec whenNotPaused returns (bool) { IERC20(token).safeTransferFrom( getCaller(), // Account to pull from receiver, amount ); return true; } // ===== Execute ===== /** * @notice Executes a call with `params` to the target `connector` contract from `_route`. * @param connector The Primitive Connector module to call. * @param params The encoded function data to use. */ function executeCall(address connector, bytes calldata params) external payable override whenNotPaused { require(_registeredConnectors[connector], "Router: INVALID_CONNECTOR"); _CALLER = _msgSender(); _CONNECTOR = connector; _route.executeCall.value(msg.value)(connector, params); _CALLER = _NO_CALLER; _CONNECTOR = _NO_CALLER; emit Executed(_msgSender(), connector, params); } // ===== Fallback ===== receive() external payable whenNotPaused { assert(_msgSender() == address(_weth)); // only accept ETH via fallback from the WETH contract } // ===== View ===== /** * @notice Returns the IWETH contract address. */ function getWeth() public view override returns (IWETH) { return _weth; } /** * @notice Returns the Route contract which executes functions on behalf of this contract. */ function getRoute() public view override returns (address) { return address(_route); } /** * @notice Returns the `_CALLER` which is set to `_msgSender()` during an `executeCall` invocation. */ function getCaller() public view override returns (address) { return _CALLER; } /** * @notice Returns the Primitive Registry contract address. */ function getRegistry() public view override returns (IRegistry) { return _registry; } /** * @notice Returns a bool if `option` is registered or not. * @param option The address of the Option to check if registered. */ function getRegisteredOption(address option) external view override returns (bool) { return _registeredOptions[option]; } /** * @notice Returns a bool if `connector` is registered or not. * @param connector The address of the Connector contract to check if registered. */ function getRegisteredConnector(address connector) external view override returns (bool) { return _registeredConnectors[connector]; } /** * @notice Returns the NPM package version and github version of this contract. * @dev For the npm package: @primitivefi/v1-connectors * For the repository: github.com/primitivefinance/primitive-v1-connectors * @return The apiVersion string. */ function apiVersion() public pure override returns (string memory) { return "2.0.0"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../GSN/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor () internal { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!_paused, "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(_paused, "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; import {IOption} from "@primitivefi/contracts/contracts/option/interfaces/IOption.sol"; import {IERC20Permit} from "./IERC20Permit.sol"; interface IPrimitiveCore { // ===== External ===== function safeMintWithETH(IOption optionToken) external payable returns (uint256, uint256); function safeMintWithPermit( IOption optionToken, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (uint256, uint256); function safeExerciseWithETH(IOption optionToken) external payable returns (uint256, uint256); function safeExerciseForETH(IOption optionToken, uint256 exerciseQuantity) external returns (uint256, uint256); function safeRedeemForETH(IOption optionToken, uint256 redeemQuantity) external returns (uint256); function safeCloseForETH(IOption optionToken, uint256 closeQuantity) external returns ( uint256, uint256, uint256 ); }
// SPDX-License-Identifier: MIT // Copyright 2021 Primitive Finance // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. pragma solidity 0.6.2; /** * @title Primitive Core * @author Primitive * @notice A Connector with Ether abstractions for Primitive Option tokens. * @dev @primitivefi/[email protected] */ // Open Zeppelin import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // Primitive import {CoreLib, SafeMath} from "../libraries/CoreLib.sol"; import {IPrimitiveCore, IERC20Permit, IOption} from "../interfaces/IPrimitiveCore.sol"; import {PrimitiveConnector} from "./PrimitiveConnector.sol"; contract PrimitiveCore is PrimitiveConnector, IPrimitiveCore, 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 event Initialized(address indexed from); // Emmitted on deployment event Minted( address indexed from, address indexed option, uint256 longQuantity, uint256 shortQuantity ); event Exercised(address indexed from, address indexed option, uint256 quantity); event Redeemed(address indexed from, address indexed option, uint256 quantity); event Closed(address indexed from, address indexed option, uint256 quantity); // ===== Constructor ===== constructor(address weth_, address primitiveRouter_) public PrimitiveConnector(weth_, primitiveRouter_) { emit Initialized(_msgSender()); } // ===== Weth Abstraction ===== /** * @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. * @return (uint, uint) Returns the (long, short) option tokens minted */ function safeMintWithETH(IOption optionToken) external payable override nonReentrant onlyRegistered(optionToken) returns (uint256, uint256) { require(msg.value > 0, "PrimitiveCore: ERR_ZERO"); address caller = getCaller(); _depositETH(); // Deposits `msg.value` to Weth contract. (uint256 long, uint256 short) = _mintOptionsToReceiver(optionToken, caller); emit Minted(caller, address(optionToken), long, short); return (long, short); } /** * @dev Mints "amount" quantity of options and "quote" (option parameter) quantity of redeem tokens. * @notice This function is for options that have an EIP2612 (permit) enabled token as the underlying asset. * @param optionToken The address of the option token to mint. * @param amount The quantity of options to mint. * @param deadline The timestamp which expires the `permit` call. * @return (uint, uint) Returns the (long, short) option tokens minted */ function safeMintWithPermit( IOption optionToken, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override nonReentrant onlyRegistered(optionToken) returns (uint256, uint256) { // Permit minting using the caller's underlying tokens. IERC20Permit(optionToken.getUnderlyingTokenAddress()).permit( getCaller(), address(_primitiveRouter), amount, deadline, v, r, s ); (uint256 long, uint256 short) = _mintOptionsFromCaller(optionToken, amount); emit Minted(getCaller(), address(optionToken), long, short); return (long, short); } /** * @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. */ function safeExerciseWithETH(IOption optionToken) public payable override nonReentrant onlyRegistered(optionToken) returns (uint256, uint256) { require(msg.value > 0, "PrimitiveCore: ZERO"); _depositETH(); // Deposits `msg.value` to Weth contract. uint256 long = CoreLib.getProportionalLongOptions(optionToken, msg.value); _transferFromCaller(address(optionToken), long); // Pull option tokens. // Pushes option tokens and weth (strike asset), receives underlying tokens. emit Exercised(getCaller(), address(optionToken), long); return _exerciseOptions(optionToken, long); } /** * @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. */ function safeExerciseForETH(IOption optionToken, uint256 exerciseQuantity) public override nonReentrant onlyRegistered(optionToken) returns (uint256, uint256) { address underlying = optionToken.getUnderlyingTokenAddress(); address strike = optionToken.getStrikeTokenAddress(); uint256 strikeQuantity = CoreLib.getProportionalShortOptions(optionToken, exerciseQuantity); // Pull options and strike assets from `getCaller()` and send to option contract. _transferFromCallerToReceiver( address(optionToken), exerciseQuantity, address(optionToken) ); _transferFromCallerToReceiver(strike, strikeQuantity, address(optionToken)); // Release underlying tokens by invoking `exerciseOptions()` (uint256 strikesPaid, uint256 options) = optionToken.exerciseOptions(address(this), exerciseQuantity, new bytes(0)); _withdrawETH(); // Unwraps this contract's balance of Weth and sends to `getCaller()`. emit Exercised(getCaller(), address(optionToken), exerciseQuantity); return (strikesPaid, options); } /** * @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. */ function safeRedeemForETH(IOption optionToken, uint256 redeemQuantity) public override nonReentrant onlyRegistered(optionToken) returns (uint256) { // Require the strike token to be Weth. address redeem = optionToken.redeemToken(); // Pull redeem tokens from `getCaller()` and send to option contract. _transferFromCallerToReceiver(redeem, redeemQuantity, address(optionToken)); uint256 short = optionToken.redeemStrikeTokens(address(this)); _withdrawETH(); // Unwraps this contract's balance of Weth and sends to `getCaller()`. emit Redeemed(getCaller(), address(optionToken), redeemQuantity); return short; } /** * @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. */ function safeCloseForETH(IOption optionToken, uint256 closeQuantity) public override nonReentrant onlyRegistered(optionToken) returns ( uint256, uint256, uint256 ) { address redeem = optionToken.redeemToken(); uint256 short = CoreLib.getProportionalShortOptions(optionToken, closeQuantity); // Pull redeem tokens from `getCaller()` and send to option contract. _transferFromCallerToReceiver(redeem, short, address(optionToken)); // Pull options if not expired, and send to option contract. if (optionToken.getExpiryTime() >= now) { _transferFromCallerToReceiver( address(optionToken), closeQuantity, address(optionToken) ); } // Release underlyingTokens by invoking `closeOptions()` (uint256 inputRedeems, uint256 inputOptions, uint256 outUnderlyings) = optionToken.closeOptions(address(this)); _withdrawETH(); // Unwraps this contract's balance of Weth and sends to `getCaller()`. emit Closed(getCaller(), address(optionToken), closeQuantity); return (inputRedeems, inputOptions, outUnderlyings); } }
{ "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
[{"inputs":[{"internalType":"address","name":"weth_","type":"address"},{"internalType":"address","name":"primitiveRouter_","type":"address"},{"internalType":"address","name":"factory_","type":"address"},{"internalType":"address","name":"router_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"option","type":"address"},{"indexed":false,"internalType":"uint256","name":"sum","type":"uint256"}],"name":"AddLiquidity","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":true,"internalType":"address","name":"option","type":"address"},{"indexed":false,"internalType":"uint256","name":"sum","type":"uint256"}],"name":"RemoveLiquidity","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":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"addShortLiquidityDAIWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"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":"uint256","name":"deadline","type":"uint256"}],"name":"addShortLiquidityWithETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"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":"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":"address","name":"optionAddress","type":"address"},{"internalType":"uint256","name":"quantityOptions","type":"uint256"},{"internalType":"uint256","name":"amountBMax","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"addShortLiquidityWithUnderlyingWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"checkApproval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFactory","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IOption","name":"option","type":"address"}],"name":"getOptionPair","outputs":[{"internalType":"contract IUniswapV2Pair","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrimitiveRouter","outputs":[{"internalType":"contract IPrimitiveRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRouter","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWeth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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"}],"name":"removeShortLiquidityThenCloseOptions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeShortLiquidityThenCloseOptionsWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162002dad38038062002dad833981810160405260808110156200003757600080fd5b50805160208201516040830151606090930151600080546001600160a01b038086166001600160a01b03199283161790925560018054928516929091169190911790559192909183836200009582826001600160e01b036200011f16565b5050600160035550600480546001600160a01b038085166001600160a01b0319928316179092556005805492841692909116919091179055620000e06001600160e01b03620001b016565b6001600160a01b03167f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e660405160405180910390a2505050506200057d565b6001600160a01b03808316600090815260026020908152604080832093851683529290529081205460ff16620001a7576200017682600019856001600160a01b0316620001b460201b62001eb3179092919060201c565b6001600160a01b038084166000908152600260209081526040808320938616835292905220805460ff191660011790555b50600192915050565b3390565b8015806200023e575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156200020e57600080fd5b505afa15801562000223573d6000803e3d6000fd5b505050506040513d60208110156200023a57600080fd5b5051155b6200027b5760405162461bcd60e51b815260040180806020018281038252603681526020018062002d776036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620002d3918591620002d816565b505050565b606062000334826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200039460201b62002424179092919060201c565b805190915015620002d3578080602001905160208110156200035557600080fd5b5051620002d35760405162461bcd60e51b815260040180806020018281038252602a81526020018062002d4d602a913960400191505060405180910390fd5b6060620003ae84846000856001600160e01b03620003b616565b949350505050565b6060620003cc856001600160e01b036200057716565b6200041e576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106200045f5780518252601f1990920191602091820191016200043e565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114620004c3576040519150601f19603f3d011682016040523d82523d6000602084013e620004c8565b606091505b50915091508115620004de579150620003ae9050565b805115620004ef5780518082602001fd5b8360405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156200053b57818101518382015260200162000521565b50505050905090810190601f168015620005695780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b3b151590565b6127c0806200058d6000396000f3fe6080604052600436106100e15760003560e01c80635cda4b411161007f578063ab470f0511610059578063ab470f05146103c9578063b0f479a1146103de578063cfae2273146103f3578063dfaa35cc146104545761010c565b80635cda4b411461032a57806388cc58e414610379578063a389783e1461038e5761010c565b806327fb8380116100bb57806327fb8380146101f75780632da318c11461020c5780632f10e49a1461028b5780635369ee46146102ec5761010c565b8063107c279f1461011157806322ef3f851461014257806324e01330146101a05761010c565b3661010c576000546001600160a01b03166100fa61049f565b6001600160a01b03161461010a57fe5b005b600080fd5b34801561011d57600080fd5b506101266104a4565b604080516001600160a01b039092168252519081900360200190f35b34801561014e57600080fd5b506101756004803603602081101561016557600080fd5b50356001600160a01b03166104b3565b604080516001600160a01b039485168152928416602084015292168183015290519081900360600190f35b3480156101ac57600080fd5b506101e5600480360360808110156101c357600080fd5b506001600160a01b038135169060208101359060408101359060600135610628565b60408051918252519081900360200190f35b34801561020357600080fd5b50610126610844565b34801561021857600080fd5b5061026d600480360361010081101561023057600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e00135610853565b60408051938452602084019290925282820152519081900360600190f35b34801561029757600080fd5b506101e560048036036101008110156102af57600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e00135610a07565b61026d600480360360a081101561030257600080fd5b506001600160a01b038135169060208101359060408101359060608101359060800135610ad9565b34801561033657600080fd5b506103656004803603604081101561034d57600080fd5b506001600160a01b0381358116916020013516610f89565b604080519115158252519081900360200190f35b34801561038557600080fd5b5061012661100f565b34801561039a57600080fd5b50610365600480360360408110156103b157600080fd5b506001600160a01b038135811691602001351661101e565b3480156103d557600080fd5b5061012661104c565b3480156103ea57600080fd5b506101266110c2565b3480156103ff57600080fd5b5061026d600480360361010081101561041757600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e001356110d1565b34801561046057600080fd5b5061026d600480360360a081101561047757600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001356111e0565b335b90565b6000546001600160a01b031690565b600080600080846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104f257600080fd5b505afa158015610506573d6000803e3d6000fd5b505050506040513d602081101561051c57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b15801561056457600080fd5b505afa158015610578573d6000803e3d6000fd5b505050506040513d602081101561058e57600080fd5b5051600480546040805163e6a4390560e01b81526001600160a01b038781169482019490945283851660248201529051939450600093929091169163e6a4390591604480820192602092909190829003018186803b1580156105ef57600080fd5b505afa158015610603573d6000803e3d6000fd5b505050506040513d602081101561061957600080fd5b50519791965091945092505050565b600060026003541415610682576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b0380891660048301529151889392909216916394669bf591602480820192602092909190829003018186803b1580156106d757600080fd5b505afa1580156106eb573d6000803e3d6000fd5b505050506040513d602081101561070157600080fd5b5051610754576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b8560008080610762846104b3565b925092509250610770612650565b898152602081018990526040810188905261078c848b81611541565b50600061079b858486856115e8565b91505060006107a98761173b565b90506107b3611a0a565b506107bd84611baa565b506107c785611baa565b5060006107da828463ffffffff611c5e16565b9050876001600160a01b03166107ee61104c565b6001600160a01b03167fd8ae9b9ba89e637bcb66a69ac91e8f688018e81d6f92c57e02226425c8efbdf6836040518082815260200191505060405180910390a360016003559d9c50505050505050505050505050565b6001546001600160a01b031690565b6000806000808b6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561089257600080fd5b505afa1580156108a6573d6000803e3d6000fd5b505050506040513d60208110156108bc57600080fd5b5051905060006108ca61104c565b60015460408051623f675f60e91b81526001600160a01b038085166004830152915193945081861693638fcbaf0c93869316918591637ecebe0091602480820192602092909190829003018186803b15801561092557600080fd5b505afa158015610939573d6000803e3d6000fd5b505050506040513d602081101561094f57600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152606481018d90526001608482015260ff8c1660a482015260c481018b905260e481018a9052905161010480830192600092919082900301818387803b1580156109cc57600080fd5b505af11580156109e0573d6000803e3d6000fd5b505050506109f18d8d8d8d8d6111e0565b9450945094505050985098509895505050505050565b60008881610a14826104b3565b50509050806001600160a01b031663d505accf610a2f61104c565b600154604080516001600160e01b031960e086901b1681526001600160a01b039384166004820152929091166024830152604482018e9052606482018b905260ff8a16608483015260a4820189905260c482018890525160e480830192600092919082900301818387803b158015610aa657600080fd5b505af1158015610aba573d6000803e3d6000fd5b50505050610aca828b8b8b610628565b9b9a5050505050505050505050565b600080600060026003541415610b36576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b03808c16600483015291518b9392909216916394669bf591602480820192602092909190829003018186803b158015610b8b57600080fd5b505afa158015610b9f573d6000803e3d6000fd5b505050506040513d6020811015610bb557600080fd5b5051610c08576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b610c18888863ffffffff611c5e16565b341015610c6c576040805162461bcd60e51b815260206004820181905260248201527f5072696d69746976654c69717569646974793a20494e53554646494349454e54604482015290519081900360640190fd5b6000806000808c6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015610cab57600080fd5b505afa158015610cbf573d6000803e3d6000fd5b505050506040513d6020811015610cd557600080fd5b50516000549091506001600160a01b03808316911614610d3c576040805162461bcd60e51b815260206004820152601c60248201527f5072696d69746976654c69717569646974793a204e4f545f5745544800000000604482015290519081900360640190fd5b610d44611cb8565b50610d5f6001600160a01b0382168e8e63ffffffff611d2d16565b60008d6001600160a01b031663fa9ad7c4306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506040805180830381600087803b158015610db857600080fd5b505af1158015610dcc573d6000803e3d6000fd5b505050506040513d6040811015610de257600080fd5b81019080805190602001909291908051906020019092919050505091505060008e90506000816001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4057600080fd5b505afa158015610e54573d6000803e3d6000fd5b505050506040513d6020811015610e6a57600080fd5b50519050610e76612671565b838152602081018f905260408101849052606081018e9052608081018d9052600554610eac9083906001600160a01b0316610f89565b50600554610ec49086906001600160a01b0316610f89565b50610ed0828683611d84565b91995097509550838814610ee057fe5b610ee8611a0a565b50610ef282611baa565b50610efc83611baa565b5050505060008e905060008e905060008e9050826001600160a01b0316610f2161104c565b6001600160a01b03167f668256213e6a9a0247adc238fcbf44cc6b98921642fca93479c5dc3873660837610f5b858563ffffffff611c5e16565b60408051918252519081900360200190a35050600160035550939d929c50909a509098505050505050505050565b6001600160a01b03808316600090815260026020908152604080832093851683529290529081205460ff1661100557610fd46001600160a01b0384168360001963ffffffff611eb316565b6001600160a01b038084166000908152600260209081526040808320938616835292905220805460ff191660011790555b5060015b92915050565b6004546001600160a01b031690565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b6001546040805163ab470f0560e01b815290516000926001600160a01b03169163ab470f05916004808301926020929190829003018186803b15801561109157600080fd5b505afa1580156110a5573d6000803e3d6000fd5b505050506040513d60208110156110bb57600080fd5b5051905090565b6005546001600160a01b031690565b6000806000808b6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561111057600080fd5b505afa158015611124573d6000803e3d6000fd5b505050506040513d602081101561113a57600080fd5b5051905060006111508c8c63ffffffff611c5e16565b9050816001600160a01b031663d505accf61116961104c565b600154604080516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201859052606482018d905260ff8c16608483015260a482018b905260c482018a90525160e480830192600092919082900301818387803b1580156109cc57600080fd5b60008060006002600354141561123d576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b03808c16600483015291518b9392909216916394669bf591602480820192602092909190829003018186803b15801561129257600080fd5b505afa1580156112a6573d6000803e3d6000fd5b505050506040513d60208110156112bc57600080fd5b505161130f576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b6000806000808c6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561134e57600080fd5b505afa158015611362573d6000803e3d6000fd5b505050506040513d602081101561137857600080fd5b50519050600061138e8d8d63ffffffff611c5e16565b905061139a8282611fc6565b506113b790506001600160a01b0382168e8e63ffffffff611d2d16565b60008d6001600160a01b031663fa9ad7c4306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506040805180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b505050506040513d604081101561143a57600080fd5b81019080805190602001909291908051906020019092919050505091505060008e90506000816001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561149857600080fd5b505afa1580156114ac573d6000803e3d6000fd5b505050506040513d60208110156114c257600080fd5b505190506114ce612671565b838152602081018f905260408101849052606081018e9052608081018d90526005546115049083906001600160a01b0316610f89565b5060055461151c9086906001600160a01b0316610f89565b50611528828683611d84565b9199509750955083881461153857fe5b610ee885611baa565b600082156115dd576001546040805163a2e49f1760e01b81526001600160a01b0387811660048301526024820187905285811660448301529151919092169163a2e49f179160648083019260209291908290030181600087803b1580156115a757600080fd5b505af11580156115bb573d6000803e3d6000fd5b505050506040513d60208110156115d157600080fd5b50600191506115e19050565b5060005b9392505050565b6040805163226bf2d160e21b815230600482015281516000928392839283926001600160a01b038b16926389afcb44926024808301939282900301818787803b15801561163457600080fd5b505af1158015611648573d6000803e3d6000fd5b505050506040513d604081101561165e57600080fd5b50805160209091015190925090506000611678888861205a565b509050600080826001600160a01b03168a6001600160a01b03161461169e5783856116a1565b84845b9150915087602001518210156116e85760405162461bcd60e51b81526004018080602001828103825260228152602001806127096022913960400191505060405180910390fd5b876040015181101561172b5760405162461bcd60e51b81526004018080602001828103825260228152602001806126a16022913960400191505060405180910390fd5b909a909950975050505050505050565b600080826001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561177757600080fd5b505afa15801561178b573d6000803e3d6000fd5b505050506040513d60208110156117a157600080fd5b5051604080516370a0823160e01b815230600482015290519192506000916001600160a01b038416916370a08231916024808301926020929190829003018186803b1580156117ef57600080fd5b505afa158015611803573d6000803e3d6000fd5b505050506040513d602081101561181957600080fd5b5051905060006001600160a01b0385166370a0823161183661104c565b6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561187c57600080fd5b505afa158015611890573d6000803e3d6000fd5b505050506040513d60208110156118a657600080fd5b5051905060006118b68683612138565b9050828111156118c35750815b42866001600160a01b03166325cb5bc06040518163ffffffff1660e01b815260040160206040518083038186803b1580156118fd57600080fd5b505afa158015611911573d6000803e3d6000fd5b505050506040513d602081101561192757600080fd5b505110611962576119486001600160a01b038516878363ffffffff611d2d16565b61195c86611956888461222a565b88611541565b5061197c565b61197c6001600160a01b038516878563ffffffff611d2d16565b600081156119fe5760408051638349980560e01b815230600482015290516001600160a01b0389169163834998059160248083019260609291908290030181600087803b1580156119cc57600080fd5b505af11580156119e0573d6000803e3d6000fd5b505050506040513d60608110156119f657600080fd5b506040015190505b9450505050505b919050565b60008054604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015611a5557600080fd5b505afa158015611a69573d6000803e3d6000fd5b505050506040513d6020811015611a7f57600080fd5b505190508015611ba2576000805460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921692632e1a7d4d9260248084019382900301818387803b158015611ad157600080fd5b505af1158015611ae5573d6000803e3d6000fd5b505050506000611af361104c565b6040516001600160a01b0391909116908390600081818185875af1925050503d8060008114611b3e576040519150601f19603f3d011682016040523d82523d6000602084013e611b43565b606091505b5050905080611b99576040805162461bcd60e51b815260206004820152601c60248201527f436f6e6e6563746f723a204552525f53454e44494e475f455448455200000000604482015290519081900360640190fd5b91506104a19050565b600191505090565b604080516370a0823160e01b8152306004820152905160009182916001600160a01b038516916370a08231916024808301926020929190829003018186803b158015611bf557600080fd5b505afa158015611c09573d6000803e3d6000fd5b505050506040513d6020811015611c1f57600080fd5b505190508015611c5557611c4b611c3461104c565b6001600160a01b038516908363ffffffff611d2d16565b6001915050611a05565b50600092915050565b6000828201838110156115e1576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60003415611d27576000805460408051630d0e30db60e41b815290516001600160a01b039092169263d0e30db0923492600480820193929182900301818588803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b5050505050600190506104a1565b50600090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611d7f9084906122d8565b505050565b6000806000600560009054906101000a90046001600160a01b03166001600160a01b031663e8e3370087878760000151886020015189604001518a60600151611dcb61104c565b8c608001516040518963ffffffff1660e01b815260040180896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001878152602001868152602001858152602001848152602001836001600160a01b03166001600160a01b0316815260200182815260200198505050505050505050606060405180830381600087803b158015611e6d57600080fd5b505af1158015611e81573d6000803e3d6000fd5b505050506040513d6060811015611e9757600080fd5b5080516020820151604090920151909891975095509350505050565b801580611f39575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015611f0b57600080fd5b505afa158015611f1f573d6000803e3d6000fd5b505050506040513d6020811015611f3557600080fd5b5051155b611f745760405162461bcd60e51b81526004018080602001828103825260368152602001806127556036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611d7f9084906122d8565b60008115611c55576001546040805163a3e3808f60e01b81526001600160a01b038681166004830152602482018690529151919092169163a3e3808f9160448083019260209291908290030181600087803b15801561202457600080fd5b505af1158015612038573d6000803e3d6000fd5b505050506040513d602081101561204e57600080fd5b50600191506110099050565b600080826001600160a01b0316846001600160a01b031614156120ae5760405162461bcd60e51b81526004018080602001828103825260258152602001806126c36025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b0316106120ce5782846120d1565b83835b90925090506001600160a01b038216612131576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b60006115e1836001600160a01b031663b592a1386040518163ffffffff1660e01b815260040160206040518083038186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60208110156121a057600080fd5b505160408051627f0cfd60e11b8152905161221e916001600160a01b0388169162fe19fa91600480820192602092909190829003018186803b1580156121e557600080fd5b505afa1580156121f9573d6000803e3d6000fd5b505050506040513d602081101561220f57600080fd5b5051859063ffffffff61238916565b9063ffffffff6123e216565b60006115e1836001600160a01b031662fe19fa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561226757600080fd5b505afa15801561227b573d6000803e3d6000fd5b505050506040513d602081101561229157600080fd5b5051604080516316b2542760e31b8152905161221e916001600160a01b0388169163b592a13891600480820192602092909190829003018186803b1580156121e557600080fd5b606061232d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124249092919063ffffffff16565b805190915015611d7f5780806020019051602081101561234c57600080fd5b5051611d7f5760405162461bcd60e51b815260040180806020018281038252602a81526020018061272b602a913960400191505060405180910390fd5b60008261239857506000611009565b828202828482816123a557fe5b04146115e15760405162461bcd60e51b81526004018080602001828103825260218152602001806126e86021913960400191505060405180910390fd5b60006115e183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061243b565b606061243384846000856124dd565b949350505050565b600081836124c75760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561248c578181015183820152602001612474565b50505050905090810190601f1680156124b95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816124d357fe5b0495945050505050565b60606124e88561264a565b612539576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106125785780518252601f199092019160209182019101612559565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146125da576040519150601f19603f3d011682016040523d82523d6000602084013e6125df565b606091505b509150915081156125f35791506124339050565b8051156126035780518082602001fd5b60405162461bcd60e51b815260206004820181815286516024840152865187939192839260440191908501908083836000831561248c578181015183820152602001612474565b3b151590565b60405180606001604052806000815260200160008152602001600081525090565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509056fe5072696d69746976654c69717569646974793a20494e53554646494349454e545f42556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775072696d69746976654c69717569646974793a20494e53554646494349454e545f415361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a26469706673582212203a1e694d13fea4f12731209fd94497be9a1cdfa2d4f923402a519dc889f8c2b164736f6c634300060200335361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009264416b621054e16fab8d6423b5a1e354d19fec000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
Deployed Bytecode
0x6080604052600436106100e15760003560e01c80635cda4b411161007f578063ab470f0511610059578063ab470f05146103c9578063b0f479a1146103de578063cfae2273146103f3578063dfaa35cc146104545761010c565b80635cda4b411461032a57806388cc58e414610379578063a389783e1461038e5761010c565b806327fb8380116100bb57806327fb8380146101f75780632da318c11461020c5780632f10e49a1461028b5780635369ee46146102ec5761010c565b8063107c279f1461011157806322ef3f851461014257806324e01330146101a05761010c565b3661010c576000546001600160a01b03166100fa61049f565b6001600160a01b03161461010a57fe5b005b600080fd5b34801561011d57600080fd5b506101266104a4565b604080516001600160a01b039092168252519081900360200190f35b34801561014e57600080fd5b506101756004803603602081101561016557600080fd5b50356001600160a01b03166104b3565b604080516001600160a01b039485168152928416602084015292168183015290519081900360600190f35b3480156101ac57600080fd5b506101e5600480360360808110156101c357600080fd5b506001600160a01b038135169060208101359060408101359060600135610628565b60408051918252519081900360200190f35b34801561020357600080fd5b50610126610844565b34801561021857600080fd5b5061026d600480360361010081101561023057600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e00135610853565b60408051938452602084019290925282820152519081900360600190f35b34801561029757600080fd5b506101e560048036036101008110156102af57600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e00135610a07565b61026d600480360360a081101561030257600080fd5b506001600160a01b038135169060208101359060408101359060608101359060800135610ad9565b34801561033657600080fd5b506103656004803603604081101561034d57600080fd5b506001600160a01b0381358116916020013516610f89565b604080519115158252519081900360200190f35b34801561038557600080fd5b5061012661100f565b34801561039a57600080fd5b50610365600480360360408110156103b157600080fd5b506001600160a01b038135811691602001351661101e565b3480156103d557600080fd5b5061012661104c565b3480156103ea57600080fd5b506101266110c2565b3480156103ff57600080fd5b5061026d600480360361010081101561041757600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060ff60a0820135169060c08101359060e001356110d1565b34801561046057600080fd5b5061026d600480360360a081101561047757600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001356111e0565b335b90565b6000546001600160a01b031690565b600080600080846001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104f257600080fd5b505afa158015610506573d6000803e3d6000fd5b505050506040513d602081101561051c57600080fd5b505160408051632207afe960e11b815290519192506000916001600160a01b0388169163440f5fd2916004808301926020929190829003018186803b15801561056457600080fd5b505afa158015610578573d6000803e3d6000fd5b505050506040513d602081101561058e57600080fd5b5051600480546040805163e6a4390560e01b81526001600160a01b038781169482019490945283851660248201529051939450600093929091169163e6a4390591604480820192602092909190829003018186803b1580156105ef57600080fd5b505afa158015610603573d6000803e3d6000fd5b505050506040513d602081101561061957600080fd5b50519791965091945092505050565b600060026003541415610682576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b0380891660048301529151889392909216916394669bf591602480820192602092909190829003018186803b1580156106d757600080fd5b505afa1580156106eb573d6000803e3d6000fd5b505050506040513d602081101561070157600080fd5b5051610754576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b8560008080610762846104b3565b925092509250610770612650565b898152602081018990526040810188905261078c848b81611541565b50600061079b858486856115e8565b91505060006107a98761173b565b90506107b3611a0a565b506107bd84611baa565b506107c785611baa565b5060006107da828463ffffffff611c5e16565b9050876001600160a01b03166107ee61104c565b6001600160a01b03167fd8ae9b9ba89e637bcb66a69ac91e8f688018e81d6f92c57e02226425c8efbdf6836040518082815260200191505060405180910390a360016003559d9c50505050505050505050505050565b6001546001600160a01b031690565b6000806000808b6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561089257600080fd5b505afa1580156108a6573d6000803e3d6000fd5b505050506040513d60208110156108bc57600080fd5b5051905060006108ca61104c565b60015460408051623f675f60e91b81526001600160a01b038085166004830152915193945081861693638fcbaf0c93869316918591637ecebe0091602480820192602092909190829003018186803b15801561092557600080fd5b505afa158015610939573d6000803e3d6000fd5b505050506040513d602081101561094f57600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152606481018d90526001608482015260ff8c1660a482015260c481018b905260e481018a9052905161010480830192600092919082900301818387803b1580156109cc57600080fd5b505af11580156109e0573d6000803e3d6000fd5b505050506109f18d8d8d8d8d6111e0565b9450945094505050985098509895505050505050565b60008881610a14826104b3565b50509050806001600160a01b031663d505accf610a2f61104c565b600154604080516001600160e01b031960e086901b1681526001600160a01b039384166004820152929091166024830152604482018e9052606482018b905260ff8a16608483015260a4820189905260c482018890525160e480830192600092919082900301818387803b158015610aa657600080fd5b505af1158015610aba573d6000803e3d6000fd5b50505050610aca828b8b8b610628565b9b9a5050505050505050505050565b600080600060026003541415610b36576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b03808c16600483015291518b9392909216916394669bf591602480820192602092909190829003018186803b158015610b8b57600080fd5b505afa158015610b9f573d6000803e3d6000fd5b505050506040513d6020811015610bb557600080fd5b5051610c08576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b610c18888863ffffffff611c5e16565b341015610c6c576040805162461bcd60e51b815260206004820181905260248201527f5072696d69746976654c69717569646974793a20494e53554646494349454e54604482015290519081900360640190fd5b6000806000808c6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b158015610cab57600080fd5b505afa158015610cbf573d6000803e3d6000fd5b505050506040513d6020811015610cd557600080fd5b50516000549091506001600160a01b03808316911614610d3c576040805162461bcd60e51b815260206004820152601c60248201527f5072696d69746976654c69717569646974793a204e4f545f5745544800000000604482015290519081900360640190fd5b610d44611cb8565b50610d5f6001600160a01b0382168e8e63ffffffff611d2d16565b60008d6001600160a01b031663fa9ad7c4306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506040805180830381600087803b158015610db857600080fd5b505af1158015610dcc573d6000803e3d6000fd5b505050506040513d6040811015610de257600080fd5b81019080805190602001909291908051906020019092919050505091505060008e90506000816001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4057600080fd5b505afa158015610e54573d6000803e3d6000fd5b505050506040513d6020811015610e6a57600080fd5b50519050610e76612671565b838152602081018f905260408101849052606081018e9052608081018d9052600554610eac9083906001600160a01b0316610f89565b50600554610ec49086906001600160a01b0316610f89565b50610ed0828683611d84565b91995097509550838814610ee057fe5b610ee8611a0a565b50610ef282611baa565b50610efc83611baa565b5050505060008e905060008e905060008e9050826001600160a01b0316610f2161104c565b6001600160a01b03167f668256213e6a9a0247adc238fcbf44cc6b98921642fca93479c5dc3873660837610f5b858563ffffffff611c5e16565b60408051918252519081900360200190a35050600160035550939d929c50909a509098505050505050505050565b6001600160a01b03808316600090815260026020908152604080832093851683529290529081205460ff1661100557610fd46001600160a01b0384168360001963ffffffff611eb316565b6001600160a01b038084166000908152600260209081526040808320938616835292905220805460ff191660011790555b5060015b92915050565b6004546001600160a01b031690565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b6001546040805163ab470f0560e01b815290516000926001600160a01b03169163ab470f05916004808301926020929190829003018186803b15801561109157600080fd5b505afa1580156110a5573d6000803e3d6000fd5b505050506040513d60208110156110bb57600080fd5b5051905090565b6005546001600160a01b031690565b6000806000808b6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561111057600080fd5b505afa158015611124573d6000803e3d6000fd5b505050506040513d602081101561113a57600080fd5b5051905060006111508c8c63ffffffff611c5e16565b9050816001600160a01b031663d505accf61116961104c565b600154604080516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201859052606482018d905260ff8c16608483015260a482018b905260c482018a90525160e480830192600092919082900301818387803b1580156109cc57600080fd5b60008060006002600354141561123d576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600355600154604080516394669bf560e01b81526001600160a01b03808c16600483015291518b9392909216916394669bf591602480820192602092909190829003018186803b15801561129257600080fd5b505afa1580156112a6573d6000803e3d6000fd5b505050506040513d60208110156112bc57600080fd5b505161130f576040805162461bcd60e51b815260206004820152601b60248201527f5072696d697469766553776170733a204556494c5f4f5054494f4e0000000000604482015290519081900360640190fd5b6000806000808c6001600160a01b031663440f5fd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561134e57600080fd5b505afa158015611362573d6000803e3d6000fd5b505050506040513d602081101561137857600080fd5b50519050600061138e8d8d63ffffffff611c5e16565b905061139a8282611fc6565b506113b790506001600160a01b0382168e8e63ffffffff611d2d16565b60008d6001600160a01b031663fa9ad7c4306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b031681526020019150506040805180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b505050506040513d604081101561143a57600080fd5b81019080805190602001909291908051906020019092919050505091505060008e90506000816001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561149857600080fd5b505afa1580156114ac573d6000803e3d6000fd5b505050506040513d60208110156114c257600080fd5b505190506114ce612671565b838152602081018f905260408101849052606081018e9052608081018d90526005546115049083906001600160a01b0316610f89565b5060055461151c9086906001600160a01b0316610f89565b50611528828683611d84565b9199509750955083881461153857fe5b610ee885611baa565b600082156115dd576001546040805163a2e49f1760e01b81526001600160a01b0387811660048301526024820187905285811660448301529151919092169163a2e49f179160648083019260209291908290030181600087803b1580156115a757600080fd5b505af11580156115bb573d6000803e3d6000fd5b505050506040513d60208110156115d157600080fd5b50600191506115e19050565b5060005b9392505050565b6040805163226bf2d160e21b815230600482015281516000928392839283926001600160a01b038b16926389afcb44926024808301939282900301818787803b15801561163457600080fd5b505af1158015611648573d6000803e3d6000fd5b505050506040513d604081101561165e57600080fd5b50805160209091015190925090506000611678888861205a565b509050600080826001600160a01b03168a6001600160a01b03161461169e5783856116a1565b84845b9150915087602001518210156116e85760405162461bcd60e51b81526004018080602001828103825260228152602001806127096022913960400191505060405180910390fd5b876040015181101561172b5760405162461bcd60e51b81526004018080602001828103825260228152602001806126a16022913960400191505060405180910390fd5b909a909950975050505050505050565b600080826001600160a01b0316632f310bad6040518163ffffffff1660e01b815260040160206040518083038186803b15801561177757600080fd5b505afa15801561178b573d6000803e3d6000fd5b505050506040513d60208110156117a157600080fd5b5051604080516370a0823160e01b815230600482015290519192506000916001600160a01b038416916370a08231916024808301926020929190829003018186803b1580156117ef57600080fd5b505afa158015611803573d6000803e3d6000fd5b505050506040513d602081101561181957600080fd5b5051905060006001600160a01b0385166370a0823161183661104c565b6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561187c57600080fd5b505afa158015611890573d6000803e3d6000fd5b505050506040513d60208110156118a657600080fd5b5051905060006118b68683612138565b9050828111156118c35750815b42866001600160a01b03166325cb5bc06040518163ffffffff1660e01b815260040160206040518083038186803b1580156118fd57600080fd5b505afa158015611911573d6000803e3d6000fd5b505050506040513d602081101561192757600080fd5b505110611962576119486001600160a01b038516878363ffffffff611d2d16565b61195c86611956888461222a565b88611541565b5061197c565b61197c6001600160a01b038516878563ffffffff611d2d16565b600081156119fe5760408051638349980560e01b815230600482015290516001600160a01b0389169163834998059160248083019260609291908290030181600087803b1580156119cc57600080fd5b505af11580156119e0573d6000803e3d6000fd5b505050506040513d60608110156119f657600080fd5b506040015190505b9450505050505b919050565b60008054604080516370a0823160e01b8152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015611a5557600080fd5b505afa158015611a69573d6000803e3d6000fd5b505050506040513d6020811015611a7f57600080fd5b505190508015611ba2576000805460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921692632e1a7d4d9260248084019382900301818387803b158015611ad157600080fd5b505af1158015611ae5573d6000803e3d6000fd5b505050506000611af361104c565b6040516001600160a01b0391909116908390600081818185875af1925050503d8060008114611b3e576040519150601f19603f3d011682016040523d82523d6000602084013e611b43565b606091505b5050905080611b99576040805162461bcd60e51b815260206004820152601c60248201527f436f6e6e6563746f723a204552525f53454e44494e475f455448455200000000604482015290519081900360640190fd5b91506104a19050565b600191505090565b604080516370a0823160e01b8152306004820152905160009182916001600160a01b038516916370a08231916024808301926020929190829003018186803b158015611bf557600080fd5b505afa158015611c09573d6000803e3d6000fd5b505050506040513d6020811015611c1f57600080fd5b505190508015611c5557611c4b611c3461104c565b6001600160a01b038516908363ffffffff611d2d16565b6001915050611a05565b50600092915050565b6000828201838110156115e1576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60003415611d27576000805460408051630d0e30db60e41b815290516001600160a01b039092169263d0e30db0923492600480820193929182900301818588803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b5050505050600190506104a1565b50600090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611d7f9084906122d8565b505050565b6000806000600560009054906101000a90046001600160a01b03166001600160a01b031663e8e3370087878760000151886020015189604001518a60600151611dcb61104c565b8c608001516040518963ffffffff1660e01b815260040180896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001878152602001868152602001858152602001848152602001836001600160a01b03166001600160a01b0316815260200182815260200198505050505050505050606060405180830381600087803b158015611e6d57600080fd5b505af1158015611e81573d6000803e3d6000fd5b505050506040513d6060811015611e9757600080fd5b5080516020820151604090920151909891975095509350505050565b801580611f39575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015611f0b57600080fd5b505afa158015611f1f573d6000803e3d6000fd5b505050506040513d6020811015611f3557600080fd5b5051155b611f745760405162461bcd60e51b81526004018080602001828103825260368152602001806127556036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611d7f9084906122d8565b60008115611c55576001546040805163a3e3808f60e01b81526001600160a01b038681166004830152602482018690529151919092169163a3e3808f9160448083019260209291908290030181600087803b15801561202457600080fd5b505af1158015612038573d6000803e3d6000fd5b505050506040513d602081101561204e57600080fd5b50600191506110099050565b600080826001600160a01b0316846001600160a01b031614156120ae5760405162461bcd60e51b81526004018080602001828103825260258152602001806126c36025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b0316106120ce5782846120d1565b83835b90925090506001600160a01b038216612131576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b60006115e1836001600160a01b031663b592a1386040518163ffffffff1660e01b815260040160206040518083038186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60208110156121a057600080fd5b505160408051627f0cfd60e11b8152905161221e916001600160a01b0388169162fe19fa91600480820192602092909190829003018186803b1580156121e557600080fd5b505afa1580156121f9573d6000803e3d6000fd5b505050506040513d602081101561220f57600080fd5b5051859063ffffffff61238916565b9063ffffffff6123e216565b60006115e1836001600160a01b031662fe19fa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561226757600080fd5b505afa15801561227b573d6000803e3d6000fd5b505050506040513d602081101561229157600080fd5b5051604080516316b2542760e31b8152905161221e916001600160a01b0388169163b592a13891600480820192602092909190829003018186803b1580156121e557600080fd5b606061232d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124249092919063ffffffff16565b805190915015611d7f5780806020019051602081101561234c57600080fd5b5051611d7f5760405162461bcd60e51b815260040180806020018281038252602a81526020018061272b602a913960400191505060405180910390fd5b60008261239857506000611009565b828202828482816123a557fe5b04146115e15760405162461bcd60e51b81526004018080602001828103825260218152602001806126e86021913960400191505060405180910390fd5b60006115e183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061243b565b606061243384846000856124dd565b949350505050565b600081836124c75760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561248c578181015183820152602001612474565b50505050905090810190601f1680156124b95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816124d357fe5b0495945050505050565b60606124e88561264a565b612539576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106125785780518252601f199092019160209182019101612559565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146125da576040519150601f19603f3d011682016040523d82523d6000602084013e6125df565b606091505b509150915081156125f35791506124339050565b8051156126035780518082602001fd5b60405162461bcd60e51b815260206004820181815286516024840152865187939192839260440191908501908083836000831561248c578181015183820152602001612474565b3b151590565b60405180606001604052806000815260200160008152602001600081525090565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509056fe5072696d69746976654c69717569646974793a20494e53554646494349454e545f42556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775072696d69746976654c69717569646974793a20494e53554646494349454e545f415361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a26469706673582212203a1e694d13fea4f12731209fd94497be9a1cdfa2d4f923402a519dc889f8c2b164736f6c63430006020033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009264416b621054e16fab8d6423b5a1e354d19fec000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
-----Decoded View---------------
Arg [0] : weth_ (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : primitiveRouter_ (address): 0x9264416B621054e16fAB8d6423b5a1e354D19fEc
Arg [2] : factory_ (address): 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac
Arg [3] : router_ (address): 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 0000000000000000000000009264416b621054e16fab8d6423b5a1e354d19fec
Arg [2] : 000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac
Arg [3] : 000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ 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.