Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 18 from a total of 18 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Deploy Yield Tok... | 18936263 | 420 days ago | IN | 0 ETH | 0.04670657 | ||||
Deploy Yield Tok... | 18823271 | 436 days ago | IN | 0 ETH | 0.0871424 | ||||
Deploy Yield Tok... | 18674538 | 457 days ago | IN | 0 ETH | 0.0708347 | ||||
Deploy Yield Tok... | 18674533 | 457 days ago | IN | 0 ETH | 0.07567113 | ||||
Deploy Yield Tok... | 18445974 | 489 days ago | IN | 0 ETH | 0.02383986 | ||||
Deploy Yield Tok... | 18376718 | 498 days ago | IN | 0 ETH | 0.01315968 | ||||
Deploy Yield Tok... | 18376434 | 498 days ago | IN | 0 ETH | 0.01347421 | ||||
Deploy Yield Tok... | 18376386 | 498 days ago | IN | 0 ETH | 0.01379546 | ||||
Deploy Yield Tok... | 18233365 | 518 days ago | IN | 0 ETH | 0.02166358 | ||||
Deploy Yield Tok... | 17494421 | 622 days ago | IN | 0 ETH | 0.04429672 | ||||
Deploy Yield Tok... | 17093502 | 679 days ago | IN | 0 ETH | 0.07692979 | ||||
Deploy Yield Tok... | 15522634 | 899 days ago | IN | 0 ETH | 0.04643283 | ||||
Deploy Yield Tok... | 15393164 | 920 days ago | IN | 0 ETH | 0.04243984 | ||||
Deploy Yield Tok... | 15318433 | 932 days ago | IN | 0 ETH | 0.0327907 | ||||
Deploy Yield Tok... | 15060407 | 972 days ago | IN | 0 ETH | 0.04379897 | ||||
Deploy Yield Tok... | 14963363 | 989 days ago | IN | 0 ETH | 0.05254123 | ||||
Deploy Yield Tok... | 14916927 | 997 days ago | IN | 0 ETH | 0.09399558 | ||||
Transfer Ownersh... | 14916848 | 997 days ago | IN | 0 ETH | 0.00186083 |
Latest 25 internal transactions (View All)
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Factory
Compiler Version
v0.8.13+commit.abaa5c0e
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2022-06-06 */ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.13; // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol // Simplified by BoringCrypto contract BoringOwnableData { address public owner; address public pendingOwner; } contract BoringOwnable is BoringOwnableData { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /// @notice `owner` defaults to msg.sender on construction. constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner. /// Can only be invoked by the current `owner`. /// @param newOwner Address of the new owner. /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`. /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise. function transferOwnership( address newOwner, bool direct, bool renounce ) public onlyOwner { if (direct) { // Checks require(newOwner != address(0) || renounce, "Ownable: zero address"); // Effects emit OwnershipTransferred(owner, newOwner); owner = newOwner; pendingOwner = address(0); } else { // Effects pendingOwner = newOwner; } } /// @notice Needs to be called by `pendingOwner` to claim ownership. function claimOwnership() public { address _pendingOwner = pendingOwner; // Checks require(msg.sender == _pendingOwner, "Ownable: caller != pending owner"); // Effects emit OwnershipTransferred(owner, _pendingOwner); owner = _pendingOwner; pendingOwner = address(0); } /// @notice Only allows the `owner` to execute the function. modifier onlyOwner() { require(msg.sender == owner, "Ownable: caller is not the owner"); _; } } /// @notice Library for converting between addresses and bytes32 values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/Bytes32AddressLib.sol) library Bytes32AddressLib { function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { return address(uint160(uint256(bytesValue))); } function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { return bytes32(bytes20(addressValue)); } } /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { event Debug(bool one, bool two, uint256 retsize); /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } } /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { // Start off with z at 1. z := 1 // Used below to help find a nearby power of 2. let y := x // Find the lowest power of 2 that is at least sqrt(x). if iszero(lt(y, 0x100000000000000000000000000000000)) { y := shr(128, y) // Like dividing by 2 ** 128. z := shl(64, z) // Like multiplying by 2 ** 64. } if iszero(lt(y, 0x10000000000000000)) { y := shr(64, y) // Like dividing by 2 ** 64. z := shl(32, z) // Like multiplying by 2 ** 32. } if iszero(lt(y, 0x100000000)) { y := shr(32, y) // Like dividing by 2 ** 32. z := shl(16, z) // Like multiplying by 2 ** 16. } if iszero(lt(y, 0x10000)) { y := shr(16, y) // Like dividing by 2 ** 16. z := shl(8, z) // Like multiplying by 2 ** 8. } if iszero(lt(y, 0x100)) { y := shr(8, y) // Like dividing by 2 ** 8. z := shl(4, z) // Like multiplying by 2 ** 4. } if iszero(lt(y, 0x10)) { y := shr(4, y) // Like dividing by 2 ** 4. z := shl(2, z) // Like multiplying by 2 ** 2. } if iszero(lt(y, 0x8)) { // Equivalent to 2 ** z. z := shl(1, z) } // Shifting right by 1 is like dividing by 2. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // Compute a rounded down version of z. let zRoundDown := div(x, z) // If zRoundDown is smaller, use it. if lt(zRoundDown, z) { z := zRoundDown } } } } /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} } /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } } abstract contract IxPYT is ERC4626 { function sweep(address receiver) external virtual returns (uint256 shares); } /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits library FullMath { /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv function mulDiv( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2**256 + prod0 uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { require(denominator > 0); assembly { result := div(prod0, denominator) } return result; } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod uint256 remainder; assembly { remainder := mulmod(a, b, denominator) } // Subtract 256 bit number from 512 bit number assembly { prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator // Compute largest power of two divisor of denominator. // Always >= 1. uint256 twos = (0 - denominator) & denominator; // Divide denominator by power of two assembly { denominator := div(denominator, twos) } // Divide [prod1 prod0] by the factors of two assembly { prod0 := div(prod0, twos) } // Shift in bits from prod1 into prod0. For this we need // to flip `twos` such that it is 2**256 / twos. // If twos is zero, then it becomes one assembly { twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // Invert denominator mod 2**256 // Now that denominator is an odd number, it has an inverse // modulo 2**256 such that denominator * inv = 1 mod 2**256. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, denominator * inv = 1 mod 2**4 uint256 inv = (3 * denominator) ^ 2; // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv *= 2 - denominator * inv; // inverse mod 2**8 inv *= 2 - denominator * inv; // inverse mod 2**16 inv *= 2 - denominator * inv; // inverse mod 2**32 inv *= 2 - denominator * inv; // inverse mod 2**64 inv *= 2 - denominator * inv; // inverse mod 2**128 inv *= 2 - denominator * inv; // inverse mod 2**256 // Because the division is now exact we can divide by multiplying // with the modular inverse of denominator. This will give us the // correct result modulo 2**256. Since the precoditions guarantee // that the outcome is less than 2**256, this is the final result. // We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inv; return result; } } /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result function mulDivRoundingUp( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } } } } /// @title Multicall /// @notice Enables calling multiple methods in a single call to the contract abstract contract Multicall { function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { (bool success, bytes memory result) = address(this).delegatecall( data[i] ); if (!success) { // Next 5 lines from https://ethereum.stackexchange.com/a/83577 if (result.length < 68) revert(); assembly { result := add(result, 0x04) } revert(abi.decode(result, (string))); } results[i] = result; } } } /// @title Self Permit /// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route /// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function /// that requires an approval in a single transaction. abstract contract SelfPermit { function selfPermit( ERC20 token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public payable { token.permit(msg.sender, address(this), value, deadline, v, r, s); } function selfPermitIfNecessary( ERC20 token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable { if (token.allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s); } } /// @title BaseERC20 /// @author zefram.eth /// @notice The base ERC20 contract used by NegativeYieldToken and PerpetualYieldToken /// @dev Uses the same number of decimals as the vault's underlying token contract BaseERC20 is ERC20 { /// ----------------------------------------------------------------------- /// Errors /// ----------------------------------------------------------------------- error Error_NotGate(); /// ----------------------------------------------------------------------- /// Immutable parameters /// ----------------------------------------------------------------------- Gate public immutable gate; address public immutable vault; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor( string memory name_, string memory symbol_, Gate gate_, address vault_ ) ERC20(name_, symbol_, gate_.getUnderlyingOfVault(vault_).decimals()) { gate = gate_; vault = vault_; } /// ----------------------------------------------------------------------- /// Gate-callable functions /// ----------------------------------------------------------------------- function gateMint(address to, uint256 amount) external virtual { if (msg.sender != address(gate)) { revert Error_NotGate(); } _mint(to, amount); } function gateBurn(address from, uint256 amount) external virtual { if (msg.sender != address(gate)) { revert Error_NotGate(); } _burn(from, amount); } } /// @title NegativeYieldToken /// @author zefram.eth /// @notice The ERC20 contract representing negative yield tokens contract NegativeYieldToken is BaseERC20 { /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(Gate gate_, address vault_) BaseERC20( gate_.negativeYieldTokenName(vault_), gate_.negativeYieldTokenSymbol(vault_), gate_, vault_ ) {} } /// @title PerpetualYieldToken /// @author zefram.eth /// @notice The ERC20 contract representing perpetual yield tokens contract PerpetualYieldToken is BaseERC20 { /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(Gate gate_, address vault_) BaseERC20( gate_.perpetualYieldTokenName(vault_), gate_.perpetualYieldTokenSymbol(vault_), gate_, vault_ ) {} /// ----------------------------------------------------------------------- /// ERC20 overrides /// ----------------------------------------------------------------------- function transfer(address to, uint256 amount) public virtual override returns (bool) { // load balances to save gas uint256 fromBalance = balanceOf[msg.sender]; uint256 toBalance = balanceOf[to]; // call transfer hook gate.beforePerpetualYieldTokenTransfer( msg.sender, to, amount, fromBalance, toBalance ); // do transfer // skip during self transfers since toBalance is cached // which leads to free minting, a critical issue if (msg.sender != to) { balanceOf[msg.sender] = fromBalance - amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] = toBalance + amount; } } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { // load balances to save gas uint256 fromBalance = balanceOf[from]; uint256 toBalance = balanceOf[to]; // call transfer hook gate.beforePerpetualYieldTokenTransfer( from, to, amount, fromBalance, toBalance ); // update allowance uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; // do transfer // skip during self transfers since toBalance is cached // which leads to free minting, a critical issue if (from != to) { balanceOf[from] = fromBalance - amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] = toBalance + amount; } } emit Transfer(from, to, amount); return true; } } /// @title Gate /// @author zefram.eth /// @notice Gate is the main contract users interact with to mint/burn NegativeYieldToken /// and PerpetualYieldToken, as well as claim the yield earned by PYTs. /// @dev Gate is an abstract contract that should be inherited from in order to support /// a specific vault protocol (e.g. YearnGate supports YearnVault). Each Gate handles /// all vaults & associated NYTs/PYTs of a specific vault protocol. /// /// Vaults are yield-generating contracts used by Gate. Gate makes several assumptions about /// a vault: /// 1) A vault has a single associated underlying token that is immutable. /// 2) A vault gives depositors yield denominated in the underlying token. /// 3) A vault depositor owns shares in the vault, which represents their deposit. /// 4) Vaults have a notion of "price per share", which is the amount of underlying tokens /// each vault share can be redeemed for. /// 5) If vault shares are represented using an ERC20 token, then the ERC20 token contract must be /// the vault contract itself. abstract contract Gate is ReentrancyGuard, Multicall, SelfPermit, BoringOwnable { /// ----------------------------------------------------------------------- /// Library usage /// ----------------------------------------------------------------------- using SafeTransferLib for ERC20; using SafeTransferLib for ERC4626; /// ----------------------------------------------------------------------- /// Errors /// ----------------------------------------------------------------------- error Error_InvalidInput(); error Error_VaultSharesNotERC20(); error Error_TokenPairNotDeployed(); error Error_EmergencyExitNotActivated(); error Error_SenderNotPerpetualYieldToken(); error Error_EmergencyExitAlreadyActivated(); /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- event EnterWithUnderlying( address sender, address indexed nytRecipient, address indexed pytRecipient, address indexed vault, IxPYT xPYT, uint256 underlyingAmount ); event EnterWithVaultShares( address sender, address indexed nytRecipient, address indexed pytRecipient, address indexed vault, IxPYT xPYT, uint256 vaultSharesAmount ); event ExitToUnderlying( address indexed sender, address indexed recipient, address indexed vault, IxPYT xPYT, uint256 underlyingAmount ); event ExitToVaultShares( address indexed sender, address indexed recipient, address indexed vault, IxPYT xPYT, uint256 vaultSharesAmount ); event ClaimYieldInUnderlying( address indexed sender, address indexed recipient, address indexed vault, uint256 underlyingAmount ); event ClaimYieldInVaultShares( address indexed sender, address indexed recipient, address indexed vault, uint256 vaultSharesAmount ); event ClaimYieldAndEnter( address sender, address indexed nytRecipient, address indexed pytRecipient, address indexed vault, IxPYT xPYT, uint256 amount ); /// ----------------------------------------------------------------------- /// Structs /// ----------------------------------------------------------------------- /// @param activated True if emergency exit has been activated, false if not /// @param pytPriceInUnderlying The amount of underlying assets each PYT can redeem for. /// Should be a value in the range [0, PRECISION] struct EmergencyExitStatus { bool activated; uint96 pytPriceInUnderlying; } /// ----------------------------------------------------------------------- /// Constants /// ----------------------------------------------------------------------- /// @notice The decimals of precision used by yieldPerTokenStored and pricePerVaultShareStored uint256 internal constant PRECISION_DECIMALS = 27; /// @notice The precision used by yieldPerTokenStored and pricePerVaultShareStored uint256 internal constant PRECISION = 10**PRECISION_DECIMALS; /// ----------------------------------------------------------------------- /// Immutable parameters /// ----------------------------------------------------------------------- Factory public immutable factory; /// ----------------------------------------------------------------------- /// Storage variables /// ----------------------------------------------------------------------- /// @notice The amount of underlying tokens each vault share is worth, at the time of the last update. /// Uses PRECISION. /// @dev vault => value mapping(address => uint256) public pricePerVaultShareStored; /// @notice The amount of yield each PYT has accrued, at the time of the last update. /// Scaled by PRECISION. /// @dev vault => value mapping(address => uint256) public yieldPerTokenStored; /// @notice The amount of yield each PYT has accrued, at the time when a user has last interacted /// with the gate/PYT. Shifted by 1, so e.g. 3 represents 2, 10 represents 9. /// @dev vault => user => value /// The value is shifted to use 0 for representing uninitialized users. mapping(address => mapping(address => uint256)) public userYieldPerTokenStored; /// @notice The amount of yield a user has accrued, at the time when they last interacted /// with the gate/PYT (without calling claimYieldInUnderlying()). /// Shifted by 1, so e.g. 3 represents 2, 10 represents 9. /// @dev vault => user => value mapping(address => mapping(address => uint256)) public userAccruedYield; /// @notice Stores info relevant to emergency exits of a vault. /// @dev vault => value mapping(address => EmergencyExitStatus) public emergencyExitStatusOfVault; /// ----------------------------------------------------------------------- /// Initialization /// ----------------------------------------------------------------------- constructor(Factory factory_) { factory = factory_; } /// ----------------------------------------------------------------------- /// User actions /// ----------------------------------------------------------------------- /// @notice Converts underlying tokens into NegativeYieldToken and PerpetualYieldToken. /// The amount of NYT and PYT minted will be equal to the underlying token amount. /// @dev The underlying tokens will be immediately deposited into the specified vault. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// deploy them before proceeding, which will increase the gas cost significantly. /// @param nytRecipient The recipient of the minted NYT /// @param pytRecipient The recipient of the minted PYT /// @param vault The vault to mint NYT and PYT for /// @param xPYT The xPYT contract to deposit the minted PYT into. Set to 0 to receive raw PYT instead. /// @param underlyingAmount The amount of underlying tokens to use /// @return mintAmount The amount of NYT and PYT minted (the amounts are equal) function enterWithUnderlying( address nytRecipient, address pytRecipient, address vault, IxPYT xPYT, uint256 underlyingAmount ) external virtual nonReentrant returns (uint256 mintAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- if (underlyingAmount == 0) { return 0; } /// ----------------------------------------------------------------------- /// State updates & effects /// ----------------------------------------------------------------------- // mint PYT and NYT mintAmount = underlyingAmount; _enter( nytRecipient, pytRecipient, vault, xPYT, underlyingAmount, getPricePerVaultShare(vault) ); // transfer underlying from msg.sender to address(this) ERC20 underlying = getUnderlyingOfVault(vault); underlying.safeTransferFrom( msg.sender, address(this), underlyingAmount ); // deposit underlying into vault _depositIntoVault(underlying, underlyingAmount, vault); emit EnterWithUnderlying( msg.sender, nytRecipient, pytRecipient, vault, xPYT, underlyingAmount ); } /// @notice Converts vault share tokens into NegativeYieldToken and PerpetualYieldToken. /// @dev Only available if vault shares are transferrable ERC20 tokens. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// deploy them before proceeding, which will increase the gas cost significantly. /// @param nytRecipient The recipient of the minted NYT /// @param pytRecipient The recipient of the minted PYT /// @param vault The vault to mint NYT and PYT for /// @param xPYT The xPYT contract to deposit the minted PYT into. Set to 0 to receive raw PYT instead. /// @param vaultSharesAmount The amount of vault share tokens to use /// @return mintAmount The amount of NYT and PYT minted (the amounts are equal) function enterWithVaultShares( address nytRecipient, address pytRecipient, address vault, IxPYT xPYT, uint256 vaultSharesAmount ) external virtual nonReentrant returns (uint256 mintAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- if (vaultSharesAmount == 0) { return 0; } // only supported if vault shares are ERC20 if (!vaultSharesIsERC20()) { revert Error_VaultSharesNotERC20(); } /// ----------------------------------------------------------------------- /// State updates & effects /// ----------------------------------------------------------------------- // mint PYT and NYT uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); mintAmount = _vaultSharesAmountToUnderlyingAmount( vault, vaultSharesAmount, updatedPricePerVaultShare ); _enter( nytRecipient, pytRecipient, vault, xPYT, mintAmount, updatedPricePerVaultShare ); // transfer vault tokens from msg.sender to address(this) ERC20(vault).safeTransferFrom( msg.sender, address(this), vaultSharesAmount ); emit EnterWithVaultShares( msg.sender, nytRecipient, pytRecipient, vault, xPYT, vaultSharesAmount ); } /// @notice Converts NegativeYieldToken and PerpetualYieldToken to underlying tokens. /// The amount of NYT and PYT burned will be equal to the underlying token amount. /// @dev The underlying tokens will be immediately withdrawn from the specified vault. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// revert. /// @param recipient The recipient of the minted NYT and PYT /// @param vault The vault to mint NYT and PYT for /// @param xPYT The xPYT contract to use for burning PYT. Set to 0 to burn raw PYT instead. /// @param underlyingAmount The amount of underlying tokens requested /// @return burnAmount The amount of NYT and PYT burned (the amounts are equal) function exitToUnderlying( address recipient, address vault, IxPYT xPYT, uint256 underlyingAmount ) external virtual nonReentrant returns (uint256 burnAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- if (underlyingAmount == 0) { return 0; } /// ----------------------------------------------------------------------- /// State updates & effects /// ----------------------------------------------------------------------- // burn PYT and NYT uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); burnAmount = underlyingAmount; _exit(vault, xPYT, underlyingAmount, updatedPricePerVaultShare); // withdraw underlying from vault to recipient // don't check balance since user can just withdraw slightly less // saves gas this way underlyingAmount = _withdrawFromVault( recipient, vault, underlyingAmount, updatedPricePerVaultShare, false ); emit ExitToUnderlying( msg.sender, recipient, vault, xPYT, underlyingAmount ); } /// @notice Converts NegativeYieldToken and PerpetualYieldToken to vault share tokens. /// The amount of NYT and PYT burned will be equal to the underlying token amount. /// @dev Only available if vault shares are transferrable ERC20 tokens. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// revert. /// @param recipient The recipient of the minted NYT and PYT /// @param vault The vault to mint NYT and PYT for /// @param xPYT The xPYT contract to use for burning PYT. Set to 0 to burn raw PYT instead. /// @param vaultSharesAmount The amount of vault share tokens requested /// @return burnAmount The amount of NYT and PYT burned (the amounts are equal) function exitToVaultShares( address recipient, address vault, IxPYT xPYT, uint256 vaultSharesAmount ) external virtual nonReentrant returns (uint256 burnAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- if (vaultSharesAmount == 0) { return 0; } // only supported if vault shares are ERC20 if (!vaultSharesIsERC20()) { revert Error_VaultSharesNotERC20(); } /// ----------------------------------------------------------------------- /// State updates & effects /// ----------------------------------------------------------------------- // burn PYT and NYT uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); burnAmount = _vaultSharesAmountToUnderlyingAmountRoundingUp( vault, vaultSharesAmount, updatedPricePerVaultShare ); _exit(vault, xPYT, burnAmount, updatedPricePerVaultShare); // transfer vault tokens to recipient ERC20(vault).safeTransfer(recipient, vaultSharesAmount); emit ExitToVaultShares( msg.sender, recipient, vault, xPYT, vaultSharesAmount ); } /// @notice Claims the yield earned by the PerpetualYieldToken balance of msg.sender, in the underlying token. /// @dev If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// revert. /// @param recipient The recipient of the yield /// @param vault The vault to claim yield from /// @return yieldAmount The amount of yield claimed, in underlying tokens function claimYieldInUnderlying(address recipient, address vault) external virtual nonReentrant returns (uint256 yieldAmount) { /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // update storage variables and compute yield amount uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); yieldAmount = _claimYield(vault, updatedPricePerVaultShare); // withdraw yield if (yieldAmount != 0) { /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- (uint8 fee, address protocolFeeRecipient) = factory .protocolFeeInfo(); if (fee != 0) { uint256 protocolFee = (yieldAmount * fee) / 1000; unchecked { // can't underflow since fee < 256 yieldAmount -= protocolFee; } if (vaultSharesIsERC20()) { // vault shares are in ERC20 // do share transfer protocolFee = _underlyingAmountToVaultSharesAmount( vault, protocolFee, updatedPricePerVaultShare ); uint256 vaultSharesBalance = ERC20(vault).balanceOf( address(this) ); if (protocolFee > vaultSharesBalance) { protocolFee = vaultSharesBalance; } if (protocolFee != 0) { ERC20(vault).safeTransfer( protocolFeeRecipient, protocolFee ); } } else { // vault shares are not in ERC20 // withdraw underlying from vault // checkBalance is set to true to prevent getting stuck // due to rounding errors if (protocolFee != 0) { _withdrawFromVault( protocolFeeRecipient, vault, protocolFee, updatedPricePerVaultShare, true ); } } } // withdraw underlying to recipient // checkBalance is set to true to prevent getting stuck // due to rounding errors yieldAmount = _withdrawFromVault( recipient, vault, yieldAmount, updatedPricePerVaultShare, true ); emit ClaimYieldInUnderlying( msg.sender, recipient, vault, yieldAmount ); } } /// @notice Claims the yield earned by the PerpetualYieldToken balance of msg.sender, in vault shares. /// @dev Only available if vault shares are transferrable ERC20 tokens. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// revert. /// @param recipient The recipient of the yield /// @param vault The vault to claim yield from /// @return yieldAmount The amount of yield claimed, in vault shares function claimYieldInVaultShares(address recipient, address vault) external virtual nonReentrant returns (uint256 yieldAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- // only supported if vault shares are ERC20 if (!vaultSharesIsERC20()) { revert Error_VaultSharesNotERC20(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // update storage variables and compute yield amount uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); yieldAmount = _claimYield(vault, updatedPricePerVaultShare); // withdraw yield if (yieldAmount != 0) { /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // convert yieldAmount to be denominated in vault shares yieldAmount = _underlyingAmountToVaultSharesAmount( vault, yieldAmount, updatedPricePerVaultShare ); (uint8 fee, address protocolFeeRecipient) = factory .protocolFeeInfo(); uint256 vaultSharesBalance = getVaultShareBalance(vault); if (fee != 0) { uint256 protocolFee = (yieldAmount * fee) / 1000; protocolFee = protocolFee > vaultSharesBalance ? vaultSharesBalance : protocolFee; unchecked { // can't underflow since fee < 256 yieldAmount -= protocolFee; } if (protocolFee > 0) { ERC20(vault).safeTransfer( protocolFeeRecipient, protocolFee ); vaultSharesBalance -= protocolFee; } } // transfer vault shares to recipient // check if vault shares is enough to prevent getting stuck // from rounding errors yieldAmount = yieldAmount > vaultSharesBalance ? vaultSharesBalance : yieldAmount; if (yieldAmount > 0) { ERC20(vault).safeTransfer(recipient, yieldAmount); } emit ClaimYieldInVaultShares( msg.sender, recipient, vault, yieldAmount ); } } /// @notice Claims the yield earned by the PerpetualYieldToken balance of msg.sender, and immediately /// use the yield to mint NYT and PYT. /// @dev Introduced to save gas for xPYT compounding, since it avoids vault withdraws/transfers. /// If the NYT and PYT for the specified vault haven't been deployed yet, this call will /// revert. /// @param nytRecipient The recipient of the minted NYT /// @param pytRecipient The recipient of the minted PYT /// @param vault The vault to claim yield from /// @param xPYT The xPYT contract to deposit the minted PYT into. Set to 0 to receive raw PYT instead. /// @return yieldAmount The amount of yield claimed, in underlying tokens function claimYieldAndEnter( address nytRecipient, address pytRecipient, address vault, IxPYT xPYT ) external virtual nonReentrant returns (uint256 yieldAmount) { // update storage variables and compute yield amount uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); yieldAmount = _claimYield(vault, updatedPricePerVaultShare); // use yield to mint NYT and PYT if (yieldAmount != 0) { (uint8 fee, address protocolFeeRecipient) = factory .protocolFeeInfo(); if (fee != 0) { uint256 protocolFee = (yieldAmount * fee) / 1000; unchecked { // can't underflow since fee < 256 yieldAmount -= protocolFee; } if (vaultSharesIsERC20()) { // vault shares are in ERC20 // do share transfer protocolFee = _underlyingAmountToVaultSharesAmount( vault, protocolFee, updatedPricePerVaultShare ); uint256 vaultSharesBalance = ERC20(vault).balanceOf( address(this) ); if (protocolFee > vaultSharesBalance) { protocolFee = vaultSharesBalance; } if (protocolFee != 0) { ERC20(vault).safeTransfer( protocolFeeRecipient, protocolFee ); } } else { // vault shares are not in ERC20 // withdraw underlying from vault // checkBalance is set to true to prevent getting stuck // due to rounding errors if (protocolFee != 0) { _withdrawFromVault( protocolFeeRecipient, vault, protocolFee, updatedPricePerVaultShare, true ); } } } NegativeYieldToken nyt = getNegativeYieldTokenForVault(vault); PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); if (address(xPYT) == address(0)) { // accrue yield to pytRecipient if they're not msg.sender // no need to do it if the recipient is msg.sender, since // we already accrued yield in _claimYield if (pytRecipient != msg.sender) { _accrueYield( vault, pyt, pytRecipient, updatedPricePerVaultShare ); } } else { // accrue yield to xPYT contract since it gets minted PYT _accrueYield( vault, pyt, address(xPYT), updatedPricePerVaultShare ); } // mint NYTs and PYTs nyt.gateMint(nytRecipient, yieldAmount); if (address(xPYT) == address(0)) { // mint raw PYT to recipient pyt.gateMint(pytRecipient, yieldAmount); } else { // mint PYT to xPYT contract pyt.gateMint(address(xPYT), yieldAmount); /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // call sweep to mint xPYT using the PYT xPYT.sweep(pytRecipient); } emit ClaimYieldAndEnter( msg.sender, nytRecipient, pytRecipient, vault, xPYT, yieldAmount ); } } /// ----------------------------------------------------------------------- /// Getters /// ----------------------------------------------------------------------- /// @notice Returns the NegativeYieldToken associated with a vault. /// @dev Returns non-zero value even if the contract hasn't been deployed yet. /// @param vault The vault to query /// @return The NegativeYieldToken address function getNegativeYieldTokenForVault(address vault) public view virtual returns (NegativeYieldToken) { return factory.getNegativeYieldToken(this, vault); } /// @notice Returns the PerpetualYieldToken associated with a vault. /// @dev Returns non-zero value even if the contract hasn't been deployed yet. /// @param vault The vault to query /// @return The PerpetualYieldToken address function getPerpetualYieldTokenForVault(address vault) public view virtual returns (PerpetualYieldToken) { return factory.getPerpetualYieldToken(this, vault); } /// @notice Returns the amount of yield claimable by a PerpetualYieldToken holder from a vault. /// Accounts for protocol fees. /// @param vault The vault to query /// @param user The PYT holder to query /// @return yieldAmount The amount of yield claimable function getClaimableYieldAmount(address vault, address user) external view virtual returns (uint256 yieldAmount) { PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); uint256 userYieldPerTokenStored_ = userYieldPerTokenStored[vault][user]; if (userYieldPerTokenStored_ == 0) { // uninitialized account return 0; } yieldAmount = _getClaimableYieldAmount( vault, user, _computeYieldPerToken(vault, pyt, getPricePerVaultShare(vault)), userYieldPerTokenStored_, pyt.balanceOf(user) ); (uint8 fee, ) = factory.protocolFeeInfo(); if (fee != 0) { uint256 protocolFee = (yieldAmount * fee) / 1000; unchecked { // can't underflow since fee < 256 yieldAmount -= protocolFee; } } } /// @notice Computes the latest yieldPerToken value for a vault. /// @param vault The vault to query /// @return The latest yieldPerToken value function computeYieldPerToken(address vault) external view virtual returns (uint256) { return _computeYieldPerToken( vault, getPerpetualYieldTokenForVault(vault), getPricePerVaultShare(vault) ); } /// @notice Returns the underlying token of a vault. /// @param vault The vault to query /// @return The underlying token function getUnderlyingOfVault(address vault) public view virtual returns (ERC20); /// @notice Returns the amount of underlying tokens each share of a vault is worth. /// @param vault The vault to query /// @return The pricePerVaultShare value function getPricePerVaultShare(address vault) public view virtual returns (uint256); /// @notice Returns the amount of vault shares owned by the gate. /// @param vault The vault to query /// @return The gate's vault share balance function getVaultShareBalance(address vault) public view virtual returns (uint256); /// @return True if the vaults supported by this gate use transferrable ERC20 tokens /// to represent shares, false otherwise. function vaultSharesIsERC20() public pure virtual returns (bool); /// @notice Computes the ERC20 name of the NegativeYieldToken of a vault. /// @param vault The vault to query /// @return The ERC20 name function negativeYieldTokenName(address vault) external view virtual returns (string memory); /// @notice Computes the ERC20 symbol of the NegativeYieldToken of a vault. /// @param vault The vault to query /// @return The ERC20 symbol function negativeYieldTokenSymbol(address vault) external view virtual returns (string memory); /// @notice Computes the ERC20 name of the PerpetualYieldToken of a vault. /// @param vault The vault to query /// @return The ERC20 name function perpetualYieldTokenName(address vault) external view virtual returns (string memory); /// @notice Computes the ERC20 symbol of the NegativeYieldToken of a vault. /// @param vault The vault to query /// @return The ERC20 symbol function perpetualYieldTokenSymbol(address vault) external view virtual returns (string memory); /// ----------------------------------------------------------------------- /// PYT transfer hook /// ----------------------------------------------------------------------- /// @notice SHOULD NOT BE CALLED BY USERS, ONLY CALLED BY PERPETUAL YIELD TOKEN CONTRACTS /// @dev Called by PYT contracts deployed by this gate before each token transfer, in order to /// accrue the yield earned by the from & to accounts /// @param from The token transfer from account /// @param to The token transfer to account /// @param fromBalance The token balance of the from account before the transfer /// @param toBalance The token balance of the to account before the transfer function beforePerpetualYieldTokenTransfer( address from, address to, uint256 amount, uint256 fromBalance, uint256 toBalance ) external virtual { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- if (amount == 0) { return; } address vault = PerpetualYieldToken(msg.sender).vault(); PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); if (msg.sender != address(pyt)) { revert Error_SenderNotPerpetualYieldToken(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // accrue yield uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); uint256 updatedYieldPerToken = _computeYieldPerToken( vault, pyt, updatedPricePerVaultShare ); yieldPerTokenStored[vault] = updatedYieldPerToken; pricePerVaultShareStored[vault] = updatedPricePerVaultShare; // we know the from account must have held PYTs before // so we will always accrue the yield earned by the from account userAccruedYield[vault][from] = _getClaimableYieldAmount( vault, from, updatedYieldPerToken, userYieldPerTokenStored[vault][from], fromBalance ) + 1; userYieldPerTokenStored[vault][from] = updatedYieldPerToken + 1; // the to account might not have held PYTs before // we only accrue yield if they have uint256 toUserYieldPerTokenStored = userYieldPerTokenStored[vault][to]; if (toUserYieldPerTokenStored != 0) { // to account has held PYTs before userAccruedYield[vault][to] = _getClaimableYieldAmount( vault, to, updatedYieldPerToken, toUserYieldPerTokenStored, toBalance ) + 1; } userYieldPerTokenStored[vault][to] = updatedYieldPerToken + 1; } /// ----------------------------------------------------------------------- /// Emergency exit /// ----------------------------------------------------------------------- /// @notice Activates the emergency exit mode for a certain vault. Only callable by owner. /// @dev Activating emergency exit allows PYT/NYT holders to do single-sided burns to redeem the underlying /// collateral. This is to prevent cases where a large portion of PYT/NYT is locked up in a buggy/malicious contract /// and locks up the underlying collateral forever. /// @param vault The vault to activate emergency exit for /// @param pytPriceInUnderlying The amount of underlying asset burning each PYT can redeem. Scaled by PRECISION. function ownerActivateEmergencyExitForVault( address vault, uint96 pytPriceInUnderlying ) external virtual onlyOwner { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- // we only allow emergency exit to be activated once (until deactivation) // because if pytPriceInUnderlying is ever modified after activation // then PYT/NYT will become unbacked if (emergencyExitStatusOfVault[vault].activated) { revert Error_EmergencyExitAlreadyActivated(); } // we need to ensure the PYT price value is within the range [0, PRECISION] if (pytPriceInUnderlying > PRECISION) { revert Error_InvalidInput(); } // the PYT & NYT must have already been deployed NegativeYieldToken nyt = getNegativeYieldTokenForVault(vault); if (address(nyt).code.length == 0) { revert Error_TokenPairNotDeployed(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- emergencyExitStatusOfVault[vault] = EmergencyExitStatus({ activated: true, pytPriceInUnderlying: pytPriceInUnderlying }); } /// @notice Deactivates the emergency exit mode for a certain vault. Only callable by owner. /// @param vault The vault to deactivate emergency exit for function ownerDeactivateEmergencyExitForVault(address vault) external virtual onlyOwner { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- // can only deactivate emergency exit when it's already activated if (!emergencyExitStatusOfVault[vault].activated) { revert Error_EmergencyExitNotActivated(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // reset the emergency exit status delete emergencyExitStatusOfVault[vault]; } /// @notice Emergency exit NYTs into the underlying asset. Only callable when emergency exit has /// been activated for the vault. /// @param vault The vault to exit NYT for /// @param amount The amount of NYT to exit /// @param recipient The recipient of the underlying asset /// @return underlyingAmount The amount of underlying asset exited function emergencyExitNegativeYieldToken( address vault, uint256 amount, address recipient ) external virtual returns (uint256 underlyingAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- // ensure emergency exit is active EmergencyExitStatus memory status = emergencyExitStatusOfVault[vault]; if (!status.activated) { revert Error_EmergencyExitNotActivated(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); // accrue yield _accrueYield(vault, pyt, msg.sender, updatedPricePerVaultShare); // burn NYT from the sender NegativeYieldToken nyt = getNegativeYieldTokenForVault(vault); nyt.gateBurn(msg.sender, amount); /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // compute how much of the underlying assets to give the recipient // rounds down underlyingAmount = FullMath.mulDiv( amount, PRECISION - status.pytPriceInUnderlying, PRECISION ); // withdraw underlying from vault to recipient // don't check balance since user can just withdraw slightly less // saves gas this way underlyingAmount = _withdrawFromVault( recipient, vault, underlyingAmount, updatedPricePerVaultShare, false ); } /// @notice Emergency exit PYTs into the underlying asset. Only callable when emergency exit has /// been activated for the vault. /// @param vault The vault to exit PYT for /// @param xPYT The xPYT contract to use for burning PYT. Set to 0 to burn raw PYT instead. /// @param amount The amount of PYT to exit /// @param recipient The recipient of the underlying asset /// @return underlyingAmount The amount of underlying asset exited function emergencyExitPerpetualYieldToken( address vault, IxPYT xPYT, uint256 amount, address recipient ) external virtual returns (uint256 underlyingAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- // ensure emergency exit is active EmergencyExitStatus memory status = emergencyExitStatusOfVault[vault]; if (!status.activated) { revert Error_EmergencyExitNotActivated(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); uint256 updatedPricePerVaultShare = getPricePerVaultShare(vault); // accrue yield _accrueYield(vault, pyt, msg.sender, updatedPricePerVaultShare); if (address(xPYT) == address(0)) { // burn raw PYT from sender pyt.gateBurn(msg.sender, amount); } else { /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // convert xPYT to PYT then burn xPYT.withdraw(amount, address(this), msg.sender); pyt.gateBurn(address(this), amount); } /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // compute how much of the underlying assets to give the recipient // rounds down underlyingAmount = FullMath.mulDiv( amount, status.pytPriceInUnderlying, PRECISION ); // withdraw underlying from vault to recipient // don't check balance since user can just withdraw slightly less // saves gas this way underlyingAmount = _withdrawFromVault( recipient, vault, underlyingAmount, updatedPricePerVaultShare, false ); } /// ----------------------------------------------------------------------- /// Internal utilities /// ----------------------------------------------------------------------- /// @dev Updates the yield earned globally and for a particular user. function _accrueYield( address vault, PerpetualYieldToken pyt, address user, uint256 updatedPricePerVaultShare ) internal virtual { uint256 updatedYieldPerToken = _computeYieldPerToken( vault, pyt, updatedPricePerVaultShare ); uint256 userYieldPerTokenStored_ = userYieldPerTokenStored[vault][user]; if (userYieldPerTokenStored_ != 0) { userAccruedYield[vault][user] = _getClaimableYieldAmount( vault, user, updatedYieldPerToken, userYieldPerTokenStored_, pyt.balanceOf(user) ) + 1; } yieldPerTokenStored[vault] = updatedYieldPerToken; pricePerVaultShareStored[vault] = updatedPricePerVaultShare; userYieldPerTokenStored[vault][user] = updatedYieldPerToken + 1; } /// @dev Mints PYTs and NYTs to the recipient given the amount of underlying deposited. function _enter( address nytRecipient, address pytRecipient, address vault, IxPYT xPYT, uint256 underlyingAmount, uint256 updatedPricePerVaultShare ) internal virtual { NegativeYieldToken nyt = getNegativeYieldTokenForVault(vault); if (address(nyt).code.length == 0) { // token pair hasn't been deployed yet // do the deployment now // only need to check nyt since nyt and pyt are always deployed in pairs factory.deployYieldTokenPair(this, vault); } PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // accrue yield _accrueYield( vault, pyt, address(xPYT) == address(0) ? pytRecipient : address(xPYT), updatedPricePerVaultShare ); // mint NYTs and PYTs nyt.gateMint(nytRecipient, underlyingAmount); if (address(xPYT) == address(0)) { // mint raw PYT to recipient pyt.gateMint(pytRecipient, underlyingAmount); } else { // mint PYT to xPYT contract pyt.gateMint(address(xPYT), underlyingAmount); /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // call sweep to mint xPYT using the PYT xPYT.sweep(pytRecipient); } } /// @dev Burns PYTs and NYTs from msg.sender given the amount of underlying withdrawn. function _exit( address vault, IxPYT xPYT, uint256 underlyingAmount, uint256 updatedPricePerVaultShare ) internal virtual { NegativeYieldToken nyt = getNegativeYieldTokenForVault(vault); PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); if (address(nyt).code.length == 0) { revert Error_TokenPairNotDeployed(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // accrue yield _accrueYield( vault, pyt, address(xPYT) == address(0) ? msg.sender : address(this), updatedPricePerVaultShare ); // burn NYTs and PYTs nyt.gateBurn(msg.sender, underlyingAmount); if (address(xPYT) == address(0)) { // burn raw PYT from sender pyt.gateBurn(msg.sender, underlyingAmount); } else { /// ----------------------------------------------------------------------- /// Effects /// ----------------------------------------------------------------------- // convert xPYT to PYT then burn xPYT.withdraw(underlyingAmount, address(this), msg.sender); pyt.gateBurn(address(this), underlyingAmount); } } /// @dev Updates storage variables for when a PYT holder claims the accrued yield. function _claimYield(address vault, uint256 updatedPricePerVaultShare) internal virtual returns (uint256 yieldAmount) { /// ----------------------------------------------------------------------- /// Validation /// ----------------------------------------------------------------------- PerpetualYieldToken pyt = getPerpetualYieldTokenForVault(vault); if (address(pyt).code.length == 0) { revert Error_TokenPairNotDeployed(); } /// ----------------------------------------------------------------------- /// State updates /// ----------------------------------------------------------------------- // accrue yield uint256 updatedYieldPerToken = _computeYieldPerToken( vault, pyt, updatedPricePerVaultShare ); uint256 userYieldPerTokenStored_ = userYieldPerTokenStored[vault][ msg.sender ]; if (userYieldPerTokenStored_ != 0) { yieldAmount = _getClaimableYieldAmount( vault, msg.sender, updatedYieldPerToken, userYieldPerTokenStored_, pyt.balanceOf(msg.sender) ); } yieldPerTokenStored[vault] = updatedYieldPerToken; pricePerVaultShareStored[vault] = updatedPricePerVaultShare; userYieldPerTokenStored[vault][msg.sender] = updatedYieldPerToken + 1; if (yieldAmount != 0) { userAccruedYield[vault][msg.sender] = 1; } } /// @dev Returns the amount of yield claimable by a PerpetualYieldToken holder from a vault. /// Assumes userYieldPerTokenStored_ != 0. Does not account for protocol fees. function _getClaimableYieldAmount( address vault, address user, uint256 updatedYieldPerToken, uint256 userYieldPerTokenStored_, uint256 userPYTBalance ) internal view virtual returns (uint256 yieldAmount) { unchecked { // the stored value is shifted by one uint256 actualUserYieldPerToken = userYieldPerTokenStored_ - 1; // updatedYieldPerToken - actualUserYieldPerToken won't underflow since we check updatedYieldPerToken > actualUserYieldPerToken yieldAmount = FullMath.mulDiv( userPYTBalance, updatedYieldPerToken > actualUserYieldPerToken ? updatedYieldPerToken - actualUserYieldPerToken : 0, PRECISION ); uint256 accruedYield = userAccruedYield[vault][user]; if (accruedYield > 1) { // won't overflow since the sum is at most the totalSupply of the vault's underlying, which // is at most 256 bits. // the stored accruedYield value is shifted by one yieldAmount += accruedYield - 1; } } } /// @dev Deposits underlying tokens into a vault /// @param underlying The underlying token to deposit /// @param underlyingAmount The amount of tokens to deposit /// @param vault The vault to deposit into function _depositIntoVault( ERC20 underlying, uint256 underlyingAmount, address vault ) internal virtual; /// @dev Withdraws underlying tokens from a vault /// @param recipient The recipient of the underlying tokens /// @param vault The vault to withdraw from /// @param underlyingAmount The amount of tokens to withdraw /// @param pricePerVaultShare The latest price per vault share value /// @param checkBalance Set to true to withdraw the entire balance if we're trying /// to withdraw more than the balance (due to rounding errors) /// @return withdrawnUnderlyingAmount The amount of underlying tokens withdrawn function _withdrawFromVault( address recipient, address vault, uint256 underlyingAmount, uint256 pricePerVaultShare, bool checkBalance ) internal virtual returns (uint256 withdrawnUnderlyingAmount); /// @dev Converts a vault share amount into an equivalent underlying asset amount function _vaultSharesAmountToUnderlyingAmount( address vault, uint256 vaultSharesAmount, uint256 pricePerVaultShare ) internal view virtual returns (uint256); /// @dev Converts a vault share amount into an equivalent underlying asset amount, rounding up function _vaultSharesAmountToUnderlyingAmountRoundingUp( address vault, uint256 vaultSharesAmount, uint256 pricePerVaultShare ) internal view virtual returns (uint256); /// @dev Converts an underlying asset amount into an equivalent vault shares amount function _underlyingAmountToVaultSharesAmount( address vault, uint256 underlyingAmount, uint256 pricePerVaultShare ) internal view virtual returns (uint256); /// @dev Computes the latest yieldPerToken value for a vault. function _computeYieldPerToken( address vault, PerpetualYieldToken pyt, uint256 updatedPricePerVaultShare ) internal view virtual returns (uint256) { uint256 pytTotalSupply = pyt.totalSupply(); if (pytTotalSupply == 0) { return yieldPerTokenStored[vault]; } uint256 pricePerVaultShareStored_ = pricePerVaultShareStored[vault]; if (updatedPricePerVaultShare <= pricePerVaultShareStored_) { // rounding error in vault share or no yield accrued return yieldPerTokenStored[vault]; } uint256 newYieldPerTokenAccrued; unchecked { // can't underflow since we know updatedPricePerVaultShare > pricePerVaultShareStored_ newYieldPerTokenAccrued = FullMath.mulDiv( updatedPricePerVaultShare - pricePerVaultShareStored_, getVaultShareBalance(vault), pytTotalSupply ); } return yieldPerTokenStored[vault] + newYieldPerTokenAccrued; } } contract Factory is BoringOwnable { /// ----------------------------------------------------------------------- /// Library usage /// ----------------------------------------------------------------------- using Bytes32AddressLib for address; using Bytes32AddressLib for bytes32; /// ----------------------------------------------------------------------- /// Errors /// ----------------------------------------------------------------------- error Error_ProtocolFeeRecipientIsZero(); /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- event SetProtocolFee(ProtocolFeeInfo protocolFeeInfo_); event DeployYieldTokenPair( Gate indexed gate, address indexed vault, NegativeYieldToken nyt, PerpetualYieldToken pyt ); /// ----------------------------------------------------------------------- /// Storage variables /// ----------------------------------------------------------------------- struct ProtocolFeeInfo { uint8 fee; // each increment represents 0.1%, so max is 25.5% address recipient; } /// @notice The protocol fee and the fee recipient address. ProtocolFeeInfo public protocolFeeInfo; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(ProtocolFeeInfo memory protocolFeeInfo_) { if ( protocolFeeInfo_.fee != 0 && protocolFeeInfo_.recipient == address(0) ) { revert Error_ProtocolFeeRecipientIsZero(); } protocolFeeInfo = protocolFeeInfo_; emit SetProtocolFee(protocolFeeInfo_); } /// ----------------------------------------------------------------------- /// User actions /// ----------------------------------------------------------------------- /// @notice Deploys the NegativeYieldToken and PerpetualYieldToken associated with a vault. /// @dev Will revert if they have already been deployed. /// @param gate The gate that will use the NYT and PYT /// @param vault The vault to deploy NYT and PYT for /// @return nyt The deployed NegativeYieldToken /// @return pyt The deployed PerpetualYieldToken function deployYieldTokenPair(Gate gate, address vault) public virtual returns (NegativeYieldToken nyt, PerpetualYieldToken pyt) { // Use the CREATE2 opcode to deploy new NegativeYieldToken and PerpetualYieldToken contracts. // This will revert if the contracts have already been deployed, // as the salt & bytecode hash would be the same and we can't deploy with it twice. nyt = new NegativeYieldToken{salt: bytes32(0)}(gate, vault); pyt = new PerpetualYieldToken{salt: bytes32(0)}(gate, vault); emit DeployYieldTokenPair(gate, vault, nyt, pyt); } /// ----------------------------------------------------------------------- /// Getters /// ----------------------------------------------------------------------- /// @notice Returns the NegativeYieldToken associated with a gate & vault pair. /// @dev Returns non-zero value even if the contract hasn't been deployed yet. /// @param gate The gate to query /// @param vault The vault to query /// @return The NegativeYieldToken address function getNegativeYieldToken(Gate gate, address vault) public view virtual returns (NegativeYieldToken) { return NegativeYieldToken(_computeYieldTokenAddress(gate, vault, false)); } /// @notice Returns the PerpetualYieldToken associated with a gate & vault pair. /// @dev Returns non-zero value even if the contract hasn't been deployed yet. /// @param gate The gate to query /// @param vault The vault to query /// @return The PerpetualYieldToken address function getPerpetualYieldToken(Gate gate, address vault) public view virtual returns (PerpetualYieldToken) { return PerpetualYieldToken(_computeYieldTokenAddress(gate, vault, true)); } /// ----------------------------------------------------------------------- /// Owner functions /// ----------------------------------------------------------------------- /// @notice Updates the protocol fee and/or the protocol fee recipient. /// Only callable by the owner. /// @param protocolFeeInfo_ The new protocol fee info function ownerSetProtocolFee(ProtocolFeeInfo calldata protocolFeeInfo_) external virtual onlyOwner { if ( protocolFeeInfo_.fee != 0 && protocolFeeInfo_.recipient == address(0) ) { revert Error_ProtocolFeeRecipientIsZero(); } protocolFeeInfo = protocolFeeInfo_; emit SetProtocolFee(protocolFeeInfo_); } /// ----------------------------------------------------------------------- /// Internal utilities /// ----------------------------------------------------------------------- /// @dev Computes the address of PYTs and NYTs using CREATE2. function _computeYieldTokenAddress( Gate gate, address vault, bool isPerpetualYieldToken ) internal view virtual returns (address) { return keccak256( abi.encodePacked( // Prefix: bytes1(0xFF), // Creator: address(this), // Salt: bytes32(0), // Bytecode hash: keccak256( abi.encodePacked( // Deployment bytecode: isPerpetualYieldToken ? type(PerpetualYieldToken).creationCode : type(NegativeYieldToken).creationCode, // Constructor arguments: abi.encode(gate, vault) ) ) ) ).fromLast20Bytes(); // Convert the CREATE2 hash into an address. } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct Factory.ProtocolFeeInfo","name":"protocolFeeInfo_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Error_ProtocolFeeRecipientIsZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Gate","name":"gate","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"contract NegativeYieldToken","name":"nyt","type":"address"},{"indexed":false,"internalType":"contract PerpetualYieldToken","name":"pyt","type":"address"}],"name":"DeployYieldTokenPair","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"}],"indexed":false,"internalType":"struct Factory.ProtocolFeeInfo","name":"protocolFeeInfo_","type":"tuple"}],"name":"SetProtocolFee","type":"event"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Gate","name":"gate","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"deployYieldTokenPair","outputs":[{"internalType":"contract NegativeYieldToken","name":"nyt","type":"address"},{"internalType":"contract PerpetualYieldToken","name":"pyt","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Gate","name":"gate","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"getNegativeYieldToken","outputs":[{"internalType":"contract NegativeYieldToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Gate","name":"gate","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"getPerpetualYieldToken","outputs":[{"internalType":"contract PerpetualYieldToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct Factory.ProtocolFeeInfo","name":"protocolFeeInfo_","type":"tuple"}],"name":"ownerSetProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeInfo","outputs":[{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"direct","type":"bool"},{"internalType":"bool","name":"renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50604051613b8d380380613b8d83398101604081905261002f91610121565b600080546001600160a01b0319163390811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3805160ff161580159061008c575060208101516001600160a01b0316155b156100aa5760405163fd15c34360e01b815260040160405180910390fd5b8051600280546020808501805160ff9095166001600160a81b031990931683176101006001600160a01b039687160217909355604080519283529251909316928101929092527fbb1e7f6bcb2654c0e4c0f3cfb23f810d2babfb459af4ad2efcd1de09b452ceab910160405180910390a15061019f565b60006040828403121561013357600080fd5b604080519081016001600160401b038111828210171561016357634e487b7160e01b600052604160045260246000fd5b604052825160ff8116811461017757600080fd5b815260208301516001600160a01b038116811461019357600080fd5b60208201529392505050565b6139df806101ae6000396000f3fe60806040523480156200001157600080fd5b5060043610620000b05760003560e01c806388321738116200007f578063aec0b5431162000062578063aec0b5431462000168578063e30c397814620001c2578063e31ce2f314620001e357600080fd5b80638832173814620001305780638da5cb5b146200014757600080fd5b8063078dfbe714620000b55780631a3df32e14620000ce5780634e71e0c8146200010f578063782f2b771462000119575b600080fd5b620000cc620000c6366004620009cc565b62000228565b005b620000e5620000df36600462000a18565b6200041e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620000cc62000435565b620000e56200012a36600462000a18565b6200054d565b620000cc6200014136600462000a56565b6200055d565b600054620000e59073ffffffffffffffffffffffffffffffffffffffff1681565b600254620001939060ff811690610100900473ffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff909316835273ffffffffffffffffffffffffffffffffffffffff90911660208301520162000106565b600154620000e59073ffffffffffffffffffffffffffffffffffffffff1681565b620001fa620001f436600462000a18565b620006ae565b6040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301520162000106565b60005473ffffffffffffffffffffffffffffffffffffffff163314620002af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b8115620003d75773ffffffffffffffffffffffffffffffffffffffff8316151580620002d85750805b62000340576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f776e61626c653a207a65726f206164647265737300000000000000000000006044820152606401620002a6565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617909155600180549091169055505050565b6001805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055505050565b60006200042e83836000620007c9565b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16338114620004b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e65726044820152606401620002a6565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600180549091169055565b60006200042e83836001620007c9565b60005473ffffffffffffffffffffffffffffffffffffffff163314620005e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620002a6565b620005ef602082018262000a7f565b60ff1615801590620006285750600062000610604083016020840162000a9f565b73ffffffffffffffffffffffffffffffffffffffff16145b1562000660576040517ffd15c34300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060026200066f828262000abf565b9050507fbb1e7f6bcb2654c0e4c0f3cfb23f810d2babfb459af4ad2efcd1de09b452ceab81604051620006a3919062000b55565b60405180910390a150565b6000806000801b8484604051620006c59062000974565b73ffffffffffffffffffffffffffffffffffffffff9283168152911660208201526040018190604051809103906000f59050801580156200070a573d6000803e3d6000fd5b5091506000801b8484604051620007219062000982565b73ffffffffffffffffffffffffffffffffffffffff9283168152911660208201526040018190604051809103906000f590508015801562000766573d6000803e3d6000fd5b506040805173ffffffffffffffffffffffffffffffffffffffff85811682528084166020830152929350828616928716917f2ba651335cb36a0327665b260df4a5bd395a6807ba07425159234671d3ed3a1c910160405180910390a39250929050565b60006200096c7fff000000000000000000000000000000000000000000000000000000000000003083856200084057604051620008096020820162000974565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f9091011660405262000883565b604051620008516020820162000982565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f909101166040525b6040805173ffffffffffffffffffffffffffffffffffffffff8b811660208301528a168183015281518082038301815260608201909252620008ca92919060800162000bdc565b604051602081830303815290604052805190602001206040516020016200095394939291907fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b6040516020818303038152906040528051906020012090565b949350505050565b6115ba8062000bf683390190565b6117fa80620021b083390190565b73ffffffffffffffffffffffffffffffffffffffff81168114620009b357600080fd5b50565b80358015158114620009c757600080fd5b919050565b600080600060608486031215620009e257600080fd5b8335620009ef8162000990565b9250620009ff60208501620009b6565b915062000a0f60408501620009b6565b90509250925092565b6000806040838503121562000a2c57600080fd5b823562000a398162000990565b9150602083013562000a4b8162000990565b809150509250929050565b60006040828403121562000a6957600080fd5b50919050565b60ff81168114620009b357600080fd5b60006020828403121562000a9257600080fd5b81356200042e8162000a6f565b60006020828403121562000ab257600080fd5b81356200042e8162000990565b813562000acc8162000a6f565b60ff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008216178355602084013562000b0b8162000990565b74ffffffffffffffffffffffffffffffffffffffff008160081b16837fffffffffffffffffffffff0000000000000000000000000000000000000000008416171784555050505050565b60408101823562000b668162000a6f565b60ff168252602083013562000b7b8162000990565b73ffffffffffffffffffffffffffffffffffffffff811660208401525092915050565b6000815160005b8181101562000bc1576020818501810151868301520162000ba5565b8181111562000bd1576000828601525b509290920192915050565b60006200096c62000bee838662000b9e565b8462000b9e56fe6101206040523480156200001257600080fd5b50604051620015ba380380620015ba8339810160408190526200003591620003b0565b604051631f27f92760e01b81526001600160a01b038281166004830152831690631f27f92790602401600060405180830381865afa1580156200007c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000a6919081019062000405565b604051631c4d2fd760e21b81526001600160a01b038381166004830152841690637134bf5c90602401600060405180830381865afa158015620000ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000117919081019062000405565b60405163249f75af60e21b81526001600160a01b038085166004830152859185918591859185169063927dd6bc90602401602060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200018c9190620004e1565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f0919062000508565b825162000205906000906020860190620002f1565b5081516200021b906001906020850190620002f1565b5060ff81166080524660a0526200023162000255565b60c0525050506001600160a01b0391821660e0521661010052506200060c92505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000289919062000569565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b828054620002ff906200052d565b90600052602060002090601f0160209004810192826200032357600085556200036e565b82601f106200033e57805160ff19168380011785556200036e565b828001600101855582156200036e579182015b828111156200036e57825182559160200191906001019062000351565b506200037c92915062000380565b5090565b5b808211156200037c576000815560010162000381565b6001600160a01b0381168114620003ad57600080fd5b50565b60008060408385031215620003c457600080fd5b8251620003d18162000397565b6020840151909250620003e48162000397565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600060208083850312156200041957600080fd5b82516001600160401b03808211156200043157600080fd5b818501915085601f8301126200044657600080fd5b8151818111156200045b576200045b620003ef565b604051601f8201601f19908116603f01168101908382118183101715620004865762000486620003ef565b8160405282815288868487010111156200049f57600080fd5b600093505b82841015620004c35784840186015181850187015292850192620004a4565b82841115620004d55760008684830101525b98975050505050505050565b600060208284031215620004f457600080fd5b8151620005018162000397565b9392505050565b6000602082840312156200051b57600080fd5b815160ff811681146200050157600080fd5b600181811c908216806200054257607f821691505b6020821081036200056357634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200058657607f831692505b60208084108203620005a657634e487b7160e01b86526022600452602486fd5b818015620005bd5760018114620005cf57620005fe565b60ff19861689528489019650620005fe565b60008a81526020902060005b86811015620005f65781548b820152908501908301620005db565b505084890196505b509498975050505050505050565b60805160a05160c05160e05161010051610f5b6200065f60003960006102c30152600081816101eb015281816105a301526106b20152600061056901526000610534015260006101750152610f5b6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80637a0ebc8811610097578063b132d08f11610066578063b132d08f1461026d578063d505accf14610280578063dd62ed3e14610293578063fbfa77cf146102be57600080fd5b80637a0ebc88146101e65780637ecebe001461023257806395d89b4114610252578063a9059cbb1461025a57600080fd5b8063313ce567116100d3578063313ce567146101705780633644e515146101a957806340471768146101b157806370a08231146101c657600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd1461015d575b600080fd5b61010d6102e5565b60405161011a9190610bd8565b60405180910390f35b610136610131366004610c74565b610373565b604051901515815260200161011a565b61014f60025481565b60405190815260200161011a565b61013661016b366004610c9e565b6103ec565b6101977f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161011a565b61014f610530565b6101c46101bf366004610c74565b61058b565b005b61014f6101d4366004610cda565b60036020526000908152604090205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61014f610240366004610cda565b60056020526000908152604090205481565b61010d610608565b610136610268366004610c74565b610615565b6101c461027b366004610c74565b61069a565b6101c461028e366004610cfc565b610713565b61014f6102a1366004610d6f565b600460209081526000928352604080842090915290825290205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b600080546102f290610da2565b80601f016020809104026020016040519081016040528092919081815260200182805461031e90610da2565b801561036b5780601f106103405761010080835404028352916020019161036b565b820191906000526020600020905b81548152906001019060200180831161034e57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103db9086815260200190565b60405180910390a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146104805761044e8382610e24565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040812080548592906104b5908490610e24565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061051d9087815260200190565b60405180910390a3506001949350505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461056657610561610a37565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146105fa576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106048282610ad1565b5050565b600180546102f290610da2565b33600090815260036020526040812080548391908390610636908490610e24565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103db9086815260200190565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610709576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106048282610b4a565b42841015610782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161078e610530565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156108e0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580159061095b57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6109c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610779565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051610a699190610e3b565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b8060026000828254610ae39190610f0d565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290610b7f908490610e24565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610b3e565b600060208083528351808285015260005b81811015610c0557858101830151858201604001528201610be9565b81811115610c17576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610c6f57600080fd5b919050565b60008060408385031215610c8757600080fd5b610c9083610c4b565b946020939093013593505050565b600080600060608486031215610cb357600080fd5b610cbc84610c4b565b9250610cca60208501610c4b565b9150604084013590509250925092565b600060208284031215610cec57600080fd5b610cf582610c4b565b9392505050565b600080600080600080600060e0888a031215610d1757600080fd5b610d2088610c4b565b9650610d2e60208901610c4b565b95506040880135945060608801359350608088013560ff81168114610d5257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215610d8257600080fd5b610d8b83610c4b565b9150610d9960208401610c4b565b90509250929050565b600181811c90821680610db657607f821691505b602082108103610def577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015610e3657610e36610df5565b500390565b600080835481600182811c915080831680610e5757607f831692505b60208084108203610e8f577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015610ea35760018114610ed257610eff565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650610eff565b60008a81526020902060005b86811015610ef75781548b820152908501908301610ede565b505084890196505b509498975050505050505050565b60008219821115610f2057610f20610df5565b50019056fea2646970667358221220a11195b24f792f2cb954969f59cfbfbceed801b77214aa564d22d213e974851964736f6c634300080d00336101206040523480156200001257600080fd5b50604051620017fa380380620017fa8339810160408190526200003591620003b0565b60405163172693e760e01b81526001600160a01b03828116600483015283169063172693e790602401600060405180830381865afa1580156200007c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000a6919081019062000405565b6040516369f6d9b360e01b81526001600160a01b0383811660048301528416906369f6d9b390602401600060405180830381865afa158015620000ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000117919081019062000405565b60405163249f75af60e21b81526001600160a01b038085166004830152859185918591859185169063927dd6bc90602401602060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200018c9190620004e1565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f0919062000508565b825162000205906000906020860190620002f1565b5081516200021b906001906020850190620002f1565b5060ff81166080524660a0526200023162000255565b60c0525050506001600160a01b0391821660e0521661010052506200060c92505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000289919062000569565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b828054620002ff906200052d565b90600052602060002090601f0160209004810192826200032357600085556200036e565b82601f106200033e57805160ff19168380011785556200036e565b828001600101855582156200036e579182015b828111156200036e57825182559160200191906001019062000351565b506200037c92915062000380565b5090565b5b808211156200037c576000815560010162000381565b6001600160a01b0381168114620003ad57600080fd5b50565b60008060408385031215620003c457600080fd5b8251620003d18162000397565b6020840151909250620003e48162000397565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600060208083850312156200041957600080fd5b82516001600160401b03808211156200043157600080fd5b818501915085601f8301126200044657600080fd5b8151818111156200045b576200045b620003ef565b604051601f8201601f19908116603f01168101908382118183101715620004865762000486620003ef565b8160405282815288868487010111156200049f57600080fd5b600093505b82841015620004c35784840186015181850187015292850192620004a4565b82841115620004d55760008684830101525b98975050505050505050565b600060208284031215620004f457600080fd5b8151620005018162000397565b9392505050565b6000602082840312156200051b57600080fd5b815160ff811681146200050157600080fd5b600181811c908216806200054257607f821691505b6020821081036200056357634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200058657607f831692505b60208084108203620005a657634e487b7160e01b86526022600452602486fd5b818015620005bd5760018114620005cf57620005fe565b60ff19861689528489019650620005fe565b60008a81526020902060005b86811015620005f65781548b820152908501908301620005db565b505084890196505b509498975050505050505050565b60805160a05160c05160e0516101005161118d6200066d60003960006102c30152600081816101eb0152818161046d015281816106b8015281816107a701526108e40152600061067e0152600061064901526000610175015261118d6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80637a0ebc8811610097578063b132d08f11610066578063b132d08f1461026d578063d505accf14610280578063dd62ed3e14610293578063fbfa77cf146102be57600080fd5b80637a0ebc88146101e65780637ecebe001461023257806395d89b4114610252578063a9059cbb1461025a57600080fd5b8063313ce567116100d3578063313ce567146101705780633644e515146101a957806340471768146101b157806370a08231146101c657600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd1461015d575b600080fd5b61010d6102e5565b60405161011a9190610e0a565b60405180910390f35b610136610131366004610ea6565b610373565b604051901515815260200161011a565b61014f60025481565b60405190815260200161011a565b61013661016b366004610ed0565b6103ec565b6101977f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161011a565b61014f610645565b6101c46101bf366004610ea6565b6106a0565b005b61014f6101d4366004610f0c565b60036020526000908152604090205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61014f610240366004610f0c565b60056020526000908152604090205481565b61010d61071d565b610136610268366004610ea6565b61072a565b6101c461027b366004610ea6565b6108cc565b6101c461028e366004610f2e565b610945565b61014f6102a1366004610fa1565b600460209081526000928352604080842090915290825290205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b600080546102f290610fd4565b80601f016020809104026020016040519081016040528092919081815260200182805461031e90610fd4565b801561036b5780601f106103405761010080835404028352916020019161036b565b820191906000526020600020905b81548152906001019060200180831161034e57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103db9086815260200190565b60405180910390a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260036020526040808220548685168084528284205492517f5fb87d1c000000000000000000000000000000000000000000000000000000008152600481019590955260248501526044840186905260648401819052608484018290529193919290917f00000000000000000000000000000000000000000000000000000000000000001690635fb87d1c9060a401600060405180830381600087803b1580156104b157600080fd5b505af11580156104c5573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461055d5761052b8582611056565b73ffffffffffffffffffffffffffffffffffffffff881660009081526004602090815260408083203384529091529020555b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16146105d15761059a8584611056565b73ffffffffffffffffffffffffffffffffffffffff8089166000908152600360205260408082209390935590881681522082860190555b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8760405161063091815260200190565b60405180910390a35060019695505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461067b57610676610c69565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461070f576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107198282610d03565b5050565b600180546102f290610fd4565b336000818152600360205260408082205473ffffffffffffffffffffffffffffffffffffffff8681168085528385205493517f5fb87d1c00000000000000000000000000000000000000000000000000000000815260048101969096526024860152604485018690526064850182905260848501839052929390927f00000000000000000000000000000000000000000000000000000000000000001690635fb87d1c9060a401600060405180830381600087803b1580156107eb57600080fd5b505af11580156107ff573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610874576108408483611056565b336000908152600360205260408082209290925573ffffffffffffffffffffffffffffffffffffffff871681522081850190555b60405184815273ffffffffffffffffffffffffffffffffffffffff86169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3506001949350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461093b576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107198282610d7c565b428410156109b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b600060016109c0610645565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610b12573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610b8d57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610bf3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e455200000000000000000000000000000000000060448201526064016109ab565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051610c9b919061106d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b8060026000828254610d15919061113f565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290610db1908490611056565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610d70565b600060208083528351808285015260005b81811015610e3757858101830151858201604001528201610e1b565b81811115610e49576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ea157600080fd5b919050565b60008060408385031215610eb957600080fd5b610ec283610e7d565b946020939093013593505050565b600080600060608486031215610ee557600080fd5b610eee84610e7d565b9250610efc60208501610e7d565b9150604084013590509250925092565b600060208284031215610f1e57600080fd5b610f2782610e7d565b9392505050565b600080600080600080600060e0888a031215610f4957600080fd5b610f5288610e7d565b9650610f6060208901610e7d565b95506040880135945060608801359350608088013560ff81168114610f8457600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215610fb457600080fd5b610fbd83610e7d565b9150610fcb60208401610e7d565b90509250929050565b600181811c90821680610fe857607f821691505b602082108103611021577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561106857611068611027565b500390565b600080835481600182811c91508083168061108957607f831692505b602080841082036110c1577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156110d5576001811461110457611131565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650611131565b60008a81526020902060005b868110156111295781548b820152908501908301611110565b505084890196505b509498975050505050505050565b6000821982111561115257611152611027565b50019056fea2646970667358221220b0af09bcdb5d8d91dcc5380c797a7eba643d58c9b1a86205c45950f00830724f64736f6c634300080d0033a26469706673582212209adc5f544130c6c75170a6a26695abd2108aaa40b7e59962e198321baa147b7564736f6c634300080d003300000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620000b05760003560e01c806388321738116200007f578063aec0b5431162000062578063aec0b5431462000168578063e30c397814620001c2578063e31ce2f314620001e357600080fd5b80638832173814620001305780638da5cb5b146200014757600080fd5b8063078dfbe714620000b55780631a3df32e14620000ce5780634e71e0c8146200010f578063782f2b771462000119575b600080fd5b620000cc620000c6366004620009cc565b62000228565b005b620000e5620000df36600462000a18565b6200041e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620000cc62000435565b620000e56200012a36600462000a18565b6200054d565b620000cc6200014136600462000a56565b6200055d565b600054620000e59073ffffffffffffffffffffffffffffffffffffffff1681565b600254620001939060ff811690610100900473ffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff909316835273ffffffffffffffffffffffffffffffffffffffff90911660208301520162000106565b600154620000e59073ffffffffffffffffffffffffffffffffffffffff1681565b620001fa620001f436600462000a18565b620006ae565b6040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301520162000106565b60005473ffffffffffffffffffffffffffffffffffffffff163314620002af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b8115620003d75773ffffffffffffffffffffffffffffffffffffffff8316151580620002d85750805b62000340576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f776e61626c653a207a65726f206164647265737300000000000000000000006044820152606401620002a6565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617909155600180549091169055505050565b6001805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055505050565b60006200042e83836000620007c9565b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16338114620004b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e65726044820152606401620002a6565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600180549091169055565b60006200042e83836001620007c9565b60005473ffffffffffffffffffffffffffffffffffffffff163314620005e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620002a6565b620005ef602082018262000a7f565b60ff1615801590620006285750600062000610604083016020840162000a9f565b73ffffffffffffffffffffffffffffffffffffffff16145b1562000660576040517ffd15c34300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060026200066f828262000abf565b9050507fbb1e7f6bcb2654c0e4c0f3cfb23f810d2babfb459af4ad2efcd1de09b452ceab81604051620006a3919062000b55565b60405180910390a150565b6000806000801b8484604051620006c59062000974565b73ffffffffffffffffffffffffffffffffffffffff9283168152911660208201526040018190604051809103906000f59050801580156200070a573d6000803e3d6000fd5b5091506000801b8484604051620007219062000982565b73ffffffffffffffffffffffffffffffffffffffff9283168152911660208201526040018190604051809103906000f590508015801562000766573d6000803e3d6000fd5b506040805173ffffffffffffffffffffffffffffffffffffffff85811682528084166020830152929350828616928716917f2ba651335cb36a0327665b260df4a5bd395a6807ba07425159234671d3ed3a1c910160405180910390a39250929050565b60006200096c7fff000000000000000000000000000000000000000000000000000000000000003083856200084057604051620008096020820162000974565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f9091011660405262000883565b604051620008516020820162000982565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f909101166040525b6040805173ffffffffffffffffffffffffffffffffffffffff8b811660208301528a168183015281518082038301815260608201909252620008ca92919060800162000bdc565b604051602081830303815290604052805190602001206040516020016200095394939291907fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b6040516020818303038152906040528051906020012090565b949350505050565b6115ba8062000bf683390190565b6117fa80620021b083390190565b73ffffffffffffffffffffffffffffffffffffffff81168114620009b357600080fd5b50565b80358015158114620009c757600080fd5b919050565b600080600060608486031215620009e257600080fd5b8335620009ef8162000990565b9250620009ff60208501620009b6565b915062000a0f60408501620009b6565b90509250925092565b6000806040838503121562000a2c57600080fd5b823562000a398162000990565b9150602083013562000a4b8162000990565b809150509250929050565b60006040828403121562000a6957600080fd5b50919050565b60ff81168114620009b357600080fd5b60006020828403121562000a9257600080fd5b81356200042e8162000a6f565b60006020828403121562000ab257600080fd5b81356200042e8162000990565b813562000acc8162000a6f565b60ff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008216178355602084013562000b0b8162000990565b74ffffffffffffffffffffffffffffffffffffffff008160081b16837fffffffffffffffffffffff0000000000000000000000000000000000000000008416171784555050505050565b60408101823562000b668162000a6f565b60ff168252602083013562000b7b8162000990565b73ffffffffffffffffffffffffffffffffffffffff811660208401525092915050565b6000815160005b8181101562000bc1576020818501810151868301520162000ba5565b8181111562000bd1576000828601525b509290920192915050565b60006200096c62000bee838662000b9e565b8462000b9e56fe6101206040523480156200001257600080fd5b50604051620015ba380380620015ba8339810160408190526200003591620003b0565b604051631f27f92760e01b81526001600160a01b038281166004830152831690631f27f92790602401600060405180830381865afa1580156200007c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000a6919081019062000405565b604051631c4d2fd760e21b81526001600160a01b038381166004830152841690637134bf5c90602401600060405180830381865afa158015620000ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000117919081019062000405565b60405163249f75af60e21b81526001600160a01b038085166004830152859185918591859185169063927dd6bc90602401602060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200018c9190620004e1565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f0919062000508565b825162000205906000906020860190620002f1565b5081516200021b906001906020850190620002f1565b5060ff81166080524660a0526200023162000255565b60c0525050506001600160a01b0391821660e0521661010052506200060c92505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000289919062000569565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b828054620002ff906200052d565b90600052602060002090601f0160209004810192826200032357600085556200036e565b82601f106200033e57805160ff19168380011785556200036e565b828001600101855582156200036e579182015b828111156200036e57825182559160200191906001019062000351565b506200037c92915062000380565b5090565b5b808211156200037c576000815560010162000381565b6001600160a01b0381168114620003ad57600080fd5b50565b60008060408385031215620003c457600080fd5b8251620003d18162000397565b6020840151909250620003e48162000397565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600060208083850312156200041957600080fd5b82516001600160401b03808211156200043157600080fd5b818501915085601f8301126200044657600080fd5b8151818111156200045b576200045b620003ef565b604051601f8201601f19908116603f01168101908382118183101715620004865762000486620003ef565b8160405282815288868487010111156200049f57600080fd5b600093505b82841015620004c35784840186015181850187015292850192620004a4565b82841115620004d55760008684830101525b98975050505050505050565b600060208284031215620004f457600080fd5b8151620005018162000397565b9392505050565b6000602082840312156200051b57600080fd5b815160ff811681146200050157600080fd5b600181811c908216806200054257607f821691505b6020821081036200056357634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200058657607f831692505b60208084108203620005a657634e487b7160e01b86526022600452602486fd5b818015620005bd5760018114620005cf57620005fe565b60ff19861689528489019650620005fe565b60008a81526020902060005b86811015620005f65781548b820152908501908301620005db565b505084890196505b509498975050505050505050565b60805160a05160c05160e05161010051610f5b6200065f60003960006102c30152600081816101eb015281816105a301526106b20152600061056901526000610534015260006101750152610f5b6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80637a0ebc8811610097578063b132d08f11610066578063b132d08f1461026d578063d505accf14610280578063dd62ed3e14610293578063fbfa77cf146102be57600080fd5b80637a0ebc88146101e65780637ecebe001461023257806395d89b4114610252578063a9059cbb1461025a57600080fd5b8063313ce567116100d3578063313ce567146101705780633644e515146101a957806340471768146101b157806370a08231146101c657600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd1461015d575b600080fd5b61010d6102e5565b60405161011a9190610bd8565b60405180910390f35b610136610131366004610c74565b610373565b604051901515815260200161011a565b61014f60025481565b60405190815260200161011a565b61013661016b366004610c9e565b6103ec565b6101977f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161011a565b61014f610530565b6101c46101bf366004610c74565b61058b565b005b61014f6101d4366004610cda565b60036020526000908152604090205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61014f610240366004610cda565b60056020526000908152604090205481565b61010d610608565b610136610268366004610c74565b610615565b6101c461027b366004610c74565b61069a565b6101c461028e366004610cfc565b610713565b61014f6102a1366004610d6f565b600460209081526000928352604080842090915290825290205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b600080546102f290610da2565b80601f016020809104026020016040519081016040528092919081815260200182805461031e90610da2565b801561036b5780601f106103405761010080835404028352916020019161036b565b820191906000526020600020905b81548152906001019060200180831161034e57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103db9086815260200190565b60405180910390a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146104805761044e8382610e24565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040812080548592906104b5908490610e24565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061051d9087815260200190565b60405180910390a3506001949350505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461056657610561610a37565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146105fa576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106048282610ad1565b5050565b600180546102f290610da2565b33600090815260036020526040812080548391908390610636908490610e24565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103db9086815260200190565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610709576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106048282610b4a565b42841015610782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161078e610530565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156108e0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580159061095b57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6109c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610779565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051610a699190610e3b565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b8060026000828254610ae39190610f0d565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290610b7f908490610e24565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610b3e565b600060208083528351808285015260005b81811015610c0557858101830151858201604001528201610be9565b81811115610c17576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610c6f57600080fd5b919050565b60008060408385031215610c8757600080fd5b610c9083610c4b565b946020939093013593505050565b600080600060608486031215610cb357600080fd5b610cbc84610c4b565b9250610cca60208501610c4b565b9150604084013590509250925092565b600060208284031215610cec57600080fd5b610cf582610c4b565b9392505050565b600080600080600080600060e0888a031215610d1757600080fd5b610d2088610c4b565b9650610d2e60208901610c4b565b95506040880135945060608801359350608088013560ff81168114610d5257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215610d8257600080fd5b610d8b83610c4b565b9150610d9960208401610c4b565b90509250929050565b600181811c90821680610db657607f821691505b602082108103610def577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015610e3657610e36610df5565b500390565b600080835481600182811c915080831680610e5757607f831692505b60208084108203610e8f577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015610ea35760018114610ed257610eff565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650610eff565b60008a81526020902060005b86811015610ef75781548b820152908501908301610ede565b505084890196505b509498975050505050505050565b60008219821115610f2057610f20610df5565b50019056fea2646970667358221220a11195b24f792f2cb954969f59cfbfbceed801b77214aa564d22d213e974851964736f6c634300080d00336101206040523480156200001257600080fd5b50604051620017fa380380620017fa8339810160408190526200003591620003b0565b60405163172693e760e01b81526001600160a01b03828116600483015283169063172693e790602401600060405180830381865afa1580156200007c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000a6919081019062000405565b6040516369f6d9b360e01b81526001600160a01b0383811660048301528416906369f6d9b390602401600060405180830381865afa158015620000ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000117919081019062000405565b60405163249f75af60e21b81526001600160a01b038085166004830152859185918591859185169063927dd6bc90602401602060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200018c9190620004e1565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f0919062000508565b825162000205906000906020860190620002f1565b5081516200021b906001906020850190620002f1565b5060ff81166080524660a0526200023162000255565b60c0525050506001600160a01b0391821660e0521661010052506200060c92505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000289919062000569565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b828054620002ff906200052d565b90600052602060002090601f0160209004810192826200032357600085556200036e565b82601f106200033e57805160ff19168380011785556200036e565b828001600101855582156200036e579182015b828111156200036e57825182559160200191906001019062000351565b506200037c92915062000380565b5090565b5b808211156200037c576000815560010162000381565b6001600160a01b0381168114620003ad57600080fd5b50565b60008060408385031215620003c457600080fd5b8251620003d18162000397565b6020840151909250620003e48162000397565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600060208083850312156200041957600080fd5b82516001600160401b03808211156200043157600080fd5b818501915085601f8301126200044657600080fd5b8151818111156200045b576200045b620003ef565b604051601f8201601f19908116603f01168101908382118183101715620004865762000486620003ef565b8160405282815288868487010111156200049f57600080fd5b600093505b82841015620004c35784840186015181850187015292850192620004a4565b82841115620004d55760008684830101525b98975050505050505050565b600060208284031215620004f457600080fd5b8151620005018162000397565b9392505050565b6000602082840312156200051b57600080fd5b815160ff811681146200050157600080fd5b600181811c908216806200054257607f821691505b6020821081036200056357634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200058657607f831692505b60208084108203620005a657634e487b7160e01b86526022600452602486fd5b818015620005bd5760018114620005cf57620005fe565b60ff19861689528489019650620005fe565b60008a81526020902060005b86811015620005f65781548b820152908501908301620005db565b505084890196505b509498975050505050505050565b60805160a05160c05160e0516101005161118d6200066d60003960006102c30152600081816101eb0152818161046d015281816106b8015281816107a701526108e40152600061067e0152600061064901526000610175015261118d6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80637a0ebc8811610097578063b132d08f11610066578063b132d08f1461026d578063d505accf14610280578063dd62ed3e14610293578063fbfa77cf146102be57600080fd5b80637a0ebc88146101e65780637ecebe001461023257806395d89b4114610252578063a9059cbb1461025a57600080fd5b8063313ce567116100d3578063313ce567146101705780633644e515146101a957806340471768146101b157806370a08231146101c657600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd1461015d575b600080fd5b61010d6102e5565b60405161011a9190610e0a565b60405180910390f35b610136610131366004610ea6565b610373565b604051901515815260200161011a565b61014f60025481565b60405190815260200161011a565b61013661016b366004610ed0565b6103ec565b6101977f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161011a565b61014f610645565b6101c46101bf366004610ea6565b6106a0565b005b61014f6101d4366004610f0c565b60036020526000908152604090205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61014f610240366004610f0c565b60056020526000908152604090205481565b61010d61071d565b610136610268366004610ea6565b61072a565b6101c461027b366004610ea6565b6108cc565b6101c461028e366004610f2e565b610945565b61014f6102a1366004610fa1565b600460209081526000928352604080842090915290825290205481565b61020d7f000000000000000000000000000000000000000000000000000000000000000081565b600080546102f290610fd4565b80601f016020809104026020016040519081016040528092919081815260200182805461031e90610fd4565b801561036b5780601f106103405761010080835404028352916020019161036b565b820191906000526020600020905b81548152906001019060200180831161034e57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103db9086815260200190565b60405180910390a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260036020526040808220548685168084528284205492517f5fb87d1c000000000000000000000000000000000000000000000000000000008152600481019590955260248501526044840186905260648401819052608484018290529193919290917f00000000000000000000000000000000000000000000000000000000000000001690635fb87d1c9060a401600060405180830381600087803b1580156104b157600080fd5b505af11580156104c5573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461055d5761052b8582611056565b73ffffffffffffffffffffffffffffffffffffffff881660009081526004602090815260408083203384529091529020555b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16146105d15761059a8584611056565b73ffffffffffffffffffffffffffffffffffffffff8089166000908152600360205260408082209390935590881681522082860190555b8573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8760405161063091815260200190565b60405180910390a35060019695505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461067b57610676610c69565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461070f576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107198282610d03565b5050565b600180546102f290610fd4565b336000818152600360205260408082205473ffffffffffffffffffffffffffffffffffffffff8681168085528385205493517f5fb87d1c00000000000000000000000000000000000000000000000000000000815260048101969096526024860152604485018690526064850182905260848501839052929390927f00000000000000000000000000000000000000000000000000000000000000001690635fb87d1c9060a401600060405180830381600087803b1580156107eb57600080fd5b505af11580156107ff573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610874576108408483611056565b336000908152600360205260408082209290925573ffffffffffffffffffffffffffffffffffffffff871681522081850190555b60405184815273ffffffffffffffffffffffffffffffffffffffff86169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3506001949350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461093b576040517fb8965d0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107198282610d7c565b428410156109b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b600060016109c0610645565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610b12573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610b8d57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610bf3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e455200000000000000000000000000000000000060448201526064016109ab565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051610c9b919061106d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b8060026000828254610d15919061113f565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290610db1908490611056565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610d70565b600060208083528351808285015260005b81811015610e3757858101830151858201604001528201610e1b565b81811115610e49576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ea157600080fd5b919050565b60008060408385031215610eb957600080fd5b610ec283610e7d565b946020939093013593505050565b600080600060608486031215610ee557600080fd5b610eee84610e7d565b9250610efc60208501610e7d565b9150604084013590509250925092565b600060208284031215610f1e57600080fd5b610f2782610e7d565b9392505050565b600080600080600080600060e0888a031215610f4957600080fd5b610f5288610e7d565b9650610f6060208901610e7d565b95506040880135945060608801359350608088013560ff81168114610f8457600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215610fb457600080fd5b610fbd83610e7d565b9150610fcb60208401610e7d565b90509250929050565b600181811c90821680610fe857607f821691505b602082108103611021577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561106857611068611027565b500390565b600080835481600182811c91508083168061108957607f831692505b602080841082036110c1577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156110d5576001811461110457611131565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650611131565b60008a81526020902060005b868110156111295781548b820152908501908301611110565b505084890196505b509498975050505050505050565b6000821982111561115257611152611027565b50019056fea2646970667358221220b0af09bcdb5d8d91dcc5380c797a7eba643d58c9b1a86205c45950f00830724f64736f6c634300080d0033a26469706673582212209adc5f544130c6c75170a6a26695abd2108aaa40b7e59962e198321baa147b7564736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13
-----Decoded View---------------
Arg [0] : protocolFeeInfo_ (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [1] : 0000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13
Deployed Bytecode Sourcemap
99589:6568:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1103:506;;;;;;:::i;:::-;;:::i;:::-;;103215:251;;;;;;:::i;:::-;;:::i;:::-;;;1335:42:1;1323:55;;;1305:74;;1293:2;1278:18;103215:251:0;;;;;;;;1691:340;;;:::i;103773:253::-;;;;;;:::i;:::-;;:::i;104396:424::-;;;;;;:::i;:::-;;:::i;256:20::-;;;;;;;;;100934:38;;;;;;;;;;;;;;;;;;;;2286:4:1;2274:17;;;2256:36;;2340:42;2328:55;;;2323:2;2308:18;;2301:83;2229:18;100934:38:0;2086:304:1;283:27:0;;;;;;;;;102088:641;;;;;;:::i;:::-;;:::i;:::-;;;;2634:42:1;2703:15;;;2685:34;;2755:15;;;;2750:2;2735:18;;2728:43;2597:18;102088:641:0;2395:382:1;1103:506:0;2159:5;;;;2145:10;:19;2137:64;;;;;;;2984:2:1;2137:64:0;;;2966:21:1;;;3003:18;;;2996:30;3062:34;3042:18;;;3035:62;3114:18;;2137:64:0;;;;;;;;;1242:6:::1;1238:364;;;1296:22;::::0;::::1;::::0;::::1;::::0;:34:::1;;;1322:8;1296:34;1288:68;;;::::0;::::1;::::0;;3345:2:1;1288:68:0::1;::::0;::::1;3327:21:1::0;3384:2;3364:18;;;3357:30;3423:23;3403:18;;;3396:51;3464:18;;1288:68:0::1;3143:345:1::0;1288:68:0::1;1423:5;::::0;;1402:37:::1;::::0;::::1;::::0;;::::1;::::0;1423:5;::::1;::::0;1402:37:::1;::::0;::::1;1454:5;:16:::0;;::::1;::::0;::::1;::::0;;;::::1;;::::0;;;;1485:25;;;;::::1;::::0;;1103:506;;;:::o;1238:364::-:1;1567:12;:23:::0;;::::1;::::0;::::1;::::0;;;::::1;;::::0;;1103:506;;;:::o;103215:251::-;103337:18;103412:45;103438:4;103444:5;103451;103412:25;:45::i;:::-;103373:85;103215:251;-1:-1:-1;;;103215:251:0:o;1691:340::-;1759:12;;;;1811:10;:27;;1803:72;;;;;;;3695:2:1;1803:72:0;;;3677:21:1;;;3714:18;;;3707:30;3773:34;3753:18;;;3746:62;3825:18;;1803:72:0;3493:356:1;1803:72:0;1934:5;;;1913:42;;;;;;;1934:5;;;1913:42;;;1966:5;:21;;;;;;;;;;;;;;1998:25;;;;;;;1691:340::o;103773:253::-;103896:19;103973:44;103999:4;104005:5;104012:4;103973:25;:44::i;104396:424::-;2159:5;;;;2145:10;:19;2137:64;;;;;;;2984:2:1;2137:64:0;;;2966:21:1;;;3003:18;;;2996:30;3062:34;3042:18;;;3035:62;3114:18;;2137:64:0;2782:356:1;2137:64:0;104556:20:::1;;::::0;::::1;:16:::0;:20:::1;:::i;:::-;:25;;::::0;;::::1;::::0;:82:::1;;-1:-1:-1::0;104636:1:0::1;104598:26;::::0;;;::::1;::::0;::::1;;:::i;:::-;:40;;;104556:82;104538:180;;;104672:34;;;;;;;;;;;;;;104538:180;104746:16:::0;104728:15:::1;:34;104746:16:::0;104728:15;:34:::1;:::i;:::-;;;;104780:32;104795:16;104780:32;;;;;;:::i;:::-;;;;;;;;104396:424:::0;:::o;102088:641::-;102195:22;102219:23;102573:1;102565:10;;102577:4;102583:5;102536:53;;;;;:::i;:::-;2634:42:1;2703:15;;;2685:34;;2755:15;;2750:2;2735:18;;2728:43;2612:2;2597:18;102536:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;102530:59;;102644:1;102636:10;;102648:4;102654:5;102606:54;;;;;:::i;:::-;2634:42:1;2703:15;;;2685:34;;2755:15;;2750:2;2735:18;;2728:43;2612:2;2597:18;102606:54:0;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;102678:43:0;;;;2703:15:1;;;2685:34;;2755:15;;;2750:2;2735:18;;2728:43;102600:60:0;;-1:-1:-1;102678:43:0;;;;;;;;;2597:18:1;102678:43:0;;;;;;;102088:641;;;;;:::o;105087:1067::-;105241:7;105281:820;105380:12;105456:4;105241:7;105722:21;:168;;105853:37;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;105722:168;;;105779:38;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;105722:168;105976:23;;;2634:42:1;2703:15;;;105976:23:0;;;2685:34:1;2755:15;;2735:18;;;2728:43;105976:23:0;;;;;;;;;2597:18:1;;;105976:23:0;;;105622:404;;;105976:23;105622:404;;;:::i;:::-;;;;;;;;;;;;;105586:463;;;;;;105309:759;;;;;;;;;;6875:66:1;6863:79;;;;6851:92;;6980:2;6976:15;;;;6993:66;6972:88;6968:1;6959:11;;6952:109;7086:2;7077:12;;7070:28;7123:2;7114:12;;7107:28;7160:2;7151:12;;6640:529;105309:759:0;;;;;;;;;;;;;105281:802;;;;;;2559:10;2440:140;105281:820;105261:840;105087:1067;-1:-1:-1;;;;105087:1067:0:o;-1:-1:-1:-;;;;;;;;:::o;:::-;;;;;;;;:::o;14:154:1:-;100:42;93:5;89:54;82:5;79:65;69:93;;158:1;155;148:12;69:93;14:154;:::o;173:160::-;238:20;;294:13;;287:21;277:32;;267:60;;323:1;320;313:12;267:60;173:160;;;:::o;338:383::-;409:6;417;425;478:2;466:9;457:7;453:23;449:32;446:52;;;494:1;491;484:12;446:52;533:9;520:23;552:31;577:5;552:31;:::i;:::-;602:5;-1:-1:-1;626:35:1;657:2;642:18;;626:35;:::i;:::-;616:45;;680:35;711:2;700:9;696:18;680:35;:::i;:::-;670:45;;338:383;;;;;:::o;726:401::-;807:6;815;868:2;856:9;847:7;843:23;839:32;836:52;;;884:1;881;874:12;836:52;923:9;910:23;942:31;967:5;942:31;:::i;:::-;992:5;-1:-1:-1;1049:2:1;1034:18;;1021:32;1062:33;1021:32;1062:33;:::i;:::-;1114:7;1104:17;;;726:401;;;;;:::o;1649:201::-;1743:6;1796:2;1784:9;1775:7;1771:23;1767:32;1764:52;;;1812:1;1809;1802:12;1764:52;-1:-1:-1;1835:9:1;1649:201;-1:-1:-1;1649:201:1:o;3854:114::-;3938:4;3931:5;3927:16;3920:5;3917:27;3907:55;;3958:1;3955;3948:12;3973:243;4030:6;4083:2;4071:9;4062:7;4058:23;4054:32;4051:52;;;4099:1;4096;4089:12;4051:52;4138:9;4125:23;4157:29;4180:5;4157:29;:::i;4221:247::-;4280:6;4333:2;4321:9;4312:7;4308:23;4304:32;4301:52;;;4349:1;4346;4339:12;4301:52;4388:9;4375:23;4407:31;4432:5;4407:31;:::i;4473:678::-;4654:5;4641:19;4669:31;4692:7;4669:31;:::i;:::-;4732:4;4723:7;4719:18;4709:28;;4762:4;4756:11;4869:2;4800:66;4796:2;4792:75;4789:83;4783:4;4776:97;4921:2;4914:5;4910:14;4897:28;4934:33;4959:7;4934:33;:::i;:::-;5098:44;5088:7;5085:1;5081:15;5077:66;5072:2;5003:66;4999:2;4995:75;4992:83;4989:155;4983:4;4976:169;;;;4473:678;;:::o;5156:527::-;5358:2;5343:18;;5383:20;;5412:29;5383:20;5412:29;:::i;:::-;5479:4;5468:16;5450:35;;5534:4;5522:17;;5509:31;5549:33;5509:31;5549:33;:::i;:::-;5633:42;5624:7;5620:56;5613:4;5602:9;5598:20;5591:86;;5156:527;;;;:::o;6033:336::-;6074:3;6112:5;6106:12;6136:1;6146:128;6160:6;6157:1;6154:13;6146:128;;;6257:4;6242:13;;;6238:24;;6232:31;6219:11;;;6212:52;6175:12;6146:128;;;6292:6;6289:1;6286:13;6283:48;;;6327:1;6318:6;6313:3;6309:16;6302:27;6283:48;-1:-1:-1;6347:16:1;;;;;6033:336;-1:-1:-1;;6033:336:1:o;6374:261::-;6549:3;6574:55;6599:29;6624:3;6616:6;6599:29;:::i;:::-;6591:6;6574:55;:::i
Swarm Source
ipfs://9adc5f544130c6c75170a6a26695abd2108aaa40b7e59962e198321baa147b75
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 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.