Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
9333076 | 2 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ActionSimple
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import {IPMarket} from "../interfaces/IPMarket.sol"; import {IStandardizedYield} from "../interfaces/IStandardizedYield.sol"; import {IPPrincipalToken} from "../interfaces/IPPrincipalToken.sol"; import {IPActionSimple} from "../interfaces/IPActionSimple.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {TokenHelper} from "../core/libraries/TokenHelper.sol"; import {PYIndexLib, IPYieldToken, PYIndex} from "../core/StandardizedYield/PYIndex.sol"; import {MarketState} from "../core/Market/MarketMathCore.sol"; import {PMath} from "../core/libraries/math/PMath.sol"; import {CallbackHelper} from "./base/CallbackHelper.sol"; import {MarketApproxPtInLibOnchain, MarketApproxPtOutLibOnchain} from "./math/MarketApproxLibOnchain.sol"; import {TokenInput, TokenOutput} from "../interfaces/IPAllActionTypeV3.sol"; import {ActionBase} from "./base/ActionBase.sol"; contract ActionSimple is ActionBase, IPActionSimple { using MarketApproxPtInLibOnchain for MarketState; using MarketApproxPtOutLibOnchain for MarketState; using PYIndexLib for IPYieldToken; using PYIndexLib for PYIndex; using PMath for uint256; // ------------------ SWAP TOKEN FOR PT ------------------ /// @notice This function is for internal router use only and should not be called directly. /// @dev Use swapExactTokenForPt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function swapExactTokenForPtSimple( address receiver, address market, uint256 minPtOut, TokenInput calldata input ) external payable returns (uint256 netPtOut, uint256 netSyFee, uint256 netSyInterm) { (IStandardizedYield SY, , ) = IPMarket(market).readTokens(); netSyInterm = _mintSyFromToken(_entry_swapExactSyForPt(market, false), address(SY), 1, input); (netPtOut, netSyFee) = _swapExactSyForPtSimple(receiver, market, netSyInterm, minPtOut); emit SwapPtAndToken( msg.sender, market, input.tokenIn, receiver, netPtOut.Int(), input.netTokenIn.neg(), netSyInterm ); } /// @notice This function is for internal router use only and should not be called directly. /// @dev Use swapExactSyForPt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function swapExactSyForPtSimple( address receiver, address market, uint256 exactSyIn, uint256 minPtOut ) external returns (uint256 netPtOut, uint256 netSyFee) { (IStandardizedYield SY, , ) = IPMarket(market).readTokens(); _transferFrom(SY, msg.sender, _entry_swapExactSyForPt(market, false), exactSyIn); (netPtOut, netSyFee) = _swapExactSyForPtSimple(receiver, market, exactSyIn, minPtOut); emit SwapPtAndSy(msg.sender, market, receiver, netPtOut.Int(), exactSyIn.neg()); } /// @notice This function is for internal router use only and should not be called directly. /// @dev Use swapExactTokenForYt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function swapExactTokenForYtSimple( address receiver, address market, uint256 minYtOut, TokenInput calldata input ) external payable returns (uint256 netYtOut, uint256 netSyFee, uint256 netSyInterm) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); netSyInterm = _mintSyFromToken(_entry_swapExactSyForYt(YT, false), address(SY), 1, input); (netYtOut, netSyFee) = _swapExactSyForYtSimple(receiver, market, SY, YT, netSyInterm, minYtOut); emit SwapYtAndToken( msg.sender, market, input.tokenIn, receiver, netYtOut.Int(), input.netTokenIn.neg(), netSyInterm ); } /// @notice This function is for internal router use only and should not be called directly. /// @dev Use swapExactSyForYt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function swapExactSyForYtSimple( address receiver, address market, uint256 exactSyIn, uint256 minYtOut ) external returns (uint256 netYtOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(SY, msg.sender, _entry_swapExactSyForYt(YT, false), exactSyIn); (netYtOut, netSyFee) = _swapExactSyForYtSimple(receiver, market, SY, YT, exactSyIn, minYtOut); emit SwapYtAndSy(msg.sender, market, receiver, netYtOut.Int(), exactSyIn.neg()); } /// @notice This function is for internal router use only and should not be called directly. /// @dev Use addLiquiditySinglePt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function addLiquiditySinglePtSimple( address receiver, address market, uint256 netPtIn, uint256 minLpOut ) external returns (uint256 netLpOut, uint256 netSyFee) { (, IPPrincipalToken PT, IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(PT, msg.sender, _entry_addLiquiditySinglePt(market, false), netPtIn); uint256 netPtLeft = netPtIn; uint256 netSyReceived; (uint256 netPtSwapMarket, , , ) = _readMarket(market).approxSwapPtToAddLiquidityOnchain( YT.newIndex(), netPtLeft, netSyReceived, block.timestamp ); // execute the swap (uint256 netSyOutMarket, uint256 netSyFeeMarket) = IPMarket(market).swapExactPtForSy( market, netPtSwapMarket, EMPTY_BYTES ); netPtLeft -= netPtSwapMarket; netSyReceived += netSyOutMarket; netSyFee += netSyFeeMarket; // execute the addLiquidity (netLpOut, , ) = IPMarket(market).mint(receiver, netSyReceived, netPtLeft); if (netLpOut < minLpOut) revert("Slippage: INSUFFICIENT_LP_OUT"); emit AddLiquiditySinglePt(msg.sender, market, receiver, netPtIn, netLpOut); } // ------------------ ADD LIQUIDITY SINGLE TOKEN ------------------ /// @notice This function is for internal router use only and should not be called directly. /// @dev Use addLiquiditySingleToken from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function addLiquiditySingleTokenSimple( address receiver, address market, uint256 minLpOut, TokenInput calldata input ) external payable returns (uint256 netLpOut, uint256 netSyFee, uint256 netSyInterm) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); netSyInterm = _mintSyFromToken(_entry_addLiquiditySingleSy(market, false), address(SY), 1, input); (netLpOut, netSyFee) = _addLiquiditySingleSySimple(receiver, market, SY, YT, netSyInterm, minLpOut); emit AddLiquiditySingleToken( msg.sender, market, input.tokenIn, receiver, input.netTokenIn, netLpOut, netSyInterm ); } /// @notice This function is for internal router use only and should not be called directly. /// @dev Use addLiquiditySingleSy from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function addLiquiditySingleSySimple( address receiver, address market, uint256 netSyIn, uint256 minLpOut ) external returns (uint256 netLpOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); _transferFrom(SY, msg.sender, _entry_addLiquiditySingleSy(market, false), netSyIn); (netLpOut, netSyFee) = _addLiquiditySingleSySimple(receiver, market, SY, YT, netSyIn, minLpOut); emit AddLiquiditySingleSy(msg.sender, market, receiver, netSyIn, netLpOut); } function _addLiquiditySingleSySimple( address receiver, address market, IStandardizedYield /*SY*/, IPYieldToken YT, uint256 netSyIn, uint256 minLpOut ) internal returns (uint256 netLpOut, uint256 netSyFee) { uint256 netSyLeft = netSyIn; uint256 netPtReceived; (uint256 netPtOutMarket, , , ) = _readMarket(market).approxSwapSyToAddLiquidityOnchain( YT.newIndex(), netSyLeft, netPtReceived, block.timestamp ); (uint256 netSySwapMarket, uint256 netSyFeeMarket) = IPMarket(market).swapSyForExactPt( market, netPtOutMarket, EMPTY_BYTES ); netSyLeft -= netSySwapMarket; netPtReceived += netPtOutMarket; netSyFee += netSyFeeMarket; (netLpOut, , ) = IPMarket(market).mint(receiver, netSyLeft, netPtReceived); if (netLpOut < minLpOut) revert("Slippage: INSUFFICIENT_LP_OUT"); } // ------------------ REMOVE LIQUIDITY SINGLE PT ------------------ /// @notice This function is for internal router use only and should not be called directly. /// @dev Use removeLiquiditySinglePt from the main router instead. /// @dev The interface of this simple function is subject to change without notice. function removeLiquiditySinglePtSimple( address receiver, address market, uint256 netLpToRemove, uint256 minPtOut ) external returns (uint256 netPtOut, uint256 netSyFee) { uint256 netSyLeft; // execute the burn _transferFrom(IERC20(market), msg.sender, market, netLpToRemove); (uint256 netSyOutBurn, uint256 netPtOutBurn) = IPMarket(market).burn( _entry_swapExactSyForPt(market, false), receiver, netLpToRemove ); netSyLeft += netSyOutBurn; netPtOut += netPtOutBurn; (uint256 netPtOutSwap, uint256 netSyFeeSwap) = _swapExactSyForPtSimple(receiver, market, netSyLeft, 0); netPtOut += netPtOutSwap; netSyFee += netSyFeeSwap; if (netPtOut < minPtOut) revert("Slippage: INSUFFICIENT_PT_OUT"); emit RemoveLiquiditySinglePt(msg.sender, market, receiver, netLpToRemove, netPtOut); } function _swapExactSyForPtSimple( address receiver, address market, uint256 exactSyIn, uint256 minPtOut ) internal returns (uint256 netPtOut, uint256 netSyFee) { (, , IPYieldToken YT) = IPMarket(market).readTokens(); uint256 netSyLeft = exactSyIn; (uint256 netPtOutMarket, , ) = _readMarket(market).approxSwapExactSyForPtOnchain( YT.newIndex(), netSyLeft, block.timestamp ); (, uint256 netSyFeeMarket) = IPMarket(market).swapSyForExactPt(receiver, netPtOutMarket, ""); netPtOut += netPtOutMarket; netSyFee += netSyFeeMarket; if (netPtOut < minPtOut) revert("Slippage: INSUFFICIENT_PT_OUT"); } function _swapExactSyForYtSimple( address receiver, address market, IStandardizedYield /* SY */, IPYieldToken YT, uint256 exactSyIn, uint256 minYtOut ) internal returns (uint256 netYtOut, uint256 netSyFee) { uint256 netSyLeft = exactSyIn; (uint256 netYtOutMarket, , ) = _readMarket(market).approxSwapExactSyForYtOnchain( YT.newIndex(), netSyLeft, block.timestamp ); (, uint256 netSyFeeMarket) = IPMarket(market).swapExactPtForSy( address(YT), netYtOutMarket, // exactPtIn = netYtOut _encodeSwapExactSyForYt(receiver, YT) ); netYtOut += netYtOutMarket; netSyFee += netSyFeeMarket; if (netYtOut < minYtOut) revert("Slippage: INSUFFICIENT_YT_OUT"); } }
// 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.9.0) (token/ERC20/extensions/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 (last updated v4.9.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.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/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; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ 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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ 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"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ 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"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or 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 { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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.0; 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 MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot); error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent); error MarketFactoryZeroTreasury(); error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor); error MFNotPendleMarket(address addr); // 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 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 RouterInvalidFacet(address facet); error RouterKyberSwapDataZero(); error SimulationResults(bool success, bytes res); // 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(); error SYApeDepositAmountTooSmall(uint256 amountDeposited); error SYBalancerInvalidPid(); error SYInvalidRewardToken(address token); error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable); error SYBalancerReentrancy(); error NotFromTrustedRemote(uint16 srcChainId, bytes path); error ApxETHNotEnoughBuffer(); // 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 FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount); error FDEpochLengthMismatch(); error FDInvalidPool(address pool); error FDPoolAlreadyExists(address pool); error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch); error FDInvalidStartEpoch(uint256 startEpoch); error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime); error FDFutureFunding(uint256 lastFunded, uint256 currentWTime); error BDInvalidEpoch(uint256 epoch, uint256 startTime); // 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 FailedToSendEther(); error InvalidMerkleProof(); error OnlyLayerZeroEndpoint(); error OnlyYT(); error OnlyYCFactory(); error OnlyWhitelisted(); // Swap Aggregator error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual); error UnsupportedSelector(uint256 aggregatorType, bytes4 selector); }
// 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.0; /* 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.0; /* solhint-disable private-vars-leading-underscore, reason-string */ library PMath { 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; } function rawDivUp(int256 a, int256 b) internal pure returns (int256) { return (a + b - 1) / b; } function tweakUp(uint256 a, uint256 factor) internal pure returns (uint256) { return mulDown(a, ONE + factor); } function tweakDown(uint256 a, uint256 factor) internal pure returns (uint256) { return mulDown(a, ONE - factor); } /// @return res = min(a + b, bound) /// @dev This function should handle arithmetic operation and bound check without overflow/underflow function addWithUpperBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) { unchecked { if (type(uint256).max - b < a) res = bound; else res = min(bound, a + b); } } /// @return res = max(a - b, bound) /// @dev This function should handle arithmetic operation and bound check without overflow/underflow function subWithLowerBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) { unchecked { if (b > a) res = bound; else res = max(a - b, bound); } } function clamp(uint256 x, uint256 lower, uint256 upper) internal pure returns (uint256 res) { res = x; if (x < lower) res = lower; else if (x > upper) res = upper; } // @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 square(uint256 x) internal pure returns (uint256) { return x * x; } function squareDown(uint256 x) internal pure returns (uint256) { return mulDown(x, x); } 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 Uint64(uint256 x) internal pure returns (uint64) { require(x <= type(uint64).max); return uint64(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 Uint192(uint256 x) internal pure returns (uint192) { require(x <= type(uint192).max); return uint192(x); } function Uint80(uint256 x) internal pure returns (uint80) { require(x <= type(uint80).max); return uint80(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.0; 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.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../../interfaces/IWETH.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); } } function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal { if (tokenIn == NATIVE) IWETH(tokenOut).deposit{value: netTokenIn}(); else IWETH(tokenIn).withdraw(netTokenIn); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../libraries/math/PMath.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 PMath for uint256; using PMath 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 PMath for uint256; using PMath 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 = PMath.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).rawDivUp(market.totalLp); } else { lpToAccount = netLpBySy; syUsed = syDesired; ptUsed = (market.totalPt * lpToAccount).rawDivUp(market.totalLp); } } if (lpToAccount <= 0 || syUsed <= 0 || ptUsed <= 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 < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate); fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee); } else { fee = ((preFeeAssetToAccount * (PMath.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 < PMath.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 < PMath.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 < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate); } function _logProportion(int256 proportion) internal pure returns (int256 res) { if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne(); int256 logitP = proportion.divDown(PMath.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.0; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPPrincipalToken.sol"; import "./SYUtils.sol"; import "../libraries/math/PMath.sol"; type PYIndex is uint256; library PYIndexLib { using PMath for uint256; using PMath 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.0; 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.0; import {TokenInput} from "./IPAllActionTypeV3.sol"; import {IPAllEventsV3} from "./IPAllEventsV3.sol"; import "./IPAllActionTypeV3.sol"; /// All of these functions are for internal router use only and should not be called directly. interface IPActionSimple is IPAllEventsV3 { function addLiquiditySinglePtSimple( address receiver, address market, uint256 netPtIn, uint256 minLpOut ) external returns (uint256 netLpOut, uint256 netSyFee); function addLiquiditySingleTokenSimple( address receiver, address market, uint256 minLpOut, TokenInput calldata input ) external payable returns (uint256 netLpOut, uint256 netSyFee, uint256 netSyInterm); function addLiquiditySingleSySimple( address receiver, address market, uint256 netSyIn, uint256 minLpOut ) external returns (uint256 netLpOut, uint256 netSyFee); function removeLiquiditySinglePtSimple( address receiver, address market, uint256 netLpToRemove, uint256 minPtOut ) external returns (uint256 netPtOut, uint256 netSyFee); function swapExactTokenForPtSimple( address receiver, address market, uint256 minPtOut, TokenInput calldata input ) external payable returns (uint256 netPtOut, uint256 netSyFee, uint256 netSyInterm); function swapExactSyForPtSimple( address receiver, address market, uint256 exactSyIn, uint256 minPtOut ) external returns (uint256 netPtOut, uint256 netSyFee); function swapExactTokenForYtSimple( address receiver, address market, uint256 minYtOut, TokenInput calldata input ) external payable returns (uint256 netYtOut, uint256 netSyFee, uint256 netSyInterm); function swapExactSyForYtSimple( address receiver, address market, uint256 exactSyIn, uint256 minYtOut ) external returns (uint256 netYtOut, uint256 netSyFee); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../router/swap-aggregator/IPSwapAggregator.sol"; import "./IPLimitRouter.sol"; /* * NOTICE: * For detailed information on TokenInput, TokenOutput, ApproxParams, and LimitOrderData, * refer to https://docs.pendle.finance/Developers/Contracts/PendleRouter * * It's recommended to use Pendle's Hosted SDK to generate these parameters for: * 1. Optimal liquidity and gas efficiency * 2. Access to deeper liquidity via limit orders * 3. Zapping in/out using any ERC20 token * * Else, to generate these parameters fully onchain, use the following functions: * - For TokenInput: Use createTokenInputSimple * - For TokenOutput: Use createTokenOutputSimple * - For ApproxParams: Use createDefaultApproxParams * - For LimitOrderData: Use createEmptyLimitOrderData * * These generated parameters can be directly passed into the respective function calls. * * Examples: * * addLiquiditySingleToken( * msg.sender, * MARKET_ADDRESS, * minLpOut, * createDefaultApproxParams(), * createTokenInputSimple(USDC_ADDRESS, 1000e6), * createEmptyLimitOrderData() * ) * * swapExactTokenForPt( * msg.sender, * MARKET_ADDRESS, * minPtOut, * createDefaultApproxParams(), * createTokenInputSimple(USDC_ADDRESS, 1000e6), * createEmptyLimitOrderData() * ) */ /// @dev Creates a TokenInput struct without using any swap aggregator /// @param tokenIn must be one of the SY's tokens in (obtain via `IStandardizedYield#getTokensIn`) /// @param netTokenIn amount of token in function createTokenInputSimple(address tokenIn, uint256 netTokenIn) pure returns (TokenInput memory) { return TokenInput({ tokenIn: tokenIn, netTokenIn: netTokenIn, tokenMintSy: tokenIn, pendleSwap: address(0), swapData: createSwapTypeNoAggregator() }); } /// @dev Creates a TokenOutput struct without using any swap aggregator /// @param tokenOut must be one of the SY's tokens out (obtain via `IStandardizedYield#getTokensOut`) /// @param minTokenOut minimum amount of token out function createTokenOutputSimple(address tokenOut, uint256 minTokenOut) pure returns (TokenOutput memory) { return TokenOutput({ tokenOut: tokenOut, minTokenOut: minTokenOut, tokenRedeemSy: tokenOut, pendleSwap: address(0), swapData: createSwapTypeNoAggregator() }); } function createEmptyLimitOrderData() pure returns (LimitOrderData memory) {} /// @dev Creates default ApproxParams for on-chain approximation function createDefaultApproxParams() pure returns (ApproxParams memory) { return ApproxParams({guessMin: 0, guessMax: type(uint256).max, guessOffchain: 0, maxIteration: 256, eps: 1e14}); } function createSwapTypeNoAggregator() pure returns (SwapData memory) {} struct TokenInput { address tokenIn; uint256 netTokenIn; address tokenMintSy; address pendleSwap; SwapData swapData; } struct TokenOutput { address tokenOut; uint256 minTokenOut; address tokenRedeemSy; address pendleSwap; SwapData swapData; } struct LimitOrderData { address limitRouter; uint256 epsSkipMarket; FillOrderParams[] normalFills; FillOrderParams[] flashFills; bytes optData; } struct ApproxParams { uint256 guessMin; uint256 guessMax; uint256 guessOffchain; uint256 maxIteration; uint256 eps; } struct ExitPreExpReturnParams { uint256 netPtFromRemove; uint256 netSyFromRemove; uint256 netPyRedeem; uint256 netSyFromRedeem; uint256 netPtSwap; uint256 netYtSwap; uint256 netSyFromSwap; uint256 netSyFee; uint256 totalSyOut; } struct ExitPostExpReturnParams { uint256 netPtFromRemove; uint256 netSyFromRemove; uint256 netPtRedeem; uint256 netSyFromRedeem; uint256 totalSyOut; } struct RedeemYtIncomeToTokenStruct { IPYieldToken yt; bool doRedeemInterest; bool doRedeemRewards; address tokenRedeemSy; uint256 minTokenRedeemOut; }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import {ExitPreExpReturnParams, ExitPostExpReturnParams} from "./IPAllActionTypeV3.sol"; interface IPActionAddRemoveLiqV3Events { event AddLiquidityDualSyAndPt( address indexed caller, address indexed market, address indexed receiver, uint256 netSyUsed, uint256 netPtUsed, uint256 netLpOut ); event AddLiquidityDualTokenAndPt( address indexed caller, address indexed market, address indexed tokenIn, address receiver, uint256 netTokenUsed, uint256 netPtUsed, uint256 netLpOut, uint256 netSyInterm ); event AddLiquiditySinglePt( address indexed caller, address indexed market, address indexed receiver, uint256 netPtIn, uint256 netLpOut ); event AddLiquiditySingleSy( address indexed caller, address indexed market, address indexed receiver, uint256 netSyIn, uint256 netLpOut ); event AddLiquiditySingleToken( address indexed caller, address indexed market, address indexed token, address receiver, uint256 netTokenIn, uint256 netLpOut, uint256 netSyInterm ); event AddLiquiditySingleSyKeepYt( address indexed caller, address indexed market, address indexed receiver, uint256 netSyIn, uint256 netSyMintPy, uint256 netLpOut, uint256 netYtOut ); event AddLiquiditySingleTokenKeepYt( address indexed caller, address indexed market, address indexed token, address receiver, uint256 netTokenIn, uint256 netLpOut, uint256 netYtOut, uint256 netSyMintPy, uint256 netSyInterm ); event RemoveLiquidityDualSyAndPt( address indexed caller, address indexed market, address indexed receiver, uint256 netLpToRemove, uint256 netPtOut, uint256 netSyOut ); event RemoveLiquidityDualTokenAndPt( address indexed caller, address indexed market, address indexed tokenOut, address receiver, uint256 netLpToRemove, uint256 netPtOut, uint256 netTokenOut, uint256 netSyInterm ); event RemoveLiquiditySinglePt( address indexed caller, address indexed market, address indexed receiver, uint256 netLpToRemove, uint256 netPtOut ); event RemoveLiquiditySingleSy( address indexed caller, address indexed market, address indexed receiver, uint256 netLpToRemove, uint256 netSyOut ); event RemoveLiquiditySingleToken( address indexed caller, address indexed market, address indexed token, address receiver, uint256 netLpToRemove, uint256 netTokenOut, uint256 netSyInterm ); } interface IPActionSwapPTV3Events { event SwapPtAndSy( address indexed caller, address indexed market, address indexed receiver, int256 netPtToAccount, int256 netSyToAccount ); event SwapPtAndToken( address indexed caller, address indexed market, address indexed token, address receiver, int256 netPtToAccount, int256 netTokenToAccount, uint256 netSyInterm ); } interface IPActionSwapYTV3Events { 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, uint256 netSyInterm ); } interface IPActionMiscV3Events { event MintSyFromToken( address indexed caller, address indexed tokenIn, address indexed SY, address receiver, uint256 netTokenIn, uint256 netSyOut ); event RedeemSyToToken( address indexed caller, address indexed tokenOut, address indexed SY, address receiver, uint256 netSyIn, uint256 netTokenOut ); event MintPyFromSy( address indexed caller, address indexed receiver, address indexed YT, uint256 netSyIn, uint256 netPyOut ); event RedeemPyToSy( address indexed caller, address indexed receiver, address indexed YT, uint256 netPyIn, uint256 netSyOut ); event MintPyFromToken( address indexed caller, address indexed tokenIn, address indexed YT, address receiver, uint256 netTokenIn, uint256 netPyOut, uint256 netSyInterm ); event RedeemPyToToken( address indexed caller, address indexed tokenOut, address indexed YT, address receiver, uint256 netPyIn, uint256 netTokenOut, uint256 netSyInterm ); event ExitPreExpToToken( address indexed caller, address indexed market, address indexed token, address receiver, uint256 netLpIn, uint256 totalTokenOut, ExitPreExpReturnParams params ); event ExitPreExpToSy( address indexed caller, address indexed market, address indexed receiver, uint256 netLpIn, ExitPreExpReturnParams params ); event ExitPostExpToToken( address indexed caller, address indexed market, address indexed token, address receiver, uint256 netLpIn, uint256 totalTokenOut, ExitPostExpReturnParams params ); event ExitPostExpToSy( address indexed caller, address indexed market, address indexed receiver, uint256 netLpIn, ExitPostExpReturnParams params ); } interface IPActionStorageEvents { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event SelectorToFacetSet(bytes4 indexed selector, address indexed facet); } interface IPAllEventsV3 is IPActionAddRemoveLiqV3Events, IPActionSwapPTV3Events, IPActionSwapYTV3Events, IPActionMiscV3Events, IPActionStorageEvents {}
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; interface IPGauge { function totalActiveSupply() external view returns (uint256); function activeBalance(address user) external view returns (uint256); // only available for newer factories. please check the verified contracts event RedeemRewards(address indexed user, uint256[] rewardsOut); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; interface IPInterestManagerYT { event CollectInterestFee(uint256 amountInterestFee); function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../core/StandardizedYield/PYIndex.sol"; interface IPLimitOrderType { enum OrderType { SY_FOR_PT, PT_FOR_SY, SY_FOR_YT, YT_FOR_SY } // Fixed-size order part with core information struct StaticOrder { uint256 salt; uint256 expiry; uint256 nonce; OrderType orderType; address token; address YT; address maker; address receiver; uint256 makingAmount; uint256 lnImpliedRate; uint256 failSafeRate; } struct FillResults { uint256 totalMaking; uint256 totalTaking; uint256 totalFee; uint256 totalNotionalVolume; uint256[] netMakings; uint256[] netTakings; uint256[] netFees; uint256[] notionalVolumes; } } struct Order { uint256 salt; uint256 expiry; uint256 nonce; IPLimitOrderType.OrderType orderType; address token; address YT; address maker; address receiver; uint256 makingAmount; uint256 lnImpliedRate; uint256 failSafeRate; bytes permit; } struct FillOrderParams { Order order; bytes signature; uint256 makingAmount; } interface IPLimitRouterCallback is IPLimitOrderType { function limitRouterCallback( uint256 actualMaking, uint256 actualTaking, uint256 totalFee, bytes memory data ) external returns (bytes memory); } interface IPLimitRouter is IPLimitOrderType { struct OrderStatus { uint128 filledAmount; uint128 remaining; } event OrderCanceled(address indexed maker, bytes32 indexed orderHash); event OrderFilledV2( bytes32 indexed orderHash, OrderType indexed orderType, address indexed YT, address token, uint256 netInputFromMaker, uint256 netOutputToMaker, uint256 feeAmount, uint256 notionalVolume, address maker, address taker ); // event added on 2/1/2025 event LnFeeRateRootsSet(address[] YTs, uint256[] lnFeeRateRoots); // @dev actualMaking, actualTaking are in the SY form function fill( FillOrderParams[] memory params, address receiver, uint256 maxTaking, bytes calldata optData, bytes calldata callback ) external returns (uint256 actualMaking, uint256 actualTaking, uint256 totalFee, bytes memory callbackReturn); function feeRecipient() external view returns (address); function hashOrder(Order memory order) external view returns (bytes32); function cancelSingle(Order calldata order) external; function cancelBatch(Order[] calldata orders) external; function orderStatusesRaw( bytes32[] memory orderHashes ) external view returns (uint256[] memory remainingsRaw, uint256[] memory filledAmounts); function orderStatuses( bytes32[] memory orderHashes ) external view returns (uint256[] memory remainings, uint256[] memory filledAmounts); function DOMAIN_SEPARATOR() external view returns (bytes32); function simulate(address target, bytes calldata data) external payable; function WNATIVE() external view returns (address); function _checkSig( Order memory order, bytes memory signature ) external view returns ( bytes32, /*orderHash*/ uint256, /*remainingMakerAmount*/ uint256 ); /*filledMakerAmount*/ /* --- Deprecated events --- */ // deprecate on 7/1/2024, prior to official launch event OrderFilled( bytes32 indexed orderHash, OrderType indexed orderType, address indexed YT, address token, uint256 netInputFromMaker, uint256 netOutputToMaker, uint256 feeAmount, uint256 notionalVolume ); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; 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); function observations( uint256 index ) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized); function _storage() external view returns ( int128 totalPt, int128 totalSy, uint96 lastLnImpliedRate, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext ); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; 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.0; 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 CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee); 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); function pyIndexLastUpdatedBlock() external view returns (uint128); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; 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.0; 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: * - (`tokenIn`) 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, 2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain) * @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 /* * 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.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWETH is IERC20 { event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); function deposit() external payable; function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../../core/libraries/TokenHelper.sol"; import "../../interfaces/IStandardizedYield.sol"; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPAllActionTypeV3.sol"; import "../../interfaces/IPMarket.sol"; import "../../router/math/MarketApproxLibV2.sol"; import "../../core/libraries/Errors.sol"; import "../swap-aggregator/IPSwapAggregator.sol"; import "./CallbackHelper.sol"; abstract contract ActionBase is TokenHelper, CallbackHelper, IPLimitOrderType { using MarketApproxPtInLibV2 for MarketState; using MarketApproxPtOutLibV2 for MarketState; using PMath for uint256; using PYIndexLib for IPYieldToken; using PYIndexLib for PYIndex; bytes internal constant EMPTY_BYTES = abi.encode(); // ----------------- MINT REDEEM SY PY ----------------- function _mintSyFromToken( address receiver, address SY, uint256 minSyOut, TokenInput calldata inp ) internal returns (uint256 netSyOut) { SwapType swapType = inp.swapData.swapType; uint256 netTokenMintSy; if (swapType == SwapType.NONE) { _transferIn(inp.tokenIn, msg.sender, inp.netTokenIn); netTokenMintSy = inp.netTokenIn; } else if (swapType == SwapType.ETH_WETH) { _transferIn(inp.tokenIn, msg.sender, inp.netTokenIn); _wrap_unwrap_ETH(inp.tokenIn, inp.tokenMintSy, inp.netTokenIn); netTokenMintSy = inp.netTokenIn; } else { _swapTokenInput(inp); netTokenMintSy = _selfBalance(inp.tokenMintSy); } netSyOut = __mintSy(receiver, SY, netTokenMintSy, minSyOut, inp); } function _swapTokenInput(TokenInput calldata inp) internal { if (inp.tokenIn == NATIVE) _transferIn(NATIVE, msg.sender, inp.netTokenIn); else _transferFrom(IERC20(inp.tokenIn), msg.sender, inp.pendleSwap, inp.netTokenIn); IPSwapAggregator(inp.pendleSwap).swap{value: inp.tokenIn == NATIVE ? inp.netTokenIn : 0}( inp.tokenIn, inp.netTokenIn, inp.swapData ); } function __mintSy( address receiver, address SY, uint256 netTokenMintSy, uint256 minSyOut, TokenInput calldata inp ) private returns (uint256 netSyOut) { uint256 netNative = inp.tokenMintSy == NATIVE ? netTokenMintSy : 0; _safeApproveInf(inp.tokenMintSy, SY); netSyOut = IStandardizedYield(SY).deposit{value: netNative}( receiver, inp.tokenMintSy, netTokenMintSy, minSyOut ); } function _redeemSyToToken( address receiver, address SY, uint256 netSyIn, TokenOutput calldata out, bool doPull ) internal returns (uint256 netTokenOut) { SwapType swapType = out.swapData.swapType; if (swapType == SwapType.NONE) { netTokenOut = __redeemSy(receiver, SY, netSyIn, out, doPull); } else if (swapType == SwapType.ETH_WETH) { netTokenOut = __redeemSy(address(this), SY, netSyIn, out, doPull); // ETH:WETH is 1:1 _wrap_unwrap_ETH(out.tokenRedeemSy, out.tokenOut, netTokenOut); _transferOut(out.tokenOut, receiver, netTokenOut); } else { uint256 netTokenRedeemed = __redeemSy(out.pendleSwap, SY, netSyIn, out, doPull); IPSwapAggregator(out.pendleSwap).swap(out.tokenRedeemSy, netTokenRedeemed, out.swapData); netTokenOut = _selfBalance(out.tokenOut); _transferOut(out.tokenOut, receiver, netTokenOut); } if (netTokenOut < out.minTokenOut) revert("Slippage: INSUFFICIENT_TOKEN_OUT"); } function __redeemSy( address receiver, address SY, uint256 netSyIn, TokenOutput calldata out, bool doPull ) private returns (uint256 netTokenRedeemed) { if (doPull) { _transferFrom(IERC20(SY), msg.sender, SY, netSyIn); } netTokenRedeemed = IStandardizedYield(SY).redeem(receiver, netSyIn, out.tokenRedeemSy, 0, true); } function _mintPyFromSy( address receiver, address SY, address YT, uint256 netSyIn, uint256 minPyOut, bool doPull ) internal returns (uint256 netPyOut) { if (doPull) { _transferFrom(IERC20(SY), msg.sender, YT, netSyIn); } netPyOut = IPYieldToken(YT).mintPY(receiver, receiver); if (netPyOut < minPyOut) revert("Slippage: INSUFFICIENT_PT_YT_OUT"); } 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("Slippage: INSUFFICIENT_SY_OUT"); } // ----------------- HELPER ----------------- function _readMarket(address market) internal view returns (MarketState memory) { return IPMarket(market).readState(address(this)); } // ----------------- PT SWAP ----------------- function _entry_swapExactPtForSy(address market, LimitOrderData calldata limit) internal view returns (address) { return _entry_swapExactPtForSy(market, !_isEmptyLimit(limit)); } function _entry_swapExactPtForSy(address market, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : market; } function _swapExactPtForSy( address receiver, address market, uint256 exactPtIn, uint256 minSyOut, LimitOrderData calldata limit ) internal returns (uint256 netSyOut, uint256 netSyFee) { (, IPPrincipalToken PT, ) = IPMarket(market).readTokens(); uint256 netPtLeft = exactPtIn; bool doMarketOrder = true; if (!_isEmptyLimit(limit)) { (netPtLeft, netSyOut, netSyFee, doMarketOrder) = _fillLimit(receiver, PT, netPtLeft, limit); if (doMarketOrder) { _transferOut(address(PT), market, netPtLeft); } else { _transferOut(address(PT), receiver, netPtLeft); } } if (doMarketOrder) { (uint256 netSyOutMarket, uint256 netSyFeeMarket) = IPMarket(market).swapExactPtForSy( receiver, netPtLeft, EMPTY_BYTES ); netSyOut += netSyOutMarket; netSyFee += netSyFeeMarket; } if (netSyOut < minSyOut) revert("Slippage: INSUFFICIENT_SY_OUT"); } function _entry_swapExactSyForPt(address market, LimitOrderData calldata limit) internal view returns (address) { return _entry_swapExactSyForPt(market, !_isEmptyLimit(limit)); } function _entry_swapExactSyForPt(address market, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : market; } function _swapExactSyForPt( address receiver, address market, uint256 exactSyIn, uint256 minPtOut, ApproxParams calldata guessPtOut, LimitOrderData calldata limit ) internal returns (uint256 netPtOut, uint256 netSyFee) { (IStandardizedYield SY, , IPYieldToken YT) = IPMarket(market).readTokens(); uint256 netSyLeft = exactSyIn; bool doMarketOrder = true; if (!_isEmptyLimit(limit)) { (netSyLeft, netPtOut, netSyFee, doMarketOrder) = _fillLimit(receiver, SY, netSyLeft, limit); if (doMarketOrder) { _transferOut(address(SY), market, netSyLeft); } else { _transferOut(address(SY), receiver, netSyLeft); } } if (doMarketOrder) { (uint256 netPtOutMarket, ) = _readMarket(market).approxSwapExactSyForPtV2( YT.newIndex(), netSyLeft, block.timestamp, guessPtOut ); (, uint256 netSyFeeMarket) = IPMarket(market).swapSyForExactPt(receiver, netPtOutMarket, EMPTY_BYTES); netPtOut += netPtOutMarket; netSyFee += netSyFeeMarket; } if (netPtOut < minPtOut) revert("Slippage: INSUFFICIENT_PT_OUT"); } // ----------------- YT SWAP ----------------- function _entry_swapExactYtForSy(IPYieldToken YT, LimitOrderData calldata limit) internal view returns (address) { return _entry_swapExactYtForSy(YT, !_isEmptyLimit(limit)); } function _entry_swapExactYtForSy(IPYieldToken YT, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : address(YT); } function _swapExactYtForSy( address receiver, address market, IStandardizedYield SY, IPYieldToken YT, uint256 exactYtIn, uint256 minSyOut, LimitOrderData calldata limit ) internal returns (uint256 netSyOut, uint256 netSyFee) { uint256 netYtLeft = exactYtIn; bool doMarketOrder = true; if (!_isEmptyLimit(limit)) { (netYtLeft, netSyOut, netSyFee, doMarketOrder) = _fillLimit(receiver, YT, netYtLeft, limit); if (doMarketOrder) { _transferOut(address(YT), address(YT), netYtLeft); } else { _transferOut(address(YT), receiver, netYtLeft); } } if (doMarketOrder) { uint256 preSyBalance = SY.balanceOf(receiver); (, uint256 netSyFeeMarket) = IPMarket(market).swapSyForExactPt( address(YT), netYtLeft, // exactPtOut = netYtLeft _encodeSwapYtForSy(receiver, YT) ); // avoid stack issue netSyFee += netSyFeeMarket; netSyOut += SY.balanceOf(receiver) - preSyBalance; } if (netSyOut < minSyOut) revert("Slippage: INSUFFICIENT_SY_OUT"); } function _entry_swapExactSyForYt(IPYieldToken YT, LimitOrderData calldata limit) internal view returns (address) { return _entry_swapExactSyForYt(YT, !_isEmptyLimit(limit)); } function _entry_swapExactSyForYt(IPYieldToken YT, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : address(YT); } function _swapExactSyForYt( address receiver, address market, IStandardizedYield SY, IPYieldToken YT, uint256 exactSyIn, uint256 minYtOut, ApproxParams calldata guessYtOut, LimitOrderData calldata limit ) internal returns (uint256 netYtOut, uint256 netSyFee) { uint256 netSyLeft = exactSyIn; bool doMarketOrder = true; if (!_isEmptyLimit(limit)) { (netSyLeft, netYtOut, netSyFee, doMarketOrder) = _fillLimit(receiver, SY, netSyLeft, limit); if (doMarketOrder) { _transferOut(address(SY), address(YT), netSyLeft); } else { _transferOut(address(SY), receiver, netSyLeft); } } if (doMarketOrder) { (uint256 netYtOutMarket, ) = _readMarket(market).approxSwapExactSyForYtV2( YT.newIndex(), netSyLeft, block.timestamp, guessYtOut ); (, uint256 netSyFeeMarket) = IPMarket(market).swapExactPtForSy( address(YT), netYtOutMarket, // exactPtIn = netYtOut _encodeSwapExactSyForYt(receiver, YT) ); netYtOut += netYtOutMarket; netSyFee += netSyFeeMarket; } if (netYtOut < minYtOut) revert("Slippage: INSUFFICIENT_YT_OUT"); } // ----------------- LIMIT ORDERS ----------------- function _fillLimit( address receiver, IERC20 tokenIn, uint256 netInput, LimitOrderData calldata lim ) internal returns (uint256 netLeft, uint256 netOut, uint256 netSyFee, bool doMarketOrder) { IPLimitRouter router = IPLimitRouter(lim.limitRouter); netLeft = netInput; if (lim.normalFills.length != 0) { _safeApproveInf(address(tokenIn), lim.limitRouter); (uint256 actualMaking, uint256 actualTaking, uint256 totalFee, ) = router.fill( lim.normalFills, receiver, netLeft, lim.optData, EMPTY_BYTES ); netOut += actualMaking; netLeft -= actualTaking; netSyFee += totalFee; } if (lim.flashFills.length != 0) { address YT = lim.flashFills[0].order.YT; OrderType orderType = lim.flashFills[0].order.orderType; (, , uint256 totalFee, bytes memory ret) = router.fill( lim.flashFills, YT, type(uint256).max, lim.optData, abi.encode(orderType, YT, netLeft, receiver) ); (uint256 netUse, uint256 netReceived) = abi.decode(ret, (uint256, uint256)); netOut += netReceived; netLeft -= netUse; netSyFee += totalFee; } doMarketOrder = netLeft > netInput.mulDown(lim.epsSkipMarket); } function _isEmptyLimit(LimitOrderData calldata a) internal pure returns (bool) { return a.normalFills.length == 0 && a.flashFills.length == 0; } // ----------------- ADD & REMOVE LIQUIDITY ----------------- function _entry_addLiquiditySinglePt(address market, LimitOrderData calldata lim) internal view returns (address) { return _entry_addLiquiditySinglePt(market, !_isEmptyLimit(lim)); } function _entry_addLiquiditySinglePt(address market, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : market; } function _entry_addLiquiditySingleSy(address market, LimitOrderData calldata lim) internal view returns (address) { return _entry_addLiquiditySingleSy(market, !_isEmptyLimit(lim)); } function _entry_addLiquiditySingleSy(address market, bool hasLimitOrder) internal view returns (address) { return hasLimitOrder ? address(this) : market; } // ----------------- MISC HELPER ----------------- function _delegateToSelf( bytes memory data, bool allowFailure ) internal returns (bool success, bytes memory result) { (success, result) = address(this).delegatecall(data); if (!success && !allowFailure) { assembly { // We use Yul's revert() to bubble up errors from the target contract. revert(add(32, result), mload(result)) } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPPrincipalToken.sol"; import "../../interfaces/IStandardizedYield.sol"; abstract contract CallbackHelper { enum ActionType { SwapExactSyForYt, SwapYtForSy, SwapExactYtForPt, SwapExactPtForYt } /// ------------------------------------------------------------ /// SwapExactSyForYt /// ------------------------------------------------------------ function _encodeSwapExactSyForYt(address receiver, IPYieldToken YT) internal pure returns (bytes memory res) { res = new bytes(96); uint256 actionType = uint256(ActionType.SwapExactSyForYt); assembly { mstore(add(res, 32), actionType) mstore(add(res, 64), receiver) mstore(add(res, 96), YT) } } function _decodeSwapExactSyForYt(bytes calldata data) internal pure returns (address receiver, IPYieldToken YT) { assembly { // first 32 bytes is ActionType receiver := calldataload(add(data.offset, 32)) YT := calldataload(add(data.offset, 64)) } } /// ------------------------------------------------------------ /// SwapYtForSy (common encode & decode) /// ------------------------------------------------------------ function _encodeSwapYtForSy(address receiver, IPYieldToken YT) internal pure returns (bytes memory res) { res = new bytes(96); uint256 actionType = uint256(ActionType.SwapYtForSy); assembly { mstore(add(res, 32), actionType) mstore(add(res, 64), receiver) mstore(add(res, 96), YT) } } function _decodeSwapYtForSy(bytes calldata data) internal pure returns (address receiver, IPYieldToken YT) { assembly { // first 32 bytes is ActionType receiver := calldataload(add(data.offset, 32)) YT := calldataload(add(data.offset, 64)) } } function _encodeSwapExactYtForPt( address receiver, uint256 netPtOut, IPPrincipalToken PT, IPYieldToken YT ) internal pure returns (bytes memory res) { res = new bytes(160); uint256 actionType = uint256(ActionType.SwapExactYtForPt); assembly { mstore(add(res, 32), actionType) mstore(add(res, 64), receiver) mstore(add(res, 96), netPtOut) mstore(add(res, 128), PT) mstore(add(res, 160), YT) } } function _decodeSwapExactYtForPt( bytes calldata data ) internal pure returns (address receiver, uint256 netPtOut, IPPrincipalToken PT, IPYieldToken YT) { assembly { // first 32 bytes is ActionType receiver := calldataload(add(data.offset, 32)) netPtOut := calldataload(add(data.offset, 64)) PT := calldataload(add(data.offset, 96)) YT := calldataload(add(data.offset, 128)) } } function _encodeSwapExactPtForYt( address receiver, uint256 exactPtIn, uint256 minYtOut, IPYieldToken YT ) internal pure returns (bytes memory res) { res = new bytes(160); uint256 actionType = uint256(ActionType.SwapExactPtForYt); assembly { mstore(add(res, 32), actionType) mstore(add(res, 64), receiver) mstore(add(res, 96), exactPtIn) mstore(add(res, 128), minYtOut) mstore(add(res, 160), YT) } } function _decodeSwapExactPtForYt( bytes calldata data ) internal pure returns (address receiver, uint256 exactPtIn, uint256 minYtOut, IPYieldToken YT) { assembly { // first 32 bytes is ActionType receiver := calldataload(add(data.offset, 32)) exactPtIn := calldataload(add(data.offset, 64)) minYtOut := calldataload(add(data.offset, 96)) YT := calldataload(add(data.offset, 128)) } } /// ------------------------------------------------------------ /// Misc functions /// ------------------------------------------------------------ function _getActionType(bytes calldata data) internal pure returns (ActionType actionType) { assembly { actionType := calldataload(data.offset) } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import {ApproxParams} from "../../interfaces/IPAllActionTypeV3.sol"; import {PMath} from "../../core/libraries/math/PMath.sol"; enum ApproxStage { INITIAL, RANGE_SEARCHING, RESULT_FINDING } struct ApproxState { ApproxStage stage; uint256 maxIteration; uint256 eps; uint256 startingGuess; uint256 curGuess; /// Defines the range that constrains the result. /// This range will be dynamically adjusted if the result falls outside of it. uint256[2] ranges; /// Specifies the range within which the result must fall. /// Any value outside this range is considered invalid. uint256[2] hardBounds; } using ApproxStateLib for ApproxState global; /// A library for determining the next guess from the current / `ApproxState` /// state, dynamically adjusting the search range to fit the valid / result /// range. /// /// @dev Invariants to maintain: /// - state.hardBounds should always include state.ranges /// - state.ranges should always include state.curGuess /// That is: /// state.hardBounds[0] <= state.ranges[0] <= state.curGuess <= state.ranges[1] <= state.hardBounds[1] library ApproxStateLib { using PMath for uint256; using ApproxStateLib for ApproxState; uint256 internal constant GUESS_RANGE_TWEAK = (5 * PMath.ONE) / 100; uint256 internal constant DEFAULT_MAX_ITERATION = 30; uint256 internal constant DEFAULT_EPS = 5e13; function initWithOffchain(ApproxParams memory approx) internal pure returns (ApproxState memory) { if (approx.guessMin > approx.guessOffchain || approx.guessOffchain > approx.guessMax || approx.eps > PMath.ONE) revert("Internal: INVALID_APPROX_PARAMS"); return ApproxState({ stage: ApproxStage.RESULT_FINDING, ranges: [approx.guessMin, approx.guessMax], hardBounds: [approx.guessMin, approx.guessMax], curGuess: approx.guessOffchain, startingGuess: approx.guessOffchain, maxIteration: approx.maxIteration, eps: approx.eps }); } function initNoOffChain( uint256 estimation, uint256[2] memory hardBounds ) internal pure returns (ApproxState memory) { assert(hardBounds[0] <= hardBounds[1]); uint256 startingGuess = PMath.clamp(estimation, hardBounds[0], hardBounds[1]); uint256 rangesLower = PMath.max(startingGuess.tweakDown(GUESS_RANGE_TWEAK), hardBounds[0]); uint256 rangesUpper = PMath.min(startingGuess.tweakUp(GUESS_RANGE_TWEAK), hardBounds[1]); return ApproxState({ stage: ApproxStage.INITIAL, ranges: [rangesLower, rangesUpper], hardBounds: hardBounds, curGuess: startingGuess, startingGuess: startingGuess, maxIteration: DEFAULT_MAX_ITERATION, eps: DEFAULT_EPS }); } function transitionDown(ApproxState memory state, bool excludeGuessFromRange) internal pure { state.ranges[1] = state.curGuess; if (excludeGuessFromRange) state.ranges[1]--; if (state.stage == ApproxStage.INITIAL) { state.stage = ApproxStage.RANGE_SEARCHING; state.curGuess = state.ranges[0]; } else if (state.stage == ApproxStage.RANGE_SEARCHING) { if (state.curGuess == state.hardBounds[0]) revert("Slippage: search range underflow"); bool shouldExtend = state.curGuess == state.ranges[0]; if (!shouldExtend) { state.stage = ApproxStage.RESULT_FINDING; moveGuessToMiddle(state); return; } uint256 distToStartingGuess = state.startingGuess - state.ranges[0]; uint256 extendedLower = PMath.subWithLowerBound(state.ranges[0], distToStartingGuess, state.hardBounds[0]); state.ranges[0] = extendedLower; state.curGuess = extendedLower; } else if (state.stage == ApproxStage.RESULT_FINDING) { moveGuessToMiddle(state); } else { assert(false); } } function transitionUp(ApproxState memory state, bool excludeGuessFromRange) internal pure { state.ranges[0] = state.curGuess; if (excludeGuessFromRange) state.ranges[0]++; if (state.stage == ApproxStage.INITIAL) { state.stage = ApproxStage.RANGE_SEARCHING; state.curGuess = state.ranges[1]; } else if (state.stage == ApproxStage.RANGE_SEARCHING) { if (state.curGuess == state.hardBounds[1]) revert("Slippage: search range overflow"); bool shouldExtend = state.curGuess == state.ranges[1]; if (!shouldExtend) { state.stage = ApproxStage.RESULT_FINDING; moveGuessToMiddle(state); return; } uint256 distFromStartingGuess = state.ranges[1] - state.startingGuess; uint256 extendedUpper = ( PMath.addWithUpperBound(state.ranges[1], distFromStartingGuess, state.hardBounds[1]) ); state.ranges[1] = extendedUpper; state.curGuess = extendedUpper; } else if (state.stage == ApproxStage.RESULT_FINDING) { moveGuessToMiddle(state); } else { assert(false); } } function moveGuessToMiddle(ApproxState memory state) internal pure { if (state.ranges[0] > state.ranges[1]) revert("Slippage: guessMin > guessMax"); state.curGuess = (state.ranges[0] + state.ranges[1]) / 2; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import {MarketMathCore, MarketState} from "../../core/Market/MarketMathCore.sol"; import {PYIndexLib, PYIndex} from "../../core/StandardizedYield/PYIndex.sol"; import {PMath} from "../../core/libraries/math/PMath.sol"; library MarketApproxEstimateLib { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using PMath for uint256; using PMath for int256; enum TokenType { PT, YT, SY } function estimateAmount( MarketState memory market, PYIndex index, uint256 blockTime, uint256 amountIn, TokenType tokenIn, TokenType tokenOut ) internal pure returns (uint256 estimatedAmountOut) { uint256 assetToPtRate = uint256( MarketMathCore._getExchangeRateFromImpliedRate(market.lastLnImpliedRate, market.expiry - blockTime) ); uint256 ptToAssetRate = PMath.ONE.divDown(assetToPtRate); uint256 ytToAssetRate = PMath.ONE - ptToAssetRate; uint256 exactAssetIn; if (tokenIn == TokenType.SY) { exactAssetIn = index.syToAsset(amountIn); } else if (tokenIn == TokenType.PT) { exactAssetIn = amountIn.mulDown(ptToAssetRate); } else { exactAssetIn = amountIn.mulDown(ytToAssetRate); } if (tokenOut == TokenType.SY) { estimatedAmountOut = index.assetToSy(exactAssetIn); } else if (tokenOut == TokenType.PT) { estimatedAmountOut = exactAssetIn.divDown(ptToAssetRate); } else { estimatedAmountOut = exactAssetIn.divDown(ytToAssetRate); } } function estimateSwapExactSyForPt( MarketState memory market, PYIndex index, uint256 blockTime, uint256 amountSyIn ) internal pure returns (uint256 estimatedPtOut) { return estimateAmount(market, index, blockTime, amountSyIn, TokenType.SY, TokenType.PT); } function estimateSwapExactSyForYt( MarketState memory market, PYIndex index, uint256 blockTime, uint256 amountSyIn ) internal pure returns (uint256 estimatedYtOut) { return estimateAmount(market, index, blockTime, amountSyIn, TokenType.SY, TokenType.YT); } function estimateAddLiquidity( MarketState memory market, PYIndex index, uint256 blockTime, uint256 netPtOwning, uint256 netSyOwning ) internal pure returns (uint256 estimatedPtToAdd, uint256 estimatedSyToAdd) { // Let `pa` be `estimatedPtToAdd`, `sa` be `estimatedSyToAdd`. // Conditions to satisfy: // +) Add liquidity amounts need to be proportional to the existing // liquidity: // pa / sa = totalPt / totalSy // => pa = totalPt / totalSy * sa // // +) Let `syToPtRate` be the spot price between the PT and SY amount. // Conversion between exessive/missing parts need to respect the // current price: // (sa - netSyOwning) * syToPtRate = netPtOwning - pa // <=> (sa - netSyOwning) * syToPtRate = netPtOwning - totalPt / totalSy * sa // <=> (sa - netSyOwning) * syToPtRate * totalSy = netPtOwning * totalSy - totalPt * sa // // Let x = syToPtRate * totalSy (x can be calculated with the function `estimateAmount` above). // (sa - netSyOwning) * x = netPtOwning * totalSy - totalPt * sa // <=> sa * x - netSyOwning * x = netPtOwning * totalSy - totalPt * sa // <=> sa * (x + totalPt) = netPtOwning * totalSy + netSyOwning * x uint256 totalSy = market.totalSy.Uint(); uint256 totalPt = market.totalPt.Uint(); uint256 x = estimateSwapExactSyForPt(market, index, blockTime, totalSy); uint256 sa = (netPtOwning * totalSy + netSyOwning * x) / (x + totalPt); uint256 pa = (totalPt * sa) / totalSy; return (pa, sa); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../../core/libraries/math/PMath.sol"; import "../../core/Market/MarketMathCore.sol"; import {ApproxParams} from "../../interfaces/IPAllActionTypeV3.sol"; import {ApproxState, ApproxStateLib} from "./ApproxStateLib.sol"; import {MarketApproxEstimateLib} from "./MarketApproxEstimateLib.sol"; library MarketApproxPtInLibOnchain { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using PMath for uint256; using PMath for int256; using LogExpMath for int256; using MarketApproxEstimateLib for MarketState; function approxSwapExactSyForYtOnchain( MarketState memory market, PYIndex index, uint256 exactSyIn, uint256 blockTime ) internal pure returns (uint256, /*netYtOut*/ uint256, /*netSyFee*/ uint256 /* iteration */) { MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); uint256 estimatedYtOut = market.estimateSwapExactSyForYt(index, blockTime, exactSyIn); uint256[2] memory hardBounds = [index.syToAsset(exactSyIn), calcSoftMaxPtIn(market, comp)]; ApproxState memory state = ApproxStateLib.initNoOffChain(estimatedYtOut, hardBounds); for (uint256 iter = 0; iter < state.maxIteration; ++iter) { uint256 guess = state.curGuess; (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(market, comp, index, guess); uint256 netSyToTokenizePt = index.assetToSyUp(guess); uint256 netSyToPull = netSyToTokenizePt - netSyOut; if (netSyToPull <= exactSyIn) { if (PMath.isASmallerApproxB(netSyToPull, exactSyIn, state.eps)) { return (guess, netSyFee, iter); } state.transitionUp({excludeGuessFromRange: false}); } else { state.transitionDown({excludeGuessFromRange: true}); } } revert("Slippage: APPROX_EXHAUSTED"); } function approxSwapPtToAddLiquidityOnchain( MarketState memory market, PYIndex index, uint256 totalPtIn, uint256 netSyHolding, uint256 blockTime ) internal pure returns (uint256, /*netPtSwap*/ uint256, /*netSyFromSwap*/ uint256, /*netSyFee*/ uint256 /* iteration */) { require(market.totalLp != 0, "no existing lp"); MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); (uint256 estimatedPtAdd, ) = market.estimateAddLiquidity(index, blockTime, totalPtIn, netSyHolding); uint256 estimatedPtSwap = totalPtIn.subMax0(estimatedPtAdd); uint256[2] memory hardBounds = [0, PMath.min(totalPtIn, calcSoftMaxPtIn(market, comp))]; ApproxState memory state = ApproxStateLib.initNoOffChain(estimatedPtSwap, hardBounds); for (uint256 iter = 0; iter < state.maxIteration; ++iter) { uint256 guess = state.curGuess; (uint256 syNumerator, uint256 ptNumerator, uint256 netSyOut, uint256 netSyFee, ) = ( calcNumerators(market, index, totalPtIn, netSyHolding, comp, guess) ); if (PMath.isAApproxB(syNumerator, ptNumerator, state.eps)) { return (guess, netSyOut, netSyFee, iter); } if (syNumerator <= ptNumerator) { // needs more SY --> swap more PT state.transitionUp({excludeGuessFromRange: true}); } else { // needs less SY --> swap less PT state.transitionDown({excludeGuessFromRange: true}); } } revert("Slippage: APPROX_EXHAUSTED"); } function calcNumerators( MarketState memory market, PYIndex index, uint256 totalPtIn, uint256 netSyHolding, MarketPreCompute memory comp, uint256 guess ) internal pure returns (uint256 syNumerator, uint256 ptNumerator, uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve) { (netSyOut, netSyFee, netSyToReserve) = calcSyOut(market, comp, index, guess); uint256 newTotalPt = uint256(market.totalPt) + guess; uint256 newTotalSy = (uint256(market.totalSy) - netSyOut - netSyToReserve); syNumerator = (netSyOut + netSyHolding) * newTotalPt; ptNumerator = (totalPtIn - guess) * newTotalSy; } //////////////////////////////////////////////////////////////////////////////// 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, -int256(netPtIn)); netSyOut = uint256(_netSyOut); netSyFee = uint256(_netSyFee); netSyToReserve = uint256(_netSyToReserve); } function calcSoftMaxPtIn(MarketState memory market, MarketPreCompute memory comp) internal pure returns (uint256) { return (MarketMathCore.MAX_MARKET_PROPORTION.mulDown(market.totalPt + comp.totalAsset) - market.totalPt).Uint(); } } library MarketApproxPtOutLibOnchain { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using PMath for uint256; using PMath for int256; using LogExpMath for int256; using MarketApproxEstimateLib for MarketState; function approxSwapExactSyForPtOnchain( MarketState memory market, PYIndex index, uint256 exactSyIn, uint256 blockTime ) internal pure returns (uint256, /*netPtOut*/ uint256, /*netSyFee*/ uint256 /* iteration */) { MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); uint256 estimatedPtOut = market.estimateSwapExactSyForPt(index, blockTime, exactSyIn); uint256[2] memory hardBounds = [0, calcMaxPtOut(comp, market.totalPt)]; ApproxState memory state = ApproxStateLib.initNoOffChain(estimatedPtOut, hardBounds); for (uint256 iter = 0; iter < state.maxIteration; ++iter) { uint256 guess = state.curGuess; (uint256 netSyIn, uint256 netSyFee, ) = calcSyIn(market, comp, index, guess); if (netSyIn <= exactSyIn) { if (PMath.isASmallerApproxB(netSyIn, exactSyIn, state.eps)) { return (guess, netSyFee, iter); } state.transitionUp({excludeGuessFromRange: false}); } else { state.transitionDown({excludeGuessFromRange: true}); } } revert("Slippage: APPROX_EXHAUSTED"); } function approxSwapSyToAddLiquidityOnchain( MarketState memory market, PYIndex index, uint256 totalSyIn, uint256 netPtHolding, uint256 blockTime ) internal pure returns (uint256, /*netPtFromSwap*/ uint256, /*netSySwap*/ uint256, /*netSyFee*/ uint256 /* iteration */) { require(market.totalLp != 0, "no existing lp"); MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); (uint256 estimatedPtAdd, ) = market.estimateAddLiquidity(index, blockTime, netPtHolding, totalSyIn); uint256 estimatedPtSwap = estimatedPtAdd.subMax0(netPtHolding); uint256[2] memory hardBounds = [0, calcMaxPtOut(comp, market.totalPt)]; ApproxState memory state = ApproxStateLib.initNoOffChain(estimatedPtSwap, hardBounds); for (uint256 iter = 0; iter < state.maxIteration; ++iter) { uint256 guess = state.curGuess; (uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve) = calcSyIn(market, comp, index, guess); if (netSyIn > totalSyIn) { state.transitionDown({excludeGuessFromRange: true}); continue; } uint256 syNumerator; uint256 ptNumerator; { uint256 newTotalPt = uint256(market.totalPt) - guess; uint256 netTotalSy = uint256(market.totalSy) + netSyIn - netSyToReserve; ptNumerator = (guess + netPtHolding) * netTotalSy; syNumerator = (totalSyIn - netSyIn) * newTotalPt; } if (PMath.isAApproxB(ptNumerator, syNumerator, state.eps)) { return (guess, netSyIn, netSyFee, iter); } if (ptNumerator <= syNumerator) { // needs more PT state.transitionUp({excludeGuessFromRange: true}); } else { // needs less PT state.transitionDown({excludeGuessFromRange: true}); } } revert("Slippage: APPROX_EXHAUSTED"); } //////////////////////////////////////////////////////////////////////////////// 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, int256(netPtOut)); netSyIn = uint256(-_netSyIn); netSyFee = uint256(_netSyFee); netSyToReserve = uint256(_netSyToReserve); } 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 + PMath.IONE); int256 numerator = proportion.mulDown(totalPt + comp.totalAsset); int256 maxPtOut = totalPt - numerator; return (uint256(maxPtOut) * 999) / 1000; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../../core/libraries/math/PMath.sol"; import "../../core/Market/MarketMathCore.sol"; import {ApproxParams} from "../../interfaces/IPAllActionTypeV3.sol"; uint256 constant QUICK_CALC_MAX_ITER = 50; uint256 constant CUT_OFF_SCALE_CLAMP = 2; uint256 constant QUICK_CALC_TRIGGER_EPS = 1e17; library MarketApproxPtInLibV2 { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using PMath for uint256; using PMath for int256; using LogExpMath for int256; function approxSwapExactSyForYtV2( MarketState memory market, PYIndex index, uint256 exactSyIn, uint256 blockTime, ApproxParams memory approx ) internal pure returns (uint256, /*netYtOut*/ uint256 /*netSyFee*/) { MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); if (approx.guessOffchain == 0) { approx.guessMin = PMath.max(approx.guessMin, index.syToAsset(exactSyIn)); approx.guessMax = PMath.min(approx.guessMax, calcMaxPtIn(market, comp)); validateApprox(approx); } // at minimum we will flashswap exactSyIn since we have enough SY to payback the PT loan uint256 guess = getFirstGuess(approx); for (uint256 iter = 0; iter < approx.maxIteration; ++iter) { (uint256 netSyOut, uint256 netSyFee, ) = calcSyOut(market, comp, index, guess); uint256 netSyToTokenizePt = index.assetToSyUp(guess); // for sure netSyToTokenizePt >= netSyOut since we are swapping PT to SY uint256 netSyToPull = netSyToTokenizePt - netSyOut; if (netSyToPull <= exactSyIn) { if (PMath.isASmallerApproxB(netSyToPull, exactSyIn, approx.eps)) { return (guess, netSyFee); } if (approx.guessMin == guess) { break; } approx.guessMin = guess; } else { approx.guessMax = guess - 1; } if (iter <= CUT_OFF_SCALE_CLAMP) { guess = scaleClamp(guess, exactSyIn, netSyToPull, approx); } else { guess = calcMidpoint(approx); } } revert("Slippage: APPROX_EXHAUSTED"); } struct Args5 { MarketState market; PYIndex index; uint256 totalPtIn; uint256 netSyHolding; uint256 blockTime; ApproxParams approx; } function approxSwapPtToAddLiquidityV2( MarketState memory _market, PYIndex _index, uint256 _totalPtIn, uint256 _netSyHolding, uint256 _blockTime, ApproxParams memory approx ) internal pure returns (uint256, /*netPtSwap*/ uint256, /*netSyFromSwap*/ uint256 /*netSyFee*/) { Args5 memory a = Args5(_market, _index, _totalPtIn, _netSyHolding, _blockTime, approx); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); if (approx.guessOffchain == 0) { // no limit on min approx.guessMax = PMath.min(approx.guessMax, calcMaxPtIn(a.market, comp)); approx.guessMax = PMath.min(approx.guessMax, a.totalPtIn); validateApprox(approx); require(a.market.totalLp != 0, "no existing lp"); } uint256 guess = getFirstGuess(approx); bool quickCalcRan = false; for (uint256 iter = 0; iter < approx.maxIteration; ++iter) { ( uint256 syNumerator, uint256 ptNumerator, uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve ) = calcNumerators(a.market, a.index, a.totalPtIn, a.netSyHolding, comp, guess); if (PMath.isAApproxB(syNumerator, ptNumerator, approx.eps)) { return (guess, netSyOut, netSyFee); } if (syNumerator <= ptNumerator) { // needs more SY --> swap more PT if (approx.guessMin == guess) { break; } approx.guessMin = guess; } else { // needs less SY --> swap less PT approx.guessMax = guess - 1; } if (!quickCalcRan && PMath.isAApproxB(syNumerator, ptNumerator, QUICK_CALC_TRIGGER_EPS)) { quickCalcRan = true; guess = quickCalc(a, guess, netSyOut, netSyToReserve); if (guess <= a.approx.guessMin || guess >= a.approx.guessMax) guess = calcMidpoint(a.approx); } else { guess = calcMidpoint(a.approx); } } revert("Slippage: APPROX_EXHAUSTED"); } function quickCalc( Args5 memory a, uint256 _guess, uint256 _netSyOut, uint256 _netSyToReserve ) internal pure returns (uint256) { unchecked { uint256 low = a.approx.guessMin; uint256 high = a.approx.guessMax; for (uint256 i = 0; i < QUICK_CALC_MAX_ITER; i++) { uint256 mid = (low + high) / 2; uint256 thisNetSyOut = (_netSyOut * mid) / _guess; uint256 thisNetSyToReserve = (_netSyToReserve * mid) / _guess; uint256 newTotalPt = uint256(a.market.totalPt) + mid; uint256 newTotalSy = (uint256(a.market.totalSy) - thisNetSyOut - thisNetSyToReserve); uint256 syNumerator = (thisNetSyOut + a.netSyHolding) * newTotalPt; uint256 ptNumerator = (a.totalPtIn - mid) * newTotalSy; if (isAApproxBUnchecked(syNumerator, ptNumerator, a.approx.eps)) { return mid; } if (syNumerator <= ptNumerator) { if (low == mid) { break; } low = mid; } else { high = mid - 1; } if (low > high) return mid; } return (low + high) / 2; } } function calcNumerators( MarketState memory market, PYIndex index, uint256 totalPtIn, uint256 netSyHolding, MarketPreCompute memory comp, uint256 guess ) internal pure returns (uint256 syNumerator, uint256 ptNumerator, uint256 netSyOut, uint256 netSyFee, uint256 netSyToReserve) { (netSyOut, netSyFee, netSyToReserve) = calcSyOut(market, comp, index, guess); uint256 newTotalPt = uint256(market.totalPt) + guess; uint256 newTotalSy = (uint256(market.totalSy) - netSyOut - netSyToReserve); // it is desired that // (netSyOut + netSyHolding) / newTotalSy = netPtRemaining / newTotalPt // which is equivalent to // (netSyOut + netSyHolding) * newTotalPt = netPtRemaining * newTotalSy syNumerator = (netSyOut + netSyHolding) * newTotalPt; ptNumerator = (totalPtIn - guess) * newTotalSy; } //////////////////////////////////////////////////////////////////////////////// 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, -int256(netPtIn)); netSyOut = uint256(_netSyOut); netSyFee = uint256(_netSyFee); netSyToReserve = uint256(_netSyToReserve); } /// INTENDED TO BE CALLED BY WHEN GUESS.OFFCHAIN == 0 ONLY /// function validateApprox(ApproxParams memory approx) internal pure { if (approx.guessMin > approx.guessMax || approx.eps > PMath.ONE) revert("Internal: INVALID_APPROX_PARAMS"); } function calcMaxPtIn(MarketState memory market, MarketPreCompute memory comp) internal pure returns (uint256) { uint256 low = 0; uint256 hi = uint256(comp.totalAsset) - 1; while (low != hi) { uint256 mid = (low + hi + 1) / 2; if (calcSlope(comp, market.totalPt, int256(mid)) < 0) hi = mid - 1; else low = mid; } low = PMath.min( low, (MarketMathCore.MAX_MARKET_PROPORTION.mulDown(market.totalPt + comp.totalAsset) - market.totalPt).Uint() ); return low; } function calcSlope(MarketPreCompute memory comp, int256 totalPt, int256 ptToMarket) internal pure returns (int256) { int256 diffAssetPtToMarket = comp.totalAsset - ptToMarket; int256 sumPt = ptToMarket + totalPt; require(diffAssetPtToMarket > 0 && sumPt > 0, "invalid ptToMarket"); int256 part1 = (ptToMarket * (totalPt + comp.totalAsset)).divDown(sumPt * diffAssetPtToMarket); int256 part2 = sumPt.divDown(diffAssetPtToMarket).ln(); int256 part3 = PMath.IONE.divDown(comp.rateScalar); return comp.rateAnchor - (part1 - part2).mulDown(part3); } } library MarketApproxPtOutLibV2 { using MarketMathCore for MarketState; using PYIndexLib for PYIndex; using PMath for uint256; using PMath for int256; using LogExpMath for int256; function approxSwapExactSyForPtV2( MarketState memory market, PYIndex index, uint256 exactSyIn, uint256 blockTime, ApproxParams memory approx ) internal pure returns (uint256, /*netPtOut*/ uint256 /*netSyFee*/) { MarketPreCompute memory comp = market.getMarketPreCompute(index, blockTime); if (approx.guessOffchain == 0) { // no limit on min approx.guessMax = PMath.min(approx.guessMax, calcMaxPtOut(comp, market.totalPt)); validateApprox(approx); } uint256 guess = getFirstGuess(approx); for (uint256 iter = 0; iter < approx.maxIteration; ++iter) { (uint256 netSyIn, uint256 netSyFee, ) = calcSyIn(market, comp, index, guess); if (netSyIn <= exactSyIn) { if (PMath.isASmallerApproxB(netSyIn, exactSyIn, approx.eps)) { return (guess, netSyFee); } if (guess == approx.guessMin) { break; } approx.guessMin = guess; } else { approx.guessMax = guess - 1; } if (iter <= CUT_OFF_SCALE_CLAMP) { guess = scaleClamp(guess, exactSyIn, netSyIn, approx); } else { guess = calcMidpoint(approx); } } revert("Slippage: APPROX_EXHAUSTED"); } struct Args6 { MarketState market; PYIndex index; uint256 totalSyIn; uint256 netPtHolding; uint256 blockTime; ApproxParams approx; } function approxSwapSyToAddLiquidityV2( MarketState memory _market, PYIndex _index, uint256 _totalSyIn, uint256 _netPtHolding, uint256 _blockTime, ApproxParams memory _approx ) internal pure returns (uint256, /*netPtFromSwap*/ uint256, /*netSySwap*/ uint256 /*netSyFee*/) { Args6 memory a = Args6(_market, _index, _totalSyIn, _netPtHolding, _blockTime, _approx); MarketPreCompute memory comp = a.market.getMarketPreCompute(a.index, a.blockTime); if (a.approx.guessOffchain == 0) { // no limit on min a.approx.guessMax = PMath.min(a.approx.guessMax, calcMaxPtOut(comp, a.market.totalPt)); validateApprox(a.approx); require(a.market.totalLp != 0, "no existing lp"); } uint256 guess = getFirstGuess(a.approx); bool quickCalcRan = false; for (uint256 iter = 0; iter < a.approx.maxIteration; ++iter) { (uint256 netSyIn, uint256 netSyFee, uint256 netSyToReserve) = calcSyIn(a.market, comp, a.index, guess); if (netSyIn > a.totalSyIn) { a.approx.guessMax = guess - 1; guess = calcMidpoint(a.approx); continue; } uint256 syNumerator; uint256 ptNumerator; { uint256 newTotalPt = uint256(a.market.totalPt) - guess; uint256 netTotalSy = uint256(a.market.totalSy) + netSyIn - netSyToReserve; // it is desired that // (netPtFromSwap + netPtHolding) / newTotalPt = netSyRemaining / netTotalSy // which is equivalent to // (netPtFromSwap + netPtHolding) * netTotalSy = netSyRemaining * newTotalPt ptNumerator = (guess + a.netPtHolding) * netTotalSy; syNumerator = (a.totalSyIn - netSyIn) * newTotalPt; } if (PMath.isAApproxB(syNumerator, ptNumerator, a.approx.eps)) { return (guess, netSyIn, netSyFee); } if (ptNumerator <= syNumerator) { if (a.approx.guessMin == guess) { break; } a.approx.guessMin = guess; } else { a.approx.guessMax = guess - 1; } if (!quickCalcRan && PMath.isAApproxB(syNumerator, ptNumerator, QUICK_CALC_TRIGGER_EPS)) { quickCalcRan = true; guess = quickCalc(a, guess, netSyIn, netSyToReserve); if (guess <= a.approx.guessMin || guess >= a.approx.guessMax) guess = calcMidpoint(a.approx); } else { guess = calcMidpoint(a.approx); } } revert("Slippage: APPROX_EXHAUSTED"); } function quickCalc( Args6 memory a, uint256 _guess, uint256 _netSyIn, uint256 _netSyToReserve ) internal pure returns (uint256) { uint256 low = a.approx.guessMin; uint256 high = a.approx.guessMax; unchecked { for (uint256 i = 0; i < QUICK_CALC_MAX_ITER; i++) { uint256 mid = (low + high) / 2; uint256 newTotalPt = uint256(a.market.totalPt) - mid; uint256 thisNetSyIn = (_netSyIn * mid) / _guess; uint256 thisNetSyToReserve = (_netSyToReserve * mid) / _guess; if (thisNetSyIn > a.totalSyIn) { high = mid - 1; if (low > high) return mid; continue; } uint256 netTotalSy = uint256(a.market.totalSy) + thisNetSyIn - thisNetSyToReserve; uint256 ptNumerator = (mid + a.netPtHolding) * netTotalSy; uint256 syNumerator = (a.totalSyIn - thisNetSyIn) * newTotalPt; if (isAApproxBUnchecked(syNumerator, ptNumerator, a.approx.eps)) { return mid; } if (ptNumerator <= syNumerator) { low = mid; } else { high = mid - 1; } if (low > high) return mid; } return (low + high) / 2; } } //////////////////////////////////////////////////////////////////////////////// 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, int256(netPtOut)); // all safe since totalPt and totalSy is int128 netSyIn = uint256(-_netSyIn); netSyFee = uint256(_netSyFee); netSyToReserve = uint256(_netSyToReserve); } 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 + PMath.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 (uint256(maxPtOut) * 999) / 1000; } function validateApprox(ApproxParams memory approx) internal pure { if (approx.guessMin > approx.guessMax || approx.eps > PMath.ONE) revert("Internal: INVALID_APPROX_PARAMS"); } } function scaleClamp( uint256 original, uint256 target, uint256 current, ApproxParams memory approx ) pure returns (uint256) { uint256 scaled = (original * target) / current; if (scaled >= approx.guessMax) return calcMidpoint(approx); if (scaled <= approx.guessMin) return calcMidpoint(approx); return scaled; } function getFirstGuess(ApproxParams memory approx) pure returns (uint256) { return (approx.guessOffchain != 0) ? approx.guessOffchain : calcMidpoint(approx); } function calcMidpoint(ApproxParams memory approx) pure returns (uint256) { return (approx.guessMin + approx.guessMax + 1) / 2; } function isAApproxBUnchecked(uint256 a, uint256 b, uint256 eps) pure returns (bool) { unchecked { uint256 bLow = (b * (1e18 - eps)) / 1e18; uint256 bHigh = (b * (1e18 + eps)) / 1e18; return bLow <= a && a <= bHigh; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; struct SwapData { SwapType swapType; address extRouter; bytes extCalldata; bool needScale; } struct SwapDataExtra { address tokenIn; address tokenOut; uint256 minOut; SwapData swapData; } enum SwapType { NONE, KYBERSWAP, ODOS, // ETH_WETH not used in Aggregator ETH_WETH, OKX, ONE_INCH, RESERVE_1, RESERVE_2, RESERVE_3, RESERVE_4, RESERVE_5 } interface IPSwapAggregator { event SwapSingle(SwapType indexed swapType, address indexed tokenIn, uint256 amountIn); function swap(address tokenIn, uint256 amountIn, SwapData calldata swapData) external payable; }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "viaIR": true, "evmVersion": "cancun", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"},{"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":"uint256","name":"netSyUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"}],"name":"AddLiquidityDualSyAndPt","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":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netTokenUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"AddLiquidityDualTokenAndPt","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netPtIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"}],"name":"AddLiquiditySinglePt","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netSyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"}],"name":"AddLiquiditySingleSy","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netSyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyMintPy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netYtOut","type":"uint256"}],"name":"AddLiquiditySingleSyKeepYt","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":"uint256","name":"netTokenIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"AddLiquiditySingleToken","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":"uint256","name":"netTokenIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netLpOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netYtOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyMintPy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"AddLiquiditySingleTokenKeepYt","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpIn","type":"uint256"},{"components":[{"internalType":"uint256","name":"netPtFromRemove","type":"uint256"},{"internalType":"uint256","name":"netSyFromRemove","type":"uint256"},{"internalType":"uint256","name":"netPtRedeem","type":"uint256"},{"internalType":"uint256","name":"netSyFromRedeem","type":"uint256"},{"internalType":"uint256","name":"totalSyOut","type":"uint256"}],"indexed":false,"internalType":"struct ExitPostExpReturnParams","name":"params","type":"tuple"}],"name":"ExitPostExpToSy","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":"uint256","name":"netLpIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalTokenOut","type":"uint256"},{"components":[{"internalType":"uint256","name":"netPtFromRemove","type":"uint256"},{"internalType":"uint256","name":"netSyFromRemove","type":"uint256"},{"internalType":"uint256","name":"netPtRedeem","type":"uint256"},{"internalType":"uint256","name":"netSyFromRedeem","type":"uint256"},{"internalType":"uint256","name":"totalSyOut","type":"uint256"}],"indexed":false,"internalType":"struct ExitPostExpReturnParams","name":"params","type":"tuple"}],"name":"ExitPostExpToToken","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpIn","type":"uint256"},{"components":[{"internalType":"uint256","name":"netPtFromRemove","type":"uint256"},{"internalType":"uint256","name":"netSyFromRemove","type":"uint256"},{"internalType":"uint256","name":"netPyRedeem","type":"uint256"},{"internalType":"uint256","name":"netSyFromRedeem","type":"uint256"},{"internalType":"uint256","name":"netPtSwap","type":"uint256"},{"internalType":"uint256","name":"netYtSwap","type":"uint256"},{"internalType":"uint256","name":"netSyFromSwap","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"},{"internalType":"uint256","name":"totalSyOut","type":"uint256"}],"indexed":false,"internalType":"struct ExitPreExpReturnParams","name":"params","type":"tuple"}],"name":"ExitPreExpToSy","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":"uint256","name":"netLpIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalTokenOut","type":"uint256"},{"components":[{"internalType":"uint256","name":"netPtFromRemove","type":"uint256"},{"internalType":"uint256","name":"netSyFromRemove","type":"uint256"},{"internalType":"uint256","name":"netPyRedeem","type":"uint256"},{"internalType":"uint256","name":"netSyFromRedeem","type":"uint256"},{"internalType":"uint256","name":"netPtSwap","type":"uint256"},{"internalType":"uint256","name":"netYtSwap","type":"uint256"},{"internalType":"uint256","name":"netSyFromSwap","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"},{"internalType":"uint256","name":"totalSyOut","type":"uint256"}],"indexed":false,"internalType":"struct ExitPreExpReturnParams","name":"params","type":"tuple"}],"name":"ExitPreExpToToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"YT","type":"address"},{"indexed":false,"internalType":"uint256","name":"netSyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPyOut","type":"uint256"}],"name":"MintPyFromSy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"YT","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPyOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"MintPyFromToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"SY","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyOut","type":"uint256"}],"name":"MintSyFromToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"YT","type":"address"},{"indexed":false,"internalType":"uint256","name":"netPyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyOut","type":"uint256"}],"name":"RedeemPyToSy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":true,"internalType":"address","name":"YT","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netPyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netTokenOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"RedeemPyToToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":true,"internalType":"address","name":"SY","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netSyIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netTokenOut","type":"uint256"}],"name":"RedeemSyToToken","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpToRemove","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyOut","type":"uint256"}],"name":"RemoveLiquidityDualSyAndPt","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":"tokenOut","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpToRemove","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netTokenOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"RemoveLiquidityDualTokenAndPt","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpToRemove","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtOut","type":"uint256"}],"name":"RemoveLiquiditySinglePt","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":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpToRemove","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyOut","type":"uint256"}],"name":"RemoveLiquiditySingleSy","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":"uint256","name":"netLpToRemove","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netTokenOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"RemoveLiquiditySingleToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":true,"internalType":"address","name":"facet","type":"address"}],"name":"SelectorToFacetSet","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":"receiver","type":"address"},{"indexed":false,"internalType":"int256","name":"netPtToAccount","type":"int256"},{"indexed":false,"internalType":"int256","name":"netSyToAccount","type":"int256"}],"name":"SwapPtAndSy","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":"netPtToAccount","type":"int256"},{"indexed":false,"internalType":"int256","name":"netTokenToAccount","type":"int256"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"SwapPtAndToken","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":"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"},{"indexed":false,"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"name":"SwapYtAndToken","type":"event"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"netPtIn","type":"uint256"},{"internalType":"uint256","name":"minLpOut","type":"uint256"}],"name":"addLiquiditySinglePtSimple","outputs":[{"internalType":"uint256","name":"netLpOut","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":"netSyIn","type":"uint256"},{"internalType":"uint256","name":"minLpOut","type":"uint256"}],"name":"addLiquiditySingleSySimple","outputs":[{"internalType":"uint256","name":"netLpOut","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":"minLpOut","type":"uint256"},{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"internalType":"address","name":"tokenMintSy","type":"address"},{"internalType":"address","name":"pendleSwap","type":"address"},{"components":[{"internalType":"enum SwapType","name":"swapType","type":"uint8"},{"internalType":"address","name":"extRouter","type":"address"},{"internalType":"bytes","name":"extCalldata","type":"bytes"},{"internalType":"bool","name":"needScale","type":"bool"}],"internalType":"struct SwapData","name":"swapData","type":"tuple"}],"internalType":"struct TokenInput","name":"input","type":"tuple"}],"name":"addLiquiditySingleTokenSimple","outputs":[{"internalType":"uint256","name":"netLpOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"},{"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"netLpToRemove","type":"uint256"},{"internalType":"uint256","name":"minPtOut","type":"uint256"}],"name":"removeLiquiditySinglePtSimple","outputs":[{"internalType":"uint256","name":"netPtOut","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":"exactSyIn","type":"uint256"},{"internalType":"uint256","name":"minPtOut","type":"uint256"}],"name":"swapExactSyForPtSimple","outputs":[{"internalType":"uint256","name":"netPtOut","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":"exactSyIn","type":"uint256"},{"internalType":"uint256","name":"minYtOut","type":"uint256"}],"name":"swapExactSyForYtSimple","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":"minPtOut","type":"uint256"},{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"internalType":"address","name":"tokenMintSy","type":"address"},{"internalType":"address","name":"pendleSwap","type":"address"},{"components":[{"internalType":"enum SwapType","name":"swapType","type":"uint8"},{"internalType":"address","name":"extRouter","type":"address"},{"internalType":"bytes","name":"extCalldata","type":"bytes"},{"internalType":"bool","name":"needScale","type":"bool"}],"internalType":"struct SwapData","name":"swapData","type":"tuple"}],"internalType":"struct TokenInput","name":"input","type":"tuple"}],"name":"swapExactTokenForPtSimple","outputs":[{"internalType":"uint256","name":"netPtOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"},{"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"minYtOut","type":"uint256"},{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"netTokenIn","type":"uint256"},{"internalType":"address","name":"tokenMintSy","type":"address"},{"internalType":"address","name":"pendleSwap","type":"address"},{"components":[{"internalType":"enum SwapType","name":"swapType","type":"uint8"},{"internalType":"address","name":"extRouter","type":"address"},{"internalType":"bytes","name":"extCalldata","type":"bytes"},{"internalType":"bool","name":"needScale","type":"bool"}],"internalType":"struct SwapData","name":"swapData","type":"tuple"}],"internalType":"struct TokenInput","name":"input","type":"tuple"}],"name":"swapExactTokenForYtSimple","outputs":[{"internalType":"uint256","name":"netYtOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"},{"internalType":"uint256","name":"netSyInterm","type":"uint256"}],"stateMutability":"payable","type":"function"}]
Contract Creation Code
60808060405234601557614600908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80630af8a5cf1461009457806322bfddd01461008f578063252f9db31461008a57806340a169f9146100855780634cc30915146100805780636afe69981461007b578063f2f6eae5146100765763feb9d1d214610071575f80fd5b610ca5565b6109d8565b6108bc565b610754565b610634565b610516565b610365565b34610267576100a236610289565b91929073ffffffffffffffffffffffffffffffffffffffff8416906100c981863385610f94565b604080517ff6b911bc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152851660248201526044810183905290816064815f875af1958615610262575f915f97610226575b50610149916101439186610fb5565b96610e78565b9384106101c2577fac97b87f5422fa3beec99bff8f336310d8ebc7d33d909b7d534cd7c72f61e87173ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b0390a460408051918252602082019290925290819081015b0390f35b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f50545f4f5554000000604482015280606481015b0390fd5b61014391975061014992506102529060403d60401161025b575b61024a8183610de9565b810190610e2a565b97915091610134565b503d610240565b610e40565b5f80fd5b73ffffffffffffffffffffffffffffffffffffffff81160361026757565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080910112610267576004356102bf8161026b565b906024356102cc8161026b565b906044359060643590565b9060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126102675760043561030e8161026b565b9160243561031b8161026b565b91604435916064359067ffffffffffffffff8211610267577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8260a0920301126102675760040190565b61036e366102d7565b73ffffffffffffffffffffffffffffffffffffffff839294931693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa918215610262576101be945f945f946104d4575b5061041283859673ffffffffffffffffffffffffffffffffffffffff61040a61041b979873ffffffffffffffffffffffffffffffffffffffff1690565b911690611286565b94859188611475565b9590947fa3a2846538c60e47775faa60c6ae79b67dee6d97bb70e386ebbaf4c3a38e8b8173ffffffffffffffffffffffffffffffffffffffff61045d85610ec0565b6104b487610477602061046f8d611599565b990135611659565b60405194859416983397859094939260609273ffffffffffffffffffffffffffffffffffffffff6080840197168352602083015260408201520152565b0390a4604051938493846040919493926060820195825260208201520152565b83955061041b9394506105016104129160603d60601161050f575b6104f98183610de9565b810190610e8a565b9791905096955093506103cd565b503d6104ef565b346102675761052436610289565b92909173ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa8015610262576105ae938680935f905f94610607575b5090826105a89273ffffffffffffffffffffffffffffffffffffffff339116610f94565b85611689565b9390927fb51950711c9b21dc7888d41f68a19540231ffb5f0d19d8f75cbccaf90ffa7fa573ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b8394506105a89291506106289060603d60601161050f576104f98183610de9565b95919050919250610584565b346102675761064236610289565b92909173ffffffffffffffffffffffffffffffffffffffff811693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa908115610262576106c4868281946106ca975f91610733575b5073ffffffffffffffffffffffffffffffffffffffff339116610f94565b846110f5565b9390927f3f5e2944826baeaed8eb77f0f74e6088a154a0fc1317f062fd984585607b473973ffffffffffffffffffffffffffffffffffffffff61071561070f87611599565b93611659565b604080519485526020850191909152941693339290819081016101a6565b61074c915060603d60601161050f576104f98183610de9565b50505f6106a6565b61075d366102d7565b73ffffffffffffffffffffffffffffffffffffffff839294931693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa918215610262576101be945f945f94610874575b506107e38373ffffffffffffffffffffffffffffffffffffffff6107ec9596971683611286565b94859188611689565b9590947f387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee73ffffffffffffffffffffffffffffffffffffffff602061083086610ec0565b6040805173ffffffffffffffffffffffffffffffffffffffff90961686529190960135602085015283018890526060830186905290931692339180608081016104b4565b73ffffffffffffffffffffffffffffffffffffffff9550836107ec9495506108ad6107e39260603d60601161050f576104f98183610de9565b989190509796505093506107bc565b34610267576108ca36610289565b92909173ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa80156102625761096b938680935f905f946109b0575b50610965919073ffffffffffffffffffffffffffffffffffffffff85169073ffffffffffffffffffffffffffffffffffffffff339116610f94565b85611475565b9390927f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d73ffffffffffffffffffffffffffffffffffffffff61071561070f87611599565b6109659294506109cf915060603d60601161050f576104f98183610de9565b9492905061092a565b34610267576109e636610289565b929073ffffffffffffffffffffffffffffffffffffffff8316906040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481865afa90811561026257610a84918387925f905f93610c7e575b5092610a6a929373ffffffffffffffffffffffffffffffffffffffff339116610f94565b82610a7d610a77886118a0565b92611999565b4292611a81565b50505094604086610a93610eca565b96610aca835198899384937f29910b1100000000000000000000000000000000000000000000000000000000855260048501610f1e565b03815f875af1948515610262575f905f96610c50575b50606090610af2610b4d979885610f6c565b6040517f156e29f600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff881660048201526024810192909252604482015295869081906064820190565b03815f875af1948515610262575f95610c1d575b508410610bbe577fc87b85efc5055ef177e0092af0d4e624fff4b1d57db748857f65e4b7e4a28a3673ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f4c505f4f55540000006044820152606490fd5b610c4091955060603d606011610c49575b610c388183610de9565b810190610f79565b5050935f610b61565b503d610c2e565b610b4d965060609150610c74610af29160403d60401161025b5761024a8183610de9565b9750919050610ae0565b610a6a9350610c9c915060603d60601161050f576104f98183610de9565b93909150610a46565b610cae366102d7565b929173ffffffffffffffffffffffffffffffffffffffff811693604051927f2c8ce6bc000000000000000000000000000000000000000000000000000000008452606084600481895afa90811561026257610d2f8373ffffffffffffffffffffffffffffffffffffffff610d37946101be985f91610d79575b501686611286565b8094876110f5565b9590947fd3c1d9b397236779b29ee5b5b150c1110fc8221b6b6ec0be49c9f4860ceb203673ffffffffffffffffffffffffffffffffffffffff61045d85610ec0565b610d92915060603d60601161050f576104f98183610de9565b50505f610d27565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610120810190811067ffffffffffffffff821117610de457604052565b610d9a565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610de457604052565b9190826040910312610267576020825192015190565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211610e8557565b610e4b565b90816060910312610267578051610ea08161026b565b9160406020830151610eb18161026b565b920151610ebd8161026b565b90565b35610ebd8161026b565b6040515f8152610ebd602082610de9565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b610ebd939273ffffffffffffffffffffffffffffffffffffffff60609316825260208201528160408201520190610edb565b670de0b6b3a76400000390670de0b6b3a76400008211610e8557565b91908203918211610e8557565b90816060910312610267578051916040602083015192015190565b92919082610fa3575b50505050565b610fac93611bc8565b5f808080610f9d565b9092919273ffffffffffffffffffffffffffffffffffffffff8116916040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481875afa8015610262576110949660409461102c935f936110c9575b50610a77611025916118a0565b4292611d7a565b50509283915f84518098819582947f5b709f1700000000000000000000000000000000000000000000000000000000845260048401909173ffffffffffffffffffffffffffffffffffffffff6080931682526020820152606060408201525f60608201520190565b03925af1928315610262575f936110aa57509190565b6110c491935060403d60401161025b5761024a8183610de9565b905090565b6110259193506110ea610a779160603d60601161050f576104f98183610de9565b915050939150611018565b929093919373ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa8015610262576111cd97604094611165935f936110c95750610a77611025916118a0565b50509485915f84518099819582947f5b709f1700000000000000000000000000000000000000000000000000000000845260048401909173ffffffffffffffffffffffffffffffffffffffff6080931682526020820152606060408201525f60608201520190565b03925af1938415610262575f946111ea575b50829392106101c257565b61120491945060403d60401161025b5761024a8183610de9565b9050925f6111df565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8181360301821215610267570190565b600b111561026757565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600b111561128157565b61124a565b9190611295608083018361120d565b3561129f81611240565b6112a881611277565b806112d55750610ebd926001916112be84610ec0565b6112cf602086013580923390612047565b9161215c565b806112e1600392611277565b0361132657610ebd926001916112f684610ec0565b611307602086013580923390612047565b6112cf8161131487610ec0565b61132060408901610ec0565b90612086565b73ffffffffffffffffffffffffffffffffffffffff61134483610ec0565b1661144857611357602083013533611fe0565b61138261136961136960608501610ec0565b73ffffffffffffffffffffffffffffffffffffffff1690565b9261138f61136984610ec0565b611441576020830135935b6113a384610ec0565b906020850135906113b7608087018761120d565b91813b15610267576113fb975f94604051998a95869485937f2bdb823c00000000000000000000000000000000000000000000000000000000855260048501611e8e565b03925af191821561026257610ebd94600193611427575b506112cf61142260408601610ec0565b611f76565b806114355f61143b93610de9565b80611e31565b5f611412565b5f9361139a565b61147061145761136984610ec0565b61146360608501610ec0565b6020850135913390610f94565b611357565b60409173ffffffffffffffffffffffffffffffffffffffff5f816114bf6114b5889b999a989a6114a4876118a0565b906114ae8d611999565b4292612232565b5050998a966122fa565b996114f987519b8c97889687947f29910b110000000000000000000000000000000000000000000000000000000086521660048501610f1e565b0393165af1938415610262575f94611576575b508293921061151757565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f59545f4f55540000006044820152606490fd5b61159091945060403d60401161025b5761024a8183610de9565b9050925f61150c565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116102675790565b90815f03918083057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1490151715610e8557565b90670de0b6b3a7640000820291808305670de0b6b3a76400001490151715610e8557565b81810292915f82127f8000000000000000000000000000000000000000000000000000000000000000821416610e85578184051490151715610e8557565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd906115c3565b93916116a79095939195826116a0610a77896118a0565b429261235d565b50505073ffffffffffffffffffffffffffffffffffffffff86166040826116cc610eca565b9861170383519a8b9384937f5b709f1700000000000000000000000000000000000000000000000000000000855260048501610f1e565b03815f855af180156102625761178a976060945f915f936117c8575b505f9161172b91610f6c565b9197604051998a95869485937f156e29f60000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af1938415610262575f946117a5575b508310610bbe57565b6117bf91945060603d606011610c4957610c388183610de9565b5050925f61179c565b61172b9193505f92506117e99060403d60401161025b5761024a8183610de9565b9391509161171f565b6040519061180261012083610de9565b565b6040519061180260e083610de9565b906118026040519283610de9565b51906118028261026b565b9081610120910312610267576101006118436117f2565b9180518352602081015160208401526040810151604084015261186860608201611821565b60608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e0840152015161010082015290565b5f6101006040516118b081610dc7565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152015261012060405180927f794052f30000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff8161194b306004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0392165afa908115610262575f91611961575090565b610ebd91506101203d8111611983575b61197b8183610de9565b81019061182c565b503d611971565b90816020910312610267575190565b602073ffffffffffffffffffffffffffffffffffffffff60045f9360405194859384927f1d52edc4000000000000000000000000000000000000000000000000000000008452165af1908115610262575f916119f3575090565b610ebd915060203d602011611a15575b611a0d8183610de9565b81019061198a565b503d611a03565b15611a2357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e6f206578697374696e67206c700000000000000000000000000000000000006044820152fd5b9290939193611a9560408501511515611a1c565b611ae5611aba611ab35f88611aab87878b6124b5565b96868a6125e0565b508761264c565b611ac46040611813565b905f8252611adb611ad586896126c2565b8961270f565b60208301526127c2565b925f956020850195608086019460408701985b8851811015611b6957611b2487518b611b1582865f8a8d8b612870565b509294919590935186866128de565b611b5557505050906001929111155f14611b4757611b4189612b00565b01611af8565b611b5089612984565b611b41565b9d50909b5099509197509495505050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536c6970706167653a20415050524f585f4558484155535445440000000000006044820152606490fd5b5f73ffffffffffffffffffffffffffffffffffffffff8192611c77611cc69796611c4b60405198899260208401997f23b872dd000000000000000000000000000000000000000000000000000000008b526024850173ffffffffffffffffffffffffffffffffffffffff6040929594938160608401971683521660208201520152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101885287610de9565b169260405194611c88604087610de9565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1611cc06139a6565b916140c1565b8051908115918215611d60575b505015611cdc57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b611d739250602080918301019101612c80565b5f80611cd3565b939092611d9483611d8c8387896124b5565b928688613273565b611db3604091611da46040611813565b905f8252611adb895186612c95565b955f90602088019660808901925b8851811015611b69578484518b8a611ddb83878c8a612d61565b50949092828411611e19570151611df492909190612d80565b611e0c575050600190611e068b612c67565b01611dc1565b9a50985096505050505050565b925050506001939250611e2c9150612984565b611e06565b5f91031261026757565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b8015150361026757565b359061180282611e79565b919073ffffffffffffffffffffffffffffffffffffffff1682526020820152606060408201528135611ebf81611240565b600b81101561128157606082015273ffffffffffffffffffffffffffffffffffffffff6020830135611ef08161026b565b16608082015260408201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1833603018112156102675782019060208235920167ffffffffffffffff831161026757823603811361026757611f6f6060611f6860c093610ebd96608060a088015260e0870191611e3b565b9501611e83565b1515910152565b73ffffffffffffffffffffffffffffffffffffffff1680611f9657504790565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610262575f916119f3575090565b503403611fe957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f657468206d69736d6174636800000000000000000000000000000000000000006044820152fd5b90919073ffffffffffffffffffffffffffffffffffffffff1680612070575090503403611fe957565b8161207b575b505050565b611802923091611bc8565b73ffffffffffffffffffffffffffffffffffffffff16908161210c5773ffffffffffffffffffffffffffffffffffffffff915016803b15610267575f906004604051809481937fd0e30db00000000000000000000000000000000000000000000000000000000083525af18015610262576120fe5750565b806114355f61180293610de9565b50803b15610267576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af18015610262576120fe5750565b6040602094950173ffffffffffffffffffffffffffffffffffffffff6121a88235926121878461026b565b838316612227576121a38688955b61219e8161026b565b612dcd565b610ec0565b92612211604051988997889687947f20e8c565000000000000000000000000000000000000000000000000000000008652600486019094939273ffffffffffffffffffffffffffffffffffffffff60609381608085019816845216602083015260408201520152565b0393165af1908115610262575f916119f3575090565b6121a3865f95612195565b929390919361224e856122468386886124b5565b9285876132db565b61227560409161225e6040611813565b906122698988613b8c565b8252611adb85896126c2565b956020870195608088015f5b8851811015611b695787868b86886122b4856122af6122a68b8b51998a998a92612e97565b50989092613b9f565b610f6c565b928284116122e25701516122ca92909190612d80565b611e0c5750506001906122dc8b612c67565b01612281565b9250505060019392506122f59150612984565b6122dc565b9190606060405161230c608082610de9565b818152602081019460603687375f82965260408201520152565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610e8557565b81810292918115918404141715610e8557565b9193909361237060408401511515611a1c565b6123ae6123955f61238f84826123878a8c8b6124b5565b998b8a6125e0565b5061264c565b61239f6040611813565b905f8252611adb865188612c95565b925f926020850195608086019060408701955b8851811015611b69578251856123d9828d8684612d61565b8a831161246f5761241261240361241d926122af8660206123fb8b8b51610f6c565b990151610e78565b61240d5f88610e78565b61234a565b9361240d848d610f6c565b9361242a8c5186866128de565b61245b57505050906001929111155f1461244d5761244789612b00565b016123c1565b61245689612984565b612447565b9d50909b5099509197509395505050505050565b505050505060019061245689612984565b604051906080820182811067ffffffffffffffff821117610de4576040525f6060838281528260208201528260408201520152565b9291926124c0612480565b9360a082016124d0828251111590565b61257c576124fa916124e29151610f6c565b926124ed8484612f07565b8652602083015190612f81565b6020850190808252825180158015612574575b612546575050816125338460c09361253f969551906101008501519051908a5192612ffd565b604087015201516130a4565b6060830152565b7fb1c4aefb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b50811561250d565b7fb2094b59000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b81156125db570490565b6125a4565b949391946020810151935f8512610267578151965f88126102675761260e8661261692819661261c96613273565b93849261234a565b9361234a565b8201809211610e8557848101809111610e855780156125db576126419104809461234a565b81156125db57049190565b818110612657570390565b50505f90565b9190915f8382019384129112908015821691151617610e8557565b905f82670de0b6b3a7640000039212670de0b6b3a76400008312811690670de0b6b3a7640000841390151617610e8557565b81810392915f138015828513169184121617610e8557565b906126d490602083519101519061265d565b80670d529ae9e86000000290670d529ae9e8600000820503610e8557670de0b6b3a7640000612705925191056126aa565b5f81126102675790565b90808210156110c4575090565b6040519060e0820182811067ffffffffffffffff821117610de457604052815f81525f60208201525f60408201525f60608201525f608082015260c060409182516127678482610de9565b8336823760a082015282519261277d8185610de9565b3684370152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b1561278457565b6003111561128157565b906127cb61271c565b506127f38151926127e96127df8460200190565b94855110156127b1565b8251845191613120565b9161282766b1a2bc2ec500009161281f6128186128108588613142565b865190613174565b9386613181565b90519061270f565b6128316040611813565b9182526020820152612841611804565b925f8452601e6020850152652d79883d20006040850152806060850152608084015260a083015260c082015290565b92936128899196959661288288612d35565b9185613869565b90929181939481968151918a8301809311610e85576020015193838503948511610e85578403938411610e85578201809211610e85576128c89161234a565b968203918211610e85576128db9161234a565b93565b9082670de0b6b3a764000003670de0b6b3a76400008111610e8557670de0b6b3a764000061290d84928461234a565b041115928361291d575b50505090565b90919250670de0b6b3a7640000019081670de0b6b3a764000011610e8557670de0b6b3a76400009161294e9161234a565b0410155f8080612917565b8015610e85577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b608081019081519060a081019161299c835160200190565b5281516020016129ac8151612959565b905280516129b9816127b8565b6129c2816127b8565b6129d0576001905251519052565b600181939293516129e0816127b8565b6129e9816127b8565b03612aa65781519060c081019182515114612a4757825184515103612a305790612a1d6060612a2993015185515190610f6c565b8451519151519161323c565b8092515252565b6002815261180293509150612a429050565b6131af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f536c6970706167653a207365617263682072616e676520756e646572666c6f776044820152606490fd5b8092506002915051612ab7816127b8565b612ac0816127b8565b03612ace57611802906131af565b612784565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e855760010190565b60808101805160a08301908151528051612b1a8151612ad3565b90528251612b27816127b8565b612b30816127b8565b612b4e57612b42612b49929360019052565b5160200190565b519052565b60018351612b5b816127b8565b612b64816127b8565b03612c5857815160c0840190612b7b825160200190565b5114612bf957612b978351612b91845160200190565b51141590565b612be957612bdb90612bbd612be5956060612bb3865160200190565b5191015190610f6c565b612bd4612bcb855160200190565b51925160200190565b5191613256565b9283915160200190565b5252565b50506002825250611802906131af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f536c6970706167653a207365617263682072616e6765206f766572666c6f77006044820152606490fd5b505060028151612ab7816127b8565b60808101805160a08301908151528251612b27816127b8565b908160209103126102675751610ebd81611e79565b90612cc6670de0b6b3a7640000612cc0612cb860608601516040870151906126aa565b85519061161b565b05613398565b91670de0b6b3a7640000830190670de0b6b3a764000082125f8512908015821691151617610e8557612d0c6020612d03612d1294612d1897613860565b9201518461265d565b90613109565b906126aa565b6103e78102908082046103e71490151715610e85576103e8900490565b7f80000000000000000000000000000000000000000000000000000000000000008114610e85575f0390565b91612d7b949391612d7193613869565b9291939093612d35565b929190565b90808211159283612d915750505090565b90919250670de0b6b3a76400000390670de0b6b3a76400008211610e8557670de0b6b3a764000091612dc29161234a565b0411155f8080612917565b9073ffffffffffffffffffffffffffffffffffffffff82168015612076576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316602482015290602090829060449082905afa908115610262575f91612e78575b506b7fffffffffffffffffffffff11612e65575050565b81612e738261180294613a03565b613b0b565b612e91915060203d602011611a1557611a0d8183610de9565b5f612e4e565b9190612ea5612eab94612d35565b92613869565b9192909190565b81156125db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82147f8000000000000000000000000000000000000000000000000000000000000000821416610e85570590565b608001516301e13380612f199161161b565b907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757612f4a91612eb2565b905f821315612f5557565b507f1ca41876000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b612fc1670de0b6b3a7640000915f84125f14612ff25761240d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94613bff565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b61240d600194613bff565b91936130089161234a565b6301e1338090047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116102675761303f90613398565b92670de0b6b3a76400008412613078579161307361306e83613068610ebd9796612d129661265d565b90613860565b613c13565b613860565b837fca78c8a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b906130ae9161234a565b6301e1338090047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd90613398565b80156125db576ec097ce7bc90715b34b9f10000000000590565b81156125db570590565b670de0b6b3a76400009161311c9161161b565b0590565b909181838110156131315750505090565b81929493501161313e5750565b9150565b90670de0b6b3a76400000390670de0b6b3a76400008211610e8557670de0b6b3a7640000916131709161234a565b0490565b90808211156110c4575090565b90670de0b6b3a7640000019081670de0b6b3a764000011610e8557670de0b6b3a7640000916131709161234a565b60a08101805151602082510151106131de576020815151915101518101809111610e855760809060011c910152565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a2067756573734d696e203e2067756573734d61780000006044820152fd5b91908281111561324c5750905090565b610ebd9203613174565b9190828119105f146132685750905090565b610ebd92019061270f565b909160a0610100830151920151908103908111610e8557613293916130a4565b6ec097ce7bc90715b34b9f100000000081156125db5704916132b483610f50565b50670de0b6b3a7640000916132c89161234a565b04906132d6610ebd92612326565b6125d1565b909160a0610100830151920151908103908111610e85576132fb916130a4565b6ec097ce7bc90715b34b9f10000000009181156125db5761261661332992670de0b6b3a76400009404610f50565b046125d190612326565b1561333a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e74000000000000000000000000000000006044820152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc00008112158061384d575b6133cb90613333565b5f811261383957610ebd906806f05b59d3b200000081126137e2577ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e000000016135ab6064770195e54c5dd42177f53a27172fa9ec630262827000000000925b0268056bc75e2d631000009068ad78ebc5ac620000008112156137a9575b6856bc75e2d63100000081121561375f575b682b5e3af16b18800000811215613717575b6815af1d78b58c4000008112156136cf575b680ad78ebc5ac6200000811215613688575b68056bc75e2d63100000811215613641575b6802b5e3af16b18800008112156135fa575b68015af1d78b58c400008112156135b3575b600268056bc75e2d631000008280020505600368056bc75e2d631000008383020505600468056bc75e2d631000008483020505600568056bc75e2d631000008583020505600668056bc75e2d631000008683020505600768056bc75e2d63100000878302050590600868056bc75e2d63100000888402050592600968056bc75e2d6310000089860205059468056bc75e2d63100000600a8a8802829005059761357c68056bc75e2d631000008c8b02055b600b900590565b99600c68056bc75e2d631000008d8d0205059b0101010101010101010101010268056bc75e2d63100000900590565b026064900590565b6806f5f17757889379377ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c00006135f49201920268056bc75e2d63100000900590565b906134c4565b6808f00f760a4b2db55d7ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000061363b9201920268056bc75e2d63100000900590565b906134b2565b680ebc5fb417461211107ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000006136829201920268056bc75e2d63100000900590565b906134a0565b68280e60114edb805d037ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e000006136c99201920268056bc75e2d63100000900590565b9061348e565b690127fa27722cc06cc5e27fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000006137119201920268056bc75e2d63100000900590565b9061347c565b693f1fce3da636ea5cf8507fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000006137599201920268056bc75e2d63100000900590565b9061346a565b6b02df0ab5a80a22c61ab5a7007fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000006137a39201920268056bc75e2d63100000900590565b90613458565b6e01855144814a7ff805980ff008400091507fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e00000001613446565b6803782dace9d9000000811261382c577ffffffffffffffffffffffffffffffffffffffffffffffffc87d2531627000000016135ab60646b1425982cf597cd205cef738092613428565b6135ab6064600192613428565b613844905f03613398565b610ebd906130e5565b5068070c1cc73b00c800008113156133c2565b6130ff906115f7565b90939192613886818351602088015188519060408a015192613c8c565b5f60606138a361389e84613899876115f7565b6130ff565b6115c3565b97015192131561396957816138996138ba926115f7565b670de0b6b3a7640000811261393e575093613929613910610ebd9361390a6139036138fd6138f26138ec60e09c612678565b88613109565b9a8b945b0151611599565b8361161b565b6064900590565b936126aa565b5f81121561392f576139229086613e3e565b9585613da9565b93613da9565b6139399086613da9565b613922565b7fca78c8a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b5093613929613910610ebd9361390a6139036138fd61399e61389e8c61399961399360e09f612678565b8b61161b565b612eb2565b9a8b946138f6565b3d156139fe573d9067ffffffffffffffff8211610de457604051916139f3601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200184610de9565b82523d5f602084013e565b606090565b5f919082918260405173ffffffffffffffffffffffffffffffffffffffff60208201937f095ea7b300000000000000000000000000000000000000000000000000000000855216602482015281604482015260448152613a64606482610de9565b51925af1613a706139a6565b81613adc575b5015613a7e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5361666520417070726f766500000000000000000000000000000000000000006044820152fd5b8051801592508215613af1575b50505f613a76565b613b049250602080918301019101612c80565b5f80613ae9565b5f919082918260405173ffffffffffffffffffffffffffffffffffffffff60208201937f095ea7b30000000000000000000000000000000000000000000000000000000085521660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604482015260448152613a64606482610de9565b61317090670de0b6b3a76400009261234a565b90670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610e8557818101809111610e85577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908111610e855781156125db570490565b5f811315613c0a5790565b610ebd90612d35565b670de0b6b3a76400008114613c645780670de0b6b3a764000003905f8112670de0b6b3a76400008312811690670de0b6b3a7640000841390151617610e8557610ebd91613c5f91613860565b613eb5565b7fa9c8b14d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90919493808212613d4b57613cae92613ca861306892846126aa565b9261265d565b92670d529ae9e86000008413613d1357613ccf90613073613cd49495613c13565b61265d565b90670de0b6b3a76400008212613ce657565b7fca78c8a4000000000000000000000000000000000000000000000000000000005f52600482905260245ffd5b837ffc68d09e000000000000000000000000000000000000000000000000000000005f52600452670d529ae9e860000060245260445ffd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e656761746976650000000000000000000000000000000000000000000000006044820152fd5b5f821215613e3357613ddb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92613bff565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610e855781156125db57047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b613ddb600192613bff565b613e7a905f831215613eaa57613e747fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93613bff565b90613b9f565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b613e74600193613bff565b5f8113156140635780670c7d713b49da00001280614052575b1561404957670de0b6b3a7640000026ec097ce7bc90715b34b9f100000000081019081156125db57614037614030613fbc614013613f9f613ff6610ebd976ec097ce7bc90715b34b9f10000000007fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f000000000613f8799010205613fd96ec097ce7bc90715b34b9f10000000008280020580968180968186613f7e828099026ec097ce7bc90715b34b9f1000000000900590565b9e8f6003900590565b019d026ec097ce7bc90715b34b9f1000000000900590565b9b60058d05019b026ec097ce7bc90715b34b9f1000000000900590565b9960078b050199026ec097ce7bc90715b34b9f1000000000900590565b97600989050197026ec097ce7bc90715b34b9f1000000000900590565b95600b87050195026ec097ce7bc90715b34b9f1000000000900590565b93600d85050193026ec097ce7bc90715b34b9f1000000000900590565b600f900590565b0160011b670de0b6b3a7640000900590565b610ebd9061418d565b50670f43fc2c04ee00008112613ece565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e6473000000000000000000000000000000000000006044820152fd5b9192901561413c57508151156140d5575090565b3b156140de5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561414f5750805190602001fd5b610222906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401526024830190610edb565b670de0b6b3a764000081126145b457610ebd905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215614589575b73011798004d755d3c8bc8e03204cf44619e00000082121561455c575b6142e9906064808492029302906e01855144814a7ff805980ff0084000821215614522575b506b02df0ab5a80a22c61ab5a7008112156144ef575b693f1fce3da636ea5cf8508112156144be575b690127fa27722cc06cc5e281121561448d575b68280e60114edb805d0381121561445d575b680ebc5fb4174612111081121561443d575b6808f00f760a4b2db55d81121561440d575b6806f5f17757889379378112156143dd575b6806248f33704b2866038112156143ae575b6805c548670b9510e7ac81121561437f575b68056bc75e2d631000007ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000008183019201026130ff565b61437361357568056bc75e2d631000008380020561435c816143458161432e8168056bc75e2d631000008a8202059960038b0501990268056bc75e2d63100000900590565b976005890501970268056bc75e2d63100000900590565b956007870501950268056bc75e2d63100000900590565b936009850501930268056bc75e2d63100000900590565b0160011b016064900590565b6143a668056bc75e2d631000006756bc75e2d631000092026805c548670b9510e7ac900590565b9201916142b3565b6143d568056bc75e2d6310000067ad78ebc5ac62000092026806248f33704b286603900590565b9201916142a1565b61440568056bc75e2d6310000068015af1d78b58c4000092026806f5f1775788937937900590565b92019161428f565b61443568056bc75e2d631000006802b5e3af16b188000092026808f00f760a4b2db55d900590565b92019161427d565b68056bc75e2d6310000092830192680ebc5fb4174612111091020561426b565b61448568056bc75e2d63100000680ad78ebc5ac6200000920268280e60114edb805d03900590565b920191614259565b6144b668056bc75e2d631000006815af1d78b58c4000009202690127fa27722cc06cc5e2900590565b920191614247565b6144e768056bc75e2d63100000682b5e3af16b188000009202693f1fce3da636ea5cf850900590565b920191614234565b61451a68056bc75e2d631000006856bc75e2d63100000092026b02df0ab5a80a22c61ab5a700900590565b920191614221565b68ad78ebc5ac62000000915069021e19e0c9bab240000061455391026e01855144814a7ff805980ff0084000900590565b9201915f61420b565b906803782dace9d90000006145806142e9926b1425982cf597cd205cef7380900590565b920190506141e6565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b20000006141c9565b6145c06145c5916130e5565b61418d565b5f039056fea2646970667358221220bc99ced2e9e8cb93db1f0f9bef74def300a0c60623c45b720aed516b545b8dd264736f6c634300081c0033
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c80630af8a5cf1461009457806322bfddd01461008f578063252f9db31461008a57806340a169f9146100855780634cc30915146100805780636afe69981461007b578063f2f6eae5146100765763feb9d1d214610071575f80fd5b610ca5565b6109d8565b6108bc565b610754565b610634565b610516565b610365565b34610267576100a236610289565b91929073ffffffffffffffffffffffffffffffffffffffff8416906100c981863385610f94565b604080517ff6b911bc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152851660248201526044810183905290816064815f875af1958615610262575f915f97610226575b50610149916101439186610fb5565b96610e78565b9384106101c2577fac97b87f5422fa3beec99bff8f336310d8ebc7d33d909b7d534cd7c72f61e87173ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b0390a460408051918252602082019290925290819081015b0390f35b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f50545f4f5554000000604482015280606481015b0390fd5b61014391975061014992506102529060403d60401161025b575b61024a8183610de9565b810190610e2a565b97915091610134565b503d610240565b610e40565b5f80fd5b73ffffffffffffffffffffffffffffffffffffffff81160361026757565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080910112610267576004356102bf8161026b565b906024356102cc8161026b565b906044359060643590565b9060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126102675760043561030e8161026b565b9160243561031b8161026b565b91604435916064359067ffffffffffffffff8211610267577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8260a0920301126102675760040190565b61036e366102d7565b73ffffffffffffffffffffffffffffffffffffffff839294931693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa918215610262576101be945f945f946104d4575b5061041283859673ffffffffffffffffffffffffffffffffffffffff61040a61041b979873ffffffffffffffffffffffffffffffffffffffff1690565b911690611286565b94859188611475565b9590947fa3a2846538c60e47775faa60c6ae79b67dee6d97bb70e386ebbaf4c3a38e8b8173ffffffffffffffffffffffffffffffffffffffff61045d85610ec0565b6104b487610477602061046f8d611599565b990135611659565b60405194859416983397859094939260609273ffffffffffffffffffffffffffffffffffffffff6080840197168352602083015260408201520152565b0390a4604051938493846040919493926060820195825260208201520152565b83955061041b9394506105016104129160603d60601161050f575b6104f98183610de9565b810190610e8a565b9791905096955093506103cd565b503d6104ef565b346102675761052436610289565b92909173ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa8015610262576105ae938680935f905f94610607575b5090826105a89273ffffffffffffffffffffffffffffffffffffffff339116610f94565b85611689565b9390927fb51950711c9b21dc7888d41f68a19540231ffb5f0d19d8f75cbccaf90ffa7fa573ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b8394506105a89291506106289060603d60601161050f576104f98183610de9565b95919050919250610584565b346102675761064236610289565b92909173ffffffffffffffffffffffffffffffffffffffff811693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa908115610262576106c4868281946106ca975f91610733575b5073ffffffffffffffffffffffffffffffffffffffff339116610f94565b846110f5565b9390927f3f5e2944826baeaed8eb77f0f74e6088a154a0fc1317f062fd984585607b473973ffffffffffffffffffffffffffffffffffffffff61071561070f87611599565b93611659565b604080519485526020850191909152941693339290819081016101a6565b61074c915060603d60601161050f576104f98183610de9565b50505f6106a6565b61075d366102d7565b73ffffffffffffffffffffffffffffffffffffffff839294931693604051917f2c8ce6bc000000000000000000000000000000000000000000000000000000008352606083600481895afa918215610262576101be945f945f94610874575b506107e38373ffffffffffffffffffffffffffffffffffffffff6107ec9596971683611286565b94859188611689565b9590947f387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee73ffffffffffffffffffffffffffffffffffffffff602061083086610ec0565b6040805173ffffffffffffffffffffffffffffffffffffffff90961686529190960135602085015283018890526060830186905290931692339180608081016104b4565b73ffffffffffffffffffffffffffffffffffffffff9550836107ec9495506108ad6107e39260603d60601161050f576104f98183610de9565b989190509796505093506107bc565b34610267576108ca36610289565b92909173ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa80156102625761096b938680935f905f946109b0575b50610965919073ffffffffffffffffffffffffffffffffffffffff85169073ffffffffffffffffffffffffffffffffffffffff339116610f94565b85611475565b9390927f05499aba408f669fb848399c146fad5bd604d50b15566bdc19e81c40922fab8d73ffffffffffffffffffffffffffffffffffffffff61071561070f87611599565b6109659294506109cf915060603d60601161050f576104f98183610de9565b9492905061092a565b34610267576109e636610289565b929073ffffffffffffffffffffffffffffffffffffffff8316906040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481865afa90811561026257610a84918387925f905f93610c7e575b5092610a6a929373ffffffffffffffffffffffffffffffffffffffff339116610f94565b82610a7d610a77886118a0565b92611999565b4292611a81565b50505094604086610a93610eca565b96610aca835198899384937f29910b1100000000000000000000000000000000000000000000000000000000855260048501610f1e565b03815f875af1948515610262575f905f96610c50575b50606090610af2610b4d979885610f6c565b6040517f156e29f600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff881660048201526024810192909252604482015295869081906064820190565b03815f875af1948515610262575f95610c1d575b508410610bbe577fc87b85efc5055ef177e0092af0d4e624fff4b1d57db748857f65e4b7e4a28a3673ffffffffffffffffffffffffffffffffffffffff604051941693806101a68733958360209093929193604081019481520152565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f4c505f4f55540000006044820152606490fd5b610c4091955060603d606011610c49575b610c388183610de9565b810190610f79565b5050935f610b61565b503d610c2e565b610b4d965060609150610c74610af29160403d60401161025b5761024a8183610de9565b9750919050610ae0565b610a6a9350610c9c915060603d60601161050f576104f98183610de9565b93909150610a46565b610cae366102d7565b929173ffffffffffffffffffffffffffffffffffffffff811693604051927f2c8ce6bc000000000000000000000000000000000000000000000000000000008452606084600481895afa90811561026257610d2f8373ffffffffffffffffffffffffffffffffffffffff610d37946101be985f91610d79575b501686611286565b8094876110f5565b9590947fd3c1d9b397236779b29ee5b5b150c1110fc8221b6b6ec0be49c9f4860ceb203673ffffffffffffffffffffffffffffffffffffffff61045d85610ec0565b610d92915060603d60601161050f576104f98183610de9565b50505f610d27565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610120810190811067ffffffffffffffff821117610de457604052565b610d9a565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610de457604052565b9190826040910312610267576020825192015190565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211610e8557565b610e4b565b90816060910312610267578051610ea08161026b565b9160406020830151610eb18161026b565b920151610ebd8161026b565b90565b35610ebd8161026b565b6040515f8152610ebd602082610de9565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b610ebd939273ffffffffffffffffffffffffffffffffffffffff60609316825260208201528160408201520190610edb565b670de0b6b3a76400000390670de0b6b3a76400008211610e8557565b91908203918211610e8557565b90816060910312610267578051916040602083015192015190565b92919082610fa3575b50505050565b610fac93611bc8565b5f808080610f9d565b9092919273ffffffffffffffffffffffffffffffffffffffff8116916040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481875afa8015610262576110949660409461102c935f936110c9575b50610a77611025916118a0565b4292611d7a565b50509283915f84518098819582947f5b709f1700000000000000000000000000000000000000000000000000000000845260048401909173ffffffffffffffffffffffffffffffffffffffff6080931682526020820152606060408201525f60608201520190565b03925af1928315610262575f936110aa57509190565b6110c491935060403d60401161025b5761024a8183610de9565b905090565b6110259193506110ea610a779160603d60601161050f576104f98183610de9565b915050939150611018565b929093919373ffffffffffffffffffffffffffffffffffffffff8116936040517f2c8ce6bc000000000000000000000000000000000000000000000000000000008152606081600481895afa8015610262576111cd97604094611165935f936110c95750610a77611025916118a0565b50509485915f84518099819582947f5b709f1700000000000000000000000000000000000000000000000000000000845260048401909173ffffffffffffffffffffffffffffffffffffffff6080931682526020820152606060408201525f60608201520190565b03925af1938415610262575f946111ea575b50829392106101c257565b61120491945060403d60401161025b5761024a8183610de9565b9050925f6111df565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8181360301821215610267570190565b600b111561026757565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600b111561128157565b61124a565b9190611295608083018361120d565b3561129f81611240565b6112a881611277565b806112d55750610ebd926001916112be84610ec0565b6112cf602086013580923390612047565b9161215c565b806112e1600392611277565b0361132657610ebd926001916112f684610ec0565b611307602086013580923390612047565b6112cf8161131487610ec0565b61132060408901610ec0565b90612086565b73ffffffffffffffffffffffffffffffffffffffff61134483610ec0565b1661144857611357602083013533611fe0565b61138261136961136960608501610ec0565b73ffffffffffffffffffffffffffffffffffffffff1690565b9261138f61136984610ec0565b611441576020830135935b6113a384610ec0565b906020850135906113b7608087018761120d565b91813b15610267576113fb975f94604051998a95869485937f2bdb823c00000000000000000000000000000000000000000000000000000000855260048501611e8e565b03925af191821561026257610ebd94600193611427575b506112cf61142260408601610ec0565b611f76565b806114355f61143b93610de9565b80611e31565b5f611412565b5f9361139a565b61147061145761136984610ec0565b61146360608501610ec0565b6020850135913390610f94565b611357565b60409173ffffffffffffffffffffffffffffffffffffffff5f816114bf6114b5889b999a989a6114a4876118a0565b906114ae8d611999565b4292612232565b5050998a966122fa565b996114f987519b8c97889687947f29910b110000000000000000000000000000000000000000000000000000000086521660048501610f1e565b0393165af1938415610262575f94611576575b508293921061151757565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a20494e53554646494349454e545f59545f4f55540000006044820152606490fd5b61159091945060403d60401161025b5761024a8183610de9565b9050925f61150c565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116102675790565b90815f03918083057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1490151715610e8557565b90670de0b6b3a7640000820291808305670de0b6b3a76400001490151715610e8557565b81810292915f82127f8000000000000000000000000000000000000000000000000000000000000000821416610e85578184051490151715610e8557565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd906115c3565b93916116a79095939195826116a0610a77896118a0565b429261235d565b50505073ffffffffffffffffffffffffffffffffffffffff86166040826116cc610eca565b9861170383519a8b9384937f5b709f1700000000000000000000000000000000000000000000000000000000855260048501610f1e565b03815f855af180156102625761178a976060945f915f936117c8575b505f9161172b91610f6c565b9197604051998a95869485937f156e29f60000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af1938415610262575f946117a5575b508310610bbe57565b6117bf91945060603d606011610c4957610c388183610de9565b5050925f61179c565b61172b9193505f92506117e99060403d60401161025b5761024a8183610de9565b9391509161171f565b6040519061180261012083610de9565b565b6040519061180260e083610de9565b906118026040519283610de9565b51906118028261026b565b9081610120910312610267576101006118436117f2565b9180518352602081015160208401526040810151604084015261186860608201611821565b60608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e0840152015161010082015290565b5f6101006040516118b081610dc7565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152015261012060405180927f794052f30000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff8161194b306004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0392165afa908115610262575f91611961575090565b610ebd91506101203d8111611983575b61197b8183610de9565b81019061182c565b503d611971565b90816020910312610267575190565b602073ffffffffffffffffffffffffffffffffffffffff60045f9360405194859384927f1d52edc4000000000000000000000000000000000000000000000000000000008452165af1908115610262575f916119f3575090565b610ebd915060203d602011611a15575b611a0d8183610de9565b81019061198a565b503d611a03565b15611a2357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e6f206578697374696e67206c700000000000000000000000000000000000006044820152fd5b9290939193611a9560408501511515611a1c565b611ae5611aba611ab35f88611aab87878b6124b5565b96868a6125e0565b508761264c565b611ac46040611813565b905f8252611adb611ad586896126c2565b8961270f565b60208301526127c2565b925f956020850195608086019460408701985b8851811015611b6957611b2487518b611b1582865f8a8d8b612870565b509294919590935186866128de565b611b5557505050906001929111155f14611b4757611b4189612b00565b01611af8565b611b5089612984565b611b41565b9d50909b5099509197509495505050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536c6970706167653a20415050524f585f4558484155535445440000000000006044820152606490fd5b5f73ffffffffffffffffffffffffffffffffffffffff8192611c77611cc69796611c4b60405198899260208401997f23b872dd000000000000000000000000000000000000000000000000000000008b526024850173ffffffffffffffffffffffffffffffffffffffff6040929594938160608401971683521660208201520152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101885287610de9565b169260405194611c88604087610de9565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af1611cc06139a6565b916140c1565b8051908115918215611d60575b505015611cdc57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b611d739250602080918301019101612c80565b5f80611cd3565b939092611d9483611d8c8387896124b5565b928688613273565b611db3604091611da46040611813565b905f8252611adb895186612c95565b955f90602088019660808901925b8851811015611b69578484518b8a611ddb83878c8a612d61565b50949092828411611e19570151611df492909190612d80565b611e0c575050600190611e068b612c67565b01611dc1565b9a50985096505050505050565b925050506001939250611e2c9150612984565b611e06565b5f91031261026757565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b8015150361026757565b359061180282611e79565b919073ffffffffffffffffffffffffffffffffffffffff1682526020820152606060408201528135611ebf81611240565b600b81101561128157606082015273ffffffffffffffffffffffffffffffffffffffff6020830135611ef08161026b565b16608082015260408201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1833603018112156102675782019060208235920167ffffffffffffffff831161026757823603811361026757611f6f6060611f6860c093610ebd96608060a088015260e0870191611e3b565b9501611e83565b1515910152565b73ffffffffffffffffffffffffffffffffffffffff1680611f9657504790565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610262575f916119f3575090565b503403611fe957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f657468206d69736d6174636800000000000000000000000000000000000000006044820152fd5b90919073ffffffffffffffffffffffffffffffffffffffff1680612070575090503403611fe957565b8161207b575b505050565b611802923091611bc8565b73ffffffffffffffffffffffffffffffffffffffff16908161210c5773ffffffffffffffffffffffffffffffffffffffff915016803b15610267575f906004604051809481937fd0e30db00000000000000000000000000000000000000000000000000000000083525af18015610262576120fe5750565b806114355f61180293610de9565b50803b15610267576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af18015610262576120fe5750565b6040602094950173ffffffffffffffffffffffffffffffffffffffff6121a88235926121878461026b565b838316612227576121a38688955b61219e8161026b565b612dcd565b610ec0565b92612211604051988997889687947f20e8c565000000000000000000000000000000000000000000000000000000008652600486019094939273ffffffffffffffffffffffffffffffffffffffff60609381608085019816845216602083015260408201520152565b0393165af1908115610262575f916119f3575090565b6121a3865f95612195565b929390919361224e856122468386886124b5565b9285876132db565b61227560409161225e6040611813565b906122698988613b8c565b8252611adb85896126c2565b956020870195608088015f5b8851811015611b695787868b86886122b4856122af6122a68b8b51998a998a92612e97565b50989092613b9f565b610f6c565b928284116122e25701516122ca92909190612d80565b611e0c5750506001906122dc8b612c67565b01612281565b9250505060019392506122f59150612984565b6122dc565b9190606060405161230c608082610de9565b818152602081019460603687375f82965260408201520152565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610e8557565b81810292918115918404141715610e8557565b9193909361237060408401511515611a1c565b6123ae6123955f61238f84826123878a8c8b6124b5565b998b8a6125e0565b5061264c565b61239f6040611813565b905f8252611adb865188612c95565b925f926020850195608086019060408701955b8851811015611b69578251856123d9828d8684612d61565b8a831161246f5761241261240361241d926122af8660206123fb8b8b51610f6c565b990151610e78565b61240d5f88610e78565b61234a565b9361240d848d610f6c565b9361242a8c5186866128de565b61245b57505050906001929111155f1461244d5761244789612b00565b016123c1565b61245689612984565b612447565b9d50909b5099509197509395505050505050565b505050505060019061245689612984565b604051906080820182811067ffffffffffffffff821117610de4576040525f6060838281528260208201528260408201520152565b9291926124c0612480565b9360a082016124d0828251111590565b61257c576124fa916124e29151610f6c565b926124ed8484612f07565b8652602083015190612f81565b6020850190808252825180158015612574575b612546575050816125338460c09361253f969551906101008501519051908a5192612ffd565b604087015201516130a4565b6060830152565b7fb1c4aefb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b50811561250d565b7fb2094b59000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b81156125db570490565b6125a4565b949391946020810151935f8512610267578151965f88126102675761260e8661261692819661261c96613273565b93849261234a565b9361234a565b8201809211610e8557848101809111610e855780156125db576126419104809461234a565b81156125db57049190565b818110612657570390565b50505f90565b9190915f8382019384129112908015821691151617610e8557565b905f82670de0b6b3a7640000039212670de0b6b3a76400008312811690670de0b6b3a7640000841390151617610e8557565b81810392915f138015828513169184121617610e8557565b906126d490602083519101519061265d565b80670d529ae9e86000000290670d529ae9e8600000820503610e8557670de0b6b3a7640000612705925191056126aa565b5f81126102675790565b90808210156110c4575090565b6040519060e0820182811067ffffffffffffffff821117610de457604052815f81525f60208201525f60408201525f60608201525f608082015260c060409182516127678482610de9565b8336823760a082015282519261277d8185610de9565b3684370152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b1561278457565b6003111561128157565b906127cb61271c565b506127f38151926127e96127df8460200190565b94855110156127b1565b8251845191613120565b9161282766b1a2bc2ec500009161281f6128186128108588613142565b865190613174565b9386613181565b90519061270f565b6128316040611813565b9182526020820152612841611804565b925f8452601e6020850152652d79883d20006040850152806060850152608084015260a083015260c082015290565b92936128899196959661288288612d35565b9185613869565b90929181939481968151918a8301809311610e85576020015193838503948511610e85578403938411610e85578201809211610e85576128c89161234a565b968203918211610e85576128db9161234a565b93565b9082670de0b6b3a764000003670de0b6b3a76400008111610e8557670de0b6b3a764000061290d84928461234a565b041115928361291d575b50505090565b90919250670de0b6b3a7640000019081670de0b6b3a764000011610e8557670de0b6b3a76400009161294e9161234a565b0410155f8080612917565b8015610e85577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b608081019081519060a081019161299c835160200190565b5281516020016129ac8151612959565b905280516129b9816127b8565b6129c2816127b8565b6129d0576001905251519052565b600181939293516129e0816127b8565b6129e9816127b8565b03612aa65781519060c081019182515114612a4757825184515103612a305790612a1d6060612a2993015185515190610f6c565b8451519151519161323c565b8092515252565b6002815261180293509150612a429050565b6131af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f536c6970706167653a207365617263682072616e676520756e646572666c6f776044820152606490fd5b8092506002915051612ab7816127b8565b612ac0816127b8565b03612ace57611802906131af565b612784565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e855760010190565b60808101805160a08301908151528051612b1a8151612ad3565b90528251612b27816127b8565b612b30816127b8565b612b4e57612b42612b49929360019052565b5160200190565b519052565b60018351612b5b816127b8565b612b64816127b8565b03612c5857815160c0840190612b7b825160200190565b5114612bf957612b978351612b91845160200190565b51141590565b612be957612bdb90612bbd612be5956060612bb3865160200190565b5191015190610f6c565b612bd4612bcb855160200190565b51925160200190565b5191613256565b9283915160200190565b5252565b50506002825250611802906131af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f536c6970706167653a207365617263682072616e6765206f766572666c6f77006044820152606490fd5b505060028151612ab7816127b8565b60808101805160a08301908151528251612b27816127b8565b908160209103126102675751610ebd81611e79565b90612cc6670de0b6b3a7640000612cc0612cb860608601516040870151906126aa565b85519061161b565b05613398565b91670de0b6b3a7640000830190670de0b6b3a764000082125f8512908015821691151617610e8557612d0c6020612d03612d1294612d1897613860565b9201518461265d565b90613109565b906126aa565b6103e78102908082046103e71490151715610e85576103e8900490565b7f80000000000000000000000000000000000000000000000000000000000000008114610e85575f0390565b91612d7b949391612d7193613869565b9291939093612d35565b929190565b90808211159283612d915750505090565b90919250670de0b6b3a76400000390670de0b6b3a76400008211610e8557670de0b6b3a764000091612dc29161234a565b0411155f8080612917565b9073ffffffffffffffffffffffffffffffffffffffff82168015612076576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316602482015290602090829060449082905afa908115610262575f91612e78575b506b7fffffffffffffffffffffff11612e65575050565b81612e738261180294613a03565b613b0b565b612e91915060203d602011611a1557611a0d8183610de9565b5f612e4e565b9190612ea5612eab94612d35565b92613869565b9192909190565b81156125db577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82147f8000000000000000000000000000000000000000000000000000000000000000821416610e85570590565b608001516301e13380612f199161161b565b907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757612f4a91612eb2565b905f821315612f5557565b507f1ca41876000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b612fc1670de0b6b3a7640000915f84125f14612ff25761240d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94613bff565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b61240d600194613bff565b91936130089161234a565b6301e1338090047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116102675761303f90613398565b92670de0b6b3a76400008412613078579161307361306e83613068610ebd9796612d129661265d565b90613860565b613c13565b613860565b837fca78c8a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b906130ae9161234a565b6301e1338090047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd90613398565b80156125db576ec097ce7bc90715b34b9f10000000000590565b81156125db570590565b670de0b6b3a76400009161311c9161161b565b0590565b909181838110156131315750505090565b81929493501161313e5750565b9150565b90670de0b6b3a76400000390670de0b6b3a76400008211610e8557670de0b6b3a7640000916131709161234a565b0490565b90808211156110c4575090565b90670de0b6b3a7640000019081670de0b6b3a764000011610e8557670de0b6b3a7640000916131709161234a565b60a08101805151602082510151106131de576020815151915101518101809111610e855760809060011c910152565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f536c6970706167653a2067756573734d696e203e2067756573734d61780000006044820152fd5b91908281111561324c5750905090565b610ebd9203613174565b9190828119105f146132685750905090565b610ebd92019061270f565b909160a0610100830151920151908103908111610e8557613293916130a4565b6ec097ce7bc90715b34b9f100000000081156125db5704916132b483610f50565b50670de0b6b3a7640000916132c89161234a565b04906132d6610ebd92612326565b6125d1565b909160a0610100830151920151908103908111610e85576132fb916130a4565b6ec097ce7bc90715b34b9f10000000009181156125db5761261661332992670de0b6b3a76400009404610f50565b046125d190612326565b1561333a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e74000000000000000000000000000000006044820152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc00008112158061384d575b6133cb90613333565b5f811261383957610ebd906806f05b59d3b200000081126137e2577ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e000000016135ab6064770195e54c5dd42177f53a27172fa9ec630262827000000000925b0268056bc75e2d631000009068ad78ebc5ac620000008112156137a9575b6856bc75e2d63100000081121561375f575b682b5e3af16b18800000811215613717575b6815af1d78b58c4000008112156136cf575b680ad78ebc5ac6200000811215613688575b68056bc75e2d63100000811215613641575b6802b5e3af16b18800008112156135fa575b68015af1d78b58c400008112156135b3575b600268056bc75e2d631000008280020505600368056bc75e2d631000008383020505600468056bc75e2d631000008483020505600568056bc75e2d631000008583020505600668056bc75e2d631000008683020505600768056bc75e2d63100000878302050590600868056bc75e2d63100000888402050592600968056bc75e2d6310000089860205059468056bc75e2d63100000600a8a8802829005059761357c68056bc75e2d631000008c8b02055b600b900590565b99600c68056bc75e2d631000008d8d0205059b0101010101010101010101010268056bc75e2d63100000900590565b026064900590565b6806f5f17757889379377ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c00006135f49201920268056bc75e2d63100000900590565b906134c4565b6808f00f760a4b2db55d7ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000061363b9201920268056bc75e2d63100000900590565b906134b2565b680ebc5fb417461211107ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000006136829201920268056bc75e2d63100000900590565b906134a0565b68280e60114edb805d037ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e000006136c99201920268056bc75e2d63100000900590565b9061348e565b690127fa27722cc06cc5e27fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000006137119201920268056bc75e2d63100000900590565b9061347c565b693f1fce3da636ea5cf8507fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000006137599201920268056bc75e2d63100000900590565b9061346a565b6b02df0ab5a80a22c61ab5a7007fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000006137a39201920268056bc75e2d63100000900590565b90613458565b6e01855144814a7ff805980ff008400091507fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e00000001613446565b6803782dace9d9000000811261382c577ffffffffffffffffffffffffffffffffffffffffffffffffc87d2531627000000016135ab60646b1425982cf597cd205cef738092613428565b6135ab6064600192613428565b613844905f03613398565b610ebd906130e5565b5068070c1cc73b00c800008113156133c2565b6130ff906115f7565b90939192613886818351602088015188519060408a015192613c8c565b5f60606138a361389e84613899876115f7565b6130ff565b6115c3565b97015192131561396957816138996138ba926115f7565b670de0b6b3a7640000811261393e575093613929613910610ebd9361390a6139036138fd6138f26138ec60e09c612678565b88613109565b9a8b945b0151611599565b8361161b565b6064900590565b936126aa565b5f81121561392f576139229086613e3e565b9585613da9565b93613da9565b6139399086613da9565b613922565b7fca78c8a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b5093613929613910610ebd9361390a6139036138fd61399e61389e8c61399961399360e09f612678565b8b61161b565b612eb2565b9a8b946138f6565b3d156139fe573d9067ffffffffffffffff8211610de457604051916139f3601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200184610de9565b82523d5f602084013e565b606090565b5f919082918260405173ffffffffffffffffffffffffffffffffffffffff60208201937f095ea7b300000000000000000000000000000000000000000000000000000000855216602482015281604482015260448152613a64606482610de9565b51925af1613a706139a6565b81613adc575b5015613a7e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f5361666520417070726f766500000000000000000000000000000000000000006044820152fd5b8051801592508215613af1575b50505f613a76565b613b049250602080918301019101612c80565b5f80613ae9565b5f919082918260405173ffffffffffffffffffffffffffffffffffffffff60208201937f095ea7b30000000000000000000000000000000000000000000000000000000085521660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604482015260448152613a64606482610de9565b61317090670de0b6b3a76400009261234a565b90670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610e8557818101809111610e85577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908111610e855781156125db570490565b5f811315613c0a5790565b610ebd90612d35565b670de0b6b3a76400008114613c645780670de0b6b3a764000003905f8112670de0b6b3a76400008312811690670de0b6b3a7640000841390151617610e8557610ebd91613c5f91613860565b613eb5565b7fa9c8b14d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90919493808212613d4b57613cae92613ca861306892846126aa565b9261265d565b92670d529ae9e86000008413613d1357613ccf90613073613cd49495613c13565b61265d565b90670de0b6b3a76400008212613ce657565b7fca78c8a4000000000000000000000000000000000000000000000000000000005f52600482905260245ffd5b837ffc68d09e000000000000000000000000000000000000000000000000000000005f52600452670d529ae9e860000060245260445ffd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e656761746976650000000000000000000000000000000000000000000000006044820152fd5b5f821215613e3357613ddb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92613bff565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610e855781156125db57047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b613ddb600192613bff565b613e7a905f831215613eaa57613e747fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93613bff565b90613b9f565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161026757610ebd9161161b565b613e74600193613bff565b5f8113156140635780670c7d713b49da00001280614052575b1561404957670de0b6b3a7640000026ec097ce7bc90715b34b9f100000000081019081156125db57614037614030613fbc614013613f9f613ff6610ebd976ec097ce7bc90715b34b9f10000000007fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f000000000613f8799010205613fd96ec097ce7bc90715b34b9f10000000008280020580968180968186613f7e828099026ec097ce7bc90715b34b9f1000000000900590565b9e8f6003900590565b019d026ec097ce7bc90715b34b9f1000000000900590565b9b60058d05019b026ec097ce7bc90715b34b9f1000000000900590565b9960078b050199026ec097ce7bc90715b34b9f1000000000900590565b97600989050197026ec097ce7bc90715b34b9f1000000000900590565b95600b87050195026ec097ce7bc90715b34b9f1000000000900590565b93600d85050193026ec097ce7bc90715b34b9f1000000000900590565b600f900590565b0160011b670de0b6b3a7640000900590565b610ebd9061418d565b50670f43fc2c04ee00008112613ece565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e6473000000000000000000000000000000000000006044820152fd5b9192901561413c57508151156140d5575090565b3b156140de5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561414f5750805190602001fd5b610222906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401526024830190610edb565b670de0b6b3a764000081126145b457610ebd905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215614589575b73011798004d755d3c8bc8e03204cf44619e00000082121561455c575b6142e9906064808492029302906e01855144814a7ff805980ff0084000821215614522575b506b02df0ab5a80a22c61ab5a7008112156144ef575b693f1fce3da636ea5cf8508112156144be575b690127fa27722cc06cc5e281121561448d575b68280e60114edb805d0381121561445d575b680ebc5fb4174612111081121561443d575b6808f00f760a4b2db55d81121561440d575b6806f5f17757889379378112156143dd575b6806248f33704b2866038112156143ae575b6805c548670b9510e7ac81121561437f575b68056bc75e2d631000007ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000008183019201026130ff565b61437361357568056bc75e2d631000008380020561435c816143458161432e8168056bc75e2d631000008a8202059960038b0501990268056bc75e2d63100000900590565b976005890501970268056bc75e2d63100000900590565b956007870501950268056bc75e2d63100000900590565b936009850501930268056bc75e2d63100000900590565b0160011b016064900590565b6143a668056bc75e2d631000006756bc75e2d631000092026805c548670b9510e7ac900590565b9201916142b3565b6143d568056bc75e2d6310000067ad78ebc5ac62000092026806248f33704b286603900590565b9201916142a1565b61440568056bc75e2d6310000068015af1d78b58c4000092026806f5f1775788937937900590565b92019161428f565b61443568056bc75e2d631000006802b5e3af16b188000092026808f00f760a4b2db55d900590565b92019161427d565b68056bc75e2d6310000092830192680ebc5fb4174612111091020561426b565b61448568056bc75e2d63100000680ad78ebc5ac6200000920268280e60114edb805d03900590565b920191614259565b6144b668056bc75e2d631000006815af1d78b58c4000009202690127fa27722cc06cc5e2900590565b920191614247565b6144e768056bc75e2d63100000682b5e3af16b188000009202693f1fce3da636ea5cf850900590565b920191614234565b61451a68056bc75e2d631000006856bc75e2d63100000092026b02df0ab5a80a22c61ab5a700900590565b920191614221565b68ad78ebc5ac62000000915069021e19e0c9bab240000061455391026e01855144814a7ff805980ff0084000900590565b9201915f61420b565b906803782dace9d90000006145806142e9926b1425982cf597cd205cef7380900590565b920190506141e6565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b20000006141c9565b6145c06145c5916130e5565b61418d565b5f039056fea2646970667358221220bc99ced2e9e8cb93db1f0f9bef74def300a0c60623c45b720aed516b545b8dd264736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.