More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 70 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Lend | 18731998 | 328 days ago | IN | 0 ETH | 0.01007565 | ||||
Lend | 18047511 | 424 days ago | IN | 0 ETH | 0.00394505 | ||||
Lend | 17933193 | 440 days ago | IN | 0 ETH | 0.00467016 | ||||
Lend | 17816775 | 456 days ago | IN | 0 ETH | 0.00435574 | ||||
Lend | 17805745 | 458 days ago | IN | 0 ETH | 0.00607342 | ||||
Lend | 17746227 | 466 days ago | IN | 0 ETH | 0.00759814 | ||||
Lend | 17698021 | 473 days ago | IN | 0 ETH | 0.00654327 | ||||
Lend | 17697978 | 473 days ago | IN | 0 ETH | 0.00904498 | ||||
Lend | 17697958 | 473 days ago | IN | 0 ETH | 0.00647649 | ||||
Lend | 17691678 | 474 days ago | IN | 0 ETH | 0.01038852 | ||||
Lend | 17691621 | 474 days ago | IN | 0 ETH | 0.01261585 | ||||
Lend | 17691552 | 474 days ago | IN | 0 ETH | 0.00865456 | ||||
Lend | 17691530 | 474 days ago | IN | 0 ETH | 0.00770201 | ||||
Lend | 17690842 | 474 days ago | IN | 0 ETH | 0.00927867 | ||||
Lend | 17690731 | 474 days ago | IN | 0 ETH | 0.0087068 | ||||
Lend | 17690663 | 474 days ago | IN | 0 ETH | 0.0079397 | ||||
Lend | 17690426 | 474 days ago | IN | 0 ETH | 0.00852305 | ||||
Lend | 17680733 | 475 days ago | IN | 0 ETH | 0.0083292 | ||||
Lend | 17680536 | 475 days ago | IN | 0 ETH | 0.01648297 | ||||
Lend | 17679507 | 476 days ago | IN | 0 ETH | 0.01988587 | ||||
Lend | 17679145 | 476 days ago | IN | 0 ETH | 0.01748826 | ||||
Lend | 17679138 | 476 days ago | IN | 0 ETH | 0.01680517 | ||||
Lend | 17679076 | 476 days ago | IN | 0 ETH | 0.02317865 | ||||
Lend | 17679071 | 476 days ago | IN | 0 ETH | 0.02386589 | ||||
Lend | 17678029 | 476 days ago | IN | 0 ETH | 0.0113793 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Lender
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/Marketplace.sol'; import 'src/lib/Swivel.sol'; import 'src/lib/Pendle.sol'; import 'src/lib/Element.sol'; import 'src/lib/Safe.sol'; import 'src/lib/Cast.sol'; import 'src/lib/RevertMsgExtractor.sol'; import 'src/lib/Maturities.sol'; import 'src/errors/Exception.sol'; import 'src/interfaces/ITempus.sol'; import 'src/interfaces/ITempusAMM.sol'; import 'src/interfaces/ITempusPool.sol'; import 'src/interfaces/ITempusToken.sol'; import 'src/interfaces/IERC20.sol'; import 'src/interfaces/IERC5095.sol'; import 'src/interfaces/ISensePeriphery.sol'; import 'src/interfaces/ISenseDivider.sol'; import 'src/interfaces/IYield.sol'; import 'src/interfaces/ISwivel.sol'; import 'src/interfaces/IElementVault.sol'; import 'src/interfaces/IAPWineAMMPool.sol'; import 'src/interfaces/IAPWineRouter.sol'; import 'src/interfaces/INotional.sol'; import 'src/interfaces/IPendle.sol'; import 'src/interfaces/IPendleMarket.sol'; /// @title Lender /// @author Sourabh Marathe, Julian Traversa, Rob Robbins /// @notice The lender contract executes loans on behalf of users /// @notice The contract holds the principal tokens and mints an ERC-5095 tokens to users to represent their loans contract Lender { /// @notice minimum wait before the admin may withdraw funds or change the fee rate uint256 public constant HOLD = 3 days; /// @notice address that is allowed to set and withdraw fees, disable principals, etc. It is commonly used in the authorized modifier. address public admin; /// @notice address of the MarketPlace contract, used to access the markets mapping address public marketPlace; /// @notice mapping that determines if a principal has been paused by the admin mapping(uint8 => bool) public paused; /// @notice flag that allows admin to stop all lending and minting across the entire protocol bool public halted; /// @notice contract used to execute swaps on Swivel's exchange address public immutable swivelAddr; /// @notice a SushiSwap router used by Pendle to execute swaps address public immutable pendleAddr; /// @notice a pool router used by APWine to execute swaps address public immutable apwineAddr; /// @notice a mapping that tracks the amount of unswapped premium by market. This underlying is later transferred to the Redeemer during Swivel's redeem call mapping(address => mapping(uint256 => uint256)) public premiums; /// @notice this value determines the amount of fees paid on loans uint256 public feenominator; /// @notice represents a point in time where the feenominator may change uint256 public feeChange; /// @notice represents a minimum that the feenominator must exceed uint256 public constant MIN_FEENOMINATOR = 500; /// @notice maps underlying tokens to the amount of fees accumulated for that token mapping(address => uint256) public fees; /// @notice maps a token address to a point in time, a hold, after which a withdrawal can be made mapping(address => uint256) public withdrawals; // Reantrancy protection uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status = _NOT_ENTERED; // Rate limiting protection /// @notice maximum amount of value that can flow through a protocol in a day (in USD) uint256 public maximumValue = 250_000e27; /// @notice maps protocols to how much value, in USD, has flowed through each protocol mapping(uint8 => uint256) public protocolFlow; /// @notice timestamp from which values flowing through protocol has begun mapping(uint8 => uint256) public periodStart; /// @notice estimated price of ether, set by the admin uint256 public etherPrice = 2_500; /// @notice emitted upon lending to a protocol event Lend( uint8 principal, address indexed underlying, uint256 indexed maturity, uint256 returned, uint256 spent, address sender ); /// @notice emitted upon minting Illuminate principal tokens event Mint( uint8 principal, address indexed underlying, uint256 indexed maturity, uint256 amount ); /// @notice emitted upon scheduling a withdrawal event ScheduleWithdrawal(address indexed token, uint256 hold); /// @notice emitted upon blocking a scheduled withdrawal event BlockWithdrawal(address indexed token); /// @notice emitted upon changing the admin event SetAdmin(address indexed admin); /// @notice emitted upon setting the fee rate event SetFee(uint256 indexed fee); /// @notice emitted upon scheduling a fee change event ScheduleFeeChange(uint256 when); /// @notice emitted upon blocking a scheduled fee change event BlockFeeChange(); /// @notice emitted upon pausing or unpausing of a principal event PausePrincipal(uint8 principal, bool indexed state); /// @notice emitted upon pausing or unpausing minting, lending and redeeming event PauseIlluminate(bool state); /// @notice ensures that only a certain address can call the function /// @param a address that msg.sender must be to be authorized modifier authorized(address a) { if (msg.sender != a) { revert Exception(0, 0, 0, msg.sender, a); } _; } /// @notice reverts on all markets where the paused mapping returns true /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param p principal value according to the MarketPlace's Principals Enum modifier unpaused( address u, uint256 m, uint8 p ) { if (paused[p] || halted) { revert Exception(1, p, 0, address(0), address(0)); } _; } /// @notice reverts if called after maturity /// @param m maturity (timestamp) of the market modifier matured(uint256 m) { if (block.timestamp > m) { revert Exception(2, block.timestamp, m, address(0), address(0)); } _; } /// @notice prevents users from re-entering contract modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true if (_status == _ENTERED) { revert Exception(30, 0, 0, address(0), address(0)); } // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /// @notice initializes the Lender contract /// @param s the Swivel contract /// @param p the Pendle contract /// @param a the APWine contract constructor(address s, address p, address a) { admin = msg.sender; swivelAddr = s; pendleAddr = p; apwineAddr = a; feenominator = 1000; } /// @notice approves the redeemer contract to spend the principal tokens held by the lender contract. /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param r the address being approved, in this case the redeemer contract /// @return bool true if the approval was successful function approve( address u, uint256 m, address r ) external authorized(admin) returns (bool) { // approve the underlying for max per given principal for (uint8 i; i != 9; ) { // get the principal token's address address token = IMarketPlace(marketPlace).markets(u, m, i); // check that the token is defined for this particular market if (token != address(0)) { // max approve the token Safe.approve(IERC20(token), r, type(uint256).max); } unchecked { ++i; } } // approve the redeemer to receive underlying from the lender Safe.approve(IERC20(u), r, type(uint256).max); return true; } /// @notice bulk approves the usage of addresses at the given ERC20 addresses. /// @dev the lengths of the inputs must match because the arrays are paired by index /// @param u array of ERC20 token addresses that will be approved on /// @param a array of addresses that will be approved /// @return true if successful function approve( address[] calldata u, address[] calldata a ) external authorized(admin) returns (bool) { for (uint256 i; i != u.length; ) { IERC20 uToken = IERC20(u[i]); if (address(0) != (address(uToken))) { Safe.approve(uToken, a[i], type(uint256).max); } unchecked { ++i; } } return true; } /// @notice approves market contracts that require lender approval /// @param u address of an underlying asset /// @param a APWine's router contract /// @param e Element's vault contract /// @param n Notional's token contract /// @param p Sense's periphery contract function approve( address u, address a, address e, address n, address p ) external authorized(marketPlace) { uint256 max = type(uint256).max; IERC20 uToken = IERC20(u); if (a != address(0)) { Safe.approve(uToken, a, max); } if (e != address(0)) { Safe.approve(uToken, e, max); } if (n != address(0)) { Safe.approve(uToken, n, max); } if (p != address(0)) { Safe.approve(uToken, p, max); } if (IERC20(u).allowance(address(this), swivelAddr) == 0) { Safe.approve(uToken, swivelAddr, max); } } /// @notice sets the admin address /// @param a address of a new admin /// @return bool true if successful function setAdmin(address a) external authorized(admin) returns (bool) { admin = a; emit SetAdmin(a); return true; } /// @notice sets the feenominator to the given value /// @param f the new value of the feenominator, fees are not collected when the feenominator is 0 /// @return bool true if successful function setFee(uint256 f) external authorized(admin) returns (bool) { uint256 feeTime = feeChange; if (feeTime == 0) { revert Exception(23, 0, 0, address(0), address(0)); } else if (block.timestamp < feeTime) { revert Exception( 24, block.timestamp, feeTime, address(0), address(0) ); } else if (f < MIN_FEENOMINATOR) { revert Exception(25, 0, 0, address(0), address(0)); } feenominator = f; delete feeChange; emit SetFee(f); return true; } /// @notice sets the address of the marketplace contract which contains the addresses of all the fixed rate markets /// @param m the address of the marketplace contract /// @return bool true if the address was set function setMarketPlace( address m ) external authorized(admin) returns (bool) { if (marketPlace != address(0)) { revert Exception(5, 0, 0, marketPlace, address(0)); } marketPlace = m; return true; } /// @notice sets the ethereum price which is used in rate limiting /// @param p the new price /// @return bool true if the price was set function setEtherPrice( uint256 p ) external authorized(admin) returns (bool) { etherPrice = p; return true; } /// @notice sets the maximum value that can flow through a protocol /// @param m the maximum value by protocol /// @return bool true if the price was set function setMaxValue(uint256 m) external authorized(admin) returns (bool) { maximumValue = m; return true; } /// @notice mint swaps the sender's principal tokens for Illuminate's ERC5095 tokens in effect, this opens a new fixed rate position for the sender on Illuminate /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount being minted /// @return bool true if the mint was successful function mint( uint8 p, address u, uint256 m, uint256 a ) external nonReentrant unpaused(u, m, p) returns (bool) { // Fetch the desired principal token address principal = IMarketPlace(marketPlace).markets(u, m, p); // Disallow mints if market is not initialized if (principal == address(0)) { revert Exception(26, 0, 0, address(0), address(0)); } // Get the maturity of the principal token uint256 maturity; if (p == uint8(MarketPlace.Principals.Illuminate)) { revert Exception(32, 0, 0, address(0), address(0)); } else if (p == uint8(MarketPlace.Principals.Swivel)) { maturity = Maturities.swivel(principal); } else if (p == uint8(MarketPlace.Principals.Yield)) { maturity = Maturities.yield(principal); } else if (p == uint8(MarketPlace.Principals.Element)) { maturity = Maturities.element(principal); } else if (p == uint8(MarketPlace.Principals.Pendle)) { maturity = Maturities.pendle(principal); } else if (p == uint8(MarketPlace.Principals.Tempus)) { maturity = Maturities.tempus(principal); } else if (p == uint8(MarketPlace.Principals.Apwine)) { maturity = Maturities.apwine(principal); } else if (p == uint8(MarketPlace.Principals.Notional)) { maturity = Maturities.notional(principal); } // Confirm that the principal token has not matured yet if (block.timestamp > maturity || maturity == 0) { revert Exception( 7, maturity, block.timestamp, address(0), address(0) ); } // Transfer the users principal tokens to the lender contract Safe.transferFrom(IERC20(principal), msg.sender, address(this), a); // Calculate how much should be minted based on the decimal difference uint256 mintable = convertDecimals(u, principal, a); // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, mintable); // Mint the tokens received from the user IERC5095(principalToken(u, m)).authMint(msg.sender, mintable); emit Mint(p, u, m, mintable); return true; } /// @notice lend method for the Illuminate and Yield protocols /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying tokens to lend /// @param y Yield Space Pool for the principal token /// @param minimum slippage limit, minimum amount to PTs to buy /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256 a, address y, uint256 minimum ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Get principal token for this market address principal = IMarketPlace(marketPlace).markets(u, m, p); // Extract fee fees[u] = fees[u] + a / feenominator; // Transfer underlying from user to the lender contract Safe.transferFrom(IERC20(u), msg.sender, address(this), a); // Make sure the Yield Space Pool matches the market address fyToken = IYield(y).fyToken(); if (IYield(y).fyToken() != principal) { revert Exception(12, 0, 0, fyToken, principal); } address base = address(IYield(y).base()); if (base != u) { revert Exception(27, 0, 0, base, u); } // Set who should get the tokens that are swapped for address receiver = address(this); // If lending on Illuminate, swap directly to the caller if (p == uint8(MarketPlace.Principals.Illuminate)) { receiver = msg.sender; } // Swap underlying for PTs to lender uint256 returned = yield( u, y, a - a / feenominator, receiver, principal, minimum ); // Convert decimals from principal token to underlying returned = convertDecimals(u, principal, returned); // Only mint iPTs to user if lending through Yield protocol if (p == uint8(MarketPlace.Principals.Yield)) { // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, returned); // Mint Illuminate PTs to msg.sender IERC5095(principalToken(u, m)).authMint(msg.sender, returned); } emit Lend(p, u, m, returned, a, msg.sender); return returned; } /// @notice lend method signature for Swivel /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a array of amounts of underlying tokens lent to each order in the orders array /// @param y Yield Space Pool for the Illuminate PT in this market /// @param o array of Swivel orders being filled /// @param s array of signatures for each order in the orders array /// @param e flag to indicate if returned funds should be swapped in Yield Space Pool /// @param premiumSlippage slippage limit, minimum amount to PTs to buy /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256[] memory a, address y, Swivel.Order[] calldata o, Swivel.Components[] calldata s, bool e, uint256 premiumSlippage ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Ensure all the orders are for the underlying asset swivelVerify(o, u); // Lent represents the total amount of underlying to be lent uint256 lent = swivelAmount(a); // Get the underlying balance prior to calling initiate uint256 starting = IERC20(u).balanceOf(address(this)); // Transfer underlying token from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), lent); // Calculate fee for the total amount to be lent uint256 fee = lent / feenominator; { // Get last order to be processed's index uint256 lastIndex = a.length - 1; // Add the accumulated fees to the total a[lastIndex] = a[lastIndex] - fee; // Revert here if fee not paid // Extract fee fees[u] += fee; } uint256 received; { // Get the starting amount of principal tokens uint256 startingZcTokens = IERC20( IMarketPlace(marketPlace).markets(u, m, p) ).balanceOf(address(this)); // Fill the given orders on Swivel ISwivel(swivelAddr).initiate(o, a, s); // Compute how many principal tokens were received received = (IERC20(IMarketPlace(marketPlace).markets(u, m, p)) .balanceOf(address(this)) - startingZcTokens); // Calculate the premium uint256 premium = (IERC20(u).balanceOf(address(this)) - starting) - fee; // Calculate the fee on the premium uint256 premiumFee = premium / feenominator; // Extract fee from premium fees[u] += premiumFee; // Remove the fee from the premium premium = premium - premiumFee; // Store how much the user received in exchange for swapping the premium for iPTs uint256 swapped; if (e) { // Swap the premium for Illuminate principal tokens swapped += yield( u, y, premium, msg.sender, IMarketPlace(marketPlace).markets(u, m, 0), premiumSlippage ); } else { // Send the premium to the redeemer to hold until redemption premiums[u][m] = premiums[u][m] + premium; // Account for the premium received = received + premium; } // Mint Illuminate principal tokens to the user IERC5095(principalToken(u, m)).authMint(msg.sender, received); emit Lend( uint8(MarketPlace.Principals.Swivel), u, m, received + swapped, lent, msg.sender ); } // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, received); return received; } /// @notice lend method signature for Element /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying tokens to lend /// @param r slippage limit, minimum amount to PTs to buy /// @param d deadline is a timestamp by which the swap must be executed /// @param e Element pool that is lent to /// @param i the id of the pool /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256 a, uint256 r, uint256 d, address e, bytes32 i ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Get the principal token for this market for Element address principal = IMarketPlace(marketPlace).markets(u, m, p); // Transfer underlying token from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), a); // Track the accumulated fees fees[u] = fees[u] + a / feenominator; uint256 purchased; { // Calculate the amount to be lent uint256 lent = a - a / feenominator; // Create the variables needed to execute an Element swap Element.FundManagement memory fund = Element.FundManagement({ sender: address(this), recipient: payable(address(this)), fromInternalBalance: false, toInternalBalance: false }); Element.SingleSwap memory swap = Element.SingleSwap({ poolId: i, amount: lent, kind: Element.SwapKind.GIVEN_IN, assetIn: IAny(u), assetOut: IAny(principal), userData: '0x00000000000000000000000000000000000000000000000000000000000000' }); // Conduct the swap on Element purchased = elementSwap(e, swap, fund, r, d); // Convert decimals from principal token to underlying purchased = convertDecimals(u, principal, purchased); } // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, purchased); // Mint tokens to the user IERC5095(principalToken(u, m)).authMint(msg.sender, purchased); emit Lend(p, u, m, purchased, a, msg.sender); return purchased; } /// @notice lend method signature for Pendle /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying tokens to lend /// @param r slippage limit, minimum amount to PTs to buy /// @param g guess parameters for the swap /// @param market contract that corresponds to the market for the PT /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256 a, uint256 r, Pendle.ApproxParams calldata g, address market ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Instantiate market and tokens address principal = IMarketPlace(marketPlace).markets(u, m, p); // Confirm the market corresponds to this Illuminate market (, address marketPrincipal, ) = IPendleMarket(market).readTokens(); if (marketPrincipal != principal) { revert Exception(27, 0, 0, market, principal); } // Transfer funds from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), a); uint256 returned; { // Add the accumulated fees to the total uint256 fee = a / feenominator; fees[u] = fees[u] + fee; // Setup the token input Pendle.TokenInput memory input = Pendle.TokenInput( u, a - fee, u, address(0), address(0), '0x00000000000000000000000000000000000000000000000000000000000000' ); // Swap on the Pendle Router using the provided market and params (returned, ) = IPendle(pendleAddr).swapExactTokenForPt( address(this), market, r, g, input ); // Convert decimals from principal token to underlying returned = convertDecimals(u, principal, returned); } // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, returned); // Mint Illuminate zero coupons IERC5095(principalToken(u, m)).authMint(msg.sender, returned); emit Lend(p, u, m, returned, a, msg.sender); return returned; } /// @notice lend method signature for Tempus and APWine /// @param p value of a specific principal according to the Illuminate Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of principal tokens to lend /// @param r minimum amount to return when executing the swap (sets a limit to slippage) /// @param d deadline is a timestamp by which the swap must be executed /// @param x Tempus or APWine AMM that executes the swap /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256 a, uint256 r, uint256 d, address x ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { address principal = IMarketPlace(marketPlace).markets(u, m, p); // Transfer funds from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), a); uint256 lent; { // Add the accumulated fees to the total uint256 fee = a / feenominator; fees[u] = fees[u] + fee; // Calculate amount to be lent out lent = a - fee; } // Get the starting balance of the principal token uint256 start = IERC20(principal).balanceOf(address(this)); if (p == uint8(MarketPlace.Principals.Tempus)) { // Get the Tempus pool from the principal token ITempusPool pool = ITempusPool(ITempusToken(principal).pool()); // Get the pool of the contract the user submitted address userPool = ITempusAMM(x).tempusPool(); // Confirm that the pool matches the principal token if (address(pool) != userPool) { revert Exception(27, 0, 0, address(pool), userPool); } // Get the Tempus router from the principal token address controller = pool.controller(); // Swap on the Tempus router using the provided market and params ITempus(controller).depositAndFix(x, lent, true, 0, d); } else if (p == uint8(MarketPlace.Principals.Apwine)) { address poolUnderlying = IAPWineAMMPool(x) .getUnderlyingOfIBTAddress(); if (u != poolUnderlying) { revert Exception(27, 0, 0, u, poolUnderlying); } address poolPrincipal = IAPWineAMMPool(x).getPTAddress(); if (principal != poolPrincipal) { revert Exception(27, 0, 0, principal, poolPrincipal); } // Swap on the APWine Pool using the provided market and params IAPWineRouter(apwineAddr).swapExactAmountIn( x, apwinePairPath(), apwineTokenPath(), lent, r, address(this), d, address(0) ); } // Calculate the amount of Tempus principal tokens received after the deposit uint256 received = IERC20(principal).balanceOf(address(this)) - start; // Convert decimals from principal token to underlying received = convertDecimals(u, principal, received); // Verify that a minimum number of principal tokens were received if (p == uint8(MarketPlace.Principals.Tempus) && received < r) { revert Exception(11, received, r, address(0), address(0)); } // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, received); // Mint Illuminate zero coupons IERC5095(principalToken(u, m)).authMint(msg.sender, received); emit Lend(p, u, m, received, a, msg.sender); return received; } /// @notice lend method signature for Sense /// @dev this method can be called before maturity to lend to Sense while minting Illuminate tokens /// @dev Sense provides a [divider] contract that splits [target] assets (underlying) into PTs and YTs. Each [target] asset has a [series] of contracts, each identifiable by their [maturity]. /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying tokens to lend /// @param r slippage limit, minimum amount to PTs to buy /// @param x periphery contract that is used to conduct the swap /// @param s Sense's maturity for the given market /// @param adapter Sense's adapter necessary to facilitate the swap /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint128 a, uint256 r, address x, uint256 s, address adapter ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Get the periphery's divider contract to verify that it maps to the prinicpal token of the market address divider = ISensePeriphery(x).divider(); // Get the principal token for the user submitted periphery address userPrincipal = ISenseDivider(divider).pt(adapter, s); // Retrieve the principal token for this market IERC20 token = IERC20(IMarketPlace(marketPlace).markets(u, m, p)); // Verify that the `x` parameter matches the market's Sense principal token if (address(token) != userPrincipal) { revert Exception(27, 0, 0, address(token), userPrincipal); } // Transfer funds from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), a); // Determine the fee uint256 fee = a / feenominator; // Add the accumulated fees to the total fees[u] = fees[u] + fee; // Determine lent amount after fees uint256 lent = a - fee; // Stores the amount of principal tokens received in swap for underlying uint256 received; { // Get the starting balance of the principal token uint256 starting = token.balanceOf(address(this)); // Swap those tokens for the principal tokens ISensePeriphery(x).swapUnderlyingForPTs(adapter, s, lent, r); // Calculate number of principal tokens received in the swap received = token.balanceOf(address(this)) - starting; // Verify that we received the principal tokens if (received < r) { revert Exception(11, 0, 0, address(0), address(0)); } } // Get the Illuminate PT address ipt = principalToken(u, m); // Calculate the mintable amount of tokens for Sense due to decimal mismatch uint256 mintable = convertDecimals(u, address(token), received); // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, mintable); // Mint the Illuminate tokens based on the returned amount IERC5095(ipt).authMint(msg.sender, mintable); emit Lend(p, u, m, mintable, a, msg.sender); return mintable; } /// @dev lend method signature for Notional /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying tokens to lend /// @param r slippage limit, minimum amount to PTs to buy /// @return uint256 the amount of principal tokens lent out function lend( uint8 p, address u, uint256 m, uint256 a, uint256 r ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) { // Confirm that we are using Notional's PT to avoid conflicts with other ERC4626 tokens if (p != uint8(MarketPlace.Principals.Notional)) { revert Exception(6, p, 0, address(0), address(0)); } // Instantiate Notional princpal token address token = IMarketPlace(marketPlace).markets(u, m, p); // Transfer funds from user to Illuminate Safe.transferFrom(IERC20(u), msg.sender, address(this), a); // Determine the fee uint256 fee = a / feenominator; // Add the accumulated fees to the total fees[u] = fees[u] + fee; // Swap on the Notional Token wrapper uint256 received = INotional(token).deposit(a - fee, address(this)); // Convert decimals from principal token to underlying received = convertDecimals(u, token, received); // Verify that we received the principal tokens if (received < r) { revert Exception(16, received, r, address(0), address(0)); } // Confirm that minted iPT amount will not exceed rate limit for the protocol rateLimit(p, u, received); // Mint Illuminate zero coupons IERC5095(principalToken(u, m)).authMint(msg.sender, received); emit Lend(p, u, m, received, a, msg.sender); return received; } /// @notice allows the admin to schedule the withdrawal of tokens /// @param e address of (erc20) token to withdraw /// @return bool true if successful function scheduleWithdrawal( address e ) external authorized(admin) returns (bool) { // Calculate the timestamp that must be passed prior to withdrawal uint256 when = block.timestamp + HOLD; // Set the timestamp threshold for the token being withdrawn withdrawals[e] = when; emit ScheduleWithdrawal(e, when); return true; } /// @notice emergency function to block unplanned withdrawals /// @param e address of token withdrawal to block /// @return bool true if successful function blockWithdrawal( address e ) external authorized(admin) returns (bool) { // Resets threshold to 0 for the token, stopping withdrawl of the token delete withdrawals[e]; emit BlockWithdrawal(e); return true; } /// @notice allows the admin to schedule a change to the fee denominators function scheduleFeeChange() external authorized(admin) returns (bool) { // Calculate the timestamp that must be passed prior to setting thew new fee uint256 when = block.timestamp + HOLD; // Store the timestamp that must be passed to update the fee rate feeChange = when; emit ScheduleFeeChange(when); return true; } /// @notice Emergency function to block unplanned changes to fee structure function blockFeeChange() external authorized(admin) returns (bool) { // Resets threshold to 0 for the token, stopping the scheduling of a fee rate change delete feeChange; emit BlockFeeChange(); return true; } /// @notice allows the admin to withdraw the given token, provided the holding period has been observed /// @param e Address of token to withdraw /// @return bool true if successful function withdraw(address e) external authorized(admin) returns (bool) { // Get the minimum timestamp to withdraw the token uint256 when = withdrawals[e]; // Check that the withdrawal was scheduled for the token if (when == 0) { revert Exception(18, 0, 0, address(0), address(0)); } // Check that it is now past the scheduled timestamp for withdrawing the token if (block.timestamp < when) { revert Exception(19, 0, 0, address(0), address(0)); } // Reset the scheduled withdrawal delete withdrawals[e]; // Reset the fees for the token (relevant when withdrawing underlying for markets) delete fees[e]; // Send the token to the admin IERC20 token = IERC20(e); Safe.transfer(token, admin, token.balanceOf(address(this))); return true; } /// @notice withdraws accumulated lending fees of the underlying token /// @param e address of the underlying token to withdraw /// @return bool true if successful function withdrawFee(address e) external authorized(admin) returns (bool) { // Get the token to be withdrawn IERC20 token = IERC20(e); // Get the balance to be transferred uint256 balance = fees[e]; // Reset accumulated fees of the token to 0 fees[e] = 0; // Transfer the accumulated fees to the admin Safe.transfer(token, admin, balance); return true; } /// @notice pauses a market and prevents execution of all lending for that principal /// @param p principal value according to the MarketPlace's Principals Enum /// @param b bool representing whether to pause or unpause /// @return bool true if successful function pause(uint8 p, bool b) external authorized(admin) returns (bool) { // Set the paused state for the principal token in the market paused[p] = b; emit PausePrincipal(p, b); return true; } /// @notice pauses Illuminate's redeem, mint and lend methods from being used /// @param b bool representing whether to pause or unpause Illuminate /// @return bool true if successfully set function pauseIlluminate(bool b) external authorized(admin) returns (bool) { halted = b; emit PauseIlluminate(b); return true; } /// @notice Tranfers FYTs to Redeemer (used specifically for APWine redemptions) /// @param f FYT contract address /// @param a amount of tokens to send to the redeemer function transferFYTs( address f, uint256 a ) external authorized(IMarketPlace(marketPlace).redeemer()) { // Transfer the Lender's FYT tokens to the Redeemer Safe.transfer(IERC20(f), IMarketPlace(marketPlace).redeemer(), a); } /// @notice Transfers premium from the market to Redeemer (used specifically for Swivel redemptions) /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market function transferPremium( address u, uint256 m ) external authorized(IMarketPlace(marketPlace).redeemer()) { Safe.transfer( IERC20(u), IMarketPlace(marketPlace).redeemer(), premiums[u][m] ); premiums[u][m] = 0; } /// @notice Allows batched call to self (this contract). /// @param c An array of inputs for each call. function batch( bytes[] calldata c ) external payable returns (bytes[] memory results) { results = new bytes[](c.length); for (uint256 i; i < c.length; i++) { (bool success, bytes memory result) = address(this).delegatecall( c[i] ); if (!success) revert(RevertMsgExtractor.getRevertMsg(result)); results[i] = result; } } /// @notice swaps underlying premium via a Yield Space Pool /// @dev this method is only used by the Yield, Illuminate and Swivel protocols /// @param u address of an underlying asset /// @param y Yield Space Pool for the principal token /// @param a amount of underlying tokens to lend /// @param r the receiving address for PTs /// @param p the principal token in the Yield Space Pool /// @param m the minimum amount to purchase /// @return uint256 the amount of tokens sent to the Yield Space Pool function yield( address u, address y, uint256 a, address r, address p, uint256 m ) internal returns (uint256) { // Get the starting balance (to verify receipt of tokens) uint256 starting = IERC20(p).balanceOf(r); // Get the amount of tokens received for swapping underlying uint128 returned = IYield(y).sellBasePreview(Cast.u128(a)); // Send the remaining amount to the Yield pool Safe.transfer(IERC20(u), y, a); // Lend out the remaining tokens in the Yield pool IYield(y).sellBase(r, returned); // Get the ending balance of principal tokens (must be at least starting + returned) uint256 received = IERC20(p).balanceOf(r) - starting; // Verify receipt of PTs from Yield Space Pool if (received < m) { revert Exception(11, received, m, address(0), address(0)); } return received; } /// @notice returns the amount of underlying tokens to be used in a Swivel lend function swivelAmount(uint256[] memory a) internal pure returns (uint256) { uint256 lent; // Iterate through each order a calculate the total lent and returned for (uint256 i; i != a.length; ) { { // Sum the total amount lent to Swivel lent = lent + a[i]; } unchecked { ++i; } } return lent; } /// @notice reverts if any orders are not for the market function swivelVerify(Swivel.Order[] memory o, address u) internal pure { for (uint256 i; i != o.length; ) { address orderUnderlying = o[i].underlying; if (u != orderUnderlying) { revert Exception(3, 0, 0, u, orderUnderlying); } unchecked { ++i; } } } /// @notice executes a swap for and verifies receipt of Element PTs function elementSwap( address e, Element.SingleSwap memory s, Element.FundManagement memory f, uint256 r, uint256 d ) internal returns (uint256) { // Get the principal token address principal = address(s.assetOut); // Get the intial balance uint256 starting = IERC20(principal).balanceOf(address(this)); // Conduct the swap on Element IElementVault(e).swap(s, f, r, d); // Get how many PTs were purchased by the swap call uint256 purchased = IERC20(principal).balanceOf(address(this)) - starting; // Verify that a minimum amount was received if (purchased < r) { revert Exception(11, 0, 0, address(0), address(0)); } // Return the net amount of principal tokens acquired after the swap return purchased; } /// @notice returns array token path required for APWine's swap method /// @return array of uint256[] as laid out in APWine's docs function apwineTokenPath() internal pure returns (uint256[] memory) { uint256[] memory tokenPath = new uint256[](2); tokenPath[0] = 1; tokenPath[1] = 0; return tokenPath; } /// @notice returns array pair path required for APWine's swap method /// @return array of uint256[] as laid out in APWine's docs function apwinePairPath() internal pure returns (uint256[] memory) { uint256[] memory pairPath = new uint256[](1); pairPath[0] = 0; return pairPath; } /// @notice retrieves the ERC5095 token for the given market /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @return address of the ERC5095 token for the market function principalToken(address u, uint256 m) internal returns (address) { return IMarketPlace(marketPlace).markets(u, m, 0); } /// @notice converts principal decimal amount to underlying's decimal amount /// @param u address of an underlying asset /// @param p address of a principal token /// @param a amount denominated in principal token's decimals /// @return uint256 in underlying decimals function convertDecimals( address u, address p, uint256 a ) internal view returns (uint256) { // Get the decimals of the underlying asset uint8 underlyingDecimals = IERC20(u).decimals(); // Get the decimals of the principal token uint8 principalDecimals = IERC20(p).decimals(); // Determine which asset has more decimals if (underlyingDecimals > principalDecimals) { // Shift decimals accordingly return a * 10 ** (underlyingDecimals - principalDecimals); } return a / 10 ** (principalDecimals - underlyingDecimals); } /// @notice limits the amount of funds (in USD value) that can flow through a principal in a day /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param a amount being minted which is normalized to 18 decimals prior to check /// @return bool true if successful, reverts otherwise function rateLimit(uint8 p, address u, uint256 a) internal returns (bool) { // Get amount in USD to be minted uint256 valueToMint = a; // In case of stETH, we will calculate an approximate USD value // 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 (stETH address) if (u == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) { valueToMint = etherPrice * valueToMint; } // Normalize the value to be minted to 27 decimals valueToMint = valueToMint * 10 ** (27 - IERC20(u).decimals()); // Cache max value uint256 maxValue = maximumValue; // Transactions of greater than the max value of USD are rate limited if (valueToMint > maxValue) { revert Exception(31, protocolFlow[p], p, address(0), address(0)); } // Cache protocol flow value uint256 flow = protocolFlow[p] + valueToMint; // Update the amount of USD value flowing through the protocol protocolFlow[p] = flow; // If more than one day has passed, do not rate limit if (block.timestamp - periodStart[p] > 1 days) { // Reset the flow amount protocolFlow[p] = valueToMint; // Reset the period periodStart[p] = block.timestamp; } // If more than the max USD has flowed through the protocol, revert else if (flow > maxValue) { revert Exception(31, protocolFlow[p], p, address(0), address(0)); } return true; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/tokens/ERC5095.sol'; import 'src/lib/Safe.sol'; import 'src/lib/RevertMsgExtractor.sol'; import 'src/errors/Exception.sol'; import 'src/interfaces/ILender.sol'; import 'src/interfaces/ICreator.sol'; import 'src/interfaces/IPool.sol'; import 'src/interfaces/IPendleToken.sol'; import 'src/interfaces/IAPWineToken.sol'; import 'src/interfaces/IAPWineFutureVault.sol'; /// @title MarketPlace /// @author Sourabh Marathe, Julian Traversa, Rob Robbins /// @notice This contract is in charge of managing the available principals for each loan market. /// @notice In addition, this contract routes swap orders between Illuminate PTs and their respective underlying to YieldSpace pools. contract MarketPlace { /// @notice the available principals /// @dev the order of this enum is used to select principals from the markets /// mapping (e.g. Illuminate => 0, Swivel => 1, and so on) enum Principals { Illuminate, // 0 Swivel, // 1 Yield, // 2 Element, // 3 Pendle, // 4 Tempus, // 5 Sense, // 6 Apwine, // 7 Notional // 8 } /// @notice markets are defined by a tuple that points to a fixed length array of principal token addresses. mapping(address => mapping(uint256 => address[9])) public markets; /// @notice pools map markets to their respective YieldSpace pools for the MetaPrincipal token mapping(address => mapping(uint256 => address)) public pools; /// @notice address that is allowed to create markets, set pools, etc. It is commonly used in the authorized modifier. address public admin; /// @notice address of the deployed redeemer contract address public immutable redeemer; /// @notice address of the deployed lender contract address public immutable lender; /// @notice address of the deployed creator contract address public immutable creator; /// @notice emitted upon the creation of a new market event CreateMarket( address indexed underlying, uint256 indexed maturity, address[9] tokens, address element, address apwine ); /// @notice emitted upon setting a principal token event SetPrincipal( address indexed underlying, uint256 indexed maturity, address indexed principal, uint8 protocol ); /// @notice emitted upon swapping with the pool event Swap( address indexed underlying, uint256 indexed maturity, address sold, address bought, uint256 received, uint256 spent, address spender ); /// @notice emitted upon minting tokens with the pool event Mint( address indexed underlying, uint256 indexed maturity, uint256 underlyingIn, uint256 principalTokensIn, uint256 minted, address minter ); /// @notice emitted upon burning tokens with the pool event Burn( address indexed underlying, uint256 indexed maturity, uint256 tokensBurned, uint256 underlyingReceived, uint256 principalTokensReceived, address burner ); /// @notice emitted upon changing the admin event SetAdmin(address indexed admin); /// @notice emitted upon setting a pool event SetPool( address indexed underlying, uint256 indexed maturity, address indexed pool ); /// @notice ensures that only a certain address can call the function /// @param a address that msg.sender must be to be authorized modifier authorized(address a) { if (msg.sender != a) { revert Exception(0, 0, 0, msg.sender, a); } _; } /// @notice initializes the MarketPlace contract /// @param r address of the deployed redeemer contract /// @param l address of the deployed lender contract /// @param c address of the deployed creator contract constructor(address r, address l, address c) { admin = msg.sender; redeemer = r; lender = l; creator = c; } /// @notice creates a new market for the given underlying token and maturity /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param t principal token addresses for this market /// @param n name for the Illuminate token /// @param s symbol for the Illuminate token /// @param a address of the APWine router that corresponds to this market /// @param e address of the Element vault that corresponds to this market /// @param h address of a helper contract, used for Sense approvals if active in the market /// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender /// @return bool true if successful function createMarket( address u, uint256 m, address[8] calldata t, string calldata n, string calldata s, address a, address e, address h, address sensePeriphery ) external authorized(admin) returns (bool) { { // Get the Illuminate principal token for this market (if one exists) address illuminate = markets[u][m][0]; // If illuminate PT already exists, a new market cannot be created if (illuminate != address(0)) { revert Exception(9, 0, 0, illuminate, address(0)); } } // Create an Illuminate principal token for the new market address illuminateToken = ICreator(creator).create( u, m, redeemer, lender, address(this), n, s ); { // create the principal tokens array address[9] memory market = [ illuminateToken, // Illuminate t[0], // Swivel t[1], // Yield t[2], // Element t[3], // Pendle t[4], // Tempus t[5], // Sense t[6], // APWine t[7] // Notional ]; // Set the market markets[u][m] = market; // Have the lender contract approve the several contracts ILender(lender).approve(u, a, e, t[7], sensePeriphery); // Allow converter to spend interest bearing asset if (t[5] != address(0)) { IRedeemer(redeemer).approve(h); } // Approve interest bearing token conversion to underlying for APWine if (t[6] != address(0)) { address futureVault = IAPWineToken(t[6]).futureVault(); address interestBearingToken = IAPWineFutureVault(futureVault) .getIBTAddress(); IRedeemer(redeemer).approve(interestBearingToken); } emit CreateMarket(u, m, market, e, a); } return true; } /// @notice allows the admin to set an individual market /// @param p principal value according to the MarketPlace's Principals Enum /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a address of the new principal token /// @param h a supplementary address (apwine needs a router, element needs a vault, sense needs interest bearing asset) /// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender /// @return bool true if the principal set, false otherwise function setPrincipal( uint8 p, address u, uint256 m, address a, address h, address sensePeriphery ) external authorized(admin) returns (bool) { // Set the principal token in the markets mapping markets[u][m][p] = a; if (p == uint8(Principals.Element)) { // Approve Element vault if setting Element's principal token ILender(lender).approve(u, address(0), h, address(0), address(0)); } else if (p == uint8(Principals.Sense)) { // Approve converter to transfer yield token for Sense's redeem IRedeemer(redeemer).approve(h); // Approve Periphery to be used from Lender ILender(lender).approve( u, address(0), address(0), address(0), sensePeriphery ); } else if (p == uint8(Principals.Apwine)) { // Approve converter to transfer yield token for APWine's redeem address futureVault = IAPWineToken(a).futureVault(); address interestBearingToken = IAPWineFutureVault(futureVault) .getIBTAddress(); IRedeemer(redeemer).approve(interestBearingToken); // Approve APWine's router if setting APWine's principal token ILender(lender).approve(u, h, address(0), address(0), address(0)); } else if (p == uint8(Principals.Notional)) { // Principal token must be approved for Notional's lend ILender(lender).approve(u, address(0), address(0), a, address(0)); } emit SetPrincipal(u, m, a, p); return true; } /// @notice sets the admin address /// @param a Address of a new admin /// @return bool true if the admin set, false otherwise function setAdmin(address a) external authorized(admin) returns (bool) { admin = a; emit SetAdmin(a); return true; } /// @notice sets the address for a pool /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a address of the pool /// @return bool true if the pool set, false otherwise function setPool( address u, uint256 m, address a ) external authorized(admin) returns (bool) { // Set the pool pools[u][m] = a; // Get the principal token ERC5095 pt = ERC5095(markets[u][m][uint8(Principals.Illuminate)]); // Set the pool for the principal token pt.setPool(a); // Approve the marketplace to spend the principal and underlying tokens pt.approveMarketPlace(); emit SetPool(u, m, a); return true; } /// @notice sells the PT for the underlying via the pool /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of PTs to sell /// @param s slippage cap, minimum amount of underlying that must be received /// @return uint128 amount of underlying bought function sellPrincipalToken( address u, uint256 m, uint128 a, uint128 s ) external returns (uint128) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Preview amount of underlying received by selling `a` PTs uint256 expected = pool.sellFYTokenPreview(a); // Verify that the amount needed does not exceed the slippage parameter if (expected < s) { revert Exception(16, expected, s, address(0), address(0)); } // Transfer the principal tokens to the pool Safe.transferFrom( IERC20(address(pool.fyToken())), msg.sender, address(pool), a ); // Execute the swap uint128 received = pool.sellFYToken(msg.sender, s); emit Swap(u, m, address(pool.fyToken()), u, received, a, msg.sender); return received; } /// @notice buys the PT for the underlying via the pool /// @notice determines how many underlying to sell by using the preview /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of PTs to be purchased /// @param s slippage cap, maximum number of underlying that can be sold /// @return uint128 amount of underlying sold function buyPrincipalToken( address u, uint256 m, uint128 a, uint128 s ) external returns (uint128) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Get the amount of base hypothetically required to purchase `a` PTs uint128 expected = pool.buyFYTokenPreview(a); // Verify that the amount needed does not exceed the slippage parameter if (expected > s) { revert Exception(16, expected, 0, address(0), address(0)); } // Transfer the underlying tokens to the pool Safe.transferFrom( IERC20(pool.base()), msg.sender, address(pool), expected ); // Execute the swap to purchase `a` base tokens uint128 spent = pool.buyFYToken(msg.sender, a, s); emit Swap(u, m, u, address(pool.fyToken()), a, spent, msg.sender); return spent; } /// @notice sells the underlying for the PT via the pool /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying to sell /// @param s slippage cap, minimum number of PTs that must be received /// @return uint128 amount of PT purchased function sellUnderlying( address u, uint256 m, uint128 a, uint128 s ) external returns (uint128) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Get the number of PTs received for selling `a` underlying tokens uint128 expected = pool.sellBasePreview(a); // Verify slippage does not exceed the one set by the user if (expected < s) { revert Exception(16, expected, 0, address(0), address(0)); } // Transfer the underlying tokens to the pool Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a); // Execute the swap uint128 received = pool.sellBase(msg.sender, s); emit Swap(u, m, u, address(pool.fyToken()), received, a, msg.sender); return received; } /// @notice buys the underlying for the PT via the pool /// @notice determines how many PTs to sell by using the preview /// @param u address of an underlying asset /// @param m maturity (timestamp) of the market /// @param a amount of underlying to be purchased /// @param s slippage cap, maximum number of PTs that can be sold /// @return uint128 amount of PTs sold function buyUnderlying( address u, uint256 m, uint128 a, uint128 s ) external returns (uint128) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Get the amount of PTs hypothetically required to purchase `a` underlying uint256 expected = pool.buyBasePreview(a); // Verify that the amount needed does not exceed the slippage parameter if (expected > s) { revert Exception(16, expected, 0, address(0), address(0)); } // Transfer the principal tokens to the pool Safe.transferFrom( IERC20(address(pool.fyToken())), msg.sender, address(pool), expected ); // Execute the swap to purchase `a` underlying tokens uint128 spent = pool.buyBase(msg.sender, a, s); emit Swap(u, m, address(pool.fyToken()), u, a, spent, msg.sender); return spent; } /// @notice mint liquidity tokens in exchange for adding underlying and PT /// @dev amount of liquidity tokens to mint is calculated from the amount of unaccounted for PT in this contract. /// @dev A proportional amount of underlying tokens need to be present in this contract, also unaccounted for. /// @param u the address of the underlying token /// @param m the maturity of the principal token /// @param b number of base tokens /// @param p the principal token amount being sent /// @param minRatio minimum ratio of LP tokens to PT in the pool. /// @param maxRatio maximum ratio of LP tokens to PT in the pool. /// @return uint256 number of base tokens passed to the method /// @return uint256 number of yield tokens passed to the method /// @return uint256 the amount of tokens minted. function mint( address u, uint256 m, uint256 b, uint256 p, uint256 minRatio, uint256 maxRatio ) external returns (uint256, uint256, uint256) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Transfer the underlying tokens to the pool Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), b); // Transfer the principal tokens to the pool Safe.transferFrom( IERC20(address(pool.fyToken())), msg.sender, address(pool), p ); // Mint the tokens and return the leftover assets to the caller (uint256 underlyingIn, uint256 principalTokensIn, uint256 minted) = pool .mint(msg.sender, msg.sender, minRatio, maxRatio); emit Mint(u, m, underlyingIn, principalTokensIn, minted, msg.sender); return (underlyingIn, principalTokensIn, minted); } /// @notice Mint liquidity tokens in exchange for adding only underlying /// @dev amount of liquidity tokens is calculated from the amount of PT to buy from the pool, /// plus the amount of unaccounted for PT in this contract. /// @param u the address of the underlying token /// @param m the maturity of the principal token /// @param a the underlying amount being sent /// @param p amount of `PT` being bought in the Pool, from this we calculate how much underlying it will be taken in. /// @param minRatio minimum ratio of LP tokens to PT in the pool. /// @param maxRatio maximum ratio of LP tokens to PT in the pool. /// @return uint256 number of base tokens passed to the method /// @return uint256 number of yield tokens passed to the method /// @return uint256 the amount of tokens minted. function mintWithUnderlying( address u, uint256 m, uint256 a, uint256 p, uint256 minRatio, uint256 maxRatio ) external returns (uint256, uint256, uint256) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Transfer the underlying tokens to the pool Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a); // Mint the tokens to the user (uint256 underlyingIn, , uint256 minted) = pool.mintWithBase( msg.sender, msg.sender, p, minRatio, maxRatio ); emit Mint(u, m, underlyingIn, 0, minted, msg.sender); return (underlyingIn, 0, minted); } /// @notice burn liquidity tokens in exchange for underlying and PT. /// @param u the address of the underlying token /// @param m the maturity of the principal token /// @param a the amount of liquidity tokens to burn /// @param minRatio minimum ratio of LP tokens to PT in the pool /// @param maxRatio maximum ratio of LP tokens to PT in the pool /// @return uint256 amount of LP tokens burned /// @return uint256 amount of base tokens received /// @return uint256 amount of fyTokens received function burn( address u, uint256 m, uint256 a, uint256 minRatio, uint256 maxRatio ) external returns (uint256, uint256, uint256) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Transfer the underlying tokens to the pool Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a); // Burn the tokens ( uint256 tokensBurned, uint256 underlyingReceived, uint256 principalTokensReceived ) = pool.burn(msg.sender, msg.sender, minRatio, maxRatio); emit Burn( u, m, tokensBurned, underlyingReceived, principalTokensReceived, msg.sender ); return (tokensBurned, underlyingReceived, principalTokensReceived); } /// @notice burn liquidity tokens in exchange for underlying. /// @param u the address of the underlying token /// @param m the maturity of the principal token /// @param a the amount of liquidity tokens to burn /// @param minRatio minimum ratio of LP tokens to PT in the pool. /// @param maxRatio minimum ratio of LP tokens to PT in the pool. /// @return uint256 amount of PT tokens sent to the pool /// @return uint256 amount of underlying tokens returned function burnForUnderlying( address u, uint256 m, uint256 a, uint256 minRatio, uint256 maxRatio ) external returns (uint256, uint256) { // Get the pool for the market IPool pool = IPool(pools[u][m]); // Transfer the underlying tokens to the pool Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a); // Burn the tokens in exchange for underlying tokens (uint256 tokensBurned, uint256 underlyingReceived) = pool.burnForBase( msg.sender, minRatio, maxRatio ); emit Burn(u, m, tokensBurned, underlyingReceived, 0, msg.sender); return (tokensBurned, underlyingReceived); } /// @notice Allows batched call to self (this contract). /// @param c An array of inputs for each call. function batch( bytes[] calldata c ) external payable returns (bytes[] memory results) { results = new bytes[](c.length); for (uint256 i; i < c.length; i++) { (bool success, bytes memory result) = address(this).delegatecall( c[i] ); if (!success) revert(RevertMsgExtractor.getRevertMsg(result)); results[i] = result; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; /// @dev A single custom error capable of indicating a wide range of detected errors by providing /// an error code value whose string representation is documented in errors.txt, and any possible other values /// that are pertinent to the error. error Exception(uint8, uint256, uint256, address, address);
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAPWineAMMPool { function getUnderlyingOfIBTAddress() external view returns (address); function getPTAddress() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAPWineController { function getNextPeriodStart(uint256) external view returns (uint256); function withdraw(address, uint256) external; function createFYTDelegationTo( address, address, uint256 ) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAPWineFutureVault { function PERIOD_DURATION() external view returns (uint256); function getControllerAddress() external view returns (address); function getCurrentPeriodIndex() external view returns (uint256); function getFYTofPeriod(uint256) external view returns (address); function getIBTAddress() external view returns (address); function startNewPeriod() external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAPWineRouter { function swapExactAmountIn( address, uint256[] calldata, uint256[] calldata, uint256, uint256, address, uint256, address ) external returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAPWineToken { function futureVault() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IAny {}
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ICreator { function create( address, uint256, address, address, address, string calldata, string calldata ) external returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Returns the number of decimals the token uses - e.g. 8, means to * divide the token amount by 100000000 to get its user representation. */ function decimals() external view returns (uint8); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval( address indexed owner, address indexed spender, uint256 value ); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20.sol'; interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20Metadata.sol'; /** * @dev Interface of the ERC2612 standard as defined in the EIP. * * Adds the {permit} method, which can be used to change one's * {IERC20-allowance} without having to send a transaction, by signing a * message. This allows users to spend tokens without having to hold Ether. * * See https://eips.ethereum.org/EIPS/eip-2612. */ interface IERC2612 is IERC20Metadata { /** * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, * given `owner`'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current ERC2612 nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC2612.sol'; interface IERC5095 is IERC2612 { function maturity() external view returns (uint256); function underlying() external view returns (address); function convertToUnderlying(uint256) external view returns (uint256); function convertToShares(uint256) external view returns (uint256); function maxRedeem(address) external view returns (uint256); function previewRedeem(uint256) external view returns (uint256); function maxWithdraw(address) external view returns (uint256); function previewWithdraw(uint256) external view returns (uint256); function previewDeposit(uint256) external view returns (uint256); function withdraw( uint256, address, address ) external returns (uint256); function redeem( uint256, address, address ) external returns (uint256); function deposit(uint256, address) external returns (uint256); function mint(uint256, address) external returns (uint256); function authMint(address, uint256) external returns (bool); function authBurn(address, uint256) external returns (bool); function authApprove( address, address, uint256 ) external returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IElementToken { function unlockTimestamp() external view returns (uint256); function underlying() external returns (address); function withdrawPrincipal(uint256 amount, address destination) external returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.16; import 'src/lib/Element.sol'; interface IElementVault { function swap( Element.SingleSwap memory, Element.FundManagement memory, uint256, uint256 ) external returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ILender { function approve( address, address, address, address, address ) external; function transferFYTs(address, uint256) external; function transferPremium(address, uint256) external; function paused(uint8) external returns (bool); function halted() external returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IMarketPlace { function markets( address, uint256, uint256 ) external returns (address); function pools(address, uint256) external view returns (address); function sellPrincipalToken( address, uint256, uint128, uint128 ) external returns (uint128); function buyPrincipalToken( address, uint256, uint128, uint128 ) external returns (uint128); function sellUnderlying( address, uint256, uint128, uint128 ) external returns (uint128); function buyUnderlying( address, uint256, uint128, uint128 ) external returns (uint128); function redeemer() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20.sol'; interface INotional { function getUnderlyingToken() external view returns (IERC20, int256); function getMaturity() external view returns (uint40); function deposit(uint256, address) external returns (uint256); function maxRedeem(address) external returns (uint256); function redeem( uint256, address, address ) external returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/lib/Pendle.sol'; interface IPendle { function swapExactTokenForPt( address, address, uint256, Pendle.ApproxParams calldata, Pendle.TokenInput calldata ) external returns (uint256, uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/lib/Pendle.sol'; interface IPendleMarket { function readTokens() external view returns ( address, address, address ); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IPendleToken { function SY() external view returns (address); function YT() external view returns (address); function expiry() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import 'src/interfaces/IERC20.sol'; import 'src/interfaces/IERC5095.sol'; interface IPool { function ts() external view returns (int128); function g1() external view returns (int128); function g2() external view returns (int128); function maturity() external view returns (uint32); function scaleFactor() external view returns (uint96); function getCache() external view returns ( uint112, uint112, uint32 ); // NOTE This will be deprecated function base() external view returns (IERC20); function baseToken() external view returns (address); function fyToken() external view returns (IERC5095); function getBaseBalance() external view returns (uint112); function getFYTokenBalance() external view returns (uint112); function retrieveBase(address) external returns (uint128 retrieved); function retrieveFYToken(address) external returns (uint128 retrieved); function sellBase(address, uint128) external returns (uint128); function buyBase( address, uint128, uint128 ) external returns (uint128); function sellFYToken(address, uint128) external returns (uint128); function buyFYToken( address, uint128, uint128 ) external returns (uint128); function sellBasePreview(uint128) external view returns (uint128); function buyBasePreview(uint128) external view returns (uint128); function sellFYTokenPreview(uint128) external view returns (uint128); function buyFYTokenPreview(uint128) external view returns (uint128); function mint( address, address, uint256, uint256 ) external returns ( uint256, uint256, uint256 ); function mintWithBase( address, address, uint256, uint256, uint256 ) external returns ( uint256, uint256, uint256 ); function burn( address, address, uint256, uint256 ) external returns ( uint256, uint256, uint256 ); function burnForBase( address, uint256, uint256 ) external returns (uint256, uint256); function cumulativeBalancesRatio() external view returns (uint256); function sync() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IRedeemer { function authRedeem( address underlying, uint256 maturity, address from, address to, uint256 amount ) external returns (uint256); function approve(address p) external; function holdings(address u, uint256 m) external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ISenseDivider { function redeem( address, uint256, uint256 ) external returns (uint256); function pt(address, uint256) external view returns (address); // only used by integration tests function settleSeries(address, uint256) external; function adapterAddresses(uint256) external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ISensePeriphery { function divider() external view returns (address); function swapUnderlyingForPTs( address, uint256, uint256, uint256 ) external returns (uint256); function verified(address) external view returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/lib/Swivel.sol'; interface ISwivel { function initiate( Swivel.Order[] calldata, uint256[] calldata, Swivel.Components[] calldata ) external returns (bool); function redeemZcToken( uint8 p, address u, uint256 m, uint256 a ) external returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ISwivelToken { function maturity() external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20Metadata.sol'; import 'src/interfaces/IAny.sol'; interface ITempus { function depositAndFix( address, uint256, bool, uint256, uint256 ) external; function redeemToBacking( address, uint256, uint256, address ) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ITempusAMM { function balanceOf(address) external view returns (uint256); function tempusPool() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20Metadata.sol'; interface ITempusPool { function maturityTime() external view returns (uint256); function backingToken() external view returns (address); function controller() external view returns (address); // Used for integration testing function principalShare() external view returns (address); function currentInterestRate() external view returns (uint256); function initialInterestRate() external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface ITempusToken { function balanceOf(address) external returns (uint256); function pool() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC20.sol'; interface IYield { function maturity() external view returns (uint32); function base() external view returns (IERC20); function sellBase(address, uint128) external returns (uint128); function sellBasePreview(uint128) external view returns (uint128); function fyToken() external returns (address); function sellFYToken(address, uint128) external returns (uint128); function sellFYTokenPreview(uint128) external view returns (uint128); function buyBase( address, uint128, uint128 ) external returns (uint128); function buyBasePreview(uint128) external view returns (uint128); function buyFYToken( address, uint128, uint128 ) external returns (uint128); function buyFYTokenPreview(uint128) external view returns (uint128); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; interface IYieldToken { function redeem(address, uint256) external returns (uint256); function underlying() external returns (address); function maturity() external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; library Cast { /// @dev Safely cast an uint256 to an uint128 /// @param n the u256 to cast to u128 function u128(uint256 n) internal pure returns (uint128) { if (n > type(uint128).max) { revert(); } return uint128(n); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import 'src/interfaces/IAny.sol'; library Element { enum SwapKind { GIVEN_IN, GIVEN_OUT } struct SingleSwap { bytes32 poolId; SwapKind kind; IAny assetIn; IAny assetOut; uint256 amount; bytes userData; } struct FundManagement { address sender; bool fromInternalBalance; address payable recipient; bool toInternalBalance; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; import 'src/interfaces/IERC5095.sol'; import 'src/interfaces/ISwivelToken.sol'; import 'src/interfaces/IYieldToken.sol'; import 'src/interfaces/IElementToken.sol'; import 'src/interfaces/IPendleToken.sol'; import 'src/interfaces/ITempusToken.sol'; import 'src/interfaces/ITempusPool.sol'; import 'src/interfaces/IAPWineToken.sol'; import 'src/interfaces/IAPWineFutureVault.sol'; import 'src/interfaces/IAPWineController.sol'; import 'src/interfaces/INotional.sol'; library Maturities { /// @notice returns the maturity for an Illumiante principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function illuminate(address p) internal view returns (uint256) { return IERC5095(p).maturity(); } /// @notice returns the maturity for a Swivel principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function swivel(address p) internal view returns (uint256) { return ISwivelToken(p).maturity(); } function yield(address p) internal view returns (uint256) { return IYieldToken(p).maturity(); } /// @notice returns the maturity for an Element principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function element(address p) internal view returns (uint256) { return IElementToken(p).unlockTimestamp(); } /// @notice returns the maturity for a Pendle principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function pendle(address p) internal view returns (uint256) { return IPendleToken(p).expiry(); } /// @notice returns the maturity for a Tempus principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function tempus(address p) internal view returns (uint256) { return ITempusPool(ITempusToken(p).pool()).maturityTime(); } /// @notice returns the maturity for a APWine principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function apwine(address p) internal view returns (uint256) { address futureVault = IAPWineToken(p).futureVault(); address controller = IAPWineFutureVault(futureVault) .getControllerAddress(); uint256 duration = IAPWineFutureVault(futureVault).PERIOD_DURATION(); return IAPWineController(controller).getNextPeriodStart(duration); } /// @notice returns the maturity for a Notional principal token /// @param p address of the principal token contract /// @return uint256 maturity of the principal token function notional(address p) internal view returns (uint256) { return INotional(p).getMaturity(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; library Pendle { struct ApproxParams { uint256 guessMin; uint256 guessMax; uint256 guessOffchain; uint256 maxIteration; uint256 eps; } struct TokenInput { // Token/Sy data address tokenIn; uint256 netTokenIn; address tokenMintSy; address bulk; // Kyber data address kyberRouter; bytes kybercall; } }
// SPDX-License-Identifier: MIT // Taken from https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/contracts/BoringBatchable.sol pragma solidity >=0.6.0; library RevertMsgExtractor { /// @dev Helper function to extract a useful revert message from a failed call. /// If the returned data is malformed or not correctly abi encoded then this call can fail itself. function getRevertMsg(bytes memory returnData) internal pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (returnData.length < 68) return 'Transaction reverted silently'; assembly { // Slice the sighash. returnData := add(returnData, 0x04) } return abi.decode(returnData, (string)); // All that remains is the revert string } }
// SPDX-License-Identifier: UNLICENSED // Adapted from: https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol pragma solidity ^0.8.13; import 'src/interfaces/IERC20.sol'; /** @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol) @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. */ library Safe { /// @param e Erc20 token to execute the call with /// @param t To address /// @param a Amount being transferred function transfer( IERC20 e, address t, uint256 a ) internal { bool result; assembly { // Get a pointer to some free memory. let pointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( pointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(pointer, 4), and(t, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(pointer, 36), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. result := call(gas(), e, 0, pointer, 68, 0, 0) } require(success(result), 'transfer failed'); } /// @param e Erc20 token to execute the call with /// @param f From address /// @param t To address /// @param a Amount being transferred function transferFrom( IERC20 e, address f, address t, uint256 a ) internal { bool result; assembly { // Get a pointer to some free memory. let pointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( pointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(pointer, 4), and(f, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "from" argument. mstore( add(pointer, 36), and(t, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(pointer, 68), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 100 because the calldata length is 4 + 32 * 3. result := call(gas(), e, 0, pointer, 100, 0, 0) } require(success(result), 'transfer from failed'); } /// @notice normalize the acceptable values of true or null vs the unacceptable value of false (or something malformed) /// @param r Return value from the assembly `call()` to Erc20['selector'] function success(bool r) private pure returns (bool) { bool result; assembly { // Get how many bytes the call returned. let returnDataSize := returndatasize() // If the call reverted: if iszero(r) { // Copy the revert message into memory. returndatacopy(0, 0, returnDataSize) // Revert with the same message. revert(0, returnDataSize) } switch returnDataSize case 32 { // Copy the return data into memory. returndatacopy(0, 0, returnDataSize) // Set success to whether it returned true. result := iszero(iszero(mload(0))) } case 0 { // There was no return data. result := 1 } default { // It returned some malformed input. result := 0 } } return result; } function approve( IERC20 token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } require(didLastOptionalReturnCallSucceed(callStatus), 'APPROVE_FAILED'); } /*/////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool) { bool result; assembly { // Get how many bytes the call returned. let returnDataSize := returndatasize() // If the call reverted: if iszero(callStatus) { // Copy the revert message into memory. returndatacopy(0, 0, returnDataSize) // Revert with the same message. revert(0, returnDataSize) } switch returnDataSize case 32 { // Copy the return data into memory. returndatacopy(0, 0, returnDataSize) // Set success to whether it returned true. result := iszero(iszero(mload(0))) } case 0 { // There was no return data. result := 1 } default { // It returned some malformed input. result := 0 } } return result; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; library Swivel { // the components of a ECDSA signature struct Components { uint8 v; bytes32 r; bytes32 s; } struct Order { bytes32 key; uint8 protocol; address maker; address underlying; bool vault; bool exit; uint256 principal; uint256 premium; uint256 maturity; uint256 expiry; } }
// SPDX-License-Identifier: MIT // Inspired on token.sol from DappHub. Natspec adpated from OpenZeppelin. pragma solidity 0.8.16; import 'src/interfaces/IERC20Metadata.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Calls to {transferFrom} do not check for allowance if the caller is the owner * of the funds. This allows to reduce the number of approvals that are necessary. * * Finally, {transferFrom} does not decrease the allowance if it is set to * type(uint256).max. This reduces the gas costs without any likely impact. */ contract ERC20 is IERC20Metadata { uint256 internal _totalSupply; mapping(address => uint256) internal _balanceOf; mapping(address => mapping(address => uint256)) internal _allowance; string public override name = '???'; string public override symbol = '???'; uint8 public override decimals = 18; /** * @dev Sets the values for {name}, {symbol} and {decimals}. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() external view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address guy) external view virtual override returns (uint256) { return _balanceOf[guy]; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) external view virtual override returns (uint256) { return _allowance[owner][spender]; } /** * @dev See {IERC20-approve}. */ function approve(address spender, uint256 wad) external virtual override returns (bool) { return _setAllowance(msg.sender, spender, wad); } /** * @dev See {IERC20-transfer}. * * Requirements: * * - the caller must have a balance of at least `wad`. */ function transfer(address dst, uint256 wad) external virtual override returns (bool) { return _transfer(msg.sender, dst, wad); } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `src` must have a balance of at least `wad`. * - the caller is not `src`, it must have allowance for ``src``'s tokens of at least * `wad`. */ /// if_succeeds {:msg "TransferFrom - decrease allowance"} msg.sender != src ==> old(_allowance[src][msg.sender]) >= wad; function transferFrom( address src, address dst, uint256 wad ) external virtual override returns (bool) { _decreaseAllowance(src, wad); return _transfer(src, dst, wad); } /** * @dev Moves tokens `wad` from `src` to `dst`. * * Emits a {Transfer} event. * * Requirements: * * - `src` must have a balance of at least `amount`. */ /// if_succeeds {:msg "Transfer - src decrease"} old(_balanceOf[src]) >= _balanceOf[src]; /// if_succeeds {:msg "Transfer - dst increase"} _balanceOf[dst] >= old(_balanceOf[dst]); /// if_succeeds {:msg "Transfer - supply"} old(_balanceOf[src]) + old(_balanceOf[dst]) == _balanceOf[src] + _balanceOf[dst]; function _transfer( address src, address dst, uint256 wad ) internal virtual returns (bool) { require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance'); unchecked { _balanceOf[src] = _balanceOf[src] - wad; } _balanceOf[dst] = _balanceOf[dst] + wad; emit Transfer(src, dst, wad); return true; } /** * @dev Sets the allowance granted to `spender` by `owner`. * * Emits an {Approval} event indicating the updated allowance. */ function _setAllowance( address owner, address spender, uint256 wad ) internal virtual returns (bool) { _allowance[owner][spender] = wad; emit Approval(owner, spender, wad); return true; } /** * @dev Decreases the allowance granted to the caller by `src`, unless src == msg.sender or _allowance[src][msg.sender] == MAX * * Emits an {Approval} event indicating the updated allowance, if the allowance is updated. * * Requirements: * * - `spender` must have allowance for the caller of at least * `wad`, unless src == msg.sender */ /// if_succeeds {:msg "Decrease allowance - underflow"} old(_allowance[src][msg.sender]) <= _allowance[src][msg.sender]; function _decreaseAllowance(address src, uint256 wad) internal virtual returns (bool) { if (src != msg.sender) { uint256 allowed = _allowance[src][msg.sender]; if (allowed != type(uint256).max) { require(allowed >= wad, 'ERC20: Insufficient approval'); unchecked { _setAllowance(src, msg.sender, allowed - wad); } } } return true; } /** @dev Creates `wad` tokens and assigns them to `dst`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. */ /// if_succeeds {:msg "Mint - balance overflow"} old(_balanceOf[dst]) >= _balanceOf[dst]; /// if_succeeds {:msg "Mint - supply overflow"} old(_totalSupply) >= _totalSupply; function _mint(address dst, uint256 wad) internal virtual returns (bool) { _balanceOf[dst] = _balanceOf[dst] + wad; _totalSupply = _totalSupply + wad; emit Transfer(address(0), dst, wad); return true; } /** * @dev Destroys `wad` tokens from `src`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `src` must have at least `wad` tokens. */ /// if_succeeds {:msg "Burn - balance underflow"} old(_balanceOf[src]) <= _balanceOf[src]; /// if_succeeds {:msg "Burn - supply underflow"} old(_totalSupply) <= _totalSupply; function _burn(address src, uint256 wad) internal virtual returns (bool) { unchecked { require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance'); _balanceOf[src] = _balanceOf[src] - wad; _totalSupply = _totalSupply - wad; emit Transfer(src, address(0), wad); } return true; } }
// SPDX-License-Identifier: MIT // Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol pragma solidity 0.8.16; import 'src/tokens/ERC20.sol'; import 'src/interfaces/IERC2612.sol'; /** * @dev Extension of {ERC20} that allows token holders to use their tokens * without sending any transactions by setting {IERC20-allowance} with a * signature using the {permit} method, and then spend them via * {IERC20-transferFrom}. * * The {permit} signature mechanism conforms to the {IERC2612} interface. */ abstract contract ERC20Permit is ERC20, IERC2612 { mapping(address => uint256) public override nonces; bytes32 public immutable PERMIT_TYPEHASH = keccak256( 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)' ); bytes32 private immutable _DOMAIN_SEPARATOR; uint256 public immutable deploymentChainId; constructor( string memory name_, string memory symbol_, uint8 decimals_ ) ERC20(name_, symbol_, decimals_) { deploymentChainId = block.chainid; _DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid); } /// @dev Calculate the DOMAIN_SEPARATOR. function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) { return keccak256( abi.encode( keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ), keccak256(bytes(name)), keccak256(bytes(version())), chainId, address(this) ) ); } /// @dev Return the DOMAIN_SEPARATOR. function DOMAIN_SEPARATOR() external view returns (bytes32) { return block.chainid == deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid); } /// @dev Setting the version as a function so that it can be overriden function version() public pure virtual returns (string memory) { return '1'; } /** * @dev See {IERC2612-permit}. * * In cases where the free option is not a concern, deadline can simply be * set to uint(-1), so it should be seen as an optional parameter */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external virtual override { require(deadline >= block.timestamp, 'ERC20Permit: expired deadline'); bytes32 hashStruct = keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline ) ); bytes32 hash = keccak256( abi.encodePacked( '\x19\x01', block.chainid == deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid), hashStruct ) ); address signer = ecrecover(hash, v, r, s); require( signer != address(0) && signer == owner, 'ERC20Permit: invalid signature' ); _setAllowance(owner, spender, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import 'src/tokens/ERC20Permit.sol'; import 'src/interfaces/IERC5095.sol'; import 'src/interfaces/IRedeemer.sol'; import 'src/interfaces/IMarketPlace.sol'; import 'src/interfaces/IYield.sol'; import 'src/errors/Exception.sol'; import 'src/lib/Cast.sol'; import 'src/lib/Safe.sol'; contract ERC5095 is ERC20Permit, IERC5095 { /// @dev unix timestamp when the ERC5095 token can be redeemed uint256 public immutable override maturity; /// @dev address of the ERC20 token that is returned on ERC5095 redemption address public immutable override underlying; /// @dev address of the minting authority address public immutable lender; /// @dev address of the "marketplace" YieldSpace AMM router address public immutable marketplace; ///@dev Interface to interact with the pool address public pool; /// @dev address and interface for an external custody contract (necessary for some project's backwards compatability) address public immutable redeemer; /// @notice ensures that only a certain address can call the function /// @param a address that msg.sender must be to be authorized modifier authorized(address a) { if (msg.sender != a) { revert Exception(0, 0, 0, msg.sender, a); } _; } constructor( address _underlying, uint256 _maturity, address _redeemer, address _lender, address _marketplace, string memory name_, string memory symbol_, uint8 decimals_ ) ERC20Permit(name_, symbol_, decimals_) { underlying = _underlying; maturity = _maturity; redeemer = _redeemer; lender = _lender; marketplace = _marketplace; pool = address(0); } /// @notice Allows the marketplace to set the pool /// @param p Address of the pool /// @return bool True if successful function setPool(address p) external authorized(marketplace) returns (bool) { pool = p; return true; } /// @notice Allows the marketplace to spend underlying, principal tokens held by the token /// @dev This is necessary when MarketPlace calls pool methods to swap tokens /// @return True if successful function approveMarketPlace() external authorized(marketplace) returns (bool) { // Approve the marketplace to spend the token's underlying Safe.approve(IERC20(underlying), marketplace, type(uint256).max); // Approve the marketplace to spend illuminate PTs Safe.approve(IERC20(address(this)), marketplace, type(uint256).max); return true; } /// @notice Post or at maturity, converts an amount of principal tokens to an amount of underlying that would be returned. /// @param s The amount of principal tokens to convert /// @return uint256 The amount of underlying tokens returned by the conversion function convertToUnderlying(uint256 s) external view override returns (uint256) { if (block.timestamp < maturity) { return previewRedeem(s); } return s; } /// @notice Post or at maturity, converts a desired amount of underlying tokens returned to principal tokens needed. /// @param a The amount of underlying tokens to convert /// @return uint256 The amount of principal tokens returned by the conversion function convertToShares(uint256 a) external view override returns (uint256) { if (block.timestamp < maturity) { return previewWithdraw(a); } return a; } /// @notice Returns user's PT balance /// @param o The address of the owner for which redemption is calculated /// @return uint256 The maximum amount of principal tokens that `owner` can redeem. function maxRedeem(address o) external view override returns (uint256) { return _balanceOf[o]; } /// @notice Post or at maturity, returns user's PT balance. Prior to maturity, returns a previewRedeem for owner's PT balance. /// @param o The address of the owner for which withdrawal is calculated /// @return uint256 maximum amount of underlying tokens that `owner` can withdraw. function maxWithdraw(address o) external view override returns (uint256) { if (block.timestamp < maturity) { return previewRedeem(_balanceOf[o]); } return _balanceOf[o]; } /// @notice After maturity, returns 0. Prior to maturity, returns the amount of `shares` when spending `a` in underlying on a YieldSpace AMM. /// @param a The amount of underlying spent /// @return uint256 The amount of PT purchased by spending `a` of underlying function previewDeposit(uint256 a) public view returns (uint256) { if (block.timestamp < maturity) { return IYield(pool).sellBasePreview(Cast.u128(a)); } return 0; } /// @notice After maturity, returns 0. Prior to maturity, returns the amount of `assets` in underlying spent on a purchase of `s` in PT on a YieldSpace AMM. /// @param s The amount of principal tokens bought in the simulation /// @return uint256 The amount of underlying required to purchase `s` of PT function previewMint(uint256 s) public view returns (uint256) { if (block.timestamp < maturity) { return IYield(pool).buyFYTokenPreview(Cast.u128(s)); } return 0; } /// @notice Post or at maturity, simulates the effects of redemption. Prior to maturity, returns the amount of `assets` from a sale of `s` PTs on a YieldSpace AMM. /// @param s The amount of principal tokens redeemed in the simulation /// @return uint256 The amount of underlying returned by `s` of PT redemption function previewRedeem(uint256 s) public view override returns (uint256) { if (block.timestamp >= maturity) { // After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying return Cast.u128( s * Cast.u128( IRedeemer(redeemer).holdings(underlying, maturity) ) ) / _totalSupply; } // Prior to maturity, return a a preview of a swap on the pool return IYield(pool).sellFYTokenPreview(Cast.u128(s)); } /// @notice Post or at maturity, simulates the effects of withdrawal at the current block. Prior to maturity, simulates the amount of PTs necessary to receive `a` in underlying from the sale of PTs on a YieldSpace AMM. /// @param a The amount of underlying tokens withdrawn in the simulation /// @return uint256 The amount of principal tokens required for the withdrawal of `a` function previewWithdraw(uint256 a) public view override returns (uint256) { if (block.timestamp >= maturity) { // After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying return (a * _totalSupply) / IRedeemer(redeemer).holdings(underlying, maturity); } // Prior to maturity, return a a preview of a swap on the pool return IYield(pool).buyBasePreview(Cast.u128(a)); } /// @notice Before maturity spends `a` of underlying, and sends PTs to `r`. Post or at maturity, reverts. /// @param a The amount of underlying tokens deposited /// @param r The receiver of the principal tokens /// @param m Minimum number of shares that the user will receive /// @return uint256 The amount of principal tokens purchased function deposit( uint256 a, address r, uint256 m ) external returns (uint256) { // Execute the deposit return _deposit(r, a, m); } /// @notice Before maturity spends `assets` of underlying, and sends `shares` of PTs to `receiver`. Post or at maturity, reverts. /// @param a The amount of underlying tokens deposited /// @param r The receiver of the principal tokens /// @return uint256 The amount of principal tokens burnt by the withdrawal function deposit(uint256 a, address r) external override returns (uint256) { // Execute the deposit return _deposit(r, a, 0); } /// @notice Before maturity mints `s` of PTs to `r` by spending underlying. Post or at maturity, reverts. /// @param s The amount of shares being minted /// @param r The receiver of the underlying tokens being withdrawn /// @param m Maximum amount of underlying that the user will spend /// @return uint256 The amount of principal tokens purchased function mint( uint256 s, address r, uint256 m ) external returns (uint256) { // Execute the mint return _mint(r, s, m); } /// @notice Before maturity mints `shares` of PTs to `receiver` by spending underlying. Post or at maturity, reverts. /// @param s The amount of shares being minted /// @param r The receiver of the underlying tokens being withdrawn /// @return uint256 The amount of principal tokens purchased function mint(uint256 s, address r) external override returns (uint256) { // Execute the mint return _mint(r, s, type(uint128).max); } /// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM. /// @param a The amount of underlying tokens withdrawn /// @param r The receiver of the underlying tokens being withdrawn /// @param o The owner of the underlying tokens /// @param m Maximum amount of PTs to be sold /// @return uint256 The amount of principal tokens burnt by the withdrawal function withdraw( uint256 a, address r, address o, uint256 m ) external returns (uint256) { // Execute the withdrawal return _withdraw(a, r, o, m); } /// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM. /// @param a The amount of underlying tokens withdrawn /// @param r The receiver of the underlying tokens being withdrawn /// @param o The owner of the underlying tokens /// @return uint256 The amount of principal tokens burnt by the withdrawal function withdraw( uint256 a, address r, address o ) external override returns (uint256) { // Execute the withdrawal return _withdraw(a, r, o, type(uint128).max); } /// @notice At or after maturity, burns exactly `s` of Principal Tokens from `o` and sends underlying tokens to `r`. Before maturity, sends underlying by selling `s` of PT on a YieldSpace AMM. /// @param s The number of shares to be burned in exchange for the underlying asset /// @param r The receiver of the underlying tokens being withdrawn /// @param o Address of the owner of the shares being burned /// @param m Minimum amount of underlying that must be received /// @return uint256 The amount of underlying tokens distributed by the redemption function redeem( uint256 s, address r, address o, uint256 m ) external returns (uint256) { // Execute the redemption return _redeem(s, r, o, m); } /// @notice At or after maturity, burns exactly `shares` of Principal Tokens from `owner` and sends `assets` of underlying tokens to `receiver`. Before maturity, sells `s` of PT on a YieldSpace AMM. /// @param s The number of shares to be burned in exchange for the underlying asset /// @param r The receiver of the underlying tokens being withdrawn /// @param o Address of the owner of the shares being burned /// @return uint256 The amount of underlying tokens distributed by the redemption function redeem( uint256 s, address r, address o ) external override returns (uint256) { // Execute the redemption return _redeem(s, r, o, 0); } /// @param f Address to burn from /// @param a Amount to burn /// @return bool true if successful function authBurn(address f, uint256 a) external authorized(redeemer) returns (bool) { _burn(f, a); return true; } /// @param t Address recieving the minted amount /// @param a The amount to mint /// @return bool True if successful function authMint(address t, uint256 a) external authorized(lender) returns (bool) { _mint(t, a); return true; } /// @param o Address of the owner of the tokens /// @param s Address of the spender /// @param a Amount to be approved function authApprove( address o, address s, uint256 a ) external authorized(redeemer) returns (bool) { _allowance[o][s] = a; return true; } function _deposit( address r, uint256 a, uint256 m ) internal returns (uint256) { // Revert if called at or after maturity if (block.timestamp >= maturity) { revert Exception( 21, block.timestamp, maturity, address(0), address(0) ); } // Receive the funds from the sender Safe.transferFrom(IERC20(underlying), msg.sender, address(this), a); // Sell the underlying assets for PTs uint128 returned = IMarketPlace(marketplace).sellUnderlying( underlying, maturity, Cast.u128(a), Cast.u128(m) ); // Pass the received shares onto the intended receiver _transfer(address(this), r, returned); return returned; } function _mint( address r, uint256 s, uint256 m ) internal returns (uint256) { // Revert if called at or after maturity if (block.timestamp >= maturity) { revert Exception( 21, block.timestamp, maturity, address(0), address(0) ); } // Determine how many underlying tokens are needed to mint the shares uint256 required = IYield(pool).buyFYTokenPreview(Cast.u128(s)); // Transfer the underlying to the token Safe.transferFrom( IERC20(underlying), msg.sender, address(this), required ); // Swap the underlying for principal tokens via the pool uint128 sold = IMarketPlace(marketplace).buyPrincipalToken( underlying, maturity, Cast.u128(s), Cast.u128(m) ); // Transfer the principal tokens to the desired receiver _transfer(address(this), r, s); return sold; } function _withdraw( uint256 a, address r, address o, uint256 m ) internal returns (uint256) { // Determine how many principal tokens are needed to purchase the underlying uint256 needed = previewWithdraw(a); // Pre maturity if (block.timestamp < maturity) { // Receive the shares from the caller _transfer(o, address(this), needed); // If owner is the sender, sell PT without allowance check if (o == msg.sender) { uint128 returned = IMarketPlace(marketplace).buyUnderlying( underlying, maturity, Cast.u128(a), Cast.u128(m) ); // Transfer the underlying to the desired receiver Safe.transfer(IERC20(underlying), r, a); return returned; } else { // Else, sell PT with allowance check // Get the allowance of the user spending the tokens uint256 allowance = _allowance[o][msg.sender]; // Check for sufficient allowance if (allowance < needed) { revert Exception(20, allowance, a, address(0), address(0)); } // Update the caller's allowance _allowance[o][msg.sender] = allowance - needed; // Sell the principal tokens for underlying uint128 returned = IMarketPlace(marketplace).buyUnderlying( underlying, maturity, Cast.u128(a), Cast.u128(m) ); // Transfer the underlying to the desired receiver Safe.transfer(IERC20(underlying), r, returned); return returned; } } // Post maturity else { // If owner is the sender, redeem PT without allowance check if (o == msg.sender) { // Execute the redemption to the desired receiver return IRedeemer(redeemer).authRedeem( underlying, maturity, msg.sender, r, needed ); } else { // Get the allowance of the user spending the tokens uint256 allowance = _allowance[o][msg.sender]; // Check for sufficient allowance if (allowance < needed) { revert Exception( 20, allowance, needed, address(0), address(0) ); } // Update the callers's allowance _allowance[o][msg.sender] = allowance - needed; // Execute the redemption to the desired receiver return IRedeemer(redeemer).authRedeem( underlying, maturity, o, r, needed ); } } } function _redeem( uint256 s, address r, address o, uint256 m ) internal returns (uint256) { // Pre-maturity if (block.timestamp < maturity) { // Receive the funds from the user _transfer(o, address(this), s); // If owner is the sender, sell PT without allowance check if (o == msg.sender) { // Swap principal tokens for the underlying asset uint128 returned = IMarketPlace(marketplace).sellPrincipalToken( underlying, maturity, Cast.u128(s), Cast.u128(m) ); // Transfer underlying to the desired receiver Safe.transfer(IERC20(underlying), r, returned); return returned; // Else, sell PT with allowance check } else { // Get the allowance of the user spending the tokens uint256 allowance = _allowance[o][msg.sender]; // Check for sufficient allowance if (allowance < s) { revert Exception(20, allowance, s, address(0), address(0)); } // Update the caller's allowance _allowance[o][msg.sender] = allowance - s; // Sell the principal tokens for the underlying uint128 returned = IMarketPlace(marketplace).sellPrincipalToken( underlying, maturity, Cast.u128(s), Cast.u128(m) ); // Transfer the underlying to the desired receiver Safe.transfer(IERC20(underlying), r, returned); return returned; } // Post-maturity } else { // If owner is the sender, redeem PT without allowance check if (o == msg.sender) { // Execute the redemption to the desired receiver return IRedeemer(redeemer).authRedeem( underlying, maturity, msg.sender, r, s ); } else { // Get the allowance of the user spending the tokens uint256 allowance = _allowance[o][msg.sender]; // Check for sufficient allowance if (allowance < s) { revert Exception(20, allowance, s, address(0), address(0)); } // Update the caller's allowance _allowance[o][msg.sender] = allowance - s; // Execute the redemption to the desired receiver return IRedeemer(redeemer).authRedeem( underlying, maturity, o, r, s ); } } } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"s","type":"address"},{"internalType":"address","name":"p","type":"address"},{"internalType":"address","name":"a","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"Exception","type":"error"},{"anonymous":false,"inputs":[],"name":"BlockFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"BlockWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"returned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"spent","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Lend","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"PauseIlluminate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"bool","name":"state","type":"bool"}],"name":"PausePrincipal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"when","type":"uint256"}],"name":"ScheduleFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"hold","type":"uint256"}],"name":"ScheduleWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetFee","type":"event"},{"inputs":[],"name":"HOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"address","name":"r","type":"address"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"address","name":"a","type":"address"},{"internalType":"address","name":"e","type":"address"},{"internalType":"address","name":"n","type":"address"},{"internalType":"address","name":"p","type":"address"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"u","type":"address[]"},{"internalType":"address[]","name":"a","type":"address[]"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"apwineAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"c","type":"bytes[]"}],"name":"batch","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"blockFeeChange","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"blockWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"etherPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"fees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"halted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"uint256","name":"d","type":"uint256"},{"internalType":"address","name":"e","type":"address"},{"internalType":"bytes32","name":"i","type":"bytes32"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256[]","name":"a","type":"uint256[]"},{"internalType":"address","name":"y","type":"address"},{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"uint8","name":"protocol","type":"uint8"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bool","name":"vault","type":"bool"},{"internalType":"bool","name":"exit","type":"bool"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct Swivel.Order[]","name":"o","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct Swivel.Components[]","name":"s","type":"tuple[]"},{"internalType":"bool","name":"e","type":"bool"},{"internalType":"uint256","name":"premiumSlippage","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint128","name":"a","type":"uint128"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"address","name":"x","type":"address"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"address","name":"adapter","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"address","name":"y","type":"address"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"uint256","name":"d","type":"uint256"},{"internalType":"address","name":"x","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"components":[{"internalType":"uint256","name":"guessMin","type":"uint256"},{"internalType":"uint256","name":"guessMax","type":"uint256"},{"internalType":"uint256","name":"guessOffchain","type":"uint256"},{"internalType":"uint256","name":"maxIteration","type":"uint256"},{"internalType":"uint256","name":"eps","type":"uint256"}],"internalType":"struct Pendle.ApproxParams","name":"g","type":"tuple"},{"internalType":"address","name":"market","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketPlace","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"bool","name":"b","type":"bool"}],"name":"pause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"b","type":"bool"}],"name":"pauseIlluminate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendleAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"periodStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"premiums","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"protocolFlow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scheduleFeeChange","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"scheduleWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"a","type":"address"}],"name":"setAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"p","type":"uint256"}],"name":"setEtherPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"f","type":"uint256"}],"name":"setFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"m","type":"address"}],"name":"setMarketPlace","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"m","type":"uint256"}],"name":"setMaxValue","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swivelAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"f","type":"address"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"transferFYTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"}],"name":"transferPremium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"withdrawFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60e034620000f257601f6200581c38819003918201601f19168301916001600160401b03831184841017620000f757808492606094604052833981010312620000f2576200004d816200010d565b906200006a604062000062602084016200010d565b92016200010d565b60016009556d0c5371912364ce3056c280000000600a556109c4600d55600080546001600160a01b0319163317905560809290925260a05260c0526103e86005556040516156f99081620001238239608051818181610dfa01528181612b9f0152613c44015260a0518181816132f80152613534015260c051818181610f24015261317d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620000f25756fe60806040526004361015610013575b600080fd5b60003560e01c806303799f87146103975780630cdc7ec61461038e5780630d3f5352146103855780631177ec301461037c5780631271f09a146103735780631ac3ddeb1461036a5780631e897afb1461036157806327187991146103585780632e25d2a61461034f57806330568a8d1461034657806335197f9e1461033d578063355d63f11461033457806337a3eeec1461032b5780633faf30ad1461032257806351cff8d9146103195780635ac86ab71461031057806362a4520e1461030757806365dff1e1146102fe57806369fe0e2d146102f55780636b42450d146102ec578063704b6c02146102e35780637114d92c146102da57806375a9b172146102d15780637a9262a2146102c85780637c7cde83146102bf578063884e21d6146102b65780639984f30d146102ad5780639e307955146102a45780639e6b51731461029b578063a102e38414610292578063adea384414610289578063b49e0d7214610280578063b9b8af0b14610277578063c244e1ff1461026e578063d0886f9714610265578063dc4c7ca91461025c578063ea08c03114610253578063eda8ff6b1461024a578063ef60356914610241578063f7adf98014610238578063f851a4401461022f578063f8eaad3514610226578063f9ad473d1461021d578063faaebd21146102145763fe3ee1691461020c57600080fd5b61000e613824565b5061000e6137e6565b5061000e613789565b5061000e6136f0565b5061000e6136c6565b5061000e613327565b5061000e6132e1565b5061000e612bce565b5061000e612b88565b5061000e6127ee565b5061000e6127cf565b5061000e612489565b5061000e612465565b5061000e611fc8565b5061000e611f8f565b5061000e611efb565b5061000e611edc565b5061000e611ebd565b5061000e611e86565b5061000e61189c565b5061000e61186c565b5061000e61182e565b5061000e611787565b5061000e6116d6565b5061000e611469565b5061000e6113c2565b5061000e611298565b5061000e6111d5565b5061000e61119c565b5061000e61115e565b5061000e610fef565b5061000e610f75565b5061000e610f0d565b5061000e610d56565b5061000e610d37565b5061000e610ca5565b5061000e610c7b565b5061000e610c44565b5061000e610b41565b5061000e610a0e565b5061000e6108c2565b5061000e610829565b5061000e61080b565b5061000e6107b4565b5061000e6103fb565b60ff81160361000e57565b600435906103b8826103a0565b565b35906103b8826103a0565b6001600160a01b0381160361000e57565b602435906103b8826103c5565b608435906103b8826103c5565b35906103b8826103c5565b503461000e5761010036600319011261000e57600480359061041c826103a0565b60243590610429826103c5565b60443560c43560643561043b826103c5565b60026009541461077a57600260095560009560ff811687526020916002835260409560ff878a205416801561076e575b610753578542116107165783906104e382858a8a8e8e6104a461049861049860015460018060a01b031690565b6001600160a01b031690565b935163125cf47f60e01b81526001600160a01b03909116888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af1908115610709575b8b916106dc575b506001600160a01b038a811697908a908d906105148830338e615576565b6001600160a01b038e1660009081526007602052604090208e905460055461053c908b6139c1565b610545916139e1565b6001600160a01b0390911660009081526007602052604090205560055461056c90896139c1565b6105769089613a34565b908b610580611593565b30815294858a01859052308187015284606087015261059d6115b2565b60e43581528a8101959095526001600160a01b039091169084015285166001600160a01b0316606083015260808201526105d561440b565b60a082015260a43591608435916105eb94614b7f565b6105f5918b614e1b565b98876106028b8388614f26565b5061060c91614d32565b8851630fa9d3b160e01b815233928101928352602083018b90529a8b92839003604001918391906001600160a01b03165af161069d986000805160206156a4833981519152949180156106cf575b6106a1575b5050855160ff909216825260208201879052604082015233606082015280608081015b0390a361068f6001600955565b519081529081906020820190565b0390f35b816106c092903d106106c8575b6106b88183611572565b810190613995565b50388061065f565b503d6106ae565b6106d761390a565b61065a565b6106fc9150833d8511610702575b6106f48183611572565b8101906138f2565b386104f6565b503d6106ea565b61071161390a565b6104ef565b8651636d4c6c8960e01b8152600291810191825242602083015260408201879052600060608301819052608083015290819060a0010390fd5b0390fd5b8651636d4c6c8960e01b81529283925061074f918301613955565b5060ff6003541661046b565b61074f84604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57604036600319011261000e576004356107d2816103c5565b60018060a01b0316600052600460205260406000206024356000526020526020604060002054604051908152f35b600091031261000e57565b503461000e57600036600319011261000e5760206040516101f48152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036108a4577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f4804201804211610897575b80600655604051908152a160405160018152602090f35b61089f6139aa565b610880565b604051636d4c6c8960e01b815290819061074f9033600484016138c4565b503461000e57606036600319011261000e57600480356108e1816103c5565b602435906044356108f1816103c5565b600080546001600160a01b0391908216338190036109f15750805b60ff906009828216146109d85760019084876109788b84610939610498610498895460018060a01b031690565b8d8a60405180968195829463125cf47f60e01b845260209a8b99850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19182156109cb575b87926109ae575b505016868161099e575b5050011661090c565b6109a79161562c565b3886610995565b6109c49250803d10610702576106f48183611572565b388061098b565b6109d361390a565b610984565b5050506109e5921661562c565b60405160018152602090f35b604051636d4c6c8960e01b815290819061074f9033838b016138c4565b503461000e57602036600319011261000e57600435610a2c816103c5565b600080546001600160a01b03908116338190036108a45750806109e59316908183526007602052604083205492806040812055541690615505565b9181601f8401121561000e578235916001600160401b03831161000e576020808501948460051b01011161000e57565b60005b838110610aaa5750506000910152565b8181015183820152602001610a9a565b90602091610ad381518092818552858086019101610a97565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b848310610b135750505050505090565b9091929394958480610b31600193603f198682030187528a51610aba565b9801930193019194939290610b03565b5060208060031936011261000e57600480356001600160401b03811161000e57610b6e9036908301610a67565b9290610b79846115f1565b92604092610b8984519586611572565b858552601f19610b98876115f1565b0160005b818110610c355750505060005b858110610bbd5783518061069d8782610adf565b600080610bcb838987614700565b90610bda88518093819361474f565b0390305af4610be761475d565b9015610c125790610c0d91610bfc828861421e565b52610c07818761421e565b506146e4565b610ba9565b8261074f610c208793615451565b925162461bcd60e51b8152928392830161478d565b60608782018401528201610b9c565b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600d55602060405160018152f35b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e57600435610cc3816103c5565b6000546001600160a01b03908116338190036108a457506001541680610d0357600180546001600160a01b0319166001600160a01b0384161790556109e5565b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600654604051908152f35b503461000e5760a036600319011261000e57600435610d74816103c5565b60243590610d81826103c5565b60443590610d8e826103c5565b606435610d9a816103c5565b60843592610da7846103c5565b6001546001600160a01b039390841633819003610ecf5750831694838116610ebf575b50828116610eaf575b50818116610e9f575b508116610e8f575b50604051636eb1769f60e11b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116602483015290602081604481865afa908115610e82575b600091610e54575b5015610e4b575b005b610e499161562c565b610e75915060203d8111610e7b575b610e6d8183611572565b810190613946565b38610e42565b503d610e63565b610e8a61390a565b610e3a565b610e99908261562c565b38610de4565b610ea9908461562c565b38610ddc565b610eb9908561562c565b38610dd3565b610ec9908661562c565b38610dca565b604051636d4c6c8960e01b81526000600482018190526024820181905260448201523360648201526001600160a01b0391909116608482015260a490fd5b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b8015150361000e57565b60e435906103b882610f53565b35906103b882610f53565b503461000e57602036600319011261000e57600435610f9381610f53565b6000546001600160a01b0316338190036108a4577fb4252ba7ada6e65ca524b94dd554b37a638732fc8c63021e66782fc6e75bbb0c602083151560ff196003541660ff821617600355604051908152a160405160018152602090f35b503461000e57602036600319011261000e5760043561100d816103c5565b6000546001600160a01b03908116338190036108a457506001600160a01b03821660009081526008602052604090205480156111295742106110f4576001600160a01b03821660009081526008602052604081206109e5939190556001600160a01b0381166000908152600760205260408120556000549116906001600160a01b03166040516370a0823160e01b81523060048201529160208380602481010381845afa9283156110e7575b6000936110c7575b50615505565b6110e091935060203d8111610e7b57610e6d8183611572565b91386110c1565b6110ef61390a565b6110b9565b604051636d4c6c8960e01b8152601360048201526000602482018190526044820181905260648201819052608482015260a490fd5b604051636d4c6c8960e01b8152601260048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e5760ff60043561117e816103a0565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e5760ff6004356111bc816103a0565b16600052600c6020526020604060002054604051908152f35b503461000e57604036600319011261000e576001600160401b0360043581811161000e57611207903690600401610a67565b909160243590811161000e57611221903690600401610a67565b9060018060a01b0380600054168033036108a4575060005b84810361124c5760405160018152602090f35b808261125b600193888a61392e565b35611265816103c5565b1680611273575b5001611239565b6112929061128283888861392e565b359061128d826103c5565b61562c565b3861126c565b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036108a457506006548061130057604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461133f57604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b506101f4811061138d5761135281600555565b61135c6000600655565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57604036600319011261000e576004356113e0816103c5565b6001546004906020906113fd90610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa90811561145c575b60009161143e575b506001600160a01b03811633036108a457610e4960243583614621565b611456915060203d8111610702576106f48183611572565b38611421565b61146461390a565b611419565b503461000e57602036600319011261000e57600435611487816103c5565b6000546001600160a01b0380821692338490036114e957602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b81528061074f8633600484016138c4565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161152f57604052565b611537611505565b604052565b606081019081106001600160401b0382111761152f57604052565b604081019081106001600160401b0382111761152f57604052565b90601f801991011681019081106001600160401b0382111761152f57604052565b60405190608082018281106001600160401b0382111761152f57604052565b6040519060c082018281106001600160401b0382111761152f57604052565b6040519061014082018281106001600160401b0382111761152f57604052565b6020906001600160401b03811161160a575b60051b0190565b611612611505565b611603565b81601f8201121561000e5780359161162e836115f1565b9261163c6040519485611572565b808452602092838086019260051b82010192831161000e578301905b828210611666575050505090565b81358152908301908301611658565b9181601f8401121561000e578235916001600160401b03831161000e57602080850194610140850201011161000e57565b9181601f8401121561000e578235916001600160401b03831161000e576020808501946060850201011161000e57565b503461000e5761012036600319011261000e576116f16103ab565b6116f96103d6565b6001600160401b039060643582811161000e5761171a903690600401611617565b926117236103e3565b60a43584811161000e5761173b903690600401611675565b60c49691963595861161000e5761069d9661175d6117779736906004016116a6565b949093611768610f5d565b96610104359860443591613a41565b6040519081529081906020820190565b503461000e57604036600319011261000e576004356117a5816103c5565b6001546004906020906117c290610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa908115611821575b600091611803575b506001600160a01b03811633036108a457610e49602435836145ae565b61181b915060203d8111610702576106f48183611572565b386117e6565b61182961390a565b6117de565b503461000e57602036600319011261000e5760043561184c816103c5565b60018060a01b031660005260086020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600a54604051908152f35b6001600160801b0381160361000e57565b503461000e5761010036600319011261000e576118ba6004356103a0565b6118c56024356103c5565b6118d060643561188b565b6118db60a4356103c5565b6118e660e4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff604060002054168015611e45575b611e28576044354211611df35760405163378efa3760e01b815260208160048160a4356001600160a01b03165afa908115611de6575b600091611dc7575b5060405163d10eb4b960e01b81526001600160a01b0360e4358116600483015260c4356024830152909160209183916044918391165afa908115611dba575b600091611d9b575b506001546119b090610498906001600160a01b031681565b906020604051809363125cf47f60e01b8252816000816119f96004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1918215611d8e575b600092611d6d575b506001600160a01b0381811690831603611d475750611a446064356001600160801b031630336024356001600160a01b0316615576565b611ab1611a5e6005546001600160801b03606435166139c1565b6001600160a01b03602435166000908152600760205260409020611a859082905b546139e1565b6001600160a01b03602435166000908152600760205260409020556001600160801b0360643516613a34565b6040516370a0823160e01b808252306004830152919291611b8191906020826024816001600160a01b0388165afa918215611d3a575b600092611d19575b506040516308f1f30f60e41b81526001600160a01b0360e4358116600483015260c435602483015260448201969096526084803560648301819052966020918391829060009060a435165af18015611d0c575b611ced575b5060405190815230600482015260208180602481015b03816001600160a01b0388165afa908115611ce0575b600091611cc1575b50613a34565b918210611c8c57611baf61069d92611b9d604435602435614d32565b926001600160a01b0316602435614e1b565b90611bbf82602435600435614f26565b50604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000906001600160a01b03165af18015611c7f575b611c60575b506040805160ff600435168152602081018390526001600160801b036064351691810191909152336060820152604435906024356001600160a01b0316906000805160206156a48339815191529080608081015b0390a36117776001600955565b611c789060203d6020116106c8576106b88183611572565b5038611bff565b611c8761390a565b611bfa565b604051636d4c6c8960e01b8152600b60048201526000602482018190526044820181905260648201819052608482015260a490fd5b611cda915060203d602011610e7b57610e6d8183611572565b38611b7b565b611ce861390a565b611b73565b611d059060203d602011610e7b57610e6d8183611572565b5038611b47565b611d1461390a565b611b42565b611d3391925060203d602011610e7b57610e6d8183611572565b9038611aef565b611d4261390a565b611ae7565b604051636d4c6c8960e01b815291829161074f916001600160a01b0316600484016139f6565b611d8791925060203d602011610702576106f48183611572565b9038611a0d565b611d9661390a565b611a05565b611db4915060203d602011610702576106f48183611572565b38611998565b611dc261390a565b611990565b611de0915060203d602011610702576106f48183611572565b38611951565b611dee61390a565b611949565b604051636d4c6c8960e01b8152600260048201524260248201526044803590820152600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f60048035908301613955565b5060ff60035416611913565b604051636d4c6c8960e01b8152601e60048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600a55602060405160018152f35b503461000e57600036600319011261000e576020600d54604051908152f35b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e57600435611f19816103c5565b6000546001600160a01b039081169133839003611f73576020925016806000526008825260006040812055604051907fb1c1232c5dd039bb1c46cc05eaf25828e4f8596b7f68bdb23073ba78b9ca382d600083a260018152f35b604051636d4c6c8960e01b81528061074f8533600484016138c4565b503461000e57602036600319011261000e5760ff600435611faf816103a0565b16600052600b6020526020604060002054604051908152f35b503461000e5760c036600319011261000e576004803590611fe8826103a0565b602435611ff4816103c5565b6044359260643560843592612008846103c5565b60026009541461242b57600260095560ff8316906000918083526020926002845260409760ff8983205416801561241f575b612403578942116123ca576120a285888b8d8661206461049861049860015460018060a01b031690565b925163125cf47f60e01b81526001600160a01b038b16888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af19081156123bd575b83916123a0575b506001600160a01b03851660009081526007602052604090206120e590546120df6005548a6139c1565b906139e1565b6001600160a01b0386166000908152600760205260409020556001600160a01b0385811699906121178930338e615576565b8b5163dc3bfba960e01b80825283831691908e8b8389818c885af1928315612393575b8993612374575b50519081528a8188818b875af1908115612367575b889161234a575b508380871691160361230657508885918e5192838092635001f3b560e01b82525afa9081156122f9575b86916122dc575b50168a81036122c15750936000805160206156a483398151915296938996938d6121f361069d9f9c9560029f9e9c998a826121ec9230908a156122b9575b6121e560a435946121df600554826139c1565b90613a34565b908d6147b3565b9088614e1b565b9d8e9414612228575b50508a5160ff909716875250505050602083018890525060408201523360608201528060808101610682565b6104986104986122489288612242886122749b8d9b614f26565b50614d32565b8c51630fa9d3b160e01b8152339281019283526020830194909452929485939284929091839160400190565b03925af180156122ac575b61228e575b838982808a6121fc565b816122a492903d106106c8576106b88183611572565b503880612284565b6122b461390a565b61227f565b3391506121cc565b8361074f888e51938493636d4c6c8960e01b855284016139f6565b6122f39150893d8b11610702576106f48183611572565b3861218e565b61230161390a565b612187565b8d51636d4c6c8960e01b8152600c81880190815260006020820181905260408201526001600160a01b039283166060820152918616608083015290819060a0010390fd5b61236191508b3d8d11610702576106f48183611572565b3861215d565b61236f61390a565b612156565b61238c9193508c8d3d10610702576106f48183611572565b9138612141565b61239b61390a565b61213a565b6123b79150863d8811610702576106f48183611572565b386120b5565b6123c561390a565b6120ae565b8851636d4c6c8960e01b81526002918101918252426020830152604082018b9052600060608301819052608083015290819060a0010390fd5b8851636d4c6c8960e01b815290819061074f9089908301613955565b5060ff6003541661203a565b61074f85604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e57602060ff600354166040519015158152f35b503461000e5760a036600319011261000e5760048035906124a9826103a0565b6024356124b5816103c5565b60443590606435916084359260026009541461242b57600260095560ff86169260008481526020916002835260409560ff878420541680156127c3575b6127aa5785421161277257600803612737579661256394959697838a61252561049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038516868201908152602081018c905260ff9093166040840152988992839188918391606090910190565b03925af195861561272a575b839661270b575b506001600160a01b03818116966126339185919087906126179061259c8b30338f615576565b8d6125f06125ad8d600554906139c1565b6001600160a01b038a1660009081526007602052604090206125d0908290611a7f565b6001600160a01b038b1660009081526007602052604090205b558d613a34565b9051636e553f6560e01b815289810191825230602083015295869384928391604090910190565b039286165af19182156126fe575b86926126df575b5083614e1b565b98808a106126a4575083896000805160206156a483398151915296948c969461224861049861069d9f8d886122428861266f9b61049895614f26565b03925af180156106cf576106a1575050855160ff90921682526020820187905260408201523360608201528060808101610682565b8851636d4c6c8960e01b81526010818501908152602081018c90526040810192909252600060608301819052608083015290819060a0010390fd5b6126f7919250873d8911610e7b57610e6d8183611572565b903861262c565b61270661390a565b612625565b612723919650843d8611610702576106f48183611572565b9438612576565b61273261390a565b61256f565b8551636d4c6c8960e01b81526006818a0190815260ff8b166020820152600060408201819052606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81526002818b01908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81528061074f8c828d01613955565b5060ff600354166124f2565b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57608036600319011261000e57600480359061280e826103a0565b6024359161281b836103c5565b6044356064359160026009541461077a57600260095560ff81169460008681526020906002825260409560ff87832054168015612b7c575b612b6357908291849387898897858d6128b461287c61049861049860015460018060a01b031690565b94519b8c968795869463125cf47f60e01b8652850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1948515612b56575b8295612b37575b506001600160a01b038581169a9095908b15612afe5783908061291d5761074f8c8c51918291636d4c6c8960e01b83528201906000608060a0840193602081528260208201528260408201528260608201520152565b8c9b949392919060018103612a8a57505061293781615100565b8042118015612a82575b612a48575093878a879583956129ae8d9e9f6104987f309b03ba657e17f1beadbc6eb3c06ba79b38084eb8d0e5452cc222462a17f1f69e9f6129a761299e8b612a069f8f6129996129d99f6104989930903390615576565b614e1b565b809d819a614f26565b508d614d32565b9251630fa9d3b160e01b81523392810192835260208301949094529294859384929091839160400190565b03925af18015612a3b575b612a1d575b5050875160ff909616865260208601529116929081906040820190565b0390a3612a136001600955565b5160018152602090f35b81612a3392903d106106c8576106b88183611572565b5088806129e9565b612a4361390a565b6129e4565b8a51636d4c6c8960e01b815260078187019081526020810192909252426040830152600060608301819052608083015290819060a0010390fd5b508015612941565b60028103612aa2575050612a9d81615100565b612937565b60038103612ab5575050612a9d8161515b565b808603612ac7575050612a9d81615190565b60058103612ada575050612a9d816151c5565b60078103612aed575050612a9d81615275565b600803612a9d5750612a9d816153be565b8951636d4c6c8960e01b8152601a818d0190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b612b4f919550833d8511610702576106f48183611572565b93386128c7565b612b5e61390a565b6128c0565b8651636d4c6c8960e01b81528061074f87828c01613955565b5060ff60035416612853565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e5760e036600319011261000e57612beb6004356103a0565b612bf66024356103c5565b612c0160c4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff6040600020541680156132d5575b611e28576044354211611df357600154612c5290610498906001600160a01b031681565b6020604051809263125cf47f60e01b825281600081612c9a6004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19081156132c8575b6000916132a9575b50612cc860643530336024356001600160a01b0316615576565b612d21612cd96005546064356139c1565b6001600160a01b03602435166000908152600760205260409020612cfe908290611a7f565b6001600160a01b0360243516600090815260076020526040902055606435613a34565b6040516370a0823160e01b81523060048201529091906020816024816001600160a01b0386165afa90811561329c575b60009161327d575b5060043560ff16600503613085576040516316f0115b60e01b81526020816004816001600160a01b0387165afa908115613078575b600091613059575b5060405163f1563de960e01b815260208160048160c4356001600160a01b03165afa90811561304c575b60009161302d575b506001600160a01b0381811690831603611d47575060405163f77c479160e01b81529190602090839060049082906001600160a01b03165afa918215613020575b600092612fff575b506001600160a01b0382163b1561000e5760405163507a3baf60e01b81526001600160a01b0360c4358116600483015260248201959095526001604482015260006064820181905260a480356084840152612eab96612ea29584929183918591165af18015612ff2575b612fd9575b505b6040516370a0823160e01b81523060048201526020818060248101611b5d565b90602435614e1b565b60043560ff1660051480612fce575b612f975780612ed161069d92602435600435614f26565b50612ee6610498610498604435602435614d32565b604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000905af18015612f8a575b612f6b575b506040805160ff6004351681526020810183905260643591810191909152336060820152604435906024356001600160a01b0316906000805160206156a4833981519152908060808101611c53565b612f839060203d6020116106c8576106b88183611572565b5038612f1c565b612f9261390a565b612f17565b604051636d4c6c8960e01b8152600b600482015260248101919091526084803560448301526000606483018190529082015260a490fd5b506084358110612eba565b80612fe6612fec9261151c565b80610800565b38612e80565b612ffa61390a565b612e7b565b61301991925060203d602011610702576106f48183611572565b9038612e11565b61302861390a565b612e09565b613046915060203d602011610702576106f48183611572565b38612dc8565b61305461390a565b612dc0565b613072915060203d602011610702576106f48183611572565b38612d96565b61308061390a565b612d8e565b91600760ff60043516146130a1575b50612ea2612eab92612e82565b60405163c6ec27bf60e01b815260208160048160c4356001600160a01b03165afa908115613270575b600091613251575b506001600160a01b03818116602435909116036132315750604051631e5f74a160e01b815260208160048160c4356001600160a01b03165afa908115613224575b600091613205575b506001600160a01b03818116908416036131e75750612eab92612ea2916020613142614d00565b61314a614cad565b60405163e7ffb5f760e01b815293849283926131779260a435923092608435929160c43560048901614556565b038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156131da575b6131bb575b509250613094565b6131d39060203d602011610e7b57610e6d8183611572565b50386131b3565b6131e261390a565b6131ae565b604051636d4c6c8960e01b815290819061074f9085600484016139f6565b61321e915060203d602011610702576106f48183611572565b3861311b565b61322c61390a565b613113565b604051636d4c6c8960e01b815290819061074f90602435600484016139f6565b61326a915060203d602011610702576106f48183611572565b386130d2565b61327861390a565b6130ca565b613296915060203d602011610e7b57610e6d8183611572565b38612d59565b6132a461390a565b612d51565b6132c2915060203d602011610702576106f48183611572565b38612cae565b6132d061390a565b612ca6565b5060ff60035416612c2e565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e5761016036600319011261000e576004803590613348826103a0565b60243590613355826103c5565b60443560643560a03660a319011261000e576101443592613375846103c5565b60026009541461368c57600260095560009560ff811687526020916002835260409560ff878a2054168015613680575b6136665785421161362e5761340b9084846133cd61049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038d16878201908152602081018c905260ff909316604084015294859283918f918391606090910190565b03925af1918215613621575b8a92613602575b508751630b2339af60e21b81526001600160a01b03919060608186818686165afa9081156135f5575b8c916135c4575b50828085169116036135a95761353086938a8c858f956105f59661347b8e9b9985169e8f30903390615576565b6134f76134ce61348e8d600554906139c1565b6001600160a01b03871660009081526007602052604090206134b1908290611a7f565b6001600160a01b03881660009081526007602052604090206125e9565b946134d76115b2565b6001600160a01b038216815295868d01526001600160a01b031686860152565b80606085015280608085015261350b61440b565b60a08501528451809781958294631b7077d560e01b8452608435908d309086016144b3565b03927f0000000000000000000000000000000000000000000000000000000000000000165af191821561359c575b8d9261356c575b508b614e1b565b61358d9192508b3d8d11613595575b6135858183611572565b81019061449d565b509038613565565b503d61357b565b6135a461390a565b61355e565b8361074f848b51938493636d4c6c8960e01b855284016139f6565b6135e5915060603d81116135ee575b6135dd8183611572565b81019061446a565b5090503861344e565b503d6135d3565b6135fd61390a565b613447565b61361a919250853d8711610702576106f48183611572565b903861341e565b61362961390a565b613417565b8651636d4c6c8960e01b81526002818401908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b5061074f8651928392636d4c6c8960e01b84528301613955565b5060ff600354166133a5565b61074f90604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561370e816103c5565b6000546001600160a01b03908116338190036108a4575060207fe4b67652e856f57a7747dd2473850ce987087f4b1744a870504f1c047cb56f4f916203f48042019384421161377c575b1692836000526008825280604060002055604051908152a260405160018152602090f35b6137846139aa565b613758565b503461000e576000806003193601126137e35780546001600160a01b0316338190036108a457602082806006557f6875685eb5dbc8e2796d75d2dc9e9cb607b610d0558ee7336df418a26d4846e86040519182a160018152f35b80fd5b503461000e57602036600319011261000e57600435613804816103c5565b60018060a01b031660005260076020526020604060002054604051908152f35b503461000e57604036600319011261000e57600435613842816103a0565b6024359061384f82610f53565b6000546001600160a01b0316338190036108a45750602060ff7f09f6e15731965d66c152a2c1c1b62c94420ba4530dafe8818e1a01906641fb1e92169283600052600282526138ae8160406000209060ff801983541691151516179055565b604051938452151592a260405160018152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b9081602091031261000e5751613907816103c5565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052603260045260246000fd5b919081101561393e5760051b0190565b611612613917565b9081602091031261000e575190565b608060009193929360ff60a0820195600183521660208201528260408201528260608201520152565b50634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575161390781610f53565b50634e487b7160e01b600052601160045260246000fd5b81156139cb570490565b634e487b7160e01b600052601260045260246000fd5b919082018092116139ee57565b6103b86139aa565b601b815260006020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000198101919082116139ee57565b919082039182116139ee57565b9799949598929098969196600260095414611e5157600260095560ff8916600052600260205260ff60406000205416801561412c575b614111578742116140dd57613a968a613a9136848b614138565b614a52565b613a9f82614a13565b6040516370a0823160e01b81523060048201529b90979060208d8d81806024810103916001600160a01b03165afa9c8d156140d0575b60009d614091575b50613bc38b8d8c613d5397613d8c9997613d4e978f97613b1789613b0e613d299b303360018060a01b038c16615576565b600554906139c1565b9a8b613b41613b3a613b298851613a25565b92613b34848a61421e565b51613a34565b918761421e565b526001600160a01b0387166000908152600760205260409020613b658d82546139e1565b90556001546020908990613b8390610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b038b166004820152602481018a905260ff9092166044830152909a8b9190829060009082906064820190565b03925af1988915614084575b600099614063575b506040516370a0823160e01b8152306004820152986020908a9060249082906001600160a01b03165afa988915614056575b60009961401b575b50926020969492613c3e8893613cd39a989660405196879586956310510f1160e01b8752600487016142cb565b038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561400e575b613ff1575b50600154600090613c9590610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b039094166004850152602484019290925260ff909316604483015290938492839182906064820190565b03925af1908115613fe4575b600091613fc5575b506040516370a0823160e01b81523060048201529060209082908180602481015b03916001600160a01b03165afa908115611ce057600091611cc15750613a34565b6040516370a0823160e01b8152306004820152909c906020818e818060248101613d08565b613a34565b613d5f600554826139c1565b6001600160a01b038a166000908152600760205260409020909190613d858382546139e1565b9055613a34565b9060009015613f215750613eb5613e848860006020613e6d613e446104988f9d9b99613ebd9f9d613e3e819d613e22610498946000805160206156a48339815191529e8a868e613de961049861049860015460018060a01b031690565b908f60405180988195829463125cf47f60e01b8452600484016040906000929493606082019560018060a01b0316825260208201520152565b03925af1928315613f14575b8c93613ef5575b5033918d6147b3565b97614d32565b604051630fa9d3b160e01b8152336004820152602481018e905293849283919082906044820190565b03925af18015613ee8575b613ec9575b50886139e1565b604080516001815260208101929092528101939093523360608401526001600160a01b038616929081906080820190565b0390a3614f26565b50906103b86001600955565b613ee19060203d6020116106c8576106b88183611572565b5038613e7d565b613ef061390a565b613e78565b613f0d9193508b3d8d11610702576106f48183611572565b9138613e35565b613f1c61390a565b613e2e565b9695939150849790859350808796613f4b8860018060a01b03166000526004602052604060002090565b6000918252602052604090205490613f62916139e1565b6001600160a01b0387166000908152600460205260409020859060009182526020526040902055613f92916139e1565b96613eb5613e84613ebd9860006020613e6d613e446104988f9d6000805160206156a48339815191529961049891614d32565b613fde915060203d602011610702576106f48183611572565b38613ce7565b613fec61390a565b613cdf565b61400790853d87116106c8576106b88183611572565b5038613c7a565b61401661390a565b613c75565b613cd398969491995092613c3e6020989694936140458a95863d8811610e7b57610e6d8183611572565b9b9396989a50935050929496613c11565b61405e61390a565b613c09565b61407d91995060203d602011610702576106f48183611572565b9738613bd7565b61408c61390a565b613bcf565b8a9d508b8d8b926020989697983d6020116140c9575b6140b18183611572565b81016140bc91613946565b9f50505094939294613add565b503d6140a7565b6140d861390a565b613ad5565b604051636d4c6c8960e01b81526002600482015242602482015260448101899052600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f8b60048301613955565b5060ff60035416613a77565b929192614144826115f1565b60409461415386519283611572565b819584835260208093019161014080960285019481861161000e57925b8584106141805750505050505050565b868483031261000e578487916141946115d1565b863581526141a38388016103ba565b838201526141b28688016103f0565b8682015260606141c38189016103f0565b9082015260806141d4818901610f6a565b9082015260a06141e5818901610f6a565b9082015260c0808801359082015260e0808801359082015261010080880135908201526101208088013590820152815201930192614170565b6020918151811015614233575b60051b010190565b61423b613917565b61422b565b90815180825260208080930193019160005b828110614260575050505090565b835185529381019392810192600101614252565b9190808252602080920192916000905b828210614292575050505090565b9091929360019060ff86356142a6816103a0565b1681528583013583820152604080870135908201526060908101950193920190614284565b9391959492606096808887018988525260809081870193916000905b82821061431857505050506139079596509061430a918582036020870152614240565b926040818503910152614274565b909192946001908635815261433c6020614333818a016103ba565b60ff1690830152565b61435b604061434c818a016103f0565b6001600160a01b031690830152565b6143786143698d89016103f0565b6001600160a01b0316828e0152565b61438e614386848901610f6a565b151582850152565b6143a660a061439e818a01610f6a565b151590830152565b60c0878101359082015260e08088013590820152610100808801359082015261012080880135908201526101409081019601939201906142e7565b6020906001600160401b0381116143fe575b601f01601f19160190565b614406611505565b6143f3565b604051906144188261153c565b604082527f30303030303030303030303030303030303030303030303030303030303030306040837f307830303030303030303030303030303030303030303030303030303030303060208201520152565b9081606091031261000e578051614480816103c5565b9160406020830151614491816103c5565b920151613907816103c5565b919082604091031261000e576020825192015190565b92936101e0926139079560a093600180861b0392838092168852166020870152604086015260a435606086015260c435608086015260e435838601526101043560c08601526101243560e0860152610120806101008701528183511690860152602082015161014086015280604083015116610160860152806060830151166101808601526080820151166101a085015201519160c06101c08201520190610aba565b939796929461459460e096614586600099949560018060a01b0380971689526101008060208b0152890190614240565b908782036040890152614240565b98606086015260808501521660a083015260c08201520152565b60015460405163057453a760e31b81526103b8939290916001600160a01b0391906020908490600490829086165afa928315614614575b6000936145f4575b5016615505565b61460d91935060203d8111610702576106f48183611572565b91386145ed565b61461c61390a565b6145e5565b60015460405163057453a760e31b81526146a9929161468f916001600160a01b03916020908290600490829086165afa9081156146d7575b6000916146b9575b506001600160a01b038416600090815260046020526040902086600052602052604060002054918416615505565b6001600160a01b0316600090815260046020526040902090565b9060005260205260006040812055565b6146d1915060203d8111610702576106f48183611572565b38614661565b6146df61390a565b614659565b60019060001981146146f4570190565b6146fc6139aa565b0190565b9190811015614742575b60051b81013590601e198136030182121561000e5701908135916001600160401b03831161000e57602001823603811361000e579190565b61474a613917565b61470a565b908092918237016000815290565b3d15614788573d9061476e826143e1565b9161477c6040519384611572565b82523d6000602084013e565b606090565b906020613907928181520190610aba565b9081602091031261000e57516139078161188b565b6040516370a0823160e01b8082526001600160a01b0386811660048401526148f697929660209690959382169492939192918789602481895afa988915614a06575b6000996149c8575b506148a4846148db989694614849600095858d9b998c97169487614820846150ec565b6040516304f9ef2360e21b81526001600160801b03909116600482015294859081906024820190565b0381895afa9384156149bb575b899461499a575b50614869939416615505565b60405163bcc1694f60e01b81526001600160a01b03891660048201526001600160801b03909116602482015293849283919082906044820190565b03925af1801561498d575b614960575b506040519081526001600160a01b0390921660048301529092839190829081906024820190565b03915afa918215614953575b600092614936575b5050613a34565b90808210614902575090565b604051636d4c6c8960e01b8152600b600482015260248101929092526044820152600060648201819052608482015260a490fd5b61494c9250803d10610e7b57610e6d8183611572565b38806148ef565b61495b61390a565b6148e7565b61497f90853d8711614986575b6149778183611572565b81019061479e565b50386148b4565b503d61496d565b61499561390a565b6148af565b61486994506149b590893d8b11614986576149778183611572565b9361485d565b6149c361390a565b614856565b88969487929a506148db9896946148496000956149f46148a495873d8911610e7b57610e6d8183611572565b9d9550955050949698509496506147fd565b614a0e61390a565b6147f5565b906000805b83518214614a4d57600190614a2d838661421e565b518101809111614a40575b910190614a18565b614a486139aa565b614a38565b925050565b90600091825b81518114614abe576001600160a01b03806060614a75848661421e565b51015116908416818103614a8d575050600101614a58565b60a491869160405192636d4c6c8960e01b845260036004850152806024850152604484015260648301526084820152fd5b50505050565b9092614b69614b3260a060c09598979660e08652805160e087015260208101516002811015614b72575b610100870152600180831b038060408301511661012088015260608201511661014087015260808101516101608701520151856101808601526101a0850190610aba565b9660208401906060809160018060a01b03808251168552602082015115156020860152604082015116604085015201511515910152565b60a08201520152565b614b7a61397e565b614aee565b9193614c3b9394614ba2610498610498610498606087015160018060a01b031690565b6040516370a0823160e01b808252306004830152909560209592949193919289929087896024818a5afa988915614ca0575b600099614c6f575b5091879594939160008794614c07604051978896879586946352bbbe2960e01b865260048601614ac4565b03926001600160a01b03165af18015614c62575b614c45575b506040519081523060048201529182908180602481016148db565b908110611c8c5790565b614c5b90833d8511610e7b57610e6d8183611572565b5038614c20565b614c6a61390a565b614c1b565b8896959492908794929a50614c92600091863d8811610e7b57610e6d8183611572565b9a9294505091939495614bdc565b614ca861390a565b614bd4565b604051614cb98161153c565b600281526001602082016040368237825115614cf3575b52805160011015614ce6575b6000604082015290565b614cee613917565b614cdc565b614cfb613917565b614cd0565b604051614d0c81611557565b600181526000602082016020368237825115614d26575290565b614d2e613917565b5290565b602090606460018060a01b039160008360015416604051968795869463125cf47f60e01b865216600485015260248401528160448401525af1908115614d96575b600091614d7e575090565b613907915060203d8111610702576106f48183611572565b614d9e61390a565b614d73565b9081602091031261000e5751613907816103a0565b60ff16601b039060ff82116139ee57565b9060ff8091169116039060ff82116139ee57565b60ff16604d8111614def575b600a0a90565b614df76139aa565b614de9565b8060001904821181151516614e0f570290565b614e176139aa565b0290565b60405163313ce56760e01b8082529093926020926001600160a01b03929084908790600490829087165afa958615614f19575b600096614ef3575b50906004849260405194859384928352165afa918215614ee6575b600092614eb9575b505060ff811660ff841611614ea357614e9861390793614e9d92614dc9565b614ddd565b906139c1565b614e98614eb39161390794614dc9565b90614dfc565b614ed89250803d10614edf575b614ed08183611572565b810190614da3565b3880614e79565b503d614ec6565b614eee61390a565b614e71565b8492919650614f10600491843d8611614edf57614ed08183611572565b96919250614e56565b614f2161390a565b614e4e565b91614f86916001600160a01b03168173ae7ab96520de3a18e5e111b5eaab095312d7fe8482146150c9575b50614e9860046020614eb3936040519283809263313ce56760e01b82525afa9081156150bc575b60009161509e575b50614db8565b600a5480821161508657614fab82611a7f8560ff16600052600b602052604060002090565b80614fc38560ff16600052600b602052604060002090565b5562015180614fe9614fe28660ff16600052600c602052604060002090565b5442613a34565b11156150275750506150088260ff16600052600b602052604060002090565b55615021429160ff16600052600c602052604060002090565b55600190565b1190506150345750600190565b61504b8160ff16600052600b602052604060002090565b54604051636d4c6c8960e01b8152601f6004820152602481019190915260ff919091166044820152600060648201819052608482015260a490fd5b8261504b8160ff16600052600b602052604060002090565b6150b6915060203d8111614edf57614ed08183611572565b38614f80565b6150c461390a565b614f78565b614eb3919250600460206150e2614e9893600d54614dfc565b9493505050614f51565b6001600160801b039081811161000e571690565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa90811561514e575b600091615136575090565b613907915060203d8111610e7b57610e6d8183611572565b61515661390a565b61512b565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa908115615268575b60009161524b575b50600460405180948193632745fed560e11b8352165afa91821561523e575b60009261522857505090565b6139079250803d10610e7b57610e6d8183611572565b61524661390a565b61521c565b6152629150823d8411610702576106f48183611572565b386151fd565b61527061390a565b6151f5565b60405163247c579160e21b81526020916001600160a01b0391839161532591849184908290600490829086165afa9081156153b1575b600091615394575b50166040516362b9c05160e11b8152838082600481865afa918215615387575b600092615367575b50604051636558954f60e01b81529192829060049082905afa90811561535a575b60009161533d575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa91821561523e5760009261522857505090565b6153549150843d8611610e7b57610e6d8183611572565b38615304565b61536261390a565b6152fc565b6004925061538190823d8411610702576106f48183611572565b916152db565b61538f61390a565b6152d3565b6153ab9150843d8611610702576106f48183611572565b386152b3565b6153b961390a565b6152ab565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115615444575b6000916153fc575b5064ffffffffff1690565b6020813d821161543c575b8161541460209383611572565b8101031261543857519064ffffffffff821682036137e3575064ffffffffff6153f1565b5080fd5b3d9150615407565b61544c61390a565b6153e9565b60448151106154cb5760048101518101906020816024840193031261000e576024810151906001600160401b03821161000e57018160438201121561000e57602481015161549e816143e1565b926154ac6040519485611572565b8184526044828401011161000e57613907916044602085019101610a97565b506040516154d881611557565b601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6044600092838093615538966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af16155f5565b1561553f57565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b600092836064926155b2968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af16155f5565b156155b957565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d901561562557908160201461561857501561561357600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b039092166004830152600019602483015261566691600091829160449183905af16155f5565b1561566d57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfe4dcca373512f0049f0e5732bf8deedaf846a121102837227c421cb8d8fc83522a2646970667358221220fc2f8cfc294ef1d52a654ce80df42688b2f5b2b9c4808b13bd14eaa3b6e5907f64736f6c63430008100033000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c806303799f87146103975780630cdc7ec61461038e5780630d3f5352146103855780631177ec301461037c5780631271f09a146103735780631ac3ddeb1461036a5780631e897afb1461036157806327187991146103585780632e25d2a61461034f57806330568a8d1461034657806335197f9e1461033d578063355d63f11461033457806337a3eeec1461032b5780633faf30ad1461032257806351cff8d9146103195780635ac86ab71461031057806362a4520e1461030757806365dff1e1146102fe57806369fe0e2d146102f55780636b42450d146102ec578063704b6c02146102e35780637114d92c146102da57806375a9b172146102d15780637a9262a2146102c85780637c7cde83146102bf578063884e21d6146102b65780639984f30d146102ad5780639e307955146102a45780639e6b51731461029b578063a102e38414610292578063adea384414610289578063b49e0d7214610280578063b9b8af0b14610277578063c244e1ff1461026e578063d0886f9714610265578063dc4c7ca91461025c578063ea08c03114610253578063eda8ff6b1461024a578063ef60356914610241578063f7adf98014610238578063f851a4401461022f578063f8eaad3514610226578063f9ad473d1461021d578063faaebd21146102145763fe3ee1691461020c57600080fd5b61000e613824565b5061000e6137e6565b5061000e613789565b5061000e6136f0565b5061000e6136c6565b5061000e613327565b5061000e6132e1565b5061000e612bce565b5061000e612b88565b5061000e6127ee565b5061000e6127cf565b5061000e612489565b5061000e612465565b5061000e611fc8565b5061000e611f8f565b5061000e611efb565b5061000e611edc565b5061000e611ebd565b5061000e611e86565b5061000e61189c565b5061000e61186c565b5061000e61182e565b5061000e611787565b5061000e6116d6565b5061000e611469565b5061000e6113c2565b5061000e611298565b5061000e6111d5565b5061000e61119c565b5061000e61115e565b5061000e610fef565b5061000e610f75565b5061000e610f0d565b5061000e610d56565b5061000e610d37565b5061000e610ca5565b5061000e610c7b565b5061000e610c44565b5061000e610b41565b5061000e610a0e565b5061000e6108c2565b5061000e610829565b5061000e61080b565b5061000e6107b4565b5061000e6103fb565b60ff81160361000e57565b600435906103b8826103a0565b565b35906103b8826103a0565b6001600160a01b0381160361000e57565b602435906103b8826103c5565b608435906103b8826103c5565b35906103b8826103c5565b503461000e5761010036600319011261000e57600480359061041c826103a0565b60243590610429826103c5565b60443560c43560643561043b826103c5565b60026009541461077a57600260095560009560ff811687526020916002835260409560ff878a205416801561076e575b610753578542116107165783906104e382858a8a8e8e6104a461049861049860015460018060a01b031690565b6001600160a01b031690565b935163125cf47f60e01b81526001600160a01b03909116888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af1908115610709575b8b916106dc575b506001600160a01b038a811697908a908d906105148830338e615576565b6001600160a01b038e1660009081526007602052604090208e905460055461053c908b6139c1565b610545916139e1565b6001600160a01b0390911660009081526007602052604090205560055461056c90896139c1565b6105769089613a34565b908b610580611593565b30815294858a01859052308187015284606087015261059d6115b2565b60e43581528a8101959095526001600160a01b039091169084015285166001600160a01b0316606083015260808201526105d561440b565b60a082015260a43591608435916105eb94614b7f565b6105f5918b614e1b565b98876106028b8388614f26565b5061060c91614d32565b8851630fa9d3b160e01b815233928101928352602083018b90529a8b92839003604001918391906001600160a01b03165af161069d986000805160206156a4833981519152949180156106cf575b6106a1575b5050855160ff909216825260208201879052604082015233606082015280608081015b0390a361068f6001600955565b519081529081906020820190565b0390f35b816106c092903d106106c8575b6106b88183611572565b810190613995565b50388061065f565b503d6106ae565b6106d761390a565b61065a565b6106fc9150833d8511610702575b6106f48183611572565b8101906138f2565b386104f6565b503d6106ea565b61071161390a565b6104ef565b8651636d4c6c8960e01b8152600291810191825242602083015260408201879052600060608301819052608083015290819060a0010390fd5b0390fd5b8651636d4c6c8960e01b81529283925061074f918301613955565b5060ff6003541661046b565b61074f84604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57604036600319011261000e576004356107d2816103c5565b60018060a01b0316600052600460205260406000206024356000526020526020604060002054604051908152f35b600091031261000e57565b503461000e57600036600319011261000e5760206040516101f48152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036108a4577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f4804201804211610897575b80600655604051908152a160405160018152602090f35b61089f6139aa565b610880565b604051636d4c6c8960e01b815290819061074f9033600484016138c4565b503461000e57606036600319011261000e57600480356108e1816103c5565b602435906044356108f1816103c5565b600080546001600160a01b0391908216338190036109f15750805b60ff906009828216146109d85760019084876109788b84610939610498610498895460018060a01b031690565b8d8a60405180968195829463125cf47f60e01b845260209a8b99850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19182156109cb575b87926109ae575b505016868161099e575b5050011661090c565b6109a79161562c565b3886610995565b6109c49250803d10610702576106f48183611572565b388061098b565b6109d361390a565b610984565b5050506109e5921661562c565b60405160018152602090f35b604051636d4c6c8960e01b815290819061074f9033838b016138c4565b503461000e57602036600319011261000e57600435610a2c816103c5565b600080546001600160a01b03908116338190036108a45750806109e59316908183526007602052604083205492806040812055541690615505565b9181601f8401121561000e578235916001600160401b03831161000e576020808501948460051b01011161000e57565b60005b838110610aaa5750506000910152565b8181015183820152602001610a9a565b90602091610ad381518092818552858086019101610a97565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b848310610b135750505050505090565b9091929394958480610b31600193603f198682030187528a51610aba565b9801930193019194939290610b03565b5060208060031936011261000e57600480356001600160401b03811161000e57610b6e9036908301610a67565b9290610b79846115f1565b92604092610b8984519586611572565b858552601f19610b98876115f1565b0160005b818110610c355750505060005b858110610bbd5783518061069d8782610adf565b600080610bcb838987614700565b90610bda88518093819361474f565b0390305af4610be761475d565b9015610c125790610c0d91610bfc828861421e565b52610c07818761421e565b506146e4565b610ba9565b8261074f610c208793615451565b925162461bcd60e51b8152928392830161478d565b60608782018401528201610b9c565b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600d55602060405160018152f35b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e57600435610cc3816103c5565b6000546001600160a01b03908116338190036108a457506001541680610d0357600180546001600160a01b0319166001600160a01b0384161790556109e5565b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600654604051908152f35b503461000e5760a036600319011261000e57600435610d74816103c5565b60243590610d81826103c5565b60443590610d8e826103c5565b606435610d9a816103c5565b60843592610da7846103c5565b6001546001600160a01b039390841633819003610ecf5750831694838116610ebf575b50828116610eaf575b50818116610e9f575b508116610e8f575b50604051636eb1769f60e11b81523060048201527f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b038116602483015290602081604481865afa908115610e82575b600091610e54575b5015610e4b575b005b610e499161562c565b610e75915060203d8111610e7b575b610e6d8183611572565b810190613946565b38610e42565b503d610e63565b610e8a61390a565b610e3a565b610e99908261562c565b38610de4565b610ea9908461562c565b38610ddc565b610eb9908561562c565b38610dd3565b610ec9908661562c565b38610dca565b604051636d4c6c8960e01b81526000600482018190526024820181905260448201523360648201526001600160a01b0391909116608482015260a490fd5b503461000e57600036600319011261000e576040517f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c6266001600160a01b03168152602090f35b8015150361000e57565b60e435906103b882610f53565b35906103b882610f53565b503461000e57602036600319011261000e57600435610f9381610f53565b6000546001600160a01b0316338190036108a4577fb4252ba7ada6e65ca524b94dd554b37a638732fc8c63021e66782fc6e75bbb0c602083151560ff196003541660ff821617600355604051908152a160405160018152602090f35b503461000e57602036600319011261000e5760043561100d816103c5565b6000546001600160a01b03908116338190036108a457506001600160a01b03821660009081526008602052604090205480156111295742106110f4576001600160a01b03821660009081526008602052604081206109e5939190556001600160a01b0381166000908152600760205260408120556000549116906001600160a01b03166040516370a0823160e01b81523060048201529160208380602481010381845afa9283156110e7575b6000936110c7575b50615505565b6110e091935060203d8111610e7b57610e6d8183611572565b91386110c1565b6110ef61390a565b6110b9565b604051636d4c6c8960e01b8152601360048201526000602482018190526044820181905260648201819052608482015260a490fd5b604051636d4c6c8960e01b8152601260048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e5760ff60043561117e816103a0565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e5760ff6004356111bc816103a0565b16600052600c6020526020604060002054604051908152f35b503461000e57604036600319011261000e576001600160401b0360043581811161000e57611207903690600401610a67565b909160243590811161000e57611221903690600401610a67565b9060018060a01b0380600054168033036108a4575060005b84810361124c5760405160018152602090f35b808261125b600193888a61392e565b35611265816103c5565b1680611273575b5001611239565b6112929061128283888861392e565b359061128d826103c5565b61562c565b3861126c565b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036108a457506006548061130057604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461133f57604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b506101f4811061138d5761135281600555565b61135c6000600655565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57604036600319011261000e576004356113e0816103c5565b6001546004906020906113fd90610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa90811561145c575b60009161143e575b506001600160a01b03811633036108a457610e4960243583614621565b611456915060203d8111610702576106f48183611572565b38611421565b61146461390a565b611419565b503461000e57602036600319011261000e57600435611487816103c5565b6000546001600160a01b0380821692338490036114e957602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b81528061074f8633600484016138c4565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161152f57604052565b611537611505565b604052565b606081019081106001600160401b0382111761152f57604052565b604081019081106001600160401b0382111761152f57604052565b90601f801991011681019081106001600160401b0382111761152f57604052565b60405190608082018281106001600160401b0382111761152f57604052565b6040519060c082018281106001600160401b0382111761152f57604052565b6040519061014082018281106001600160401b0382111761152f57604052565b6020906001600160401b03811161160a575b60051b0190565b611612611505565b611603565b81601f8201121561000e5780359161162e836115f1565b9261163c6040519485611572565b808452602092838086019260051b82010192831161000e578301905b828210611666575050505090565b81358152908301908301611658565b9181601f8401121561000e578235916001600160401b03831161000e57602080850194610140850201011161000e57565b9181601f8401121561000e578235916001600160401b03831161000e576020808501946060850201011161000e57565b503461000e5761012036600319011261000e576116f16103ab565b6116f96103d6565b6001600160401b039060643582811161000e5761171a903690600401611617565b926117236103e3565b60a43584811161000e5761173b903690600401611675565b60c49691963595861161000e5761069d9661175d6117779736906004016116a6565b949093611768610f5d565b96610104359860443591613a41565b6040519081529081906020820190565b503461000e57604036600319011261000e576004356117a5816103c5565b6001546004906020906117c290610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa908115611821575b600091611803575b506001600160a01b03811633036108a457610e49602435836145ae565b61181b915060203d8111610702576106f48183611572565b386117e6565b61182961390a565b6117de565b503461000e57602036600319011261000e5760043561184c816103c5565b60018060a01b031660005260086020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600a54604051908152f35b6001600160801b0381160361000e57565b503461000e5761010036600319011261000e576118ba6004356103a0565b6118c56024356103c5565b6118d060643561188b565b6118db60a4356103c5565b6118e660e4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff604060002054168015611e45575b611e28576044354211611df35760405163378efa3760e01b815260208160048160a4356001600160a01b03165afa908115611de6575b600091611dc7575b5060405163d10eb4b960e01b81526001600160a01b0360e4358116600483015260c4356024830152909160209183916044918391165afa908115611dba575b600091611d9b575b506001546119b090610498906001600160a01b031681565b906020604051809363125cf47f60e01b8252816000816119f96004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1918215611d8e575b600092611d6d575b506001600160a01b0381811690831603611d475750611a446064356001600160801b031630336024356001600160a01b0316615576565b611ab1611a5e6005546001600160801b03606435166139c1565b6001600160a01b03602435166000908152600760205260409020611a859082905b546139e1565b6001600160a01b03602435166000908152600760205260409020556001600160801b0360643516613a34565b6040516370a0823160e01b808252306004830152919291611b8191906020826024816001600160a01b0388165afa918215611d3a575b600092611d19575b506040516308f1f30f60e41b81526001600160a01b0360e4358116600483015260c435602483015260448201969096526084803560648301819052966020918391829060009060a435165af18015611d0c575b611ced575b5060405190815230600482015260208180602481015b03816001600160a01b0388165afa908115611ce0575b600091611cc1575b50613a34565b918210611c8c57611baf61069d92611b9d604435602435614d32565b926001600160a01b0316602435614e1b565b90611bbf82602435600435614f26565b50604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000906001600160a01b03165af18015611c7f575b611c60575b506040805160ff600435168152602081018390526001600160801b036064351691810191909152336060820152604435906024356001600160a01b0316906000805160206156a48339815191529080608081015b0390a36117776001600955565b611c789060203d6020116106c8576106b88183611572565b5038611bff565b611c8761390a565b611bfa565b604051636d4c6c8960e01b8152600b60048201526000602482018190526044820181905260648201819052608482015260a490fd5b611cda915060203d602011610e7b57610e6d8183611572565b38611b7b565b611ce861390a565b611b73565b611d059060203d602011610e7b57610e6d8183611572565b5038611b47565b611d1461390a565b611b42565b611d3391925060203d602011610e7b57610e6d8183611572565b9038611aef565b611d4261390a565b611ae7565b604051636d4c6c8960e01b815291829161074f916001600160a01b0316600484016139f6565b611d8791925060203d602011610702576106f48183611572565b9038611a0d565b611d9661390a565b611a05565b611db4915060203d602011610702576106f48183611572565b38611998565b611dc261390a565b611990565b611de0915060203d602011610702576106f48183611572565b38611951565b611dee61390a565b611949565b604051636d4c6c8960e01b8152600260048201524260248201526044803590820152600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f60048035908301613955565b5060ff60035416611913565b604051636d4c6c8960e01b8152601e60048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600a55602060405160018152f35b503461000e57600036600319011261000e576020600d54604051908152f35b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e57600435611f19816103c5565b6000546001600160a01b039081169133839003611f73576020925016806000526008825260006040812055604051907fb1c1232c5dd039bb1c46cc05eaf25828e4f8596b7f68bdb23073ba78b9ca382d600083a260018152f35b604051636d4c6c8960e01b81528061074f8533600484016138c4565b503461000e57602036600319011261000e5760ff600435611faf816103a0565b16600052600b6020526020604060002054604051908152f35b503461000e5760c036600319011261000e576004803590611fe8826103a0565b602435611ff4816103c5565b6044359260643560843592612008846103c5565b60026009541461242b57600260095560ff8316906000918083526020926002845260409760ff8983205416801561241f575b612403578942116123ca576120a285888b8d8661206461049861049860015460018060a01b031690565b925163125cf47f60e01b81526001600160a01b038b16888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af19081156123bd575b83916123a0575b506001600160a01b03851660009081526007602052604090206120e590546120df6005548a6139c1565b906139e1565b6001600160a01b0386166000908152600760205260409020556001600160a01b0385811699906121178930338e615576565b8b5163dc3bfba960e01b80825283831691908e8b8389818c885af1928315612393575b8993612374575b50519081528a8188818b875af1908115612367575b889161234a575b508380871691160361230657508885918e5192838092635001f3b560e01b82525afa9081156122f9575b86916122dc575b50168a81036122c15750936000805160206156a483398151915296938996938d6121f361069d9f9c9560029f9e9c998a826121ec9230908a156122b9575b6121e560a435946121df600554826139c1565b90613a34565b908d6147b3565b9088614e1b565b9d8e9414612228575b50508a5160ff909716875250505050602083018890525060408201523360608201528060808101610682565b6104986104986122489288612242886122749b8d9b614f26565b50614d32565b8c51630fa9d3b160e01b8152339281019283526020830194909452929485939284929091839160400190565b03925af180156122ac575b61228e575b838982808a6121fc565b816122a492903d106106c8576106b88183611572565b503880612284565b6122b461390a565b61227f565b3391506121cc565b8361074f888e51938493636d4c6c8960e01b855284016139f6565b6122f39150893d8b11610702576106f48183611572565b3861218e565b61230161390a565b612187565b8d51636d4c6c8960e01b8152600c81880190815260006020820181905260408201526001600160a01b039283166060820152918616608083015290819060a0010390fd5b61236191508b3d8d11610702576106f48183611572565b3861215d565b61236f61390a565b612156565b61238c9193508c8d3d10610702576106f48183611572565b9138612141565b61239b61390a565b61213a565b6123b79150863d8811610702576106f48183611572565b386120b5565b6123c561390a565b6120ae565b8851636d4c6c8960e01b81526002918101918252426020830152604082018b9052600060608301819052608083015290819060a0010390fd5b8851636d4c6c8960e01b815290819061074f9089908301613955565b5060ff6003541661203a565b61074f85604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e57602060ff600354166040519015158152f35b503461000e5760a036600319011261000e5760048035906124a9826103a0565b6024356124b5816103c5565b60443590606435916084359260026009541461242b57600260095560ff86169260008481526020916002835260409560ff878420541680156127c3575b6127aa5785421161277257600803612737579661256394959697838a61252561049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038516868201908152602081018c905260ff9093166040840152988992839188918391606090910190565b03925af195861561272a575b839661270b575b506001600160a01b03818116966126339185919087906126179061259c8b30338f615576565b8d6125f06125ad8d600554906139c1565b6001600160a01b038a1660009081526007602052604090206125d0908290611a7f565b6001600160a01b038b1660009081526007602052604090205b558d613a34565b9051636e553f6560e01b815289810191825230602083015295869384928391604090910190565b039286165af19182156126fe575b86926126df575b5083614e1b565b98808a106126a4575083896000805160206156a483398151915296948c969461224861049861069d9f8d886122428861266f9b61049895614f26565b03925af180156106cf576106a1575050855160ff90921682526020820187905260408201523360608201528060808101610682565b8851636d4c6c8960e01b81526010818501908152602081018c90526040810192909252600060608301819052608083015290819060a0010390fd5b6126f7919250873d8911610e7b57610e6d8183611572565b903861262c565b61270661390a565b612625565b612723919650843d8611610702576106f48183611572565b9438612576565b61273261390a565b61256f565b8551636d4c6c8960e01b81526006818a0190815260ff8b166020820152600060408201819052606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81526002818b01908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81528061074f8c828d01613955565b5060ff600354166124f2565b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57608036600319011261000e57600480359061280e826103a0565b6024359161281b836103c5565b6044356064359160026009541461077a57600260095560ff81169460008681526020906002825260409560ff87832054168015612b7c575b612b6357908291849387898897858d6128b461287c61049861049860015460018060a01b031690565b94519b8c968795869463125cf47f60e01b8652850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1948515612b56575b8295612b37575b506001600160a01b038581169a9095908b15612afe5783908061291d5761074f8c8c51918291636d4c6c8960e01b83528201906000608060a0840193602081528260208201528260408201528260608201520152565b8c9b949392919060018103612a8a57505061293781615100565b8042118015612a82575b612a48575093878a879583956129ae8d9e9f6104987f309b03ba657e17f1beadbc6eb3c06ba79b38084eb8d0e5452cc222462a17f1f69e9f6129a761299e8b612a069f8f6129996129d99f6104989930903390615576565b614e1b565b809d819a614f26565b508d614d32565b9251630fa9d3b160e01b81523392810192835260208301949094529294859384929091839160400190565b03925af18015612a3b575b612a1d575b5050875160ff909616865260208601529116929081906040820190565b0390a3612a136001600955565b5160018152602090f35b81612a3392903d106106c8576106b88183611572565b5088806129e9565b612a4361390a565b6129e4565b8a51636d4c6c8960e01b815260078187019081526020810192909252426040830152600060608301819052608083015290819060a0010390fd5b508015612941565b60028103612aa2575050612a9d81615100565b612937565b60038103612ab5575050612a9d8161515b565b808603612ac7575050612a9d81615190565b60058103612ada575050612a9d816151c5565b60078103612aed575050612a9d81615275565b600803612a9d5750612a9d816153be565b8951636d4c6c8960e01b8152601a818d0190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b612b4f919550833d8511610702576106f48183611572565b93386128c7565b612b5e61390a565b6128c0565b8651636d4c6c8960e01b81528061074f87828c01613955565b5060ff60035416612853565b503461000e57600036600319011261000e576040517f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b03168152602090f35b503461000e5760e036600319011261000e57612beb6004356103a0565b612bf66024356103c5565b612c0160c4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff6040600020541680156132d5575b611e28576044354211611df357600154612c5290610498906001600160a01b031681565b6020604051809263125cf47f60e01b825281600081612c9a6004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19081156132c8575b6000916132a9575b50612cc860643530336024356001600160a01b0316615576565b612d21612cd96005546064356139c1565b6001600160a01b03602435166000908152600760205260409020612cfe908290611a7f565b6001600160a01b0360243516600090815260076020526040902055606435613a34565b6040516370a0823160e01b81523060048201529091906020816024816001600160a01b0386165afa90811561329c575b60009161327d575b5060043560ff16600503613085576040516316f0115b60e01b81526020816004816001600160a01b0387165afa908115613078575b600091613059575b5060405163f1563de960e01b815260208160048160c4356001600160a01b03165afa90811561304c575b60009161302d575b506001600160a01b0381811690831603611d47575060405163f77c479160e01b81529190602090839060049082906001600160a01b03165afa918215613020575b600092612fff575b506001600160a01b0382163b1561000e5760405163507a3baf60e01b81526001600160a01b0360c4358116600483015260248201959095526001604482015260006064820181905260a480356084840152612eab96612ea29584929183918591165af18015612ff2575b612fd9575b505b6040516370a0823160e01b81523060048201526020818060248101611b5d565b90602435614e1b565b60043560ff1660051480612fce575b612f975780612ed161069d92602435600435614f26565b50612ee6610498610498604435602435614d32565b604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000905af18015612f8a575b612f6b575b506040805160ff6004351681526020810183905260643591810191909152336060820152604435906024356001600160a01b0316906000805160206156a4833981519152908060808101611c53565b612f839060203d6020116106c8576106b88183611572565b5038612f1c565b612f9261390a565b612f17565b604051636d4c6c8960e01b8152600b600482015260248101919091526084803560448301526000606483018190529082015260a490fd5b506084358110612eba565b80612fe6612fec9261151c565b80610800565b38612e80565b612ffa61390a565b612e7b565b61301991925060203d602011610702576106f48183611572565b9038612e11565b61302861390a565b612e09565b613046915060203d602011610702576106f48183611572565b38612dc8565b61305461390a565b612dc0565b613072915060203d602011610702576106f48183611572565b38612d96565b61308061390a565b612d8e565b91600760ff60043516146130a1575b50612ea2612eab92612e82565b60405163c6ec27bf60e01b815260208160048160c4356001600160a01b03165afa908115613270575b600091613251575b506001600160a01b03818116602435909116036132315750604051631e5f74a160e01b815260208160048160c4356001600160a01b03165afa908115613224575b600091613205575b506001600160a01b03818116908416036131e75750612eab92612ea2916020613142614d00565b61314a614cad565b60405163e7ffb5f760e01b815293849283926131779260a435923092608435929160c43560048901614556565b038160007f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c6266001600160a01b03165af180156131da575b6131bb575b509250613094565b6131d39060203d602011610e7b57610e6d8183611572565b50386131b3565b6131e261390a565b6131ae565b604051636d4c6c8960e01b815290819061074f9085600484016139f6565b61321e915060203d602011610702576106f48183611572565b3861311b565b61322c61390a565b613113565b604051636d4c6c8960e01b815290819061074f90602435600484016139f6565b61326a915060203d602011610702576106f48183611572565b386130d2565b61327861390a565b6130ca565b613296915060203d602011610e7b57610e6d8183611572565b38612d59565b6132a461390a565b612d51565b6132c2915060203d602011610702576106f48183611572565b38612cae565b6132d061390a565b612ca6565b5060ff60035416612c2e565b503461000e57600036600319011261000e576040517f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f6001600160a01b03168152602090f35b503461000e5761016036600319011261000e576004803590613348826103a0565b60243590613355826103c5565b60443560643560a03660a319011261000e576101443592613375846103c5565b60026009541461368c57600260095560009560ff811687526020916002835260409560ff878a2054168015613680575b6136665785421161362e5761340b9084846133cd61049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038d16878201908152602081018c905260ff909316604084015294859283918f918391606090910190565b03925af1918215613621575b8a92613602575b508751630b2339af60e21b81526001600160a01b03919060608186818686165afa9081156135f5575b8c916135c4575b50828085169116036135a95761353086938a8c858f956105f59661347b8e9b9985169e8f30903390615576565b6134f76134ce61348e8d600554906139c1565b6001600160a01b03871660009081526007602052604090206134b1908290611a7f565b6001600160a01b03881660009081526007602052604090206125e9565b946134d76115b2565b6001600160a01b038216815295868d01526001600160a01b031686860152565b80606085015280608085015261350b61440b565b60a08501528451809781958294631b7077d560e01b8452608435908d309086016144b3565b03927f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f165af191821561359c575b8d9261356c575b508b614e1b565b61358d9192508b3d8d11613595575b6135858183611572565b81019061449d565b509038613565565b503d61357b565b6135a461390a565b61355e565b8361074f848b51938493636d4c6c8960e01b855284016139f6565b6135e5915060603d81116135ee575b6135dd8183611572565b81019061446a565b5090503861344e565b503d6135d3565b6135fd61390a565b613447565b61361a919250853d8711610702576106f48183611572565b903861341e565b61362961390a565b613417565b8651636d4c6c8960e01b81526002818401908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b5061074f8651928392636d4c6c8960e01b84528301613955565b5060ff600354166133a5565b61074f90604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561370e816103c5565b6000546001600160a01b03908116338190036108a4575060207fe4b67652e856f57a7747dd2473850ce987087f4b1744a870504f1c047cb56f4f916203f48042019384421161377c575b1692836000526008825280604060002055604051908152a260405160018152602090f35b6137846139aa565b613758565b503461000e576000806003193601126137e35780546001600160a01b0316338190036108a457602082806006557f6875685eb5dbc8e2796d75d2dc9e9cb607b610d0558ee7336df418a26d4846e86040519182a160018152f35b80fd5b503461000e57602036600319011261000e57600435613804816103c5565b60018060a01b031660005260076020526020604060002054604051908152f35b503461000e57604036600319011261000e57600435613842816103a0565b6024359061384f82610f53565b6000546001600160a01b0316338190036108a45750602060ff7f09f6e15731965d66c152a2c1c1b62c94420ba4530dafe8818e1a01906641fb1e92169283600052600282526138ae8160406000209060ff801983541691151516179055565b604051938452151592a260405160018152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b9081602091031261000e5751613907816103c5565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052603260045260246000fd5b919081101561393e5760051b0190565b611612613917565b9081602091031261000e575190565b608060009193929360ff60a0820195600183521660208201528260408201528260608201520152565b50634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575161390781610f53565b50634e487b7160e01b600052601160045260246000fd5b81156139cb570490565b634e487b7160e01b600052601260045260246000fd5b919082018092116139ee57565b6103b86139aa565b601b815260006020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000198101919082116139ee57565b919082039182116139ee57565b9799949598929098969196600260095414611e5157600260095560ff8916600052600260205260ff60406000205416801561412c575b614111578742116140dd57613a968a613a9136848b614138565b614a52565b613a9f82614a13565b6040516370a0823160e01b81523060048201529b90979060208d8d81806024810103916001600160a01b03165afa9c8d156140d0575b60009d614091575b50613bc38b8d8c613d5397613d8c9997613d4e978f97613b1789613b0e613d299b303360018060a01b038c16615576565b600554906139c1565b9a8b613b41613b3a613b298851613a25565b92613b34848a61421e565b51613a34565b918761421e565b526001600160a01b0387166000908152600760205260409020613b658d82546139e1565b90556001546020908990613b8390610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b038b166004820152602481018a905260ff9092166044830152909a8b9190829060009082906064820190565b03925af1988915614084575b600099614063575b506040516370a0823160e01b8152306004820152986020908a9060249082906001600160a01b03165afa988915614056575b60009961401b575b50926020969492613c3e8893613cd39a989660405196879586956310510f1160e01b8752600487016142cb565b038160007f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b03165af1801561400e575b613ff1575b50600154600090613c9590610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b039094166004850152602484019290925260ff909316604483015290938492839182906064820190565b03925af1908115613fe4575b600091613fc5575b506040516370a0823160e01b81523060048201529060209082908180602481015b03916001600160a01b03165afa908115611ce057600091611cc15750613a34565b6040516370a0823160e01b8152306004820152909c906020818e818060248101613d08565b613a34565b613d5f600554826139c1565b6001600160a01b038a166000908152600760205260409020909190613d858382546139e1565b9055613a34565b9060009015613f215750613eb5613e848860006020613e6d613e446104988f9d9b99613ebd9f9d613e3e819d613e22610498946000805160206156a48339815191529e8a868e613de961049861049860015460018060a01b031690565b908f60405180988195829463125cf47f60e01b8452600484016040906000929493606082019560018060a01b0316825260208201520152565b03925af1928315613f14575b8c93613ef5575b5033918d6147b3565b97614d32565b604051630fa9d3b160e01b8152336004820152602481018e905293849283919082906044820190565b03925af18015613ee8575b613ec9575b50886139e1565b604080516001815260208101929092528101939093523360608401526001600160a01b038616929081906080820190565b0390a3614f26565b50906103b86001600955565b613ee19060203d6020116106c8576106b88183611572565b5038613e7d565b613ef061390a565b613e78565b613f0d9193508b3d8d11610702576106f48183611572565b9138613e35565b613f1c61390a565b613e2e565b9695939150849790859350808796613f4b8860018060a01b03166000526004602052604060002090565b6000918252602052604090205490613f62916139e1565b6001600160a01b0387166000908152600460205260409020859060009182526020526040902055613f92916139e1565b96613eb5613e84613ebd9860006020613e6d613e446104988f9d6000805160206156a48339815191529961049891614d32565b613fde915060203d602011610702576106f48183611572565b38613ce7565b613fec61390a565b613cdf565b61400790853d87116106c8576106b88183611572565b5038613c7a565b61401661390a565b613c75565b613cd398969491995092613c3e6020989694936140458a95863d8811610e7b57610e6d8183611572565b9b9396989a50935050929496613c11565b61405e61390a565b613c09565b61407d91995060203d602011610702576106f48183611572565b9738613bd7565b61408c61390a565b613bcf565b8a9d508b8d8b926020989697983d6020116140c9575b6140b18183611572565b81016140bc91613946565b9f50505094939294613add565b503d6140a7565b6140d861390a565b613ad5565b604051636d4c6c8960e01b81526002600482015242602482015260448101899052600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f8b60048301613955565b5060ff60035416613a77565b929192614144826115f1565b60409461415386519283611572565b819584835260208093019161014080960285019481861161000e57925b8584106141805750505050505050565b868483031261000e578487916141946115d1565b863581526141a38388016103ba565b838201526141b28688016103f0565b8682015260606141c38189016103f0565b9082015260806141d4818901610f6a565b9082015260a06141e5818901610f6a565b9082015260c0808801359082015260e0808801359082015261010080880135908201526101208088013590820152815201930192614170565b6020918151811015614233575b60051b010190565b61423b613917565b61422b565b90815180825260208080930193019160005b828110614260575050505090565b835185529381019392810192600101614252565b9190808252602080920192916000905b828210614292575050505090565b9091929360019060ff86356142a6816103a0565b1681528583013583820152604080870135908201526060908101950193920190614284565b9391959492606096808887018988525260809081870193916000905b82821061431857505050506139079596509061430a918582036020870152614240565b926040818503910152614274565b909192946001908635815261433c6020614333818a016103ba565b60ff1690830152565b61435b604061434c818a016103f0565b6001600160a01b031690830152565b6143786143698d89016103f0565b6001600160a01b0316828e0152565b61438e614386848901610f6a565b151582850152565b6143a660a061439e818a01610f6a565b151590830152565b60c0878101359082015260e08088013590820152610100808801359082015261012080880135908201526101409081019601939201906142e7565b6020906001600160401b0381116143fe575b601f01601f19160190565b614406611505565b6143f3565b604051906144188261153c565b604082527f30303030303030303030303030303030303030303030303030303030303030306040837f307830303030303030303030303030303030303030303030303030303030303060208201520152565b9081606091031261000e578051614480816103c5565b9160406020830151614491816103c5565b920151613907816103c5565b919082604091031261000e576020825192015190565b92936101e0926139079560a093600180861b0392838092168852166020870152604086015260a435606086015260c435608086015260e435838601526101043560c08601526101243560e0860152610120806101008701528183511690860152602082015161014086015280604083015116610160860152806060830151166101808601526080820151166101a085015201519160c06101c08201520190610aba565b939796929461459460e096614586600099949560018060a01b0380971689526101008060208b0152890190614240565b908782036040890152614240565b98606086015260808501521660a083015260c08201520152565b60015460405163057453a760e31b81526103b8939290916001600160a01b0391906020908490600490829086165afa928315614614575b6000936145f4575b5016615505565b61460d91935060203d8111610702576106f48183611572565b91386145ed565b61461c61390a565b6145e5565b60015460405163057453a760e31b81526146a9929161468f916001600160a01b03916020908290600490829086165afa9081156146d7575b6000916146b9575b506001600160a01b038416600090815260046020526040902086600052602052604060002054918416615505565b6001600160a01b0316600090815260046020526040902090565b9060005260205260006040812055565b6146d1915060203d8111610702576106f48183611572565b38614661565b6146df61390a565b614659565b60019060001981146146f4570190565b6146fc6139aa565b0190565b9190811015614742575b60051b81013590601e198136030182121561000e5701908135916001600160401b03831161000e57602001823603811361000e579190565b61474a613917565b61470a565b908092918237016000815290565b3d15614788573d9061476e826143e1565b9161477c6040519384611572565b82523d6000602084013e565b606090565b906020613907928181520190610aba565b9081602091031261000e57516139078161188b565b6040516370a0823160e01b8082526001600160a01b0386811660048401526148f697929660209690959382169492939192918789602481895afa988915614a06575b6000996149c8575b506148a4846148db989694614849600095858d9b998c97169487614820846150ec565b6040516304f9ef2360e21b81526001600160801b03909116600482015294859081906024820190565b0381895afa9384156149bb575b899461499a575b50614869939416615505565b60405163bcc1694f60e01b81526001600160a01b03891660048201526001600160801b03909116602482015293849283919082906044820190565b03925af1801561498d575b614960575b506040519081526001600160a01b0390921660048301529092839190829081906024820190565b03915afa918215614953575b600092614936575b5050613a34565b90808210614902575090565b604051636d4c6c8960e01b8152600b600482015260248101929092526044820152600060648201819052608482015260a490fd5b61494c9250803d10610e7b57610e6d8183611572565b38806148ef565b61495b61390a565b6148e7565b61497f90853d8711614986575b6149778183611572565b81019061479e565b50386148b4565b503d61496d565b61499561390a565b6148af565b61486994506149b590893d8b11614986576149778183611572565b9361485d565b6149c361390a565b614856565b88969487929a506148db9896946148496000956149f46148a495873d8911610e7b57610e6d8183611572565b9d9550955050949698509496506147fd565b614a0e61390a565b6147f5565b906000805b83518214614a4d57600190614a2d838661421e565b518101809111614a40575b910190614a18565b614a486139aa565b614a38565b925050565b90600091825b81518114614abe576001600160a01b03806060614a75848661421e565b51015116908416818103614a8d575050600101614a58565b60a491869160405192636d4c6c8960e01b845260036004850152806024850152604484015260648301526084820152fd5b50505050565b9092614b69614b3260a060c09598979660e08652805160e087015260208101516002811015614b72575b610100870152600180831b038060408301511661012088015260608201511661014087015260808101516101608701520151856101808601526101a0850190610aba565b9660208401906060809160018060a01b03808251168552602082015115156020860152604082015116604085015201511515910152565b60a08201520152565b614b7a61397e565b614aee565b9193614c3b9394614ba2610498610498610498606087015160018060a01b031690565b6040516370a0823160e01b808252306004830152909560209592949193919289929087896024818a5afa988915614ca0575b600099614c6f575b5091879594939160008794614c07604051978896879586946352bbbe2960e01b865260048601614ac4565b03926001600160a01b03165af18015614c62575b614c45575b506040519081523060048201529182908180602481016148db565b908110611c8c5790565b614c5b90833d8511610e7b57610e6d8183611572565b5038614c20565b614c6a61390a565b614c1b565b8896959492908794929a50614c92600091863d8811610e7b57610e6d8183611572565b9a9294505091939495614bdc565b614ca861390a565b614bd4565b604051614cb98161153c565b600281526001602082016040368237825115614cf3575b52805160011015614ce6575b6000604082015290565b614cee613917565b614cdc565b614cfb613917565b614cd0565b604051614d0c81611557565b600181526000602082016020368237825115614d26575290565b614d2e613917565b5290565b602090606460018060a01b039160008360015416604051968795869463125cf47f60e01b865216600485015260248401528160448401525af1908115614d96575b600091614d7e575090565b613907915060203d8111610702576106f48183611572565b614d9e61390a565b614d73565b9081602091031261000e5751613907816103a0565b60ff16601b039060ff82116139ee57565b9060ff8091169116039060ff82116139ee57565b60ff16604d8111614def575b600a0a90565b614df76139aa565b614de9565b8060001904821181151516614e0f570290565b614e176139aa565b0290565b60405163313ce56760e01b8082529093926020926001600160a01b03929084908790600490829087165afa958615614f19575b600096614ef3575b50906004849260405194859384928352165afa918215614ee6575b600092614eb9575b505060ff811660ff841611614ea357614e9861390793614e9d92614dc9565b614ddd565b906139c1565b614e98614eb39161390794614dc9565b90614dfc565b614ed89250803d10614edf575b614ed08183611572565b810190614da3565b3880614e79565b503d614ec6565b614eee61390a565b614e71565b8492919650614f10600491843d8611614edf57614ed08183611572565b96919250614e56565b614f2161390a565b614e4e565b91614f86916001600160a01b03168173ae7ab96520de3a18e5e111b5eaab095312d7fe8482146150c9575b50614e9860046020614eb3936040519283809263313ce56760e01b82525afa9081156150bc575b60009161509e575b50614db8565b600a5480821161508657614fab82611a7f8560ff16600052600b602052604060002090565b80614fc38560ff16600052600b602052604060002090565b5562015180614fe9614fe28660ff16600052600c602052604060002090565b5442613a34565b11156150275750506150088260ff16600052600b602052604060002090565b55615021429160ff16600052600c602052604060002090565b55600190565b1190506150345750600190565b61504b8160ff16600052600b602052604060002090565b54604051636d4c6c8960e01b8152601f6004820152602481019190915260ff919091166044820152600060648201819052608482015260a490fd5b8261504b8160ff16600052600b602052604060002090565b6150b6915060203d8111614edf57614ed08183611572565b38614f80565b6150c461390a565b614f78565b614eb3919250600460206150e2614e9893600d54614dfc565b9493505050614f51565b6001600160801b039081811161000e571690565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa90811561514e575b600091615136575090565b613907915060203d8111610e7b57610e6d8183611572565b61515661390a565b61512b565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa908115615268575b60009161524b575b50600460405180948193632745fed560e11b8352165afa91821561523e575b60009261522857505090565b6139079250803d10610e7b57610e6d8183611572565b61524661390a565b61521c565b6152629150823d8411610702576106f48183611572565b386151fd565b61527061390a565b6151f5565b60405163247c579160e21b81526020916001600160a01b0391839161532591849184908290600490829086165afa9081156153b1575b600091615394575b50166040516362b9c05160e11b8152838082600481865afa918215615387575b600092615367575b50604051636558954f60e01b81529192829060049082905afa90811561535a575b60009161533d575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa91821561523e5760009261522857505090565b6153549150843d8611610e7b57610e6d8183611572565b38615304565b61536261390a565b6152fc565b6004925061538190823d8411610702576106f48183611572565b916152db565b61538f61390a565b6152d3565b6153ab9150843d8611610702576106f48183611572565b386152b3565b6153b961390a565b6152ab565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115615444575b6000916153fc575b5064ffffffffff1690565b6020813d821161543c575b8161541460209383611572565b8101031261543857519064ffffffffff821682036137e3575064ffffffffff6153f1565b5080fd5b3d9150615407565b61544c61390a565b6153e9565b60448151106154cb5760048101518101906020816024840193031261000e576024810151906001600160401b03821161000e57018160438201121561000e57602481015161549e816143e1565b926154ac6040519485611572565b8184526044828401011161000e57613907916044602085019101610a97565b506040516154d881611557565b601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6044600092838093615538966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af16155f5565b1561553f57565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b600092836064926155b2968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af16155f5565b156155b957565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d901561562557908160201461561857501561561357600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b039092166004830152600019602483015261566691600091829160449183905af16155f5565b1561566d57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfe4dcca373512f0049f0e5732bf8deedaf846a121102837227c421cb8d8fc83522a2646970667358221220fc2f8cfc294ef1d52a654ce80df42688b2f5b2b9c4808b13bd14eaa3b6e5907f64736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626
-----Decoded View---------------
Arg [0] : s (address): 0x373a06bD3067f8DA90239a47f316F09312b7800F
Arg [1] : p (address): 0x41FAD93F225b5C1C95f2445A5d7fcB85bA46713f
Arg [2] : a (address): 0xf5ba2E5DdED276fc0f7a7637A61157a4be79C626
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f
Arg [1] : 00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f
Arg [2] : 000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.