Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ActionSwapYT
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 90000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "./base/ActionBaseMintRedeem.sol"; import "./base/CallbackHelper.sol"; import "../interfaces/IPActionSwapYT.sol"; import "../interfaces/IPMarket.sol"; import "../core/libraries/Errors.sol"; /// @dev All swap actions will revert if market is expired contract ActionSwapYT is IPActionSwapYT, ActionBaseMintRedeem, CallbackHelper { using Math for uint256; using Math for int256; using MarketMathCore for MarketState; using MarketApproxPtInLib for MarketState; using MarketApproxPtOutLib for MarketState; using PYIndexLib for IPYieldToken; /// @dev since this contract will be proxied, it must not contains non-immutable variables constructor(address _kyberScalingLib) ActionBaseMintRedeem(_kyberScalingLib) //solhint-disable-next-line no-empty-blocks {} /** * @notice swap exact SY to YT with the help of flashswaps & YT tokenization / redemption * @dev inner working of this function: - `exactSyIn` SY is transferred to YT contract - `market.swapExactPtForSy` is called, which will transfer more SY directly to YT contract & callback is invoked. Note that now we owe PT - in callback, all SY in YT contract is used to mint PT + YT, with all PT used to pay back the loan, and all YT transferred to the receiver * @param exactSyIn will always consume this amount of SY for as much YT as possible * @param guessYtOut approximation data for total YT output * @dev this function works in conjunction with ActionCallback */ function swapExactSyForYt( address receiver, address market, uint256 exactSyIn, uint256 minYtOut, ApproxParams memory guessYtOut ) external returns (uint256 netYtOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(IERC20(SY), msg.sender, address(YT), exactSyIn); (netYtOut, netSyFee) = _swapExactSyForYt( receiver, market, YT, exactSyIn, minYtOut, guessYtOut ); emit SwapYtAndSy(msg.sender, market, receiver, netYtOut.Int(), exactSyIn.neg()); } /** * @notice swap exact YT to SY with the help of flashswaps & YT tokenization / redemption * @dev inner working of this function: - `exactYtIn` YT is transferred to YT contract - `market.swapSyForExactPt` is called, which will transfer PT directly to YT contract & callback is invoked. Note that now we owe SY. - In callback, all PT + YT in YT contract is used to redeem SY. A portion of SY is used to payback the loan, the rest is transferred to the `receiver` * @param exactYtIn will consume exactly this much YT for as much SY as possible * @dev this function works in conjunction with ActionCallback */ function swapExactYtForSy( address receiver, address market, uint256 exactYtIn, uint256 minSyOut ) external returns (uint256 netSyOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(IERC20(YT), msg.sender, address(YT), exactYtIn); (netSyOut, netSyFee) = _swapExactYtForSy(receiver, market, SY, YT, exactYtIn, minSyOut); emit SwapYtAndSy(msg.sender, market, receiver, exactYtIn.neg(), netSyOut.Int()); } /** * @notice swap SY to exact YT with the help of flashswaps & YT tokenization / redemption * @dev inner working of this function: - `market.swapExactPtForSy` is called, which will transfer SY directly to YT contract & callback is invoked. Note that now we owe PT - In callback, we will pull in more SY as needed from caller & mint all SY to PT + YT. PT is then used to payback the loan, while YT is transferred to `receiver` * @param exactYtOut will output exactly this amount of YT, no approximation is used * @dev this function works in conjunction with ActionCallback */ function swapSyForExactYt( address receiver, address market, uint256 exactYtOut, uint256 maxSyIn ) external returns (uint256 netSyIn, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); uint256 preSyBalance = SY.balanceOf(msg.sender); (, netSyFee) = IPMarket(market).swapExactPtForSy( address(YT), exactYtOut, // exactPtIn = exactYtOut _encodeSwapSyForExactYt(msg.sender, receiver, maxSyIn) ); netSyIn = preSyBalance - SY.balanceOf(msg.sender); emit SwapYtAndSy(msg.sender, market, receiver, exactYtOut.Int(), netSyIn.neg()); } /** * @notice swap YT to exact SY with the help of flashswaps & YT tokenization / redemption * @dev inner working of this function: - Approximates `netYtIn` using the data from `guessYtIn` - Pulls `netYtIn` amount of YT from caller - `market.swapSyForExactPt` is called, which will transfer PT directly to YT contract & callback is invoked. Note that now we owe SY - In callback, we will redeem all PT + YT to get SY. A portion of it is used to payback the loan. The rest is transferred to `receiver` * @dev this function works in conjunction with ActionCallback */ function swapYtForExactSy( address receiver, address market, uint256 exactSyOut, uint256 maxYtIn, ApproxParams memory guessYtIn ) external returns (uint256 netYtIn, uint256 netSyFee) { MarketState memory state = IPMarket(market).readState(address(this)); (, , IPYieldToken YT) = IPMarket(market).readTokens(); (netYtIn, , ) = state.approxSwapYtForExactSy( YT.newIndex(), exactSyOut, block.timestamp, guessYtIn ); if (netYtIn > maxYtIn) revert Errors.RouterExceededLimitYtIn(netYtIn, maxYtIn); _transferFrom(IERC20(YT), msg.sender, address(YT), netYtIn); (, netSyFee) = IPMarket(market).swapSyForExactPt( address(YT), netYtIn, // exactPtOut = netYtIn _encodeSwapYtForSy(receiver, exactSyOut) ); emit SwapYtAndSy(msg.sender, market, receiver, netYtIn.neg(), exactSyOut.Int()); } /** * @notice swaps any token to YT * @dev this function swaps token for SY-mintable token first through Kyberswap, then mints SY * from such, finally swaps SY to YT (see `swapSyForExactYt()`) */ function swapExactTokenForYt( address receiver, address market, uint256 minYtOut, ApproxParams memory guessYtOut, TokenInput calldata input ) external payable returns (uint256 netYtOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); uint256 netSyUsedToBuyYT = _mintSyFromToken(address(YT), address(SY), 1, input); (netYtOut, netSyFee) = _swapExactSyForYt( receiver, market, YT, netSyUsedToBuyYT, minYtOut, guessYtOut ); emit SwapYtAndToken( msg.sender, market, input.tokenIn, receiver, netYtOut.Int(), input.netTokenIn.neg() ); } /** * @notice swaps YT to a given token * @dev the function first swaps YT to SY (see `swapExactYtForSy()`), then redeems SY, * finally swaps resulting tokens to desired output token using Kyberswap */ function swapExactYtForToken( address receiver, address market, uint256 netYtIn, TokenOutput calldata output ) external returns (uint256 netTokenOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(IERC20(YT), msg.sender, address(YT), netYtIn); uint256 netSyOut; (netSyOut, netSyFee) = _swapExactYtForSy( _syOrBulk(address(SY), output), market, SY, YT, netYtIn, 1 ); netTokenOut = _redeemSyToToken(receiver, address(SY), netSyOut, output, false); emit SwapYtAndToken( msg.sender, market, output.tokenOut, receiver, netYtIn.neg(), netTokenOut.Int() ); } function _swapExactSyForYt( address receiver, address market, IPYieldToken YT, uint256 exactSyIn, uint256 minYtOut, ApproxParams memory guessYtOut ) internal returns (uint256 netYtOut, uint256 netSyFee) { MarketState memory state = IPMarket(market).readState(address(this)); (netYtOut, ) = state.approxSwapExactSyForYt( YT.newIndex(), exactSyIn, block.timestamp, guessYtOut ); // early-check if (netYtOut < minYtOut) revert Errors.RouterInsufficientYtOut(netYtOut, minYtOut); (, netSyFee) = IPMarket(market).swapExactPtForSy( address(YT), netYtOut, // exactPtIn = netYtOut _encodeSwapExactSyForYt(receiver, minYtOut) ); } function _swapExactYtForSy( address receiver, address market, IStandardizedYield SY, IPYieldToken YT, uint256 exactYtIn, uint256 minSyOut ) internal returns (uint256 netSyOut, uint256 netSyFee) { uint256 preSyBalance = SY.balanceOf(receiver); (, netSyFee) = IPMarket(market).swapSyForExactPt( address(YT), exactYtIn, // exactPtOut = exactYtIn _encodeSwapYtForSy(receiver, minSyOut) ); netSyOut = SY.balanceOf(receiver) - preSyBalance; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` 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: * * - `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 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current 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); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ 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: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../libraries/TokenHelper.sol"; import "../libraries/math/Math.sol"; import "../libraries/Errors.sol"; struct BulkSellerState { uint256 rateTokenToSy; uint256 rateSyToToken; uint256 totalToken; uint256 totalSy; uint256 feeRate; } library BulkSellerMathCore { using Math for uint256; function swapExactTokenForSy(BulkSellerState memory state, uint256 netTokenIn) internal pure returns (uint256 netSyOut) { netSyOut = calcSwapExactTokenForSy(state, netTokenIn); state.totalToken += netTokenIn; state.totalSy -= netSyOut; } function swapExactSyForToken(BulkSellerState memory state, uint256 netSyIn) internal pure returns (uint256 netTokenOut) { netTokenOut = calcSwapExactSyForToken(state, netSyIn); state.totalSy += netSyIn; state.totalToken -= netTokenOut; } function calcSwapExactTokenForSy(BulkSellerState memory state, uint256 netTokenIn) internal pure returns (uint256 netSyOut) { uint256 postFeeRate = state.rateTokenToSy.mulDown(Math.ONE - state.feeRate); assert(postFeeRate != 0); netSyOut = netTokenIn.mulDown(postFeeRate); if (netSyOut > state.totalSy) revert Errors.BulkInsufficientSyForTrade(state.totalSy, netSyOut); } function calcSwapExactSyForToken(BulkSellerState memory state, uint256 netSyIn) internal pure returns (uint256 netTokenOut) { uint256 postFeeRate = state.rateSyToToken.mulDown(Math.ONE - state.feeRate); assert(postFeeRate != 0); netTokenOut = netSyIn.mulDown(postFeeRate); if (netTokenOut > state.totalToken) revert Errors.BulkInsufficientTokenForTrade(state.totalToken, netTokenOut); } function getTokenProp(BulkSellerState memory state) internal pure returns (uint256) { uint256 totalToken = state.totalToken; uint256 totalTokenFromSy = state.totalSy.mulDown(state.rateSyToToken); return totalToken.divDown(totalToken + totalTokenFromSy); } function getReBalanceParams(BulkSellerState memory state, uint256 targetTokenProp) internal pure returns (uint256 netTokenToDeposit, uint256 netSyToRedeem) { uint256 currentTokenProp = getTokenProp(state); if (currentTokenProp > targetTokenProp) { netTokenToDeposit = state .totalToken .mulDown(currentTokenProp - targetTokenProp) .divDown(currentTokenProp); } else { uint256 currentSyProp = Math.ONE - currentTokenProp; netSyToRedeem = state.totalSy.mulDown(targetTokenProp - currentTokenProp).divDown( currentSyProp ); } } function reBalanceTokenToSy( BulkSellerState memory state, uint256 netTokenToDeposit, uint256 netSyFromToken, uint256 maxDiff ) internal pure { uint256 rate = netSyFromToken.divDown(netTokenToDeposit); if (!Math.isAApproxB(rate, state.rateTokenToSy, maxDiff)) revert Errors.BulkBadRateTokenToSy(rate, state.rateTokenToSy, maxDiff); state.totalToken -= netTokenToDeposit; state.totalSy += netSyFromToken; } function reBalanceSyToToken( BulkSellerState memory state, uint256 netSyToRedeem, uint256 netTokenFromSy, uint256 maxDiff ) internal pure { uint256 rate = netTokenFromSy.divDown(netSyToRedeem); if (!Math.isAApproxB(rate, state.rateSyToToken, maxDiff)) revert Errors.BulkBadRateSyToToken(rate, state.rateSyToToken, maxDiff); state.totalToken += netTokenFromSy; state.totalSy -= netSyToRedeem; } function setRate( BulkSellerState memory state, uint256 rateSyToToken, uint256 rateTokenToSy, uint256 maxDiff ) internal pure { if ( state.rateTokenToSy != 0 && !Math.isAApproxB(rateTokenToSy, state.rateTokenToSy, maxDiff) ) { revert Errors.BulkBadRateTokenToSy(rateTokenToSy, state.rateTokenToSy, maxDiff); } if ( state.rateSyToToken != 0 && !Math.isAApproxB(rateSyToToken, state.rateSyToToken, maxDiff) ) { revert Errors.BulkBadRateSyToToken(rateSyToToken, state.rateSyToToken, maxDiff); } state.rateTokenToSy = rateTokenToSy; state.rateSyToToken = rateSyToToken; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; /// Adapted from UniswapV3's Oracle library Errors { // BulkSeller error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount); error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount); error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut); error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut); error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance); error BulkNotMaintainer(); error BulkNotAdmin(); error BulkSellerAlreadyExisted(address token, address SY, address bulk); error BulkSellerInvalidToken(address token, address SY); error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps); error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps); // APPROX error ApproxFail(); error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps); error ApproxBinarySearchInputInvalid( uint256 approxGuessMin, uint256 approxGuessMax, uint256 minGuessMin, uint256 maxGuessMax ); // MARKET + MARKET MATH CORE error MarketExpired(); error MarketZeroAmountsInput(); error MarketZeroAmountsOutput(); error MarketZeroLnImpliedRate(); error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount); error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance); error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance); error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset); error MarketExchangeRateBelowOne(int256 exchangeRate); error MarketProportionMustNotEqualOne(); error MarketRateScalarBelowZero(int256 rateScalar); error MarketScalarRootBelowZero(int256 scalarRoot); error MarketProportionTooHigh(int256 proportion, int256 maxProportion); error OracleUninitialized(); error OracleTargetTooOld(uint32 target, uint32 oldest); error OracleZeroCardinality(); error MarketFactoryExpiredPt(); error MarketFactoryInvalidPt(); error MarketFactoryMarketExists(); error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot); error MarketFactoryReserveFeePercentTooHigh( uint8 reserveFeePercent, uint8 maxReserveFeePercent ); error MarketFactoryZeroTreasury(); error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor); // ROUTER error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut); error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut); error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut); error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut); error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut); error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut); error RouterExceededLimitSyIn(uint256 actualSyIn, uint256 limitSyIn); error RouterExceededLimitPtIn(uint256 actualPtIn, uint256 limitPtIn); error RouterExceededLimitYtIn(uint256 actualYtIn, uint256 limitYtIn); error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay); error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay); error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed); error RouterTimeRangeZero(); error RouterCallbackNotPendleMarket(address caller); error RouterInvalidAction(bytes4 selector); error RouterKyberSwapDataZero(); // YIELD CONTRACT error YCExpired(); error YCNotExpired(); error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy); error YCNothingToRedeem(); error YCPostExpiryDataNotSet(); error YCNoFloatingSy(); // YieldFactory error YCFactoryInvalidExpiry(); error YCFactoryYieldContractExisted(); error YCFactoryZeroExpiryDivisor(); error YCFactoryZeroTreasury(); error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate); error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate); // SY error SYInvalidTokenIn(address token); error SYInvalidTokenOut(address token); error SYZeroDeposit(); error SYZeroRedeem(); error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut); error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut); // SY-specific error SYQiTokenMintFailed(uint256 errCode); error SYQiTokenRedeemFailed(uint256 errCode); error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1); error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax); error SYCurveInvalidPid(); error SYCurve3crvPoolNotFound(); // Liquidity Mining error VCInactivePool(address pool); error VCPoolAlreadyActive(address pool); error VCZeroVePendle(address user); error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight); error VCEpochNotFinalized(uint256 wTime); error VCPoolAlreadyAddAndRemoved(address pool); error VEInvalidNewExpiry(uint256 newExpiry); error VEExceededMaxLockTime(); error VEInsufficientLockTime(); error VENotAllowedReduceExpiry(); error VEZeroAmountLocked(); error VEPositionNotExpired(); error VEZeroPosition(); error VEZeroSlope(uint128 bias, uint128 slope); error VEReceiveOldSupply(uint256 msgTime); error GCNotPendleMarket(address caller); error GCNotVotingController(address caller); error InvalidWTime(uint256 wTime); error ExpiryInThePast(uint256 expiry); error ChainNotSupported(uint256 chainId); error FDCantFundFutureEpoch(); error FDFactoryDistributorAlreadyExisted(address pool, address distributor); // Cross-Chain error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path); error MsgNotFromReceiveEndpoint(address sender); error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee); error ApproxDstExecutionGasNotSet(); error InvalidRetryData(); // GENERIC MSG error ArrayLengthMismatch(); error ArrayEmpty(); error ArrayOutOfBounds(); error ZeroAddress(); error OnlyLayerZeroEndpoint(); error OnlyYT(); error OnlyYCFactory(); error OnlyWhitelisted(); }
// SPDX-License-Identifier: GPL-3.0-or-later // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated // documentation files (the “Software”), to deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the // Software. // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pragma solidity 0.8.17; /* solhint-disable */ /** * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument). * * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural * exponentiation and logarithm (where the base is Euler's number). * * @author Fernando Martinelli - @fernandomartinelli * @author Sergio Yuhjtman - @sergioyuhjtman * @author Daniel Fernandez - @dmf7z */ library LogExpMath { // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying // two numbers, and multiply by ONE when dividing them. // All arguments and return values are 18 decimal fixed point numbers. int256 constant ONE_18 = 1e18; // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the // case of ln36, 36 decimals. int256 constant ONE_20 = 1e20; int256 constant ONE_36 = 1e36; // The domain of natural exponentiation is bound by the word size and number of decimals used. // // Because internally the result will be stored using 20 decimals, the largest possible result is // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221. // The smallest possible result is 10^(-18), which makes largest negative argument // ln(10^(-18)) = -41.446531673892822312. // We use 130.0 and -41.0 to have some safety margin. int256 constant MAX_NATURAL_EXPONENT = 130e18; int256 constant MIN_NATURAL_EXPONENT = -41e18; // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point // 256 bit integer. int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17; int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17; uint256 constant MILD_EXPONENT_BOUND = 2**254 / uint256(ONE_20); // 18 decimal constants int256 constant x0 = 128000000000000000000; // 2ˆ7 int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals) int256 constant x1 = 64000000000000000000; // 2ˆ6 int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals) // 20 decimal constants int256 constant x2 = 3200000000000000000000; // 2ˆ5 int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2) int256 constant x3 = 1600000000000000000000; // 2ˆ4 int256 constant a3 = 888611052050787263676000000; // eˆ(x3) int256 constant x4 = 800000000000000000000; // 2ˆ3 int256 constant a4 = 298095798704172827474000; // eˆ(x4) int256 constant x5 = 400000000000000000000; // 2ˆ2 int256 constant a5 = 5459815003314423907810; // eˆ(x5) int256 constant x6 = 200000000000000000000; // 2ˆ1 int256 constant a6 = 738905609893065022723; // eˆ(x6) int256 constant x7 = 100000000000000000000; // 2ˆ0 int256 constant a7 = 271828182845904523536; // eˆ(x7) int256 constant x8 = 50000000000000000000; // 2ˆ-1 int256 constant a8 = 164872127070012814685; // eˆ(x8) int256 constant x9 = 25000000000000000000; // 2ˆ-2 int256 constant a9 = 128402541668774148407; // eˆ(x9) int256 constant x10 = 12500000000000000000; // 2ˆ-3 int256 constant a10 = 113314845306682631683; // eˆ(x10) int256 constant x11 = 6250000000000000000; // 2ˆ-4 int256 constant a11 = 106449445891785942956; // eˆ(x11) /** * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent. * * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`. */ function exp(int256 x) internal pure returns (int256) { unchecked { require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent"); if (x < 0) { // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT). // Fixed point division requires multiplying by ONE_18. return ((ONE_18 * ONE_18) / exp(-x)); } // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n, // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7 // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the // decomposition. // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this // decomposition, which will be lower than the smallest x_n. // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1. // We mutate x by subtracting x_n, making it the remainder of the decomposition. // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause // intermediate overflows. Instead we store them as plain integers, with 0 decimals. // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the // decomposition. // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct // it and compute the accumulated product. int256 firstAN; if (x >= x0) { x -= x0; firstAN = a0; } else if (x >= x1) { x -= x1; firstAN = a1; } else { firstAN = 1; // One with no decimal places } // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the // smaller terms. x *= 100; // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point // one. Recall that fixed point multiplication requires dividing by ONE_20. int256 product = ONE_20; if (x >= x2) { x -= x2; product = (product * a2) / ONE_20; } if (x >= x3) { x -= x3; product = (product * a3) / ONE_20; } if (x >= x4) { x -= x4; product = (product * a4) / ONE_20; } if (x >= x5) { x -= x5; product = (product * a5) / ONE_20; } if (x >= x6) { x -= x6; product = (product * a6) / ONE_20; } if (x >= x7) { x -= x7; product = (product * a7) / ONE_20; } if (x >= x8) { x -= x8; product = (product * a8) / ONE_20; } if (x >= x9) { x -= x9; product = (product * a9) / ONE_20; } // x10 and x11 are unnecessary here since we have high enough precision already. // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!). int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places. int256 term; // Each term in the sum, where the nth term is (x^n / n!). // The first term is simply x. term = x; seriesSum += term; // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number, // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not. term = ((term * x) / ONE_20) / 2; seriesSum += term; term = ((term * x) / ONE_20) / 3; seriesSum += term; term = ((term * x) / ONE_20) / 4; seriesSum += term; term = ((term * x) / ONE_20) / 5; seriesSum += term; term = ((term * x) / ONE_20) / 6; seriesSum += term; term = ((term * x) / ONE_20) / 7; seriesSum += term; term = ((term * x) / ONE_20) / 8; seriesSum += term; term = ((term * x) / ONE_20) / 9; seriesSum += term; term = ((term * x) / ONE_20) / 10; seriesSum += term; term = ((term * x) / ONE_20) / 11; seriesSum += term; term = ((term * x) / ONE_20) / 12; seriesSum += term; // 12 Taylor terms are sufficient for 18 decimal precision. // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication), // and then drop two digits to return an 18 decimal value. return (((product * seriesSum) / ONE_20) * firstAN) / 100; } } /** * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument. */ function ln(int256 a) internal pure returns (int256) { unchecked { // The real natural logarithm is not defined for negative numbers or zero. require(a > 0, "out of bounds"); if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) { return _ln_36(a) / ONE_18; } else { return _ln(a); } } } /** * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent. * * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`. */ function pow(uint256 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) { // We solve the 0^0 indetermination by making it equal one. return uint256(ONE_18); } if (x == 0) { return 0; } // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means // x^y = exp(y * ln(x)). // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range. require(x < 2**255, "x out of bounds"); int256 x_int256 = int256(x); // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end. // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range. require(y < MILD_EXPONENT_BOUND, "y out of bounds"); int256 y_int256 = int256(y); int256 logx_times_y; if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) { int256 ln_36_x = _ln_36(x_int256); // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the // (downscaled) last 18 decimals. logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18); } else { logx_times_y = _ln(x_int256) * y_int256; } logx_times_y /= ONE_18; // Finally, we compute exp(y * ln(x)) to arrive at x^y require( MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT, "product out of bounds" ); return uint256(exp(logx_times_y)); } } /** * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument. */ function _ln(int256 a) private pure returns (int256) { unchecked { if (a < ONE_18) { // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call. // Fixed point division requires multiplying by ONE_18. return (-_ln((ONE_18 * ONE_18) / a)); } // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is, // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a. // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this // decomposition, which will be lower than the smallest a_n. // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1. // We mutate a by subtracting a_n, making it the remainder of the decomposition. // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by // ONE_18 to convert them to fixed point. // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide // by it and compute the accumulated sum. int256 sum = 0; if (a >= a0 * ONE_18) { a /= a0; // Integer, not fixed point division sum += x0; } if (a >= a1 * ONE_18) { a /= a1; // Integer, not fixed point division sum += x1; } // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format. sum *= 100; a *= 100; // Because further a_n are 20 digit fixed point numbers, we multiply by ONE_20 when dividing by them. if (a >= a2) { a = (a * ONE_20) / a2; sum += x2; } if (a >= a3) { a = (a * ONE_20) / a3; sum += x3; } if (a >= a4) { a = (a * ONE_20) / a4; sum += x4; } if (a >= a5) { a = (a * ONE_20) / a5; sum += x5; } if (a >= a6) { a = (a * ONE_20) / a6; sum += x6; } if (a >= a7) { a = (a * ONE_20) / a7; sum += x7; } if (a >= a8) { a = (a * ONE_20) / a8; sum += x8; } if (a >= a9) { a = (a * ONE_20) / a9; sum += x9; } if (a >= a10) { a = (a * ONE_20) / a10; sum += x10; } if (a >= a11) { a = (a * ONE_20) / a11; sum += x11; } // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series // that converges rapidly for values of `a` close to one - the same one used in ln_36. // Let z = (a - 1) / (a + 1). // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires // division by ONE_20. int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20); int256 z_squared = (z * z) / ONE_20; // num is the numerator of the series: the z^(2 * n + 1) term int256 num = z; // seriesSum holds the accumulated sum of each term in the series, starting with the initial z int256 seriesSum = num; // In each step, the numerator is multiplied by z^2 num = (num * z_squared) / ONE_20; seriesSum += num / 3; num = (num * z_squared) / ONE_20; seriesSum += num / 5; num = (num * z_squared) / ONE_20; seriesSum += num / 7; num = (num * z_squared) / ONE_20; seriesSum += num / 9; num = (num * z_squared) / ONE_20; seriesSum += num / 11; // 6 Taylor terms are sufficient for 36 decimal precision. // Finally, we multiply by 2 (non fixed point) to compute ln(remainder) seriesSum *= 2; // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal // value. return (sum + seriesSum) / 100; } } /** * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument, * for x close to one. * * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND. */ function _ln_36(int256 x) private pure returns (int256) { unchecked { // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits // worthwhile. // First, we transform x to a 36 digit fixed point value. x *= ONE_18; // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1). // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires // division by ONE_36. int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36); int256 z_squared = (z * z) / ONE_36; // num is the numerator of the series: the z^(2 * n + 1) term int256 num = z; // seriesSum holds the accumulated sum of each term in the series, starting with the initial z int256 seriesSum = num; // In each step, the numerator is multiplied by z^2 num = (num * z_squared) / ONE_36; seriesSum += num / 3; num = (num * z_squared) / ONE_36; seriesSum += num / 5; num = (num * z_squared) / ONE_36; seriesSum += num / 7; num = (num * z_squared) / ONE_36; seriesSum += num / 9; num = (num * z_squared) / ONE_36; seriesSum += num / 11; num = (num * z_squared) / ONE_36; seriesSum += num / 13; num = (num * z_squared) / ONE_36; seriesSum += num / 15; // 8 Taylor terms are sufficient for 36 decimal precision. // All that remains is multiplying by 2 (non fixed point). return seriesSum * 2; } } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity 0.8.17; /* solhint-disable private-vars-leading-underscore, reason-string */ library Math { uint256 internal constant ONE = 1e18; // 18 decimal places int256 internal constant IONE = 1e18; // 18 decimal places function subMax0(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return (a >= b ? a - b : 0); } } function subNoNeg(int256 a, int256 b) internal pure returns (int256) { require(a >= b, "negative"); return a - b; // no unchecked since if b is very negative, a - b might overflow } function mulDown(uint256 a, uint256 b) internal pure returns (uint256) { uint256 product = a * b; unchecked { return product / ONE; } } function mulDown(int256 a, int256 b) internal pure returns (int256) { int256 product = a * b; unchecked { return product / IONE; } } function divDown(uint256 a, uint256 b) internal pure returns (uint256) { uint256 aInflated = a * ONE; unchecked { return aInflated / b; } } function divDown(int256 a, int256 b) internal pure returns (int256) { int256 aInflated = a * IONE; unchecked { return aInflated / b; } } function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) { return (a + b - 1) / b; } // @author Uniswap function sqrt(uint256 y) internal pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function abs(int256 x) internal pure returns (uint256) { return uint256(x > 0 ? x : -x); } function neg(int256 x) internal pure returns (int256) { return x * (-1); } function neg(uint256 x) internal pure returns (int256) { return Int(x) * (-1); } function max(uint256 x, uint256 y) internal pure returns (uint256) { return (x > y ? x : y); } function max(int256 x, int256 y) internal pure returns (int256) { return (x > y ? x : y); } function min(uint256 x, uint256 y) internal pure returns (uint256) { return (x < y ? x : y); } function min(int256 x, int256 y) internal pure returns (int256) { return (x < y ? x : y); } /*/////////////////////////////////////////////////////////////// SIGNED CASTS //////////////////////////////////////////////////////////////*/ function Int(uint256 x) internal pure returns (int256) { require(x <= uint256(type(int256).max)); return int256(x); } function Int128(int256 x) internal pure returns (int128) { require(type(int128).min <= x && x <= type(int128).max); return int128(x); } function Int128(uint256 x) internal pure returns (int128) { return Int128(Int(x)); } /*/////////////////////////////////////////////////////////////// UNSIGNED CASTS //////////////////////////////////////////////////////////////*/ function Uint(int256 x) internal pure returns (uint256) { require(x >= 0); return uint256(x); } function Uint32(uint256 x) internal pure returns (uint32) { require(x <= type(uint32).max); return uint32(x); } function Uint112(uint256 x) internal pure returns (uint112) { require(x <= type(uint112).max); return uint112(x); } function Uint96(uint256 x) internal pure returns (uint96) { require(x <= type(uint96).max); return uint96(x); } function Uint128(uint256 x) internal pure returns (uint128) { require(x <= type(uint128).max); return uint128(x); } function isAApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps); } function isAGreaterApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return a >= b && a <= mulDown(b, ONE + eps); } function isASmallerApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return a <= b && a >= mulDown(b, ONE - eps); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; library MiniHelpers { function isCurrentlyExpired(uint256 expiry) internal view returns (bool) { return (expiry <= block.timestamp); } function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) { return (expiry <= blockTime); } function isTimeInThePast(uint256 timestamp) internal view returns (bool) { return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; abstract contract TokenHelper { using SafeERC20 for IERC20; address internal constant NATIVE = address(0); uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval function _transferIn( address token, address from, uint256 amount ) internal { if (token == NATIVE) require(msg.value == amount, "eth mismatch"); else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount); } function _transferFrom( IERC20 token, address from, address to, uint256 amount ) internal { if (amount != 0) token.safeTransferFrom(from, to, amount); } function _transferOut( address token, address to, uint256 amount ) internal { if (amount == 0) return; if (token == NATIVE) { (bool success, ) = to.call{ value: amount }(""); require(success, "eth send failed"); } else { IERC20(token).safeTransfer(to, amount); } } function _transferOut( address[] memory tokens, address to, uint256[] memory amounts ) internal { uint256 numTokens = tokens.length; require(numTokens == amounts.length, "length mismatch"); for (uint256 i = 0; i < numTokens; ) { _transferOut(tokens[i], to, amounts[i]); unchecked { i++; } } } function _selfBalance(address token) internal view returns (uint256) { return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this)); } function _selfBalance(IERC20 token) internal view returns (uint256) { return token.balanceOf(address(this)); } /// @notice Approves the stipulated contract to spend the given allowance in the given token /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it function _safeApprove( address token, address to, uint256 value ) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.approve.selector, to, value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "Safe Approve"); } function _safeApproveInf(address token, address to) internal { if (token == NATIVE) return; if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) { _safeApprove(token, to, 0); _safeApprove(token, to, type(uint256).max); } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../libraries/math/Math.sol"; import "../libraries/math/LogExpMath.sol"; import "../StandardizedYield/PYIndex.sol"; import "../libraries/MiniHelpers.sol"; import "../libraries/Errors.sol"; struct MarketState { int256 totalPt; int256 totalSy; int256 totalLp; address treasury; /// immutable variables /// int256 scalarRoot; uint256 expiry; /// fee data /// uint256 lnFeeRateRoot; uint256 reserveFeePercent; // base 100 /// last trade data /// uint256 lastLnImpliedRate; } // params that are expensive to compute, therefore we pre-compute them struct MarketPreCompute { int256 rateScalar; int256 totalAsset; int256 rateAnchor; int256 feeRate; } // solhint-disable ordering library MarketMathCore { using Math for uint256; using Math for int256; using LogExpMath for int256; using PYIndexLib for PYIndex; int256 internal constant MINIMUM_LIQUIDITY = 10**3; int256 internal constant PERCENTAGE_DECIMALS = 100; uint256 internal constant DAY = 86400; uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY; int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100; using Math for uint256; using Math for int256; /*/////////////////////////////////////////////////////////////// UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS //////////////////////////////////////////////////////////////*/ function addLiquidity( MarketState memory market, uint256 syDesired, uint256 ptDesired, uint256 blockTime ) internal pure returns ( uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed ) { ( int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed ) = addLiquidityCore(market, syDesired.Int(), ptDesired.Int(), blockTime); lpToReserve = _lpToReserve.Uint(); lpToAccount = _lpToAccount.Uint(); syUsed = _syUsed.Uint(); ptUsed = _ptUsed.Uint(); } function removeLiquidity(MarketState memory market, uint256 lpToRemove) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) { (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int()); netSyToAccount = _syToAccount.Uint(); netPtToAccount = _ptToAccount.Uint(); } function swapExactPtForSy( MarketState memory market, PYIndex index, uint256 exactPtToMarket, uint256 blockTime ) internal pure returns ( uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve ) { (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore( market, index, exactPtToMarket.neg(), blockTime ); netSyToAccount = _netSyToAccount.Uint(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } function swapSyForExactPt( MarketState memory market, PYIndex index, uint256 exactPtToAccount, uint256 blockTime ) internal pure returns ( uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve ) { (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore( market, index, exactPtToAccount.Int(), blockTime ); netSyToMarket = _netSyToAccount.neg().Uint(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } /*/////////////////////////////////////////////////////////////// CORE FUNCTIONS //////////////////////////////////////////////////////////////*/ function addLiquidityCore( MarketState memory market, int256 syDesired, int256 ptDesired, uint256 blockTime ) internal pure returns ( int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed ) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput(); if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ if (market.totalLp == 0) { lpToAccount = Math.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY; lpToReserve = MINIMUM_LIQUIDITY; syUsed = syDesired; ptUsed = ptDesired; } else { int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt; int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy; if (netLpByPt < netLpBySy) { lpToAccount = netLpByPt; ptUsed = ptDesired; syUsed = (market.totalSy * lpToAccount) / market.totalLp; } else { lpToAccount = netLpBySy; syUsed = syDesired; ptUsed = (market.totalPt * lpToAccount) / market.totalLp; } } if (lpToAccount <= 0) revert Errors.MarketZeroAmountsOutput(); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.totalSy += syUsed; market.totalPt += ptUsed; market.totalLp += lpToAccount + lpToReserve; } function removeLiquidityCore(MarketState memory market, int256 lpToRemove) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp; netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp; if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput(); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.totalLp = market.totalLp.subNoNeg(lpToRemove); market.totalPt = market.totalPt.subNoNeg(netPtToAccount); market.totalSy = market.totalSy.subNoNeg(netSyToAccount); } function executeTradeCore( MarketState memory market, PYIndex index, int256 netPtToAccount, uint256 blockTime ) internal pure returns ( int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve ) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); if (market.totalPt <= netPtToAccount) revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime); (netSyToAccount, netSyFee, netSyToReserve) = calcTrade( market, comp, index, netPtToAccount ); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ _setNewMarketStateTrade( market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime ); } function getMarketPreCompute( MarketState memory market, PYIndex index, uint256 blockTime ) internal pure returns (MarketPreCompute memory res) { if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); uint256 timeToExpiry = market.expiry - blockTime; res.rateScalar = _getRateScalar(market, timeToExpiry); res.totalAsset = index.syToAsset(market.totalSy); if (market.totalPt == 0 || res.totalAsset == 0) revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset); res.rateAnchor = _getRateAnchor( market.totalPt, market.lastLnImpliedRate, res.totalAsset, res.rateScalar, timeToExpiry ); res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry); } function calcTrade( MarketState memory market, MarketPreCompute memory comp, PYIndex index, int256 netPtToAccount ) internal pure returns ( int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve ) { int256 preFeeExchangeRate = _getExchangeRate( market.totalPt, comp.totalAsset, comp.rateScalar, comp.rateAnchor, netPtToAccount ); int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg(); int256 fee = comp.feeRate; if (netPtToAccount > 0) { int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee); if (postFeeExchangeRate < Math.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate); fee = preFeeAssetToAccount.mulDown(Math.IONE - fee); } else { fee = ((preFeeAssetToAccount * (Math.IONE - fee)) / fee).neg(); } int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS; int256 netAssetToAccount = preFeeAssetToAccount - fee; netSyToAccount = netAssetToAccount < 0 ? index.assetToSyUp(netAssetToAccount) : index.assetToSy(netAssetToAccount); netSyFee = index.assetToSy(fee); netSyToReserve = index.assetToSy(netAssetToReserve); } function _setNewMarketStateTrade( MarketState memory market, MarketPreCompute memory comp, PYIndex index, int256 netPtToAccount, int256 netSyToAccount, int256 netSyToReserve, uint256 blockTime ) internal pure { uint256 timeToExpiry = market.expiry - blockTime; market.totalPt = market.totalPt.subNoNeg(netPtToAccount); market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve); market.lastLnImpliedRate = _getLnImpliedRate( market.totalPt, index.syToAsset(market.totalSy), comp.rateScalar, comp.rateAnchor, timeToExpiry ); if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate(); } function _getRateAnchor( int256 totalPt, uint256 lastLnImpliedRate, int256 totalAsset, int256 rateScalar, uint256 timeToExpiry ) internal pure returns (int256 rateAnchor) { int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry); if (newExchangeRate < Math.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate); { int256 proportion = totalPt.divDown(totalPt + totalAsset); int256 lnProportion = _logProportion(proportion); rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar); } } /// @notice Calculates the current market implied rate. /// @return lnImpliedRate the implied rate function _getLnImpliedRate( int256 totalPt, int256 totalAsset, int256 rateScalar, int256 rateAnchor, uint256 timeToExpiry ) internal pure returns (uint256 lnImpliedRate) { // This will check for exchange rates < Math.IONE int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0); // exchangeRate >= 1 so its ln >= 0 uint256 lnRate = exchangeRate.ln().Uint(); lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry; } /// @notice Converts an implied rate to an exchange rate given a time to expiry. The /// formula is E = e^rt function _getExchangeRateFromImpliedRate(uint256 lnImpliedRate, uint256 timeToExpiry) internal pure returns (int256 exchangeRate) { uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME; exchangeRate = LogExpMath.exp(rt.Int()); } function _getExchangeRate( int256 totalPt, int256 totalAsset, int256 rateScalar, int256 rateAnchor, int256 netPtToAccount ) internal pure returns (int256 exchangeRate) { int256 numerator = totalPt.subNoNeg(netPtToAccount); int256 proportion = (numerator.divDown(totalPt + totalAsset)); if (proportion > MAX_MARKET_PROPORTION) revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION); int256 lnProportion = _logProportion(proportion); exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor; if (exchangeRate < Math.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate); } function _logProportion(int256 proportion) internal pure returns (int256 res) { if (proportion == Math.IONE) revert Errors.MarketProportionMustNotEqualOne(); int256 logitP = proportion.divDown(Math.IONE - proportion); res = logitP.ln(); } function _getRateScalar(MarketState memory market, uint256 timeToExpiry) internal pure returns (int256 rateScalar) { rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int(); if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar); } function setInitialLnImpliedRate( MarketState memory market, PYIndex index, int256 initialAnchor, uint256 blockTime ) internal pure { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ int256 totalAsset = index.syToAsset(market.totalSy); uint256 timeToExpiry = market.expiry - blockTime; int256 rateScalar = _getRateScalar(market, timeToExpiry); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.lastLnImpliedRate = _getLnImpliedRate( market.totalPt, totalAsset, rateScalar, initialAnchor, timeToExpiry ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPPrincipalToken.sol"; import "./SYUtils.sol"; import "../libraries/math/Math.sol"; type PYIndex is uint256; library PYIndexLib { using Math for uint256; using Math for int256; function newIndex(IPYieldToken YT) internal returns (PYIndex) { return PYIndex.wrap(YT.pyIndexCurrent()); } function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) { return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount); } function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) { return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount); } function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) { return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount); } function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) { uint256 _index = PYIndex.unwrap(index); return SYUtils.syToAssetUp(_index, syAmount); } function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) { int256 sign = syAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int(); } function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) { int256 sign = assetAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int(); } function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) { int256 sign = assetAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int(); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; library SYUtils { uint256 internal constant ONE = 1e18; function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) { return (syAmount * exchangeRate) / ONE; } function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) { return (syAmount * exchangeRate + ONE - 1) / ONE; } function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) { return (assetAmount * ONE) / exchangeRate; } function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) { return (assetAmount * ONE + exchangeRate - 1) / exchangeRate; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../router/base/MarketApproxLib.sol"; import "../router/kyberswap/KyberSwapHelper.sol"; interface IPActionSwapYT { event SwapYtAndSy( address indexed caller, address indexed market, address indexed receiver, int256 netYtToAccount, int256 netSyToAccount ); event SwapYtAndToken( address indexed caller, address indexed market, address indexed token, address receiver, int256 netYtToAccount, int256 netTokenToAccount ); function swapExactSyForYt( address receiver, address market, uint256 exactSyIn, uint256 minYtOut, ApproxParams memory guessYtOut ) external returns (uint256 netYtOut, uint256 netSyFee); function swapExactYtForSy( address receiver, address market, uint256 exactYtIn, uint256 minSyOut ) external returns (uint256 netSyOut, uint256 netSyFee); function swapSyForExactYt( address receiver, address market, uint256 exactYtOut, uint256 maxSyIn ) external returns (uint256 netSyIn, uint256 netSyFee); function swapYtForExactSy( address receiver, address market, uint256 exactSyOut, uint256 maxYtIn, ApproxParams memory guessYtIn ) external returns (uint256 netYtIn, uint256 netSyFee); function swapExactTokenForYt( address receiver, address market, uint256 minYtOut, ApproxParams memory guessYtOut, TokenInput calldata input ) external payable returns (uint256 netYtOut, uint256 netSyFee); function swapExactYtForToken( address receiver, address market, uint256 netYtIn, TokenOutput calldata output ) external returns (uint256 netTokenOut, uint256 netSyFee); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../core/BulkSeller/BulkSellerMathCore.sol"; interface IPBulkSeller { event SwapExactTokenForSy(address receiver, uint256 netTokenIn, uint256 netSyOut); event SwapExactSyForToken(address receiver, uint256 netSyIn, uint256 netTokenOut); event RateUpdated( uint256 newRateTokenToSy, uint256 newRateSyToToken, uint256 oldRateTokenToSy, uint256 oldRateSyToToken ); event ReBalanceTokenToSy( uint256 netTokenDeposit, uint256 netSyFromToken, uint256 newTokenProp, uint256 oldTokenProp ); event ReBalanceSyToToken( uint256 netSyRedeem, uint256 netTokenFromSy, uint256 newTokenProp, uint256 oldTokenProp ); event ReserveUpdated(uint256 totalToken, uint256 totalSy); event FeeRateUpdated(uint256 newFeeRate, uint256 oldFeeRate); function swapExactTokenForSy( address receiver, uint256 netTokenIn, uint256 minSyOut ) external payable returns (uint256 netSyOut); function swapExactSyForToken( address receiver, uint256 exactSyIn, uint256 minTokenOut, bool swapFromInternalBalance ) external returns (uint256 netTokenOut); function SY() external view returns (address); function token() external view returns (address); function readState() external view returns (BulkSellerState memory); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; interface IPGauge { function totalActiveSupply() external view returns (uint256); function activeBalance(address user) external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; interface IPInterestManagerYT { function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./IPPrincipalToken.sol"; import "./IPYieldToken.sol"; import "./IStandardizedYield.sol"; import "./IPGauge.sol"; import "../core/Market/MarketMathCore.sol"; interface IPMarket is IERC20Metadata, IPGauge { event Mint( address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed ); event Burn( address indexed receiverSy, address indexed receiverPt, uint256 netLpBurned, uint256 netSyOut, uint256 netPtOut ); event Swap( address indexed caller, address indexed receiver, int256 netPtOut, int256 netSyOut, uint256 netSyFee, uint256 netSyToReserve ); event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate); event IncreaseObservationCardinalityNext( uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew ); function mint( address receiver, uint256 netSyDesired, uint256 netPtDesired ) external returns ( uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed ); function burn( address receiverSy, address receiverPt, uint256 netLpToBurn ) external returns (uint256 netSyOut, uint256 netPtOut); function swapExactPtForSy( address receiver, uint256 exactPtIn, bytes calldata data ) external returns (uint256 netSyOut, uint256 netSyFee); function swapSyForExactPt( address receiver, uint256 exactPtOut, bytes calldata data ) external returns (uint256 netSyIn, uint256 netSyFee); function redeemRewards(address user) external returns (uint256[] memory); function readState(address router) external view returns (MarketState memory market); function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative); function increaseObservationsCardinalityNext(uint16 cardinalityNext) external; function readTokens() external view returns ( IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT ); function getRewardTokens() external view returns (address[] memory); function isExpired() external view returns (bool); function expiry() external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IPPrincipalToken is IERC20Metadata { function burnByYT(address user, uint256 amount) external; function mintByYT(address user, uint256 amount) external; function initialize(address _YT) external; function SY() external view returns (address); function YT() external view returns (address); function factory() external view returns (address); function expiry() external view returns (uint256); function isExpired() external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./IRewardManager.sol"; import "./IPInterestManagerYT.sol"; interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT { event NewInterestIndex(uint256 indexed newIndex); event Mint( address indexed caller, address indexed receiverPT, address indexed receiverYT, uint256 amountSyToMint, uint256 amountPYOut ); event Burn( address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut ); event RedeemRewards(address indexed user, uint256[] amountRewardsOut); event RedeemInterest(address indexed user, uint256 interestOut); event WithdrawFeeToTreasury(uint256[] amountRewardsOut, uint256 syOut); function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut); function redeemPY(address receiver) external returns (uint256 amountSyOut); function redeemPYMulti(address[] calldata receivers, uint256[] calldata amountPYToRedeems) external returns (uint256[] memory amountSyOuts); function redeemDueInterestAndRewards( address user, bool redeemInterest, bool redeemRewards ) external returns (uint256 interestOut, uint256[] memory rewardsOut); function rewardIndexesCurrent() external returns (uint256[] memory); function pyIndexCurrent() external returns (uint256); function pyIndexStored() external view returns (uint256); function getRewardTokens() external view returns (address[] memory); function SY() external view returns (address); function PT() external view returns (address); function factory() external view returns (address); function expiry() external view returns (uint256); function isExpired() external view returns (bool); function doCacheIndexSameBlock() external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; interface IRewardManager { function userReward(address token, address user) external view returns (uint128 index, uint128 accrued); }
// SPDX-License-Identifier: GPL-3.0-or-later /* * MIT License * =========== * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IStandardizedYield is IERC20Metadata { /// @dev Emitted when any base tokens is deposited to mint shares event Deposit( address indexed caller, address indexed receiver, address indexed tokenIn, uint256 amountDeposited, uint256 amountSyOut ); /// @dev Emitted when any shares are redeemed for base tokens event Redeem( address indexed caller, address indexed receiver, address indexed tokenOut, uint256 amountSyToRedeem, uint256 amountTokenOut ); /// @dev check `assetInfo()` for more information enum AssetType { TOKEN, LIQUIDITY } /// @dev Emitted when (`user`) claims their rewards event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts); /** * @notice mints an amount of shares by depositing a base token. * @param receiver shares recipient address * @param tokenIn address of the base tokens to mint shares * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`) * @param minSharesOut reverts if amount of shares minted is lower than this * @return amountSharesOut amount of shares minted * @dev Emits a {Deposit} event * * Requirements: * - (`baseTokenIn`) must be a valid base token. */ function deposit( address receiver, address tokenIn, uint256 amountTokenToDeposit, uint256 minSharesOut ) external payable returns (uint256 amountSharesOut); /** * @notice redeems an amount of base tokens by burning some shares * @param receiver recipient address * @param amountSharesToRedeem amount of shares to be burned * @param tokenOut address of the base token to be redeemed * @param minTokenOut reverts if amount of base token redeemed is lower than this * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender` * @return amountTokenOut amount of base tokens redeemed * @dev Emits a {Redeem} event * * Requirements: * - (`tokenOut`) must be a valid base token. */ function redeem( address receiver, uint256 amountSharesToRedeem, address tokenOut, uint256 minTokenOut, bool burnFromInternalBalance ) external returns (uint256 amountTokenOut); /** * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy he can mint must be X * exchangeRate / 1e18 * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication & division */ function exchangeRate() external view returns (uint256 res); /** * @notice claims reward for (`user`) * @param user the user receiving their rewards * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens` * @dev * Emits a `ClaimRewards` event * See {getRewardTokens} for list of reward tokens */ function claimRewards(address user) external returns (uint256[] memory rewardAmounts); /** * @notice get the amount of unclaimed rewards for (`user`) * @param user the user to check for * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens` */ function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts); function rewardIndexesCurrent() external returns (uint256[] memory indexes); function rewardIndexesStored() external view returns (uint256[] memory indexes); /** * @notice returns the list of reward token addresses */ function getRewardTokens() external view returns (address[] memory); /** * @notice returns the address of the underlying yield token */ function yieldToken() external view returns (address); /** * @notice returns all tokens that can mint this SY */ function getTokensIn() external view returns (address[] memory res); /** * @notice returns all tokens that can be redeemed by this SY */ function getTokensOut() external view returns (address[] memory res); function isValidTokenIn(address token) external view returns (bool); function isValidTokenOut(address token) external view returns (bool); function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) external view returns (uint256 amountSharesOut); function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) external view returns (uint256 amountTokenOut); /** * @notice This function contains information to interpret what the asset is * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens) * @return assetAddress the address of the asset * @return assetDecimals the decimals of the asset */ function assetInfo() external view returns ( AssetType assetType, address assetAddress, uint8 assetDecimals ); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../kyberswap/KyberSwapHelper.sol"; import "../../core/libraries/TokenHelper.sol"; import "../../interfaces/IStandardizedYield.sol"; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPBulkSeller.sol"; import "../../core/libraries/Errors.sol"; // solhint-disable no-empty-blocks abstract contract ActionBaseMintRedeem is TokenHelper, KyberSwapHelper { bytes internal constant EMPTY_BYTES = abi.encode(); /// @dev since this contract will be proxied, it must not contains non-immutable variables constructor(address _kyberScalingLib) KyberSwapHelper(_kyberScalingLib) {} function _mintSyFromToken( address receiver, address SY, uint256 minSyOut, TokenInput calldata input ) internal returns (uint256 netSyOut) { _transferIn(input.tokenIn, msg.sender, input.netTokenIn); bool requireSwap = input.tokenIn != input.tokenMintSy; uint256 netTokenMintSy; if (requireSwap) { _kyberswap(input.tokenIn, input.netTokenIn, input.kyberRouter, input.kybercall); netTokenMintSy = _selfBalance(input.tokenMintSy); } else { netTokenMintSy = input.netTokenIn; } uint256 netNative = input.tokenMintSy == NATIVE ? netTokenMintSy : 0; if (input.bulk != address(0)) { _safeApproveInf(input.tokenMintSy, input.bulk); netSyOut = IPBulkSeller(input.bulk).swapExactTokenForSy{ value: netNative }( receiver, netTokenMintSy, minSyOut ); } else { _safeApproveInf(input.tokenMintSy, SY); netSyOut = IStandardizedYield(SY).deposit{ value: netNative }( receiver, input.tokenMintSy, netTokenMintSy, minSyOut ); } } function _redeemSyToToken( address receiver, address SY, uint256 netSyIn, TokenOutput memory output, bool doPull ) internal returns (uint256 netTokenOut) { if (doPull) { _transferFrom(IERC20(SY), msg.sender, _syOrBulk(SY, output), netSyIn); } bool requireSwap = output.tokenRedeemSy != output.tokenOut; address receiverRedeemSy = requireSwap ? address(this) : receiver; uint256 netTokenRedeemed; if (output.bulk != address(0)) { netTokenRedeemed = IPBulkSeller(output.bulk).swapExactSyForToken( receiverRedeemSy, netSyIn, 0, true ); } else { netTokenRedeemed = IStandardizedYield(SY).redeem( receiverRedeemSy, netSyIn, output.tokenRedeemSy, 0, true ); } if (requireSwap) { _kyberswap( output.tokenRedeemSy, netTokenRedeemed, output.kyberRouter, output.kybercall ); netTokenOut = _selfBalance(output.tokenOut); _transferOut(output.tokenOut, receiver, netTokenOut); } else { netTokenOut = netTokenRedeemed; } if (netTokenOut < output.minTokenOut) { revert Errors.RouterInsufficientTokenOut(netTokenOut, output.minTokenOut); } } function _mintPyFromSy( address receiver, address YT, uint256 netSyIn, uint256 minPyOut, bool doPull ) internal returns (uint256 netPyOut) { address SY = IPYieldToken(YT).SY(); if (doPull) { _transferFrom(IERC20(SY), msg.sender, YT, netSyIn); } netPyOut = IPYieldToken(YT).mintPY(receiver, receiver); if (netPyOut < minPyOut) revert Errors.RouterInsufficientPYOut(netPyOut, minPyOut); } function _redeemPyToSy( address receiver, address YT, uint256 netPyIn, uint256 minSyOut ) internal returns (uint256 netSyOut) { address PT = IPYieldToken(YT).PT(); _transferFrom(IERC20(PT), msg.sender, YT, netPyIn); bool needToBurnYt = (!IPYieldToken(YT).isExpired()); if (needToBurnYt) _transferFrom(IERC20(YT), msg.sender, YT, netPyIn); netSyOut = IPYieldToken(YT).redeemPY(receiver); if (netSyOut < minSyOut) revert Errors.RouterInsufficientSyOut(netSyOut, minSyOut); } function _syOrBulk(address SY, TokenOutput memory output) internal pure returns (address addr) { return output.bulk != address(0) ? output.bulk : SY; } function _wrapTokenOutput( address tokenOut, uint256 minTokenOut, address bulk ) internal pure returns (TokenOutput memory) { return TokenOutput(tokenOut, minTokenOut, tokenOut, bulk, address(0), EMPTY_BYTES); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; abstract contract CallbackHelper { enum ActionType { SwapSyForExactYt, SwapExactSyForYt, SwapYtForSy, SwapExactYtForPt, SwapExactPtForYt } /// ------------------------------------------------------------ /// SwapExactSyForYt /// ------------------------------------------------------------ function _encodeSwapExactSyForYt(address receiver, uint256 minYtOut) internal pure returns (bytes memory) { return abi.encode(ActionType.SwapExactSyForYt, receiver, minYtOut); } function _decodeSwapExactSyForYt(bytes memory data) internal pure returns (address receiver, uint256 minYtOut) { (, receiver, minYtOut) = abi.decode(data, (ActionType, address, uint256)); } /// ------------------------------------------------------------ /// SwapSyForExactYt /// ------------------------------------------------------------ function _encodeSwapSyForExactYt( address payer, address receiver, uint256 maxSyIn ) internal pure returns (bytes memory) { return abi.encode(ActionType.SwapSyForExactYt, payer, receiver, maxSyIn); } function _decodeSwapSyForExactYt(bytes memory data) internal pure returns ( address payer, address receiver, uint256 maxSyIn ) { (, payer, receiver, maxSyIn) = abi.decode(data, (ActionType, address, address, uint256)); } /// ------------------------------------------------------------ /// SwapYtForSy (common encode & decode) /// ------------------------------------------------------------ function _encodeSwapYtForSy(address receiver, uint256 minSyOut) internal pure returns (bytes memory) { return abi.encode(ActionType.SwapYtForSy, receiver, minSyOut); } function _decodeSwapYtForSy(bytes memory data) internal pure returns (address receiver, uint256 minSyOut) { (, receiver, minSyOut) = abi.decode(data, (ActionType, address, uint256)); } function _encodeSwapExactYtForPt( address receiver, uint256 exactYtIn, uint256 minPtOut ) internal pure returns (bytes memory) { return abi.encode(ActionType.SwapExactYtForPt, receiver, exactYtIn, minPtOut); } function _decodeSwapExactYtForPt(bytes memory data) internal pure returns ( address receiver, uint256 exactYtIn, uint256 minPtOut ) { (, receiver, exactYtIn, minPtOut) = abi.decode( data, (ActionType, address, uint256, uint256) ); } function _encodeSwapExactPtForYt( address receiver, uint256 exactPtIn, uint256 minYtOut ) internal pure returns (bytes memory) { return abi.encode(ActionType.SwapExactPtForYt, receiver, exactPtIn, minYtOut); } function _decodeSwapExactPtForYt(bytes memory data) internal pure returns ( address receiver, uint256 exactPtIn, uint256 minYtOut ) { (, receiver, exactPtIn, minYtOut) = abi.decode( data, (ActionType, address, uint256, uint256) ); } /// ------------------------------------------------------------ /// Misc functions /// ------------------------------------------------------------ function _getActionType(bytes memory data) internal pure returns (ActionType actionType) { actionType = abi.decode(data, (ActionType)); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; import "../../core/libraries/math/Math.sol"; import "../../core/Market/MarketMathCore.sol"; struct ApproxParams { uint256 guessMin; uint256 guessMax; uint256 guessOffchain; // pass 0 in to skip this variable uint256 maxIteration; // every iteration, the diff between guessMin and guessMax will be divided by 2 uint256 eps; // the max eps between the returned result & the correct result, base 1e18. Normally this number will be set // to 1e15 (1e18/1000 = 0.1%) /// Further explanation of the eps. Take swapExactSyForPt for example. To calc the corresponding amount of Pt to swap out, /// it's necessary to run an approximation algorithm, because by default there only exists the Pt to Sy formula /// To approx, the 5 values above will have to be provided, and the approx process will run as follows: /// mid = (guessMin + guessMax) / 2 // mid here is the current guess of the amount of Pt out /// netSyNeed = calcSwapSyForExactPt(mid) /// if (netSyNeed > exactSyIn) guessMax = mid - 1 // since the maximum Sy in can't exceed the exactSyIn /// else guessMin = mid (1) /// For the (1), since netSyNeed <= exactSyIn, the result might be usable. If the netSyNeed is within eps of /// exactSyIn (ex eps=0.1% => we have used 99.9% the amount of Sy specified), mid will be chosen as the final guess result /// for guessOffchain, this is to provide a shortcut to guessing. The offchain SDK can precalculate the exact result /// before the tx is sent. When the tx reaches the contract, the guessOffchain will be checked first, and if it satisfies the /// approximation, it will be used (and save all the guessing). It's expected that this shortcut will be used in most cases /// except in cases that there is a trade in the same market right before the tx } library MarketApproxPtInLib { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using Math for uint256; using Math for int256; using LogExpMath for int256; struct ApproxParamsPtIn { uint256 guessMin; uint256 guessMax; uint256 guessOffchain; uint256 maxIteration; uint256 eps; // uint256 biggestGoodGuess; } struct Args1 { MarketState market; PYIndex index; uint256 minSyOut; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swap in - Try swapping & get netSyOut - Stop when netSyOut greater & approx minSyOut - guess & approx is for netPtIn */ function approxSwapPtForExactSy( MarketState memory _market, PYIndex _index, uint256 _minSyOut, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netPtIn*/ uint256, /*netSyOut*/ uint256 /*netSyFee*/ ) { Args1 memory a = Args1(_market, _index, _minSyOut, _blockTime); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtIn memory p = newApproxParamsPtIn(_approx, 0, calcMaxPtIn(comp.totalAsset)); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { (bool isGoodSlope, uint256 guess) = nextGuess(p, comp, a.market.totalPt, iter); if (!isGoodSlope) { p.guessMax = guess; continue; } (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(a.market, comp, a.index, guess); if (netSyOut >= a.minSyOut) { p.guessMax = guess; bool isAnswerAccepted = Math.isAGreaterApproxB(netSyOut, a.minSyOut, p.eps); if (isAnswerAccepted) { return (guess, netSyOut, netSyFee); } } else { p.guessMin = guess; } } revert Errors.ApproxFail(); } struct Args2 { MarketState market; PYIndex index; uint256 exactSyIn; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swap in - Flashswap the corresponding amount of SY out - Pair those amount with exactSyIn SY to tokenize into PT & YT - PT to repay the flashswap, YT transferred to user - Stop when the amount of SY to be pulled to tokenize PT to repay loan approx the exactSyIn - guess & approx is for netYtOut (also netPtIn) */ function approxSwapExactSyForYt( MarketState memory _market, PYIndex _index, uint256 _exactSyIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netYtOut*/ uint256 /*netSyFee*/ ) { Args2 memory a = Args2(_market, _index, _exactSyIn, _blockTime); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); // at minimum we will flashswap exactSyIn since we have enough SY to payback the PT loan ApproxParamsPtIn memory p = newApproxParamsPtIn( _approx, a.index.syToAsset(a.exactSyIn), calcMaxPtIn(comp.totalAsset) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { (bool isGoodSlope, uint256 guess) = nextGuess(p, comp, a.market.totalPt, iter); if (!isGoodSlope) { p.guessMax = guess; continue; } (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(a.market, comp, a.index, guess); uint256 netSyToTokenizePt = a.index.assetToSyUp(guess); // for sure netSyToTokenizePt >= netSyOut since we are swapping PT to SY uint256 netSyToPull = netSyToTokenizePt - netSyOut; if (netSyToPull <= a.exactSyIn) { p.guessMin = guess; bool isAnswerAccepted = Math.isASmallerApproxB(netSyToPull, a.exactSyIn, p.eps); if (isAnswerAccepted) return (guess, netSyFee); } else { p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } struct Args6 { MarketState market; PYIndex index; uint256 totalPtIn; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swap to SY - Swap PT to SY - Pair the remaining PT with the SY to add liquidity - Stop when the ratio of PT / totalPt & SY / totalSy is approx - guess & approx is for netPtSwap */ function approxSwapPtToAddLiquidity( MarketState memory _market, PYIndex _index, uint256 _totalPtIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netPtSwap*/ uint256, /*netSyFromSwap*/ uint256 /*netSyFee*/ ) { Args6 memory a = Args6(_market, _index, _totalPtIn, _blockTime); require(a.market.totalLp != 0, "no existing lp"); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtIn memory p = newApproxParamsPtIn( _approx, 0, Math.min(a.totalPtIn, calcMaxPtIn(comp.totalAsset)) ); p.guessMax = Math.min(p.guessMax, a.totalPtIn); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { (bool isGoodSlope, uint256 guess) = nextGuess(p, comp, a.market.totalPt, iter); if (!isGoodSlope) { p.guessMax = guess; continue; } (uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve) = calcSyOut( a.market, comp, a.index, guess ); uint256 syNumerator; uint256 ptNumerator; { uint256 newTotalPt = a.market.totalPt.Uint() + guess; uint256 newTotalSy = (a.market.totalSy.Uint() - netSyOut - netSyToReserve); // it is desired that // netSyOut / newTotalSy = netPtRemaining / newTotalPt // which is equivalent to // netSyOut * newTotalPt = netPtRemaining * newTotalSy syNumerator = netSyOut * newTotalPt; ptNumerator = (a.totalPtIn - guess) * newTotalSy; } if (Math.isAApproxB(syNumerator, ptNumerator, p.eps)) { return (guess, netSyOut, netSyFee); } if (syNumerator <= ptNumerator) { // needs more SY --> swap more PT p.guessMin = guess + 1; } else { // needs less SY --> swap less PT p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } struct Args7 { MarketState market; PYIndex index; uint256 exactPtIn; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swap to SY - Flashswap the corresponding amount of SY out - Tokenize all the SY into PT + YT - PT to repay the flashswap, YT transferred to user - Stop when the additional amount of PT to pull to repay the loan approx the exactPtIn - guess & approx is for totalPtToSwap */ function approxSwapExactPtForYt( MarketState memory _market, PYIndex _index, uint256 _exactPtIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netYtOut*/ uint256, /*totalPtToSwap*/ uint256 /*netSyFee*/ ) { Args7 memory a = Args7(_market, _index, _exactPtIn, _blockTime); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtIn memory p = newApproxParamsPtIn( _approx, a.exactPtIn, calcMaxPtIn(comp.totalAsset) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { (bool isGoodSlope, uint256 guess) = nextGuess(p, comp, a.market.totalPt, iter); if (!isGoodSlope) { p.guessMax = guess; continue; } (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(a.market, comp, a.index, guess); uint256 netAssetOut = a.index.syToAsset(netSyOut); // guess >= netAssetOut since we are swapping PT to SY uint256 netPtToPull = guess - netAssetOut; if (netPtToPull <= a.exactPtIn) { p.guessMin = guess; if (Math.isASmallerApproxB(netPtToPull, a.exactPtIn, p.eps)) { return (netAssetOut, guess, netSyFee); } } else { p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } //////////////////////////////////////////////////////////////////////////////// function calcSyOut( MarketState memory market, MarketPreCompute memory comp, PYIndex index, uint256 netPtIn ) internal pure returns ( uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve ) { (int256 _netSyOut, int256 _netSyFee, int256 _netSyToReserve) = market.calcTrade( comp, index, netPtIn.neg() ); netSyOut = _netSyOut.Uint(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } function newApproxParamsPtIn( ApproxParams memory _approx, uint256 minGuessMin, uint256 maxGuessMax ) internal pure returns (ApproxParamsPtIn memory res) { res.guessMin = Math.max(_approx.guessMin, minGuessMin); res.guessMax = Math.min(_approx.guessMax, maxGuessMax); if (res.guessMin > res.guessMax || _approx.eps > Math.ONE) revert Errors.ApproxParamsInvalid(_approx.guessMin, _approx.guessMax, _approx.eps); res.guessOffchain = _approx.guessOffchain; res.maxIteration = _approx.maxIteration; res.eps = _approx.eps; } function calcMaxPtIn(int256 totalAsset) internal pure returns (uint256) { return totalAsset.Uint() - 1; } function nextGuess( ApproxParamsPtIn memory p, MarketPreCompute memory comp, int256 totalPt, uint256 iter ) internal pure returns (bool, uint256) { uint256 guess = _nextGuessPrivate(p, iter); if (guess <= p.biggestGoodGuess) return (true, guess); int256 slope = calcSlope(comp, totalPt, guess.Int()); if (slope < 0) return (false, guess); p.biggestGoodGuess = guess; return (true, guess); } /** * @dev it is safe to assume that p.guessMin <= p.guessMax from the initialization of p * So once guessMin becomes larger, it should always be the case of ApproxFail */ function _nextGuessPrivate(ApproxParamsPtIn memory p, uint256 iter) private pure returns (uint256) { if (iter == 0 && p.guessOffchain != 0) return p.guessOffchain; if (p.guessMin <= p.guessMax) return (p.guessMin + p.guessMax) / 2; revert Errors.ApproxFail(); } function calcSlope( MarketPreCompute memory comp, int256 totalPt, int256 ptToMarket // ) internal pure returns (int256) { int256 diffAssetPtToMarket = comp.totalAsset - ptToMarket; int256 sumPt = ptToMarket + totalPt; // probably can skip sumPt check require(diffAssetPtToMarket > 0 && sumPt > 0, "invalid ptToMarket"); int256 part1 = (ptToMarket * (totalPt + comp.totalAsset)).divDown( sumPt * diffAssetPtToMarket ); int256 part2 = sumPt.divDown(diffAssetPtToMarket).ln(); int256 part3 = Math.IONE.divDown(comp.rateScalar); return comp.rateAnchor - (part1 - part2).mulDown(part3); } } library MarketApproxPtOutLib { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using Math for uint256; using Math for int256; using LogExpMath for int256; struct ApproxParamsPtOut { uint256 guessMin; uint256 guessMax; uint256 guessOffchain; uint256 maxIteration; uint256 eps; } struct Args4 { MarketState market; PYIndex index; uint256 exactSyIn; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swapExactOut - Calculate the amount of SY needed - Stop when the netSyIn is smaller approx exactSyIn - guess & approx is for netSyIn */ function approxSwapExactSyForPt( MarketState memory _market, PYIndex _index, uint256 _exactSyIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netPtOut*/ uint256 /*netSyFee*/ ) { Args4 memory a = Args4(_market, _index, _exactSyIn, _blockTime); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtOut memory p = newApproxParamsPtOut( _approx, 0, calcMaxPtOut(comp, a.market.totalPt) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { uint256 guess = nextGuess(p, iter); (uint256 netSyIn, uint256 netSyFee, ) = calcSyIn(a.market, comp, a.index, guess); if (netSyIn <= a.exactSyIn) { p.guessMin = guess; bool isAnswerAccepted = Math.isASmallerApproxB(netSyIn, a.exactSyIn, p.eps); if (isAnswerAccepted) return (guess, netSyFee); } else { p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } struct Args5 { MarketState market; PYIndex index; uint256 minSyOut; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swapExactOut - Flashswap that amount of PT & pair with YT to redeem SY - Use the SY to repay the flashswap debt and the remaining is transferred to user - Stop when the netSyOut is greater approx the minSyOut - guess & approx is for netSyOut */ function approxSwapYtForExactSy( MarketState memory _market, PYIndex _index, uint256 _minSyOut, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netYtIn*/ uint256, /*netSyOut*/ uint256 /*netSyFee*/ ) { Args5 memory a = Args5(_market, _index, _minSyOut, _blockTime); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtOut memory p = newApproxParamsPtOut( _approx, 0, calcMaxPtOut(comp, a.market.totalPt) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { uint256 guess = nextGuess(p, iter); (uint256 netSyOwed, uint256 netSyFee, ) = calcSyIn(a.market, comp, a.index, guess); uint256 netAssetToRepay = a.index.syToAssetUp(netSyOwed); uint256 netSyOut = a.index.assetToSy(guess - netAssetToRepay); if (netSyOut >= a.minSyOut) { p.guessMax = guess; if (Math.isAGreaterApproxB(netSyOut, a.minSyOut, p.eps)) { return (guess, netSyOut, netSyFee); } } else { p.guessMin = guess + 1; } } revert Errors.ApproxFail(); } struct Args6 { MarketState market; PYIndex index; uint256 totalSyIn; uint256 blockTime; } /** * @dev algorithm: - Bin search the amount of PT to swapExactOut - Swap that amount of PT out - Pair the remaining PT with the SY to add liquidity - Stop when the ratio of PT / totalPt & SY / totalSy is approx - guess & approx is for netPtFromSwap */ function approxSwapSyToAddLiquidity( MarketState memory _market, PYIndex _index, uint256 _totalSyIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netPtFromSwap*/ uint256, /*netSySwap*/ uint256 /*netSyFee*/ ) { Args6 memory a = Args6(_market, _index, _totalSyIn, _blockTime); require(a.market.totalLp != 0, "no existing lp"); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtOut memory p = newApproxParamsPtOut( _approx, 0, calcMaxPtOut(comp, a.market.totalPt) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { uint256 guess = nextGuess(p, iter); (uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve) = calcSyIn( a.market, comp, a.index, guess ); if (netSyIn > a.totalSyIn) { p.guessMax = guess - 1; continue; } uint256 syNumerator; uint256 ptNumerator; { uint256 newTotalPt = a.market.totalPt.Uint() - guess; uint256 netTotalSy = a.market.totalSy.Uint() + netSyIn - netSyToReserve; // it is desired that // netPtFromSwap / newTotalPt = netSyRemaining / netTotalSy // which is equivalent to // netPtFromSwap * netTotalSy = netSyRemaining * newTotalPt ptNumerator = guess * netTotalSy; syNumerator = (a.totalSyIn - netSyIn) * newTotalPt; } if (Math.isAApproxB(ptNumerator, syNumerator, p.eps)) { return (guess, netSyIn, netSyFee); } if (ptNumerator <= syNumerator) { // needs more PT p.guessMin = guess + 1; } else { // needs less PT p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } struct Args8 { MarketState market; PYIndex index; uint256 exactYtIn; uint256 blockTime; uint256 maxSyPayable; } /** * @dev algorithm: - Bin search the amount of PT to swapExactOut - Flashswap that amount of PT out - Pair all the PT with the YT to redeem SY - Use the SY to repay the flashswap debt - Stop when the amount of SY owed is smaller approx the amount of SY to repay the flashswap - guess & approx is for netPtFromSwap */ function approxSwapExactYtForPt( MarketState memory _market, PYIndex _index, uint256 _exactYtIn, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns ( uint256, /*netPtOut*/ uint256, /*totalPtSwapped*/ uint256 /*netSyFee*/ ) { Args8 memory a = Args8( _market, _index, _exactYtIn, _blockTime, _index.assetToSy(_exactYtIn) ); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); ApproxParamsPtOut memory p = newApproxParamsPtOut( _approx, a.exactYtIn, calcMaxPtOut(comp, a.market.totalPt) ); for (uint256 iter = 0; iter < p.maxIteration; ++iter) { uint256 guess = nextGuess(p, iter); (uint256 netSyOwed, uint256 netSyFee, ) = calcSyIn(a.market, comp, a.index, guess); if (netSyOwed <= a.maxSyPayable) { p.guessMin = guess; if (Math.isASmallerApproxB(netSyOwed, a.maxSyPayable, p.eps)) { return (guess - a.exactYtIn, guess, netSyFee); } } else { p.guessMax = guess - 1; } } revert Errors.ApproxFail(); } //////////////////////////////////////////////////////////////////////////////// function calcSyIn( MarketState memory market, MarketPreCompute memory comp, PYIndex index, uint256 netPtOut ) internal pure returns ( uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve ) { (int256 _netSyIn, int256 _netSyFee, int256 _netSyToReserve) = market.calcTrade( comp, index, netPtOut.Int() ); netSyIn = _netSyIn.abs(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } function newApproxParamsPtOut( ApproxParams memory _approx, uint256 minGuessMin, uint256 maxGuessMax ) internal pure returns (ApproxParamsPtOut memory res) { if (_approx.guessMin > _approx.guessMax || _approx.eps > Math.ONE) revert Errors.ApproxParamsInvalid(_approx.guessMin, _approx.guessMax, _approx.eps); res.guessMin = Math.max(_approx.guessMin, minGuessMin); res.guessMax = Math.min(_approx.guessMax, maxGuessMax); if (res.guessMin > res.guessMax) revert Errors.ApproxBinarySearchInputInvalid( _approx.guessMin, _approx.guessMax, minGuessMin, maxGuessMax ); res.guessOffchain = _approx.guessOffchain; res.maxIteration = _approx.maxIteration; res.eps = _approx.eps; } function calcMaxPtOut(MarketPreCompute memory comp, int256 totalPt) internal pure returns (uint256) { int256 logitP = (comp.feeRate - comp.rateAnchor).mulDown(comp.rateScalar).exp(); int256 proportion = logitP.divDown(logitP + Math.IONE); int256 numerator = proportion.mulDown(totalPt + comp.totalAsset); int256 maxPtOut = totalPt - numerator; // only get 99.9% of the theoretical max to accommodate some precision issues return (maxPtOut.Uint() * 999) / 1000; } /** * @dev it is safe to assume that p.guessMin <= p.guessMax from the initialization of p * So once guessMin becomes larger, it should always be the case of ApproxFail */ function nextGuess(ApproxParamsPtOut memory p, uint256 iter) private pure returns (uint256) { if (iter == 0 && p.guessOffchain != 0) return p.guessOffchain; if (p.guessMin <= p.guessMax) return (p.guessMin + p.guessMax) / 2; revert Errors.ApproxFail(); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.17; interface IAggregationRouterHelper { function getScaledInputData(bytes calldata kybercall, uint256 newAmount) external pure returns (bytes memory); }
// SPDX-License-Identifier: GPL-3.0-or-later /* * MIT License * =========== * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ pragma solidity 0.8.17; import "@openzeppelin/contracts/utils/Address.sol"; import "../../core/libraries/TokenHelper.sol"; import "./IAggregatorRouterHelper.sol"; import "../../core/libraries/Errors.sol"; struct TokenInput { // Token/Sy data address tokenIn; uint256 netTokenIn; address tokenMintSy; address bulk; // Kyber data address kyberRouter; bytes kybercall; } struct TokenOutput { // Token/Sy data address tokenOut; uint256 minTokenOut; address tokenRedeemSy; address bulk; // Kyber data address kyberRouter; bytes kybercall; } abstract contract KyberSwapHelper is TokenHelper { using Address for address; address public immutable kyberScalingLib; constructor(address _kyberScalingLib) { kyberScalingLib = _kyberScalingLib; } function _kyberswap( address tokenIn, uint256 amountIn, address kyberRouter, bytes memory rawKybercall ) internal { if (kyberRouter == address(0) || rawKybercall.length == 0) revert Errors.RouterKyberSwapDataZero(); _safeApproveInf(tokenIn, kyberRouter); bytes memory kybercall = IAggregationRouterHelper(kyberScalingLib).getScaledInputData( rawKybercall, amountIn ); kyberRouter.functionCallWithValue(kybercall, tokenIn == NATIVE ? amountIn : 0); } }
{ "optimizer": { "enabled": true, "runs": 90000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_kyberScalingLib","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"approxGuessMin","type":"uint256"},{"internalType":"uint256","name":"approxGuessMax","type":"uint256"},{"internalType":"uint256","name":"minGuessMin","type":"uint256"},{"internalType":"uint256","name":"maxGuessMax","type":"uint256"}],"name":"ApproxBinarySearchInputInvalid","type":"error"},{"inputs":[],"name":"ApproxFail","type":"error"},{"inputs":[{"internalType":"uint256","name":"guessMin","type":"uint256"},{"internalType":"uint256","name":"guessMax","type":"uint256"},{"internalType":"uint256","name":"eps","type":"uint256"}],"name":"ApproxParamsInvalid","type":"error"},{"inputs":[{"internalType":"int256","name":"exchangeRate","type":"int256"}],"name":"MarketExchangeRateBelowOne","type":"error"},{"inputs":[],"name":"MarketExpired","type":"error"},{"inputs":[],"name":"MarketProportionMustNotEqualOne","type":"error"},{"inputs":[{"internalType":"int256","name":"proportion","type":"int256"},{"internalType":"int256","name":"maxProportion","type":"int256"}],"name":"MarketProportionTooHigh","type":"error"},{"inputs":[{"internalType":"int256","name":"rateScalar","type":"int256"}],"name":"MarketRateScalarBelowZero","type":"error"},{"inputs":[{"internalType":"int256","name":"totalPt","type":"int256"},{"internalType":"int256","name":"totalAsset","type":"int256"}],"name":"MarketZeroTotalPtOrTotalAsset","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualYtIn","type":"uint256"},{"internalType":"uint256","name":"limitYtIn","type":"uint256"}],"name":"RouterExceededLimitYtIn","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualTokenOut","type":"uint256"},{"internalType":"uint256","name":"requiredTokenOut","type":"uint256"}],"name":"RouterInsufficientTokenOut","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualYtOut","type":"uint256"},{"internalType":"uint256","name":"requiredYtOut","type":"uint256"}],"name":"RouterInsufficientYtOut","type":"error"},{"inputs":[],"name":"RouterKyberSwapDataZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"int256","name":"netYtToAccount","type":"int256"},{"indexed":false,"internalType":"int256","name":"netSyToAccount","type":"int256"}],"name":"SwapYtAndSy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"int256","name":"netYtToAccount","type":"int256"},{"indexed":false,"internalType":"int256","name":"netTokenToAccount","type":"int256"}],"name":"SwapYtAndToken","type":"event"},{"inputs":[],"name":"kyberScalingLib","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"exactSyIn","type":"uint256"},{"internalType":"uint256","name":"minYtOut","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 ApproxParams","name":"guessYtOut","type":"tuple"}],"name":"swapExactSyForYt","outputs":[{"internalType":"uint256","name":"netYtOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"minYtOut","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 ApproxParams","name":"guessYtOut","type":"tuple"},{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"internalType":"address","name":"tokenMintSy","type":"address"},{"internalType":"address","name":"bulk","type":"address"},{"internalType":"address","name":"kyberRouter","type":"address"},{"internalType":"bytes","name":"kybercall","type":"bytes"}],"internalType":"struct TokenInput","name":"input","type":"tuple"}],"name":"swapExactTokenForYt","outputs":[{"internalType":"uint256","name":"netYtOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"exactYtIn","type":"uint256"},{"internalType":"uint256","name":"minSyOut","type":"uint256"}],"name":"swapExactYtForSy","outputs":[{"internalType":"uint256","name":"netSyOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"netYtIn","type":"uint256"},{"components":[{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minTokenOut","type":"uint256"},{"internalType":"address","name":"tokenRedeemSy","type":"address"},{"internalType":"address","name":"bulk","type":"address"},{"internalType":"address","name":"kyberRouter","type":"address"},{"internalType":"bytes","name":"kybercall","type":"bytes"}],"internalType":"struct TokenOutput","name":"output","type":"tuple"}],"name":"swapExactYtForToken","outputs":[{"internalType":"uint256","name":"netTokenOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"exactYtOut","type":"uint256"},{"internalType":"uint256","name":"maxSyIn","type":"uint256"}],"name":"swapSyForExactYt","outputs":[{"internalType":"uint256","name":"netSyIn","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"exactSyOut","type":"uint256"},{"internalType":"uint256","name":"maxYtIn","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 ApproxParams","name":"guessYtIn","type":"tuple"}],"name":"swapYtForExactSy","outputs":[{"internalType":"uint256","name":"netYtIn","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a06040523480156200001157600080fd5b506040516200486a3803806200486a833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b6080516147cf6200009b600039600081816101340152611a2a01526147cf6000f3fe6080604052600436106100705760003560e01c8063bf1bd4341161004e578063bf1bd434146100e2578063e15cc09814610102578063f953080014610122578063fdd71f431461017b57600080fd5b8063357d654014610075578063a000aaa9146100af578063abbc79d0146100c2575b600080fd5b34801561008157600080fd5b50610095610090366004613d2b565b61019b565b604080519283526020830191909152015b60405180910390f35b6100956100bd366004613ea1565b6102a6565b3480156100ce57600080fd5b506100956100dd366004613f21565b61040f565b3480156100ee57600080fd5b506100956100fd366004613d2b565b61059a565b34801561010e57600080fd5b5061009561011d366004613f8d565b610833565b34801561012e57600080fd5b506101567f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100a6565b34801561018757600080fd5b50610095610196366004613f8d565b610ac4565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156101ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102109190613fea565b925050915061022181338389610bac565b61022f888884848a8a610bda565b909450925073ffffffffffffffffffffffffffffffffffffffff808916908816337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d61027a8a610dab565b61028389610de6565b6040805192835260208301919091520160405180910390a4505094509492505050565b6000806000808773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156102f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061031b9190613fea565b9250509150600061032f8284600189610e19565b905061033f8a8a84848c8c611161565b90955093506103516020870187614037565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fc372308269f6e4dd0b4f322160cf0edef154584a3ad62248d654ae846e3eb6e88d6103c08a610de6565b6103cd8c60200135610dab565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a45050509550959350505050565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190613fea565b925050915061049581338389610bac565b60006104b76104ac846104a7896140f0565b611305565b8985858b6001610bda565b945090506104d18984836104ca8a6140f0565b600061133a565b94506104e06020870187614037565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fc372308269f6e4dd0b4f322160cf0edef154584a3ad62248d654ae846e3eb6e88c61054f8c610dab565b6105588b610de6565b6040805173ffffffffffffffffffffffffffffffffffffffff90941684526020840192909252908201526060015b60405180910390a450505094509492505050565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156105eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060f9190613fea565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152929450925060009173ffffffffffffffffffffffffffffffffffffffff851691506370a0823190602401602060405180830381865afa158015610682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a691906141ae565b90508773ffffffffffffffffffffffffffffffffffffffff166329910b1183896106d1338e8c6115b8565b6040518463ffffffff1660e01b81526004016106ef93929190614235565b60408051808303816000875af115801561070d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107319190614273565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290955073ffffffffffffffffffffffffffffffffffffffff851691506370a0823190602401602060405180830381865afa15801561079f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c391906141ae565b6107cd90826142c6565b945073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d6108158b610de6565b61081e8a610dab565b60408051928352602083019190915201610586565b6040517f794052f30000000000000000000000000000000000000000000000000000000081523060048201526000908190819073ffffffffffffffffffffffffffffffffffffffff88169063794052f39060240161012060405180830381865afa1580156108a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c991906142e4565b905060008773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093c9190613fea565b9250505061096c6109628273ffffffffffffffffffffffffffffffffffffffff166115ea565b839089428961165d565b5090945050858411156109ba576040517f570cfb8100000000000000000000000000000000000000000000000000000000815260048101859052602481018790526044015b60405180910390fd5b6109c681338387610bac565b8773ffffffffffffffffffffffffffffffffffffffff16635b709f1782866109ee8d8c6117cb565b6040518463ffffffff1660e01b8152600401610a0c93929190614235565b60408051808303816000875af1158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e9190614273565b93505073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d610a9788610dab565b610aa08c610de6565b6040805192835260208301919091520160405180910390a450509550959350505050565b6000806000808773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b399190613fea565b9250509150610b4a8233838a610bac565b610b588989838a8a8a611161565b909450925073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d610ba388610de6565b610aa08c610dab565b8015610bd457610bd473ffffffffffffffffffffffffffffffffffffffff85168484846117fa565b50505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015260009182918291908816906370a0823190602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7291906141ae565b90508773ffffffffffffffffffffffffffffffffffffffff16635b709f178787610c9c8d896117cb565b6040518463ffffffff1660e01b8152600401610cba93929190614235565b60408051808303816000875af1158015610cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfc9190614273565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c81166004830152919450839250908916906370a0823190602401602060405180830381865afa158015610d6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9391906141ae565b610d9d91906142c6565b925050965096945050505050565b6000610db682610de6565b610de0907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614366565b92915050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115610e1557600080fd5b5090565b6000610e36610e2b6020840184614037565b3384602001356118d6565b6000610e486060840160408501614037565b73ffffffffffffffffffffffffffffffffffffffff16610e6b6020850185614037565b73ffffffffffffffffffffffffffffffffffffffff161415905060008115610f1857610ef9610e9d6020860186614037565b6020860135610eb260a0880160808901614037565b610ebf60a08901896143b2565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061198792505050565b610f11610f0c6060860160408701614037565b611b15565b9050610f1f565b5060208301355b600080610f326060870160408801614037565b73ffffffffffffffffffffffffffffffffffffffff1614610f54576000610f56565b815b90506000610f6a6080870160608801614037565b73ffffffffffffffffffffffffffffffffffffffff161461106857610fad610f986060870160408801614037565b610fa86080880160608901614037565b611bcd565b610fbd6080860160608701614037565b6040517fb276707000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301526024820185905260448201899052919091169063b276707090839060640160206040518083038185885af115801561103c573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061106191906141ae565b9350611156565b61108161107b6060870160408801614037565b88611bcd565b73ffffffffffffffffffffffffffffffffffffffff87166320e8c565828a6110af60608a0160408b01614037565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260448101869052606481018a905260840160206040518083038185885af115801561112e573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061115391906141ae565b93505b505050949350505050565b6040517f794052f30000000000000000000000000000000000000000000000000000000081523060048201526000908190819073ffffffffffffffffffffffffffffffffffffffff89169063794052f39060240161012060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f791906142e4565b905061122561121b8873ffffffffffffffffffffffffffffffffffffffff166115ea565b8290884288611cec565b5092508483101561126c576040517fa59b8c3100000000000000000000000000000000000000000000000000000000815260048101849052602481018690526044016109b1565b8773ffffffffffffffffffffffffffffffffffffffff166329910b1188856112948d8a611e50565b6040518463ffffffff1660e01b81526004016112b293929190614235565b60408051808303816000875af11580156112d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f49190614273565b939a93995092975050505050505050565b606081015160009073ffffffffffffffffffffffffffffffffffffffff1661132d5782611333565b81606001515b9392505050565b600081156113575761135785336113518887611305565b87610bac565b8251604084015173ffffffffffffffffffffffffffffffffffffffff908116911614156000816113875787611389565b305b606086015190915060009073ffffffffffffffffffffffffffffffffffffffff16156114635760608601516040517f83f5b76600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018a90526000604483015260016064830152909116906383f5b766906084016020604051808303816000875af1158015611438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145c91906141ae565b905061151b565b60408681015190517f769f8e5d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018a9052918216604482015260006064820152600160848201529089169063769f8e5d9060a4016020604051808303816000875af11580156114f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151891906141ae565b90505b821561155a5761153986604001518288608001518960a00151611987565b855161154490611b15565b935061155586600001518a86611e68565b61155e565b8093505b85602001518410156115ac5760208601516040517fc5b5576d0000000000000000000000000000000000000000000000000000000081526109b1918691600401918252602082015260400190565b50505095945050505050565b606060008484846040516020016115d29493929190614459565b60405160208183030381529060405290509392505050565b60008173ffffffffffffffffffffffffffffffffffffffff16631d52edc46040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611639573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de091906141ae565b60408051608081018252868152602081018690529081018490526060810183905260009081908190816116918a8a89611f7b565b905060006116b28760006116ad858760000151600001516120ae565b612156565b905060005b816060015181101561178d5760006116cf838361229e565b90506000806116e8876000015187896020015186612323565b5091509150600061170683896020015161237790919063ffffffff16565b9050600061172261171783876142c6565b60208b01519061238c565b90508860400151811061176957602087018590526040890151608088015161174b918391612398565b1561176457939a5092985096506117c095505050505050565b611777565b61177485600161449b565b87525b505050505080611786906144ae565b90506116b7565b506040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b955095509592505050565b6060600283836040516020016117e3939291906144e6565b604051602081830303815290604052905092915050565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610bd49085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526123cb565b73ffffffffffffffffffffffffffffffffffffffff831661195f5780341461195a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f657468206d69736d61746368000000000000000000000000000000000000000060448201526064016109b1565b505050565b801561195a5761195a73ffffffffffffffffffffffffffffffffffffffff84168330846117fa565b73ffffffffffffffffffffffffffffffffffffffff821615806119a957508051155b156119e0576040517f1e59c84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119ea8483611bcd565b6040517fe4da6f2e00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063e4da6f2e90611a61908590889060040161451b565b600060405180830381865afa158015611a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611ac4919081019061453d565b9050611b0d8173ffffffffffffffffffffffffffffffffffffffff871615611aed576000611aef565b855b73ffffffffffffffffffffffffffffffffffffffff861691906124d7565b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff821615611bc6576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc191906141ae565b610de0565b4792915050565b73ffffffffffffffffffffffffffffffffffffffff8216611bec575050565b611c0360026bffffffffffffffffffffffff6145da565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301526bffffffffffffffffffffffff929092169184169063dd62ed3e90604401602060405180830381865afa158015611c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cab91906141ae565b1015611ce857611cbd828260006124fd565b611ce882827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6124fd565b5050565b604080516080810182528681526020810186905290810184905260608101839052600090819081611d1e898988611f7b565b90506000611d5386611d418560400151866020015161266d90919063ffffffff16565b611d4e8560200151612679565b612690565b905060005b816060015181101561178d57600080611d7b84868860000151600001518661275a565b9150915081611d8f57602084015250611e36565b600080611da68860000151888a60200151866127c5565b50915091506000611dc4848a602001516127eb90919063ffffffff16565b90506000611dd284836142c6565b905089604001518111611e1e5784885260408a01516080890151600091611dfb918491906127f7565b90508015611e185750939a50909850611e46975050505050505050565b50611e2f565b611e296001866142c6565b60208901525b5050505050505b611e3f816144ae565b9050611d58565b9550959350505050565b6060600183836040516020016117e3939291906144e6565b80600003611e7557505050565b73ffffffffffffffffffffffffffffffffffffffff8316611f5a5760008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114611eea576040519150601f19603f3d011682016040523d82523d6000602084013e611eef565b606091505b5050905080610bd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6574682073656e64206661696c6564000000000000000000000000000000000060448201526064016109b1565b61195a73ffffffffffffffffffffffffffffffffffffffff84168383612825565b611fa66040518060800160405280600081526020016000815260200160008152602001600081525090565b60a08401518210611fe3576040517fb2094b5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828560a00151611ff591906142c6565b9050612001858261287b565b825260208501516120139085906128f5565b60208301528451158061202857506020820151155b1561206f57845160208301516040517fb1c4aefb000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016109b1565b61208d8560000151866101000151846020015185600001518561294a565b604083015260c08501516120a190826129e4565b6060830152509392505050565b6000806120db6120d68560000151866040015187606001516120d09190614605565b90612a18565b612a3b565b905060006120fb6120f4670de0b6b3a76400008461462c565b8390612f6c565b90506000612119866020015186612112919061462c565b8390612a18565b905060006121278287614605565b90506103e861213582612f92565b612141906103e7614654565b61214b919061466b565b979650505050505050565b6121886040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6020840151845111806121a65750670de0b6b3a76400008460800151115b156121fa578351602085015160808601516040517fb301af040000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016109b1565b83516122069084612fa1565b815260208401516122179083612fb7565b602082018190528151111561227657835160208501516040517fa1f3b1b80000000000000000000000000000000000000000000000000000000081526004810192909252602482015260448101849052606481018390526084016109b1565b6040808501519082015260608085015190820152608093840151938101939093525090919050565b6000811580156122b15750604083015115155b156122c157506040820151610de0565b60208301518351116122f157602083015183516002916122e09161449b565b6122ea919061466b565b9050610de0565b6040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600080612343898961233a8a610de6565b8d929190612fc6565b92509250925061235283613133565b955061235d82612f92565b945061236881612f92565b93505050509450945094915050565b6000826123848184613145565b949350505050565b6000611333838361317b565b600082841015801561238457506123c0836123bb84670de0b6b3a764000061449b565b613190565b841115949350505050565b600061242d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166131b09092919063ffffffff16565b80519091501561195a578080602001905181019061244b919061467f565b61195a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016109b1565b6060612384848484604051806060016040528060298152602001614771602991396131bb565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052915160009283929087169161259491906146a1565b6000604051808303816000865af19150503d80600081146125d1576040519150601f19603f3d011682016040523d82523d6000602084013e6125d6565b606091505b5091509150818015612600575080511580612600575080806020019051810190612600919061467f565b612666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5361666520417070726f7665000000000000000000000000000000000000000060448201526064016109b1565b5050505050565b60006113338383613346565b6000600161268683612f92565b610de091906142c6565b6126c96040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b83516126d59084612fa1565b815260208401516126e69083612fb7565b60208201819052815111806127065750670de0b6b3a76400008460800151115b15612276578351602085015160808601516040517fb301af040000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016109b1565b6000806000612769878561229e565b90508660a001518111612781576001925090506127bc565b6000612796878761279185610de6565b61335b565b905060008112156127ad57506000925090506127bc565b5060a087018190526001925090505b94509492505050565b6000806000806000806127dc898961233a8a610dab565b92509250925061235283612f92565b60006113338383613472565b6000828411158015612384575061281a836123bb84670de0b6b3a76400006142c6565b909310159392505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261195a9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401611854565b600061288682610de6565b61289d6128986201518061016d614654565b610de6565b84608001516128ac9190614366565b6128b691906146bd565b905060008113610de0576040517f1ca41876000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b60008060008312612907576001612929565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561293b86613133565b613346565b6123849082614366565b60008061295786846129e4565b9050670de0b6b3a764000081121561299e576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b60006129b46129ad878a61462c565b8990612f6c565b905060006129c18261348a565b90506129cd8187612f6c565b6129d79084614605565b9998505050505050505050565b6000806129f66201518061016d614654565b612a008486614654565b612a0a919061466b565b90506123846120d682610de6565b600080612a258385614366565b9050670de0b6b3a7640000815b05949350505050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc00008212158015612a76575068070c1cc73b00c800008213155b612adc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e740000000000000000000000000000000060448201526064016109b1565b6000821215612b1557612af182600003612a3b565b6ec097ce7bc90715b34b9f100000000081612b0e57612b0e6145ab565b0592915050565b60006806f05b59d3b20000008312612b6b57507ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e00000090910190770195e54c5dd42177f53a27172fa9ec630262827000000000612bb7565b6803782dace9d90000008312612bb357507ffffffffffffffffffffffffffffffffffffffffffffffffc87d2531627000000909101906b1425982cf597cd205cef7380612bb7565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412612c1d577fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e0000009093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412612c6f577fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000009093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412612cbf577fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000009093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412612d0f577fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000009093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412612d5e577ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e000009093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412612dad577ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000009093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412612dfc577ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e7800009093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412612e4b577ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c00009093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b600080612f81670de0b6b3a764000085614366565b9050828181612a3257612a326145ab565b600080821215610e1557600080fd5b6000818311612fb05781611333565b5090919050565b6000818310612fb05781611333565b600080600080612fe98860000151886020015189600001518a60400151896134f6565b90506000612fff612ffa8784612f6c565b6135d2565b6060890151909150600087131561308757600061301c8483612f6c565b9050670de0b6b3a7640000811215613063576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b61307f61307883670de0b6b3a7640000614605565b8490612a18565b9150506130b4565b6130b18161309d81670de0b6b3a7640000614605565b6130a79085614366565b612ffa91906146bd565b90505b600060646130c58c60e00151610de6565b6130cf9084614366565b6130d991906146bd565b905060006130e78385614605565b905060008112613100576130fb8a826135fe565b61310a565b61310a8a82613649565b97506131168a846135fe565b96506131228a836135fe565b955050505050509450945094915050565b6000808213610e1557611bc182614725565b6000670de0b6b3a764000060018161315d8686614654565b613167919061449b565b61317191906142c6565b611333919061466b565b600082613171670de0b6b3a764000084614654565b60008061319d8385614654565b670de0b6b3a76400009004949350505050565b606061238484846000855b60608247101561324d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016109b1565b73ffffffffffffffffffffffffffffffffffffffff85163b6132cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016109b1565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516132f491906146a1565b60006040518083038185875af1925050503d8060008114613331576040519150601f19603f3d011682016040523d82523d6000602084013e613336565b606091505b509150915061214b828286613694565b6000670de0b6b3a76400006131718484614654565b60008082856020015161336e9190614605565b9050600061337c858561462c565b905060008213801561338e5750600081135b6133f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c6964207074546f4d61726b6574000000000000000000000000000060448201526064016109b1565b60006134226134038484614366565b6020890151613412908961462c565b61341c9088614366565b90612f6c565b905060006134386134338486612f6c565b6136e7565b885190915060009061345390670de0b6b3a764000090612f6c565b9050613463816120d08486614605565b89604001516129d79190614605565b60008260018161315d670de0b6b3a764000086614654565b6000670de0b6b3a764000082036134cd576040517fa9c8b14d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006134eb6134e484670de0b6b3a7640000614605565b8490612f6c565b9050611333816136e7565b60008061350387846137a2565b905060006135146120f4888a61462c565b9050670d529ae9e8600000811315613569576040517ffc68d09e00000000000000000000000000000000000000000000000000000000815260048101829052670d529ae9e860000060248201526044016109b1565b60006135748261348a565b9050856135818289612f6c565b61358b919061462c565b9350670de0b6b3a76400008412156115ac576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018590526024016109b1565b6000610de0827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614366565b60008060008312613610576001613632565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561364486613133565b61317b565b6000806000831261365b57600161367d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561368f86613133565b613472565b606083156136a3575081611333565b8251156136b35782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109b1919061475d565b6000808213613752576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e64730000000000000000000000000000000000000060448201526064016109b1565b670c7d713b49da0000821380156137705750670f43fc2c04ee000082125b1561379457670de0b6b3a764000061378783613818565b81612b0e57612b0e6145ab565b610de082613950565b919050565b60008183121561380e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e6567617469766500000000000000000000000000000000000000000000000060448201526064016109b1565b6113338284614605565b670de0b6b3a7640000026000806ec097ce7bc90715b34b9f1000000000808401907fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f0000000008501028161386a5761386a6145ab565b05905060006ec097ce7bc90715b34b9f100000000082800205905081806ec097ce7bc90715b34b9f100000000081840205915060038205016ec097ce7bc90715b34b9f100000000082840205915060058205016ec097ce7bc90715b34b9f100000000082840205915060078205016ec097ce7bc90715b34b9f100000000082840205915060098205016ec097ce7bc90715b34b9f1000000000828402059150600b8205016ec097ce7bc90715b34b9f1000000000828402059150600d8205016ec097ce7bc90715b34b9f1000000000828402059150600f82050160020295945050505050565b6000670de0b6b3a764000082121561399257613989826ec097ce7bc90715b34b9f100000000081613983576139836145ab565b05613950565b60000392915050565b60007e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c000000000000083126139e357770195e54c5dd42177f53a27172fa9ec630262827000000000830592506806f05b59d3b2000000015b73011798004d755d3c8bc8e03204cf44619e0000008312613a1b576b1425982cf597cd205cef7380830592506803782dace9d9000000015b606492830292026e01855144814a7ff805980ff00840008312613a63576e01855144814a7ff805980ff008400068056bc75e2d63100000840205925068ad78ebc5ac62000000015b6b02df0ab5a80a22c61ab5a7008312613a9e576b02df0ab5a80a22c61ab5a70068056bc75e2d6310000084020592506856bc75e2d631000000015b693f1fce3da636ea5cf8508312613ad557693f1fce3da636ea5cf85068056bc75e2d631000008402059250682b5e3af16b18800000015b690127fa27722cc06cc5e28312613b0c57690127fa27722cc06cc5e268056bc75e2d6310000084020592506815af1d78b58c400000015b68280e60114edb805d038312613b415768280e60114edb805d0368056bc75e2d631000008402059250680ad78ebc5ac6200000015b680ebc5fb417461211108312613b6c57680ebc5fb4174612111068056bc75e2d631000009384020592015b6808f00f760a4b2db55d8312613ba1576808f00f760a4b2db55d68056bc75e2d6310000084020592506802b5e3af16b1880000015b6806f5f17757889379378312613bd6576806f5f177578893793768056bc75e2d63100000840205925068015af1d78b58c40000015b6806248f33704b2866038312613c0a576806248f33704b28660368056bc75e2d63100000840205925067ad78ebc5ac620000015b6805c548670b9510e7ac8312613c3e576805c548670b9510e7ac68056bc75e2d6310000084020592506756bc75e2d6310000015b600068056bc75e2d63100000840168056bc75e2d631000008086030281613c6757613c676145ab565b059050600068056bc75e2d63100000828002059050818068056bc75e2d63100000818402059150600382050168056bc75e2d63100000828402059150600582050168056bc75e2d63100000828402059150600782050168056bc75e2d63100000828402059150600982050168056bc75e2d63100000828402059150600b820501600202606485820105979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613d1d57600080fd5b50565b803561379d81613cfb565b60008060008060808587031215613d4157600080fd5b8435613d4c81613cfb565b93506020850135613d5c81613cfb565b93969395505050506040820135916060013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613dc457613dc4613d71565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613e1157613e11613d71565b604052919050565b600060a08284031215613e2b57600080fd5b60405160a0810181811067ffffffffffffffff82111715613e4e57613e4e613d71565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b600060c08284031215613e9b57600080fd5b50919050565b60008060008060006101208688031215613eba57600080fd5b8535613ec581613cfb565b94506020860135613ed581613cfb565b935060408601359250613eeb8760608801613e19565b915061010086013567ffffffffffffffff811115613f0857600080fd5b613f1488828901613e89565b9150509295509295909350565b60008060008060808587031215613f3757600080fd5b8435613f4281613cfb565b93506020850135613f5281613cfb565b925060408501359150606085013567ffffffffffffffff811115613f7557600080fd5b613f8187828801613e89565b91505092959194509250565b60008060008060006101208688031215613fa657600080fd5b8535613fb181613cfb565b94506020860135613fc181613cfb565b93506040860135925060608601359150613fde8760808801613e19565b90509295509295909350565b600080600060608486031215613fff57600080fd5b835161400a81613cfb565b602085015190935061401b81613cfb565b604085015190925061402c81613cfb565b809150509250925092565b60006020828403121561404957600080fd5b813561133381613cfb565b600067ffffffffffffffff82111561406e5761406e613d71565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f8301126140ab57600080fd5b81356140be6140b982614054565b613dca565b8181528460208386010111156140d357600080fd5b816020850160208301376000918101602001919091529392505050565b600060c0823603121561410257600080fd5b60405160c0810167ffffffffffffffff828210818311171561412657614126613d71565b816040528435915061413782613cfb565b818352602085013560208401526040850135915061415482613cfb565b8160408401526060850135915061416a82613cfb565b81606084015261417c60808601613d20565b608084015260a085013591508082111561419557600080fd5b506141a23682860161409a565b60a08301525092915050565b6000602082840312156141c057600080fd5b5051919050565b60005b838110156141e25781810151838201526020016141ca565b50506000910152565b600081518084526142038160208601602086016141c7565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600061426a60608301846141eb565b95945050505050565b6000806040838503121561428657600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610de057610de0614297565b805161379d81613cfb565b600061012082840312156142f757600080fd5b6142ff613da0565b825181526020830151602082015260408301516040820152614323606084016142d9565b60608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561439e5761439e614297565b8181058314821517610de057610de0614297565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126143e757600080fd5b83018035915067ffffffffffffffff82111561440257600080fd5b60200191503681900382131561441757600080fd5b9250929050565b60058110614455577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b60808101614467828761441e565b73ffffffffffffffffffffffffffffffffffffffff9485166020830152929093166040840152606090920191909152919050565b80820180821115610de057610de0614297565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144df576144df614297565b5060010190565b606081016144f4828661441e565b73ffffffffffffffffffffffffffffffffffffffff93909316602082015260400152919050565b60408152600061452e60408301856141eb565b90508260208301529392505050565b60006020828403121561454f57600080fd5b815167ffffffffffffffff81111561456657600080fd5b8201601f8101841361457757600080fd5b80516145856140b982614054565b81815285602083850101111561459a57600080fd5b61426a8260208301602086016141c7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff808416806145f9576145f96145ab565b92169190910492915050565b818103600083128015838313168383128216171561462557614625614297565b5092915050565b808201828112600083128015821682158216171561464c5761464c614297565b505092915050565b8082028115828204841417610de057610de0614297565b60008261467a5761467a6145ab565b500490565b60006020828403121561469157600080fd5b8151801515811461133357600080fd5b600082516146b38184602087016141c7565b9190910192915050565b6000826146cc576146cc6145ab565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561472057614720614297565b500590565b60007f8000000000000000000000000000000000000000000000000000000000000000820361475657614756614297565b5060000390565b60208152600061133360208301846141eb56fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220bd831cbd435faccaf979952295f1d9ce4187335ec8eea1df5f0b1cf3d05933e964736f6c634300081100330000000000000000000000004546ff0fbc6fb29916ca56b4f95744b0b356112c
Deployed Bytecode
0x6080604052600436106100705760003560e01c8063bf1bd4341161004e578063bf1bd434146100e2578063e15cc09814610102578063f953080014610122578063fdd71f431461017b57600080fd5b8063357d654014610075578063a000aaa9146100af578063abbc79d0146100c2575b600080fd5b34801561008157600080fd5b50610095610090366004613d2b565b61019b565b604080519283526020830191909152015b60405180910390f35b6100956100bd366004613ea1565b6102a6565b3480156100ce57600080fd5b506100956100dd366004613f21565b61040f565b3480156100ee57600080fd5b506100956100fd366004613d2b565b61059a565b34801561010e57600080fd5b5061009561011d366004613f8d565b610833565b34801561012e57600080fd5b506101567f0000000000000000000000004546ff0fbc6fb29916ca56b4f95744b0b356112c81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100a6565b34801561018757600080fd5b50610095610196366004613f8d565b610ac4565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156101ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102109190613fea565b925050915061022181338389610bac565b61022f888884848a8a610bda565b909450925073ffffffffffffffffffffffffffffffffffffffff808916908816337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d61027a8a610dab565b61028389610de6565b6040805192835260208301919091520160405180910390a4505094509492505050565b6000806000808773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156102f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061031b9190613fea565b9250509150600061032f8284600189610e19565b905061033f8a8a84848c8c611161565b90955093506103516020870187614037565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fc372308269f6e4dd0b4f322160cf0edef154584a3ad62248d654ae846e3eb6e88d6103c08a610de6565b6103cd8c60200135610dab565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a45050509550959350505050565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190613fea565b925050915061049581338389610bac565b60006104b76104ac846104a7896140f0565b611305565b8985858b6001610bda565b945090506104d18984836104ca8a6140f0565b600061133a565b94506104e06020870187614037565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fc372308269f6e4dd0b4f322160cf0edef154584a3ad62248d654ae846e3eb6e88c61054f8c610dab565b6105588b610de6565b6040805173ffffffffffffffffffffffffffffffffffffffff90941684526020840192909252908201526060015b60405180910390a450505094509492505050565b6000806000808673ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156105eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060f9190613fea565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152929450925060009173ffffffffffffffffffffffffffffffffffffffff851691506370a0823190602401602060405180830381865afa158015610682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a691906141ae565b90508773ffffffffffffffffffffffffffffffffffffffff166329910b1183896106d1338e8c6115b8565b6040518463ffffffff1660e01b81526004016106ef93929190614235565b60408051808303816000875af115801561070d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107319190614273565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290955073ffffffffffffffffffffffffffffffffffffffff851691506370a0823190602401602060405180830381865afa15801561079f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c391906141ae565b6107cd90826142c6565b945073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d6108158b610de6565b61081e8a610dab565b60408051928352602083019190915201610586565b6040517f794052f30000000000000000000000000000000000000000000000000000000081523060048201526000908190819073ffffffffffffffffffffffffffffffffffffffff88169063794052f39060240161012060405180830381865afa1580156108a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c991906142e4565b905060008773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093c9190613fea565b9250505061096c6109628273ffffffffffffffffffffffffffffffffffffffff166115ea565b839089428961165d565b5090945050858411156109ba576040517f570cfb8100000000000000000000000000000000000000000000000000000000815260048101859052602481018790526044015b60405180910390fd5b6109c681338387610bac565b8773ffffffffffffffffffffffffffffffffffffffff16635b709f1782866109ee8d8c6117cb565b6040518463ffffffff1660e01b8152600401610a0c93929190614235565b60408051808303816000875af1158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e9190614273565b93505073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d610a9788610dab565b610aa08c610de6565b6040805192835260208301919091520160405180910390a450509550959350505050565b6000806000808773ffffffffffffffffffffffffffffffffffffffff16632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b399190613fea565b9250509150610b4a8233838a610bac565b610b588989838a8a8a611161565b909450925073ffffffffffffffffffffffffffffffffffffffff808a16908916337f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d610ba388610de6565b610aa08c610dab565b8015610bd457610bd473ffffffffffffffffffffffffffffffffffffffff85168484846117fa565b50505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015260009182918291908816906370a0823190602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7291906141ae565b90508773ffffffffffffffffffffffffffffffffffffffff16635b709f178787610c9c8d896117cb565b6040518463ffffffff1660e01b8152600401610cba93929190614235565b60408051808303816000875af1158015610cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfc9190614273565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c81166004830152919450839250908916906370a0823190602401602060405180830381865afa158015610d6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9391906141ae565b610d9d91906142c6565b925050965096945050505050565b6000610db682610de6565b610de0907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614366565b92915050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115610e1557600080fd5b5090565b6000610e36610e2b6020840184614037565b3384602001356118d6565b6000610e486060840160408501614037565b73ffffffffffffffffffffffffffffffffffffffff16610e6b6020850185614037565b73ffffffffffffffffffffffffffffffffffffffff161415905060008115610f1857610ef9610e9d6020860186614037565b6020860135610eb260a0880160808901614037565b610ebf60a08901896143b2565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061198792505050565b610f11610f0c6060860160408701614037565b611b15565b9050610f1f565b5060208301355b600080610f326060870160408801614037565b73ffffffffffffffffffffffffffffffffffffffff1614610f54576000610f56565b815b90506000610f6a6080870160608801614037565b73ffffffffffffffffffffffffffffffffffffffff161461106857610fad610f986060870160408801614037565b610fa86080880160608901614037565b611bcd565b610fbd6080860160608701614037565b6040517fb276707000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301526024820185905260448201899052919091169063b276707090839060640160206040518083038185885af115801561103c573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061106191906141ae565b9350611156565b61108161107b6060870160408801614037565b88611bcd565b73ffffffffffffffffffffffffffffffffffffffff87166320e8c565828a6110af60608a0160408b01614037565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260448101869052606481018a905260840160206040518083038185885af115801561112e573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061115391906141ae565b93505b505050949350505050565b6040517f794052f30000000000000000000000000000000000000000000000000000000081523060048201526000908190819073ffffffffffffffffffffffffffffffffffffffff89169063794052f39060240161012060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f791906142e4565b905061122561121b8873ffffffffffffffffffffffffffffffffffffffff166115ea565b8290884288611cec565b5092508483101561126c576040517fa59b8c3100000000000000000000000000000000000000000000000000000000815260048101849052602481018690526044016109b1565b8773ffffffffffffffffffffffffffffffffffffffff166329910b1188856112948d8a611e50565b6040518463ffffffff1660e01b81526004016112b293929190614235565b60408051808303816000875af11580156112d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f49190614273565b939a93995092975050505050505050565b606081015160009073ffffffffffffffffffffffffffffffffffffffff1661132d5782611333565b81606001515b9392505050565b600081156113575761135785336113518887611305565b87610bac565b8251604084015173ffffffffffffffffffffffffffffffffffffffff908116911614156000816113875787611389565b305b606086015190915060009073ffffffffffffffffffffffffffffffffffffffff16156114635760608601516040517f83f5b76600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018a90526000604483015260016064830152909116906383f5b766906084016020604051808303816000875af1158015611438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145c91906141ae565b905061151b565b60408681015190517f769f8e5d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018a9052918216604482015260006064820152600160848201529089169063769f8e5d9060a4016020604051808303816000875af11580156114f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151891906141ae565b90505b821561155a5761153986604001518288608001518960a00151611987565b855161154490611b15565b935061155586600001518a86611e68565b61155e565b8093505b85602001518410156115ac5760208601516040517fc5b5576d0000000000000000000000000000000000000000000000000000000081526109b1918691600401918252602082015260400190565b50505095945050505050565b606060008484846040516020016115d29493929190614459565b60405160208183030381529060405290509392505050565b60008173ffffffffffffffffffffffffffffffffffffffff16631d52edc46040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611639573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de091906141ae565b60408051608081018252868152602081018690529081018490526060810183905260009081908190816116918a8a89611f7b565b905060006116b28760006116ad858760000151600001516120ae565b612156565b905060005b816060015181101561178d5760006116cf838361229e565b90506000806116e8876000015187896020015186612323565b5091509150600061170683896020015161237790919063ffffffff16565b9050600061172261171783876142c6565b60208b01519061238c565b90508860400151811061176957602087018590526040890151608088015161174b918391612398565b1561176457939a5092985096506117c095505050505050565b611777565b61177485600161449b565b87525b505050505080611786906144ae565b90506116b7565b506040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b955095509592505050565b6060600283836040516020016117e3939291906144e6565b604051602081830303815290604052905092915050565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610bd49085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526123cb565b73ffffffffffffffffffffffffffffffffffffffff831661195f5780341461195a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f657468206d69736d61746368000000000000000000000000000000000000000060448201526064016109b1565b505050565b801561195a5761195a73ffffffffffffffffffffffffffffffffffffffff84168330846117fa565b73ffffffffffffffffffffffffffffffffffffffff821615806119a957508051155b156119e0576040517f1e59c84300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119ea8483611bcd565b6040517fe4da6f2e00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004546ff0fbc6fb29916ca56b4f95744b0b356112c169063e4da6f2e90611a61908590889060040161451b565b600060405180830381865afa158015611a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611ac4919081019061453d565b9050611b0d8173ffffffffffffffffffffffffffffffffffffffff871615611aed576000611aef565b855b73ffffffffffffffffffffffffffffffffffffffff861691906124d7565b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff821615611bc6576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc191906141ae565b610de0565b4792915050565b73ffffffffffffffffffffffffffffffffffffffff8216611bec575050565b611c0360026bffffffffffffffffffffffff6145da565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301526bffffffffffffffffffffffff929092169184169063dd62ed3e90604401602060405180830381865afa158015611c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cab91906141ae565b1015611ce857611cbd828260006124fd565b611ce882827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6124fd565b5050565b604080516080810182528681526020810186905290810184905260608101839052600090819081611d1e898988611f7b565b90506000611d5386611d418560400151866020015161266d90919063ffffffff16565b611d4e8560200151612679565b612690565b905060005b816060015181101561178d57600080611d7b84868860000151600001518661275a565b9150915081611d8f57602084015250611e36565b600080611da68860000151888a60200151866127c5565b50915091506000611dc4848a602001516127eb90919063ffffffff16565b90506000611dd284836142c6565b905089604001518111611e1e5784885260408a01516080890151600091611dfb918491906127f7565b90508015611e185750939a50909850611e46975050505050505050565b50611e2f565b611e296001866142c6565b60208901525b5050505050505b611e3f816144ae565b9050611d58565b9550959350505050565b6060600183836040516020016117e3939291906144e6565b80600003611e7557505050565b73ffffffffffffffffffffffffffffffffffffffff8316611f5a5760008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114611eea576040519150601f19603f3d011682016040523d82523d6000602084013e611eef565b606091505b5050905080610bd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6574682073656e64206661696c6564000000000000000000000000000000000060448201526064016109b1565b61195a73ffffffffffffffffffffffffffffffffffffffff84168383612825565b611fa66040518060800160405280600081526020016000815260200160008152602001600081525090565b60a08401518210611fe3576040517fb2094b5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828560a00151611ff591906142c6565b9050612001858261287b565b825260208501516120139085906128f5565b60208301528451158061202857506020820151155b1561206f57845160208301516040517fb1c4aefb000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016109b1565b61208d8560000151866101000151846020015185600001518561294a565b604083015260c08501516120a190826129e4565b6060830152509392505050565b6000806120db6120d68560000151866040015187606001516120d09190614605565b90612a18565b612a3b565b905060006120fb6120f4670de0b6b3a76400008461462c565b8390612f6c565b90506000612119866020015186612112919061462c565b8390612a18565b905060006121278287614605565b90506103e861213582612f92565b612141906103e7614654565b61214b919061466b565b979650505050505050565b6121886040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6020840151845111806121a65750670de0b6b3a76400008460800151115b156121fa578351602085015160808601516040517fb301af040000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016109b1565b83516122069084612fa1565b815260208401516122179083612fb7565b602082018190528151111561227657835160208501516040517fa1f3b1b80000000000000000000000000000000000000000000000000000000081526004810192909252602482015260448101849052606481018390526084016109b1565b6040808501519082015260608085015190820152608093840151938101939093525090919050565b6000811580156122b15750604083015115155b156122c157506040820151610de0565b60208301518351116122f157602083015183516002916122e09161449b565b6122ea919061466b565b9050610de0565b6040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600080612343898961233a8a610de6565b8d929190612fc6565b92509250925061235283613133565b955061235d82612f92565b945061236881612f92565b93505050509450945094915050565b6000826123848184613145565b949350505050565b6000611333838361317b565b600082841015801561238457506123c0836123bb84670de0b6b3a764000061449b565b613190565b841115949350505050565b600061242d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166131b09092919063ffffffff16565b80519091501561195a578080602001905181019061244b919061467f565b61195a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016109b1565b6060612384848484604051806060016040528060298152602001614771602991396131bb565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052915160009283929087169161259491906146a1565b6000604051808303816000865af19150503d80600081146125d1576040519150601f19603f3d011682016040523d82523d6000602084013e6125d6565b606091505b5091509150818015612600575080511580612600575080806020019051810190612600919061467f565b612666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5361666520417070726f7665000000000000000000000000000000000000000060448201526064016109b1565b5050505050565b60006113338383613346565b6000600161268683612f92565b610de091906142c6565b6126c96040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b83516126d59084612fa1565b815260208401516126e69083612fb7565b60208201819052815111806127065750670de0b6b3a76400008460800151115b15612276578351602085015160808601516040517fb301af040000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016109b1565b6000806000612769878561229e565b90508660a001518111612781576001925090506127bc565b6000612796878761279185610de6565b61335b565b905060008112156127ad57506000925090506127bc565b5060a087018190526001925090505b94509492505050565b6000806000806000806127dc898961233a8a610dab565b92509250925061235283612f92565b60006113338383613472565b6000828411158015612384575061281a836123bb84670de0b6b3a76400006142c6565b909310159392505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261195a9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401611854565b600061288682610de6565b61289d6128986201518061016d614654565b610de6565b84608001516128ac9190614366565b6128b691906146bd565b905060008113610de0576040517f1ca41876000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b60008060008312612907576001612929565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561293b86613133565b613346565b6123849082614366565b60008061295786846129e4565b9050670de0b6b3a764000081121561299e576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b60006129b46129ad878a61462c565b8990612f6c565b905060006129c18261348a565b90506129cd8187612f6c565b6129d79084614605565b9998505050505050505050565b6000806129f66201518061016d614654565b612a008486614654565b612a0a919061466b565b90506123846120d682610de6565b600080612a258385614366565b9050670de0b6b3a7640000815b05949350505050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc00008212158015612a76575068070c1cc73b00c800008213155b612adc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e740000000000000000000000000000000060448201526064016109b1565b6000821215612b1557612af182600003612a3b565b6ec097ce7bc90715b34b9f100000000081612b0e57612b0e6145ab565b0592915050565b60006806f05b59d3b20000008312612b6b57507ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e00000090910190770195e54c5dd42177f53a27172fa9ec630262827000000000612bb7565b6803782dace9d90000008312612bb357507ffffffffffffffffffffffffffffffffffffffffffffffffc87d2531627000000909101906b1425982cf597cd205cef7380612bb7565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412612c1d577fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e0000009093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412612c6f577fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000009093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412612cbf577fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000009093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412612d0f577fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000009093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412612d5e577ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e000009093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412612dad577ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000009093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412612dfc577ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e7800009093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412612e4b577ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c00009093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b600080612f81670de0b6b3a764000085614366565b9050828181612a3257612a326145ab565b600080821215610e1557600080fd5b6000818311612fb05781611333565b5090919050565b6000818310612fb05781611333565b600080600080612fe98860000151886020015189600001518a60400151896134f6565b90506000612fff612ffa8784612f6c565b6135d2565b6060890151909150600087131561308757600061301c8483612f6c565b9050670de0b6b3a7640000811215613063576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018290526024016109b1565b61307f61307883670de0b6b3a7640000614605565b8490612a18565b9150506130b4565b6130b18161309d81670de0b6b3a7640000614605565b6130a79085614366565b612ffa91906146bd565b90505b600060646130c58c60e00151610de6565b6130cf9084614366565b6130d991906146bd565b905060006130e78385614605565b905060008112613100576130fb8a826135fe565b61310a565b61310a8a82613649565b97506131168a846135fe565b96506131228a836135fe565b955050505050509450945094915050565b6000808213610e1557611bc182614725565b6000670de0b6b3a764000060018161315d8686614654565b613167919061449b565b61317191906142c6565b611333919061466b565b600082613171670de0b6b3a764000084614654565b60008061319d8385614654565b670de0b6b3a76400009004949350505050565b606061238484846000855b60608247101561324d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016109b1565b73ffffffffffffffffffffffffffffffffffffffff85163b6132cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016109b1565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516132f491906146a1565b60006040518083038185875af1925050503d8060008114613331576040519150601f19603f3d011682016040523d82523d6000602084013e613336565b606091505b509150915061214b828286613694565b6000670de0b6b3a76400006131718484614654565b60008082856020015161336e9190614605565b9050600061337c858561462c565b905060008213801561338e5750600081135b6133f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c6964207074546f4d61726b6574000000000000000000000000000060448201526064016109b1565b60006134226134038484614366565b6020890151613412908961462c565b61341c9088614366565b90612f6c565b905060006134386134338486612f6c565b6136e7565b885190915060009061345390670de0b6b3a764000090612f6c565b9050613463816120d08486614605565b89604001516129d79190614605565b60008260018161315d670de0b6b3a764000086614654565b6000670de0b6b3a764000082036134cd576040517fa9c8b14d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006134eb6134e484670de0b6b3a7640000614605565b8490612f6c565b9050611333816136e7565b60008061350387846137a2565b905060006135146120f4888a61462c565b9050670d529ae9e8600000811315613569576040517ffc68d09e00000000000000000000000000000000000000000000000000000000815260048101829052670d529ae9e860000060248201526044016109b1565b60006135748261348a565b9050856135818289612f6c565b61358b919061462c565b9350670de0b6b3a76400008412156115ac576040517fca78c8a4000000000000000000000000000000000000000000000000000000008152600481018590526024016109b1565b6000610de0827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614366565b60008060008312613610576001613632565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561364486613133565b61317b565b6000806000831261365b57600161367d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b90506129406128988561368f86613133565b613472565b606083156136a3575081611333565b8251156136b35782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109b1919061475d565b6000808213613752576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e64730000000000000000000000000000000000000060448201526064016109b1565b670c7d713b49da0000821380156137705750670f43fc2c04ee000082125b1561379457670de0b6b3a764000061378783613818565b81612b0e57612b0e6145ab565b610de082613950565b919050565b60008183121561380e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e6567617469766500000000000000000000000000000000000000000000000060448201526064016109b1565b6113338284614605565b670de0b6b3a7640000026000806ec097ce7bc90715b34b9f1000000000808401907fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f0000000008501028161386a5761386a6145ab565b05905060006ec097ce7bc90715b34b9f100000000082800205905081806ec097ce7bc90715b34b9f100000000081840205915060038205016ec097ce7bc90715b34b9f100000000082840205915060058205016ec097ce7bc90715b34b9f100000000082840205915060078205016ec097ce7bc90715b34b9f100000000082840205915060098205016ec097ce7bc90715b34b9f1000000000828402059150600b8205016ec097ce7bc90715b34b9f1000000000828402059150600d8205016ec097ce7bc90715b34b9f1000000000828402059150600f82050160020295945050505050565b6000670de0b6b3a764000082121561399257613989826ec097ce7bc90715b34b9f100000000081613983576139836145ab565b05613950565b60000392915050565b60007e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c000000000000083126139e357770195e54c5dd42177f53a27172fa9ec630262827000000000830592506806f05b59d3b2000000015b73011798004d755d3c8bc8e03204cf44619e0000008312613a1b576b1425982cf597cd205cef7380830592506803782dace9d9000000015b606492830292026e01855144814a7ff805980ff00840008312613a63576e01855144814a7ff805980ff008400068056bc75e2d63100000840205925068ad78ebc5ac62000000015b6b02df0ab5a80a22c61ab5a7008312613a9e576b02df0ab5a80a22c61ab5a70068056bc75e2d6310000084020592506856bc75e2d631000000015b693f1fce3da636ea5cf8508312613ad557693f1fce3da636ea5cf85068056bc75e2d631000008402059250682b5e3af16b18800000015b690127fa27722cc06cc5e28312613b0c57690127fa27722cc06cc5e268056bc75e2d6310000084020592506815af1d78b58c400000015b68280e60114edb805d038312613b415768280e60114edb805d0368056bc75e2d631000008402059250680ad78ebc5ac6200000015b680ebc5fb417461211108312613b6c57680ebc5fb4174612111068056bc75e2d631000009384020592015b6808f00f760a4b2db55d8312613ba1576808f00f760a4b2db55d68056bc75e2d6310000084020592506802b5e3af16b1880000015b6806f5f17757889379378312613bd6576806f5f177578893793768056bc75e2d63100000840205925068015af1d78b58c40000015b6806248f33704b2866038312613c0a576806248f33704b28660368056bc75e2d63100000840205925067ad78ebc5ac620000015b6805c548670b9510e7ac8312613c3e576805c548670b9510e7ac68056bc75e2d6310000084020592506756bc75e2d6310000015b600068056bc75e2d63100000840168056bc75e2d631000008086030281613c6757613c676145ab565b059050600068056bc75e2d63100000828002059050818068056bc75e2d63100000818402059150600382050168056bc75e2d63100000828402059150600582050168056bc75e2d63100000828402059150600782050168056bc75e2d63100000828402059150600982050168056bc75e2d63100000828402059150600b820501600202606485820105979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613d1d57600080fd5b50565b803561379d81613cfb565b60008060008060808587031215613d4157600080fd5b8435613d4c81613cfb565b93506020850135613d5c81613cfb565b93969395505050506040820135916060013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613dc457613dc4613d71565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613e1157613e11613d71565b604052919050565b600060a08284031215613e2b57600080fd5b60405160a0810181811067ffffffffffffffff82111715613e4e57613e4e613d71565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b600060c08284031215613e9b57600080fd5b50919050565b60008060008060006101208688031215613eba57600080fd5b8535613ec581613cfb565b94506020860135613ed581613cfb565b935060408601359250613eeb8760608801613e19565b915061010086013567ffffffffffffffff811115613f0857600080fd5b613f1488828901613e89565b9150509295509295909350565b60008060008060808587031215613f3757600080fd5b8435613f4281613cfb565b93506020850135613f5281613cfb565b925060408501359150606085013567ffffffffffffffff811115613f7557600080fd5b613f8187828801613e89565b91505092959194509250565b60008060008060006101208688031215613fa657600080fd5b8535613fb181613cfb565b94506020860135613fc181613cfb565b93506040860135925060608601359150613fde8760808801613e19565b90509295509295909350565b600080600060608486031215613fff57600080fd5b835161400a81613cfb565b602085015190935061401b81613cfb565b604085015190925061402c81613cfb565b809150509250925092565b60006020828403121561404957600080fd5b813561133381613cfb565b600067ffffffffffffffff82111561406e5761406e613d71565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f8301126140ab57600080fd5b81356140be6140b982614054565b613dca565b8181528460208386010111156140d357600080fd5b816020850160208301376000918101602001919091529392505050565b600060c0823603121561410257600080fd5b60405160c0810167ffffffffffffffff828210818311171561412657614126613d71565b816040528435915061413782613cfb565b818352602085013560208401526040850135915061415482613cfb565b8160408401526060850135915061416a82613cfb565b81606084015261417c60808601613d20565b608084015260a085013591508082111561419557600080fd5b506141a23682860161409a565b60a08301525092915050565b6000602082840312156141c057600080fd5b5051919050565b60005b838110156141e25781810151838201526020016141ca565b50506000910152565b600081518084526142038160208601602086016141c7565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600061426a60608301846141eb565b95945050505050565b6000806040838503121561428657600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610de057610de0614297565b805161379d81613cfb565b600061012082840312156142f757600080fd5b6142ff613da0565b825181526020830151602082015260408301516040820152614323606084016142d9565b60608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b808202600082127f80000000000000000000000000000000000000000000000000000000000000008414161561439e5761439e614297565b8181058314821517610de057610de0614297565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126143e757600080fd5b83018035915067ffffffffffffffff82111561440257600080fd5b60200191503681900382131561441757600080fd5b9250929050565b60058110614455577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b60808101614467828761441e565b73ffffffffffffffffffffffffffffffffffffffff9485166020830152929093166040840152606090920191909152919050565b80820180821115610de057610de0614297565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144df576144df614297565b5060010190565b606081016144f4828661441e565b73ffffffffffffffffffffffffffffffffffffffff93909316602082015260400152919050565b60408152600061452e60408301856141eb565b90508260208301529392505050565b60006020828403121561454f57600080fd5b815167ffffffffffffffff81111561456657600080fd5b8201601f8101841361457757600080fd5b80516145856140b982614054565b81815285602083850101111561459a57600080fd5b61426a8260208301602086016141c7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff808416806145f9576145f96145ab565b92169190910492915050565b818103600083128015838313168383128216171561462557614625614297565b5092915050565b808201828112600083128015821682158216171561464c5761464c614297565b505092915050565b8082028115828204841417610de057610de0614297565b60008261467a5761467a6145ab565b500490565b60006020828403121561469157600080fd5b8151801515811461133357600080fd5b600082516146b38184602087016141c7565b9190910192915050565b6000826146cc576146cc6145ab565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561472057614720614297565b500590565b60007f8000000000000000000000000000000000000000000000000000000000000000820361475657614756614297565b5060000390565b60208152600061133360208301846141eb56fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220bd831cbd435faccaf979952295f1d9ce4187335ec8eea1df5f0b1cf3d05933e964736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004546ff0fbc6fb29916ca56b4f95744b0b356112c
-----Decoded View---------------
Arg [0] : _kyberScalingLib (address): 0x4546FF0fbc6fb29916Ca56b4F95744B0b356112C
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000004546ff0fbc6fb29916ca56b4f95744b0b356112c
Loading...
Loading
Loading...
Loading

Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.