Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 254 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Approve | 25614559 | 23 mins ago | IN | 0 HYPE | 0.00000747 | ||||
| Approve | 25600473 | 4 hrs ago | IN | 0 HYPE | 0.00000551 | ||||
| Swap Sy For Exac... | 25579493 | 9 hrs ago | IN | 0 HYPE | 0.00029774 | ||||
| Swap Sy For Exac... | 25523113 | 25 hrs ago | IN | 0 HYPE | 0.00029849 | ||||
| Swap Sy For Exac... | 25429050 | 2 days ago | IN | 0 HYPE | 0.00029776 | ||||
| Approve | 25423779 | 2 days ago | IN | 0 HYPE | 0.00000459 | ||||
| Approve | 25423728 | 2 days ago | IN | 0 HYPE | 0.0000046 | ||||
| Approve | 25408616 | 2 days ago | IN | 0 HYPE | 0.00004142 | ||||
| Swap Exact Pt Fo... | 25400891 | 2 days ago | IN | 0 HYPE | 0.00028942 | ||||
| Swap Sy For Exac... | 25391510 | 2 days ago | IN | 0 HYPE | 0.00029696 | ||||
| Swap Sy For Exac... | 25372729 | 2 days ago | IN | 0 HYPE | 0.00029703 | ||||
| Swap Sy For Exac... | 25363340 | 2 days ago | IN | 0 HYPE | 0.00029703 | ||||
| Approve | 25288297 | 3 days ago | IN | 0 HYPE | 0.00031467 | ||||
| Approve | 25286806 | 3 days ago | IN | 0 HYPE | 0.00022897 | ||||
| Swap Exact Pt Fo... | 25175108 | 5 days ago | IN | 0 HYPE | 0.00030338 | ||||
| Approve | 25156137 | 5 days ago | IN | 0 HYPE | 0.00018576 | ||||
| Approve | 25130129 | 5 days ago | IN | 0 HYPE | 0.00000552 | ||||
| Approve | 25128884 | 5 days ago | IN | 0 HYPE | 0.00003106 | ||||
| Swap Exact Pt Fo... | 25090405 | 5 days ago | IN | 0 HYPE | 0.00028963 | ||||
| Approve | 25080177 | 6 days ago | IN | 0 HYPE | 0.00008929 | ||||
| Approve | 25076653 | 6 days ago | IN | 0 HYPE | 0.00029183 | ||||
| Approve | 25070712 | 6 days ago | IN | 0 HYPE | 0.00020584 | ||||
| Approve | 25004880 | 6 days ago | IN | 0 HYPE | 0.00000459 | ||||
| Approve | 25004658 | 6 days ago | IN | 0 HYPE | 0.00000459 | ||||
| Swap Sy For Exac... | 24977731 | 7 days ago | IN | 0 HYPE | 0.00029703 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE | |||||
| 25614583 | 23 mins ago | 0 HYPE |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PendleMarketV6
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import "../../interfaces/IPMarket.sol";
import "../../interfaces/IPMarketFactory.sol";
import "../../interfaces/IPMarketSwapCallback.sol";
import "../erc20/PendleERC20.sol";
import "./PendleGauge.sol";
import "./OracleLib.sol";
import "../libraries/StringLib.sol";
/**
Invariance to maintain:
- Internal balances totalPt & totalSy not interfered by people transferring tokens in directly
- address(0) & address(this) should never have any rewards & activeBalance accounting done. This is
guaranteed by address(0) & address(this) check in each updateForTwo function
*/
contract PendleMarketV6 is PendleERC20, PendleGauge, IPMarket {
using PMath for uint256;
using PMath for int256;
using MarketMathCore for MarketState;
using SafeERC20 for IERC20;
using PYIndexLib for IPYieldToken;
using PYIndexLib for PYIndex;
using OracleLib for OracleLib.Observation[65535];
using StringLib for string;
using StringLib for StringLib.slice;
struct MarketStorage {
int128 totalPt;
int128 totalSy;
// 1 SLOT = 256 bits
uint96 lastLnImpliedRate;
uint16 observationIndex;
uint16 observationCardinality;
uint16 observationCardinalityNext;
// 1 SLOT = 144 bits
}
string private constant PT_NAME_PREF = "PT ";
string private constant PT_SYMBOL_PREF = "PT-";
string private constant LP_NAME_PREF = "PLP ";
string private constant LP_SYMBOL_PREF = "PLP-";
uint256 public constant VERSION = 6;
IPPrincipalToken internal immutable PT;
IStandardizedYield internal immutable SY;
IPYieldToken internal immutable YT;
address public immutable factory;
uint256 public immutable expiry;
int256 internal immutable scalarRoot;
int256 internal immutable initialAnchor;
uint80 internal immutable lnFeeRateRoot;
MarketStorage public _storage;
OracleLib.Observation[65535] public observations;
modifier notExpired() {
if (isExpired()) revert Errors.MarketExpired();
_;
}
constructor(
address _PT,
int256 _scalarRoot,
int256 _initialAnchor,
uint80 _lnFeeRateRoot,
address _vePendle,
address _gaugeController
)
PendleERC20(_getLPName(_PT), _getLPSymbol(_PT), 18)
PendleGauge(IPPrincipalToken(_PT).SY(), _vePendle, _gaugeController)
{
PT = IPPrincipalToken(_PT);
SY = IStandardizedYield(PT.SY());
YT = IPYieldToken(PT.YT());
(_storage.observationCardinality, _storage.observationCardinalityNext) = observations.initialize(
uint32(block.timestamp)
);
if (_scalarRoot <= 0) revert Errors.MarketScalarRootBelowZero(_scalarRoot);
scalarRoot = _scalarRoot;
initialAnchor = _initialAnchor;
lnFeeRateRoot = _lnFeeRateRoot;
expiry = IPPrincipalToken(_PT).expiry();
factory = msg.sender;
}
function _getLPName(address _PT) internal view returns (string memory) {
return LP_NAME_PREF.toSlice().concat(IPPrincipalToken(_PT).name().stripPrefixSlice(PT_NAME_PREF));
}
function _getLPSymbol(address _PT) internal view returns (string memory) {
return LP_SYMBOL_PREF.toSlice().concat(IPPrincipalToken(_PT).symbol().stripPrefixSlice(PT_SYMBOL_PREF));
}
/**
* @notice PendleMarket allows users to provide in PT & SY in exchange for LPs, which
* will grant LP holders more exchange fee over time
* @dev will mint as much LP as possible such that the corresponding SY and PT used do
* not exceed `netSyDesired` and `netPtDesired`, respectively
* @dev PT and SY should be transferred to this contract prior to calling
* @dev will revert if PT is expired
*/
function mint(
address receiver,
uint256 netSyDesired,
uint256 netPtDesired
) external nonReentrant notExpired returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed) {
MarketState memory market = readState(msg.sender);
PYIndex index = YT.newIndex();
uint256 lpToReserve;
(lpToReserve, netLpOut, netSyUsed, netPtUsed) = market.addLiquidity(
netSyDesired,
netPtDesired,
block.timestamp
);
// initializing the market
if (lpToReserve != 0) {
market.setInitialLnImpliedRate(index, initialAnchor, block.timestamp);
_mint(address(1), lpToReserve);
}
_mint(receiver, netLpOut);
_writeState(market);
if (_selfBalance(SY) < market.totalSy.Uint())
revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());
if (_selfBalance(PT) < market.totalPt.Uint())
revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());
emit Mint(receiver, netLpOut, netSyUsed, netPtUsed);
}
/**
* @notice LP Holders can burn their LP to receive back SY & PT proportionally
* to their share of the market
*/
function burn(
address receiverSy,
address receiverPt,
uint256 netLpToBurn
) external nonReentrant returns (uint256 netSyOut, uint256 netPtOut) {
MarketState memory market = readState(msg.sender);
_burn(address(this), netLpToBurn);
(netSyOut, netPtOut) = market.removeLiquidity(netLpToBurn);
if (receiverSy != address(this)) IERC20(SY).safeTransfer(receiverSy, netSyOut);
if (receiverPt != address(this)) IERC20(PT).safeTransfer(receiverPt, netPtOut);
_writeState(market);
emit Burn(receiverSy, receiverPt, netLpToBurn, netSyOut, netPtOut);
}
/**
* @notice Pendle Market allows swaps between PT & SY it is holding. This function
* aims to swap an exact amount of PT to SY.
* @dev steps working of this contract
- The outcome amount of SY will be precomputed by MarketMathLib
- Release the calculated amount of SY to receiver
- Callback to msg.sender if data.length > 0
- Ensure exactPtIn amount of PT has been transferred to this address
* @dev will revert if PT is expired
* @param data bytes data to be sent in the callback (if any)
*/
function swapExactPtForSy(
address receiver,
uint256 exactPtIn,
bytes calldata data
) external nonReentrant notExpired returns (uint256 netSyOut, uint256 netSyFee) {
MarketState memory market = readState(msg.sender);
PYIndex index = YT.newIndex();
uint256 netSyToReserve;
(netSyOut, netSyFee, netSyToReserve) = market.swapExactPtForSy(index, exactPtIn, block.timestamp);
if (receiver != address(this)) IERC20(SY).safeTransfer(receiver, netSyOut);
IERC20(SY).safeTransfer(market.treasury, netSyToReserve);
_writeState(market);
if (data.length > 0) {
IPMarketSwapCallback(msg.sender).swapCallback(exactPtIn.neg(), netSyOut.Int(), data);
}
if (_selfBalance(PT) < market.totalPt.Uint())
revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());
if (index.syToAsset(netSyFee - netSyToReserve) == 0) {
revert Errors.MarketZeroNetLPFee();
}
emit Swap(msg.sender, receiver, exactPtIn.neg(), netSyOut.Int(), netSyFee, netSyToReserve);
}
/**
* @notice Pendle Market allows swaps between PT & SY it is holding. This function
* aims to swap SY for an exact amount of PT.
* @dev steps working of this function
- The exact outcome amount of PT will be transferred to receiver
- Callback to msg.sender if data.length > 0
- Ensure the calculated required amount of SY is transferred to this address
* @dev will revert if PT is expired
* @param data bytes data to be sent in the callback (if any)
*/
function swapSyForExactPt(
address receiver,
uint256 exactPtOut,
bytes calldata data
) external nonReentrant notExpired returns (uint256 netSyIn, uint256 netSyFee) {
MarketState memory market = readState(msg.sender);
PYIndex index = YT.newIndex();
uint256 netSyToReserve;
(netSyIn, netSyFee, netSyToReserve) = market.swapSyForExactPt(index, exactPtOut, block.timestamp);
if (receiver != address(this)) IERC20(PT).safeTransfer(receiver, exactPtOut);
IERC20(SY).safeTransfer(market.treasury, netSyToReserve);
_writeState(market);
if (data.length > 0) {
IPMarketSwapCallback(msg.sender).swapCallback(exactPtOut.Int(), netSyIn.neg(), data);
}
// have received enough SY
if (_selfBalance(SY) < market.totalSy.Uint())
revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());
if (index.syToAsset(netSyFee - netSyToReserve) == 0) {
revert Errors.MarketZeroNetLPFee();
}
emit Swap(msg.sender, receiver, exactPtOut.Int(), netSyIn.neg(), netSyFee, netSyToReserve);
}
/// @notice forces balances to match reserves
function skim() external nonReentrant {
MarketState memory market = readState(msg.sender);
uint256 excessPt = _selfBalance(PT) - market.totalPt.Uint();
uint256 excessSy = _selfBalance(SY) - market.totalSy.Uint();
if (excessPt != 0) IERC20(PT).safeTransfer(market.treasury, excessPt);
if (excessSy != 0) IERC20(SY).safeTransfer(market.treasury, excessSy);
}
/**
* @notice redeems the user's reward
* @return amount of reward token redeemed, in the same order as `getRewardTokens()`
*/
function redeemRewards(address user) external nonReentrant returns (uint256[] memory) {
return _redeemRewards(user);
}
/// @notice returns the list of reward tokens
function getRewardTokens() external view returns (address[] memory) {
return _getRewardTokens();
}
/*///////////////////////////////////////////////////////////////
ORACLE
//////////////////////////////////////////////////////////////*/
function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative) {
return
observations.observe(
uint32(block.timestamp),
secondsAgos,
_storage.lastLnImpliedRate,
_storage.observationIndex,
_storage.observationCardinality
);
}
function increaseObservationsCardinalityNext(uint16 cardinalityNext) external nonReentrant {
uint16 cardinalityNextOld = _storage.observationCardinalityNext;
uint16 cardinalityNextNew = observations.grow(cardinalityNextOld, cardinalityNext);
if (cardinalityNextOld != cardinalityNextNew) {
_storage.observationCardinalityNext = cardinalityNextNew;
emit IncreaseObservationCardinalityNext(cardinalityNextOld, cardinalityNextNew);
}
}
/*///////////////////////////////////////////////////////////////
READ/WRITE STATES
//////////////////////////////////////////////////////////////*/
/**
* @notice read the state of the market from storage into memory for gas-efficient manipulation
*/
function readState(address router) public view returns (MarketState memory market) {
market.totalPt = _storage.totalPt;
market.totalSy = _storage.totalSy;
market.totalLp = totalSupply().Int();
uint80 overriddenFee;
(market.treasury, overriddenFee, market.reserveFeePercent) = IPMarketFactory(factory).getMarketConfig(
address(this),
router
);
market.lnFeeRateRoot = overriddenFee == 0 ? lnFeeRateRoot : overriddenFee;
market.scalarRoot = scalarRoot;
market.expiry = expiry;
market.lastLnImpliedRate = _storage.lastLnImpliedRate;
}
/// @notice write back the state of the market from memory to storage
function _writeState(MarketState memory market) internal {
uint96 lastLnImpliedRate96 = market.lastLnImpliedRate.Uint96();
int128 totalPt128 = market.totalPt.Int128();
int128 totalSy128 = market.totalSy.Int128();
(uint16 observationIndex, uint16 observationCardinality) = observations.write(
_storage.observationIndex,
uint32(block.timestamp),
_storage.lastLnImpliedRate,
_storage.observationCardinality,
_storage.observationCardinalityNext
);
_storage.totalPt = totalPt128;
_storage.totalSy = totalSy128;
_storage.lastLnImpliedRate = lastLnImpliedRate96;
_storage.observationIndex = observationIndex;
_storage.observationCardinality = observationCardinality;
emit UpdateImpliedRate(block.timestamp, market.lastLnImpliedRate);
}
function getNonOverrideLnFeeRateRoot() external view returns (uint80) {
return lnFeeRateRoot;
}
/*///////////////////////////////////////////////////////////////
TRIVIAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT) {
_SY = SY;
_PT = PT;
_YT = YT;
}
function isExpired() public view returns (bool) {
return MiniHelpers.isCurrentlyExpired(expiry);
}
function reentrancyGuardEntered() external view override(PendleERC20, IPMarket) returns (bool) {
return _reentrancyGuardEntered();
}
/*///////////////////////////////////////////////////////////////
PENDLE GAUGE - RELATED
//////////////////////////////////////////////////////////////*/
function _stakedBalance(address user) internal view override returns (uint256) {
return balanceOf(user);
}
function _totalStaked() internal view override returns (uint256) {
return totalSupply();
}
// solhint-disable-next-line ordering
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal override(PendleERC20, PendleGauge) {
PendleGauge._beforeTokenTransfer(from, to, amount);
}
// solhint-disable-next-line ordering
function _afterTokenTransfer(address from, address to, uint256 amount) internal override(PendleERC20, PendleGauge) {
PendleGauge._afterTokenTransfer(from, to, amount);
}
}// 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: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/Context.sol";
/**
* @dev Pendle's ERC20 implementation, modified from @openzeppelin implementation
* Changes are:
* - comes with built-in reentrancy protection, storage-packed with totalSupply variable
* - delete increaseAllowance / decreaseAllowance
* - add nonReentrancy protection to transfer / transferFrom functions
* - allow decimals to be passed in
* - block self-transfer by default
*/
// solhint-disable
contract PendleERC20 is Context, IERC20, IERC20Metadata {
uint8 private constant _NOT_ENTERED = 1;
uint8 private constant _ENTERED = 2;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint248 private _totalSupply;
uint8 private _status;
string private _name;
string private _symbol;
uint8 public immutable decimals;
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
function reentrancyGuardEntered() external view virtual returns (bool) {
return _reentrancyGuardEntered();
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev Sets the values for {name}, {symbol} and {decimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_, uint8 decimals_) {
_name = name_;
_symbol = symbol_;
decimals = decimals_;
_status = _NOT_ENTERED;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) external virtual override nonReentrant returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) external virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external virtual override nonReentrant returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
require(from != to, "ERC20: transfer to self");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += toUint248(amount);
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= toUint248(amount);
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function toUint248(uint256 x) internal virtual returns (uint248) {
require(x <= type(uint248).max); // signed, lim = bit-1
return uint248(x);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
library ArrayLib {
function sum(uint256[] memory input) internal pure returns (uint256) {
uint256 value = 0;
for (uint256 i = 0; i < input.length; ) {
value += input[i];
unchecked {
i++;
}
}
return value;
}
/// @notice return index of the element if found, else return uint256.max
function find(address[] memory array, address element) internal pure returns (uint256 index) {
uint256 length = array.length;
for (uint256 i = 0; i < length; ) {
if (array[i] == element) return i;
unchecked {
i++;
}
}
return type(uint256).max;
}
function append(address[] memory inp, address element) internal pure returns (address[] memory out) {
uint256 length = inp.length;
out = new address[](length + 1);
for (uint256 i = 0; i < length; ) {
out[i] = inp[i];
unchecked {
i++;
}
}
out[length] = element;
}
function appendHead(address[] memory inp, address element) internal pure returns (address[] memory out) {
uint256 length = inp.length;
out = new address[](length + 1);
out[0] = element;
for (uint256 i = 1; i <= length; ) {
out[i] = inp[i - 1];
unchecked {
i++;
}
}
}
/**
* @dev This function assumes a and b each contains unidentical elements
* @param a array of addresses a
* @param b array of addresses b
* @return out Concatenation of a and b containing unidentical elements
*/
function merge(address[] memory a, address[] memory b) internal pure returns (address[] memory out) {
unchecked {
uint256 countUnidenticalB = 0;
bool[] memory isUnidentical = new bool[](b.length);
for (uint256 i = 0; i < b.length; ++i) {
if (!contains(a, b[i])) {
countUnidenticalB++;
isUnidentical[i] = true;
}
}
out = new address[](a.length + countUnidenticalB);
for (uint256 i = 0; i < a.length; ++i) {
out[i] = a[i];
}
uint256 id = a.length;
for (uint256 i = 0; i < b.length; ++i) {
if (isUnidentical[i]) {
out[id++] = b[i];
}
}
}
}
// various version of contains
function contains(address[] memory array, address element) internal pure returns (bool) {
uint256 length = array.length;
for (uint256 i = 0; i < length; ) {
if (array[i] == element) return true;
unchecked {
i++;
}
}
return false;
}
function contains(bytes4[] memory array, bytes4 element) internal pure returns (bool) {
uint256 length = array.length;
for (uint256 i = 0; i < length; ) {
if (array[i] == element) return true;
unchecked {
i++;
}
}
return false;
}
function create(address a) internal pure returns (address[] memory res) {
res = new address[](1);
res[0] = a;
}
function create(address a, address b) internal pure returns (address[] memory res) {
res = new address[](2);
res[0] = a;
res[1] = b;
}
function create(address a, address b, address c) internal pure returns (address[] memory res) {
res = new address[](3);
res[0] = a;
res[1] = b;
res[2] = c;
}
function create(address a, address b, address c, address d) internal pure returns (address[] memory res) {
res = new address[](4);
res[0] = a;
res[1] = b;
res[2] = c;
res[3] = d;
}
function create(
address a,
address b,
address c,
address d,
address e
) internal pure returns (address[] memory res) {
res = new address[](5);
res[0] = a;
res[1] = b;
res[2] = c;
res[3] = d;
res[4] = e;
}
function create(uint256 a) internal pure returns (uint256[] memory res) {
res = new uint256[](1);
res[0] = a;
}
}// 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 MarketZeroNetLPFee();
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 VCInvalidCap(uint256 cap);
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);
// Cross Chain Oracle App
error FeedNotInitialized();
error ExchangeRateCallFailed();
error InvalidDestinationEid();
error InvalidMsgType();
error NotEnoughNativeFee(uint256 msgValue, uint256 totalFee);
}// 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
}
}/* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ pragma solidity ^0.8.0; library StringLib { struct slice { uint256 _len; uint256 _ptr; } function memcpy(uint256 dest, uint256 src, uint256 len) private pure { // Copy word-length chunks while possible for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint256 mask = type(uint256).max; if (len > 0) { mask = 256 ** (32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint256 ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint256) { uint256 ret; if (self == 0) return 0; if (uint256(self) & type(uint128).max == 0) { ret += 16; self = bytes32(uint256(self) / 0x100000000000000000000000000000000); } if (uint256(self) & type(uint64).max == 0) { ret += 8; self = bytes32(uint256(self) / 0x10000000000000000); } if (uint256(self) & type(uint32).max == 0) { ret += 4; self = bytes32(uint256(self) / 0x100000000); } if (uint256(self) & type(uint16).max == 0) { ret += 2; self = bytes32(uint256(self) / 0x10000); } if (uint256(self) & type(uint8).max == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint256 retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to know whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint256 l) { // Starting at ptr-31 means the LSB will be the byte we care about uint256 ptr = self._ptr - 31; uint256 end = ptr + self._len; for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { ptr += 1; } else if (b < 0xE0) { ptr += 2; } else if (b < 0xF0) { ptr += 3; } else if (b < 0xF8) { ptr += 4; } else if (b < 0xFC) { ptr += 5; } else { ptr += 6; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int256) { uint256 shortest = self._len; if (other._len < self._len) shortest = other._len; uint256 selfptr = self._ptr; uint256 otherptr = other._ptr; for (uint256 idx = 0; idx < shortest; idx += 32) { uint256 a; uint256 b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = type(uint256).max; // 0xffff... if (shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } unchecked { uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int256(diff); } } selfptr += 32; otherptr += 32; } return int256(self._len) - int256(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint256 l; uint256 b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if (b < 0xE0) { l = 2; } else if (b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint256 ret) { if (self._len == 0) { return 0; } uint256 word; uint256 length; uint256 divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word := mload(mload(add(self, 32))) } uint256 b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if (b < 0xE0) { ret = b & 0x1F; length = 2; } else if (b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint256 i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint256 selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint256 selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr( uint256 selflen, uint256 selfptr, uint256 needlelen, uint256 needleptr ) private pure returns (uint256) { uint256 ptr = selfptr; uint256 idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask; if (needlelen > 0) { mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); } bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint256 end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr( uint256 selflen, uint256 selfptr, uint256 needlelen, uint256 needleptr ) private pure returns (uint256) { uint256 ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask; if (needlelen > 0) { mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); } bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint256 cnt) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint256 retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint256 length = self._len * (parts.length - 1); for (uint256 i = 0; i < parts.length; i++) length += parts[i]._len; string memory ret = new string(length); uint256 retptr; assembly { retptr := add(ret, 32) } for (uint256 i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } function stripPrefix(string memory _str, string memory _prefix) internal pure returns (string memory) { slice memory s = toSlice(_str); slice memory d = toSlice(_prefix); return toString(beyond(s, d)); } function stripPrefixSlice(string memory _str, string memory _delim) internal pure returns (slice memory) { slice memory s = toSlice(_str); slice memory d = toSlice(_delim); return beyond(s, d); } }
// 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 "../libraries/Errors.sol";
/// Adapted from UniswapV3's Oracle
library OracleLib {
struct Observation {
uint32 blockTimestamp;
uint216 lnImpliedRateCumulative;
bool initialized;
// 1 SLOT = 256 bits
}
function transform(
Observation memory last,
uint32 blockTimestamp,
uint96 lnImpliedRate
) public pure returns (Observation memory) {
return
Observation({
blockTimestamp: blockTimestamp,
lnImpliedRateCumulative: last.lnImpliedRateCumulative +
uint216(lnImpliedRate) *
(blockTimestamp - last.blockTimestamp),
initialized: true
});
}
function initialize(
Observation[65535] storage self,
uint32 time
) public returns (uint16 cardinality, uint16 cardinalityNext) {
self[0] = Observation({blockTimestamp: time, lnImpliedRateCumulative: 0, initialized: true});
return (1, 1);
}
function write(
Observation[65535] storage self,
uint16 index,
uint32 blockTimestamp,
uint96 lnImpliedRate,
uint16 cardinality,
uint16 cardinalityNext
) public returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
Observation memory last = self[index];
// early return if we've already written an observation this block
if (last.blockTimestamp == blockTimestamp) return (index, cardinality);
// if the conditions are right, we can bump the cardinality
if (cardinalityNext > cardinality && index == (cardinality - 1)) {
cardinalityUpdated = cardinalityNext;
} else {
cardinalityUpdated = cardinality;
}
indexUpdated = (index + 1) % cardinalityUpdated;
self[indexUpdated] = transform(last, blockTimestamp, lnImpliedRate);
}
function grow(Observation[65535] storage self, uint16 current, uint16 next) public returns (uint16) {
if (current == 0) revert Errors.OracleUninitialized();
// no-op if the passed next value isn't greater than the current next value
if (next <= current) return current;
// store in each slot to prevent fresh SSTOREs in swaps
// this data will not be used because the initialized boolean is still false
for (uint16 i = current; i != next; ) {
self[i].blockTimestamp = 1;
unchecked {
++i;
}
}
return next;
}
function binarySearch(
Observation[65535] storage self,
uint32 target,
uint16 index,
uint16 cardinality
) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
uint256 l = (index + 1) % cardinality; // oldest observation
uint256 r = l + cardinality - 1; // newest observation
uint256 i;
while (true) {
i = (l + r) / 2;
beforeOrAt = self[i % cardinality];
// we've landed on an uninitialized observation, keep searching higher (more recently)
if (!beforeOrAt.initialized) {
l = i + 1;
continue;
}
atOrAfter = self[(i + 1) % cardinality];
bool targetAtOrAfter = beforeOrAt.blockTimestamp <= target;
// check if we've found the answer!
if (targetAtOrAfter && target <= atOrAfter.blockTimestamp) break;
if (!targetAtOrAfter) r = i - 1;
else l = i + 1;
}
}
function getSurroundingObservations(
Observation[65535] storage self,
uint32 target,
uint96 lnImpliedRate,
uint16 index,
uint16 cardinality
) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
// optimistically set before to the newest observation
beforeOrAt = self[index];
// if the target is chronologically at or after the newest observation, we can early return
if (beforeOrAt.blockTimestamp <= target) {
if (beforeOrAt.blockTimestamp == target) {
// if newest observation equals target, we're in the same block, so we can ignore atOrAfter
return (beforeOrAt, atOrAfter);
} else {
// otherwise, we need to transform
return (beforeOrAt, transform(beforeOrAt, target, lnImpliedRate));
}
}
// now, set beforeOrAt to the oldest observation
beforeOrAt = self[(index + 1) % cardinality];
if (!beforeOrAt.initialized) beforeOrAt = self[0];
// ensure that the target is chronologically at or after the oldest observation
if (target < beforeOrAt.blockTimestamp) revert Errors.OracleTargetTooOld(target, beforeOrAt.blockTimestamp);
// if we've reached this point, we have to binary search
return binarySearch(self, target, index, cardinality);
}
function observeSingle(
Observation[65535] storage self,
uint32 time,
uint32 secondsAgo,
uint96 lnImpliedRate,
uint16 index,
uint16 cardinality
) public view returns (uint216 lnImpliedRateCumulative) {
if (secondsAgo == 0) {
Observation memory last = self[index];
if (last.blockTimestamp != time) {
return transform(last, time, lnImpliedRate).lnImpliedRateCumulative;
}
return last.lnImpliedRateCumulative;
}
uint32 target = time - secondsAgo;
(Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations(
self,
target,
lnImpliedRate,
index,
cardinality
);
if (target == beforeOrAt.blockTimestamp) {
// we're at the left boundary
return beforeOrAt.lnImpliedRateCumulative;
} else if (target == atOrAfter.blockTimestamp) {
// we're at the right boundary
return atOrAfter.lnImpliedRateCumulative;
} else {
// we're in the middle
return (beforeOrAt.lnImpliedRateCumulative +
uint216(
(uint256(atOrAfter.lnImpliedRateCumulative - beforeOrAt.lnImpliedRateCumulative) *
(target - beforeOrAt.blockTimestamp)) / (atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp)
));
}
}
function observe(
Observation[65535] storage self,
uint32 time,
uint32[] memory secondsAgos,
uint96 lnImpliedRate,
uint16 index,
uint16 cardinality
) public view returns (uint216[] memory lnImpliedRateCumulative) {
if (cardinality == 0) revert Errors.OracleZeroCardinality();
lnImpliedRateCumulative = new uint216[](secondsAgos.length);
for (uint256 i = 0; i < lnImpliedRateCumulative.length; ++i) {
lnImpliedRateCumulative[i] = observeSingle(self, time, secondsAgos[i], lnImpliedRate, index, cardinality);
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IPGauge.sol";
import "../../interfaces/IPVeToken.sol";
import "../../interfaces/IPGaugeController.sol";
import "../../interfaces/IStandardizedYield.sol";
import "../RewardManager/RewardManager.sol";
/**
Invariants to maintain:
- before any changes to active balance, updateAndDistributeRewards() must be called
*/
abstract contract PendleGauge is RewardManager, IPGauge {
using PMath for uint256;
using SafeERC20 for IERC20;
using ArrayLib for address[];
address private immutable SY;
uint256 internal constant TOKENLESS_PRODUCTION = 40;
address internal immutable PENDLE;
IPVeToken internal immutable vePENDLE;
address internal immutable gaugeController;
uint256 public totalActiveSupply;
mapping(address => uint256) public activeBalance;
constructor(address _SY, address _vePendle, address _gaugeController) {
SY = _SY;
vePENDLE = IPVeToken(_vePendle);
gaugeController = _gaugeController;
PENDLE = IPGaugeController(gaugeController).pendle();
}
/**
* @dev Since rewardShares is based on activeBalance, user's activeBalance must be updated AFTER
rewards is updated
* @dev It's intended to have user's activeBalance updated when rewards is redeemed
*/
function _redeemRewards(address user) internal virtual returns (uint256[] memory rewardsOut) {
_updateAndDistributeRewards(user);
_updateUserActiveBalance(user);
rewardsOut = _doTransferOutRewards(user, user);
emit RedeemRewards(user, rewardsOut);
}
function _updateUserActiveBalance(address user) internal virtual {
_updateUserActiveBalanceForTwo(user, address(0));
}
function _updateUserActiveBalanceForTwo(address user1, address user2) internal virtual {
if (user1 != address(0) && user1 != address(this)) _updateUserActiveBalancePrivate(user1);
if (user2 != address(0) && user2 != address(this)) _updateUserActiveBalancePrivate(user2);
}
/**
* @dev should only be callable from `_updateUserActiveBalanceForTwo` to guarantee user != address(0) && user != address(this)
*/
function _updateUserActiveBalancePrivate(address user) private {
assert(user != address(0) && user != address(this));
uint256 lpBalance = _stakedBalance(user);
uint256 veBoostedLpBalance = _calcVeBoostedLpBalance(user, lpBalance);
uint256 newActiveBalance = PMath.min(veBoostedLpBalance, lpBalance);
totalActiveSupply = totalActiveSupply - activeBalance[user] + newActiveBalance;
activeBalance[user] = newActiveBalance;
emit UpdateActiveBalance(user, newActiveBalance);
}
function _calcVeBoostedLpBalance(address user, uint256 lpBalance) internal virtual returns (uint256) {
(uint256 vePendleSupply, uint256 vePendleBalance) = vePENDLE.totalSupplyAndBalanceCurrent(user);
// Inspired by Curve's Gauge
uint256 veBoostedLpBalance = (lpBalance * TOKENLESS_PRODUCTION) / 100;
if (vePendleSupply > 0) {
veBoostedLpBalance +=
(((_totalStaked() * vePendleBalance) / vePendleSupply) * (100 - TOKENLESS_PRODUCTION)) /
100;
}
return veBoostedLpBalance;
}
function _redeemExternalReward() internal virtual override {
IStandardizedYield(SY).claimRewards(address(this));
IPGaugeController(gaugeController).redeemMarketReward();
}
function _stakedBalance(address user) internal view virtual returns (uint256);
function _totalStaked() internal view virtual returns (uint256);
function _rewardSharesTotal() internal view virtual override returns (uint256) {
return totalActiveSupply;
}
function _rewardSharesUser(address user) internal view virtual override returns (uint256) {
return activeBalance[user];
}
function _getRewardTokens() internal view virtual override returns (address[] memory) {
address[] memory SYRewards = IStandardizedYield(SY).getRewardTokens();
if (SYRewards.contains(PENDLE)) return SYRewards;
return SYRewards.append(PENDLE);
}
function _beforeTokenTransfer(address from, address to, uint256) internal virtual {
_updateAndDistributeRewardsForTwo(from, to);
}
function _afterTokenTransfer(address from, address to, uint256) internal virtual {
_updateUserActiveBalanceForTwo(from, to);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "./RewardManagerAbstract.sol";
/// NOTE: This RewardManager is used with SY & YTv2 & PendleMarket. For YTv1, it will use RewardManagerAbstract
/// NOTE: RewardManager must not have duplicated rewardTokens
abstract contract RewardManager is RewardManagerAbstract {
using PMath for uint256;
using ArrayLib for uint256[];
uint256 public lastRewardBlock;
mapping(address => RewardState) public rewardState;
function _updateRewardIndex()
internal
virtual
override
returns (address[] memory tokens, uint256[] memory indexes)
{
tokens = _getRewardTokens();
indexes = new uint256[](tokens.length);
if (tokens.length == 0) return (tokens, indexes);
if (lastRewardBlock != block.number) {
// if we have not yet update the index for this block
lastRewardBlock = block.number;
uint256 totalShares = _rewardSharesTotal();
_redeemExternalReward();
for (uint256 i = 0; i < tokens.length; ++i) {
address token = tokens[i];
// the entire token balance of the contract must be the rewards of the contract
RewardState memory _state = rewardState[token];
(uint256 lastBalance, uint256 index) = (_state.lastBalance, _state.index);
uint256 accrued = _selfBalance(tokens[i]) - lastBalance;
if (index == 0) index = INITIAL_REWARD_INDEX;
if (totalShares != 0) index += accrued.divDown(totalShares);
rewardState[token] = RewardState({
index: index.Uint128(),
lastBalance: (lastBalance + accrued).Uint128()
});
indexes[i] = index;
}
} else {
for (uint256 i = 0; i < tokens.length; i++) {
indexes[i] = rewardState[tokens[i]].index;
}
}
}
/// @dev this function doesn't need redeemExternal since redeemExternal is bundled in updateRewardIndex
/// @dev this function also has to update rewardState.lastBalance
function _doTransferOutRewards(
address user,
address receiver
) internal virtual override returns (uint256[] memory rewardAmounts) {
address[] memory tokens = _getRewardTokens();
rewardAmounts = new uint256[](tokens.length);
for (uint256 i = 0; i < tokens.length; i++) {
rewardAmounts[i] = userReward[tokens[i]][user].accrued;
if (rewardAmounts[i] != 0) {
userReward[tokens[i]][user].accrued = 0;
rewardState[tokens[i]].lastBalance -= rewardAmounts[i].Uint128();
_transferOut(tokens[i], receiver, rewardAmounts[i]);
}
}
}
function _getRewardTokens() internal view virtual returns (address[] memory);
function _rewardSharesTotal() internal view virtual returns (uint256);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IRewardManager.sol";
import "../libraries/ArrayLib.sol";
import "../libraries/TokenHelper.sol";
import "../libraries/math/PMath.sol";
import "./RewardManagerAbstract.sol";
/// NOTE: RewardManager must not have duplicated rewardTokens
abstract contract RewardManagerAbstract is IRewardManager, TokenHelper {
using PMath for uint256;
uint256 internal constant INITIAL_REWARD_INDEX = 1;
struct RewardState {
uint128 index;
uint128 lastBalance;
}
struct UserReward {
uint128 index;
uint128 accrued;
}
// [token] => [user] => (index,accrued)
mapping(address => mapping(address => UserReward)) public userReward;
function _updateAndDistributeRewards(address user) internal virtual {
_updateAndDistributeRewardsForTwo(user, address(0));
}
function _updateAndDistributeRewardsForTwo(address user1, address user2) internal virtual {
(address[] memory tokens, uint256[] memory indexes) = _updateRewardIndex();
if (tokens.length == 0) return;
if (user1 != address(0) && user1 != address(this)) _distributeRewardsPrivate(user1, tokens, indexes);
if (user2 != address(0) && user2 != address(this)) _distributeRewardsPrivate(user2, tokens, indexes);
}
// should only be callable from `_updateAndDistributeRewardsForTwo` to guarantee user != address(0) && user != address(this)
function _distributeRewardsPrivate(address user, address[] memory tokens, uint256[] memory indexes) private {
assert(user != address(0) && user != address(this));
uint256 userShares = _rewardSharesUser(user);
for (uint256 i = 0; i < tokens.length; ++i) {
address token = tokens[i];
uint256 index = indexes[i];
uint256 userIndex = userReward[token][user].index;
if (userIndex == 0) {
userIndex = INITIAL_REWARD_INDEX.Uint128();
}
if (userIndex == index || index == 0) continue;
uint256 deltaIndex = index - userIndex;
uint256 rewardDelta = userShares.mulDown(deltaIndex);
uint256 rewardAccrued = userReward[token][user].accrued + rewardDelta;
userReward[token][user] = UserReward({index: index.Uint128(), accrued: rewardAccrued.Uint128()});
}
}
function _updateRewardIndex() internal virtual returns (address[] memory tokens, uint256[] memory indexes);
function _redeemExternalReward() internal virtual;
function _doTransferOutRewards(
address user,
address receiver
) internal virtual returns (uint256[] memory rewardAmounts);
function _rewardSharesUser(address user) internal view virtual returns (uint256);
}// 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;
interface IPGauge {
event RedeemRewards(address indexed user, uint256[] rewardsOut);
event UpdateActiveBalance(address indexed user, uint256 newActiveBalance);
function totalActiveSupply() external view returns (uint256);
function activeBalance(address user) external view returns (uint256);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPGaugeController {
event MarketClaimReward(address indexed market, uint256 amount);
event ReceiveVotingResults(uint128 indexed wTime, address[] markets, uint256[] pendleAmounts);
event UpdateMarketReward(address indexed market, uint256 pendlePerSec, uint256 incentiveEndsAt);
function fundPendle(uint256 amount) external;
function withdrawPendle(uint256 amount) external;
function pendle() external returns (address);
function redeemMarketReward() external;
function rewardData(address pool) external view returns (uint128 pendlePerSec, uint128, uint128, uint128);
}// 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 "@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 IPGauge, IERC20Metadata {
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
);
function getNonOverrideLnFeeRateRoot() external view returns (uint80);
function VERSION() external pure returns (uint256);
function reentrancyGuardEntered() external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPMarketFactory {
event SetOverriddenFee(address indexed router, address indexed market, uint80 lnFeeRateRoot);
event CreateNewMarket(
address indexed market,
address indexed PT,
int256 scalarRoot,
int256 initialAnchor,
uint256 lnFeeRateRoot
);
event NewTreasuryAndFeeReserve(address indexed treasury, uint8 reserveFeePercent);
function VERSION() external pure returns (uint256);
function isValidMarket(address market) external view returns (bool);
// If this is changed, change the readState function in market as well
function getMarketConfig(
address market,
address router
) external view returns (address treasury, uint80 overriddenFee, uint8 reserveFeePercent);
function createNewMarket(
address PT,
int256 scalarRoot,
int256 initialAnchor,
uint80 lnFeeRateRoot
) external returns (address market);
function setOverriddenFee(address router, address market, uint80 newFee) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPMarketSwapCallback {
function swapCallback(int256 ptToAccount, int256 syToAccount, bytes calldata data) external;
}// 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);
function VERSION() external pure returns (uint256);
function reentrancyGuardEntered() external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPVeToken {
// ============= USER INFO =============
function balanceOf(address user) external view returns (uint128);
function positionData(address user) external view returns (uint128 amount, uint128 expiry);
// ============= META DATA =============
function totalSupplyStored() external view returns (uint128);
function totalSupplyCurrent() external returns (uint128);
function totalSupplyAndBalanceCurrent(address user) external returns (uint128, uint128);
}// 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);
function reentrancyGuardEntered() external view returns (bool);
function VERSION() external pure returns (uint256);
}// 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;
}{
"optimizer": {
"enabled": true,
"runs": 1000000
},
"viaIR": true,
"evmVersion": "shanghai",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/pendle/contracts/core/Market/PendleMarketV6.sol": {
"OracleLib": "0x7d20e644d2a9e149e5be9be9ad2ab243a7835d37"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_PT","type":"address"},{"internalType":"int256","name":"_scalarRoot","type":"int256"},{"internalType":"int256","name":"_initialAnchor","type":"int256"},{"internalType":"uint80","name":"_lnFeeRateRoot","type":"uint80"},{"internalType":"address","name":"_vePendle","type":"address"},{"internalType":"address","name":"_gaugeController","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"int256","name":"exchangeRate","type":"int256"}],"name":"MarketExchangeRateBelowOne","type":"error"},{"inputs":[],"name":"MarketExpired","type":"error"},{"inputs":[{"internalType":"int256","name":"currentAmount","type":"int256"},{"internalType":"int256","name":"requiredAmount","type":"int256"}],"name":"MarketInsufficientPtForTrade","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualBalance","type":"uint256"},{"internalType":"uint256","name":"requiredBalance","type":"uint256"}],"name":"MarketInsufficientPtReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualBalance","type":"uint256"},{"internalType":"uint256","name":"requiredBalance","type":"uint256"}],"name":"MarketInsufficientSyReceived","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":"scalarRoot","type":"int256"}],"name":"MarketScalarRootBelowZero","type":"error"},{"inputs":[],"name":"MarketZeroAmountsInput","type":"error"},{"inputs":[],"name":"MarketZeroAmountsOutput","type":"error"},{"inputs":[],"name":"MarketZeroLnImpliedRate","type":"error"},{"inputs":[],"name":"MarketZeroNetLPFee","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":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiverSy","type":"address"},{"indexed":true,"internalType":"address","name":"receiverPt","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpBurned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtOut","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextOld","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextNew","type":"uint16"}],"name":"IncreaseObservationCardinalityNext","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"netLpMinted","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netPtUsed","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"rewardsOut","type":"uint256[]"}],"name":"RedeemRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"int256","name":"netPtOut","type":"int256"},{"indexed":false,"internalType":"int256","name":"netSyOut","type":"int256"},{"indexed":false,"internalType":"uint256","name":"netSyFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netSyToReserve","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newActiveBalance","type":"uint256"}],"name":"UpdateActiveBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lnLastImpliedRate","type":"uint256"}],"name":"UpdateImpliedRate","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_storage","outputs":[{"internalType":"int128","name":"totalPt","type":"int128"},{"internalType":"int128","name":"totalSy","type":"int128"},{"internalType":"uint96","name":"lastLnImpliedRate","type":"uint96"},{"internalType":"uint16","name":"observationIndex","type":"uint16"},{"internalType":"uint16","name":"observationCardinality","type":"uint16"},{"internalType":"uint16","name":"observationCardinalityNext","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"activeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiverSy","type":"address"},{"internalType":"address","name":"receiverPt","type":"address"},{"internalType":"uint256","name":"netLpToBurn","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"netSyOut","type":"uint256"},{"internalType":"uint256","name":"netPtOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expiry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNonOverrideLnFeeRateRoot","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"cardinalityNext","type":"uint16"}],"name":"increaseObservationsCardinalityNext","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRewardBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"netSyDesired","type":"uint256"},{"internalType":"uint256","name":"netPtDesired","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"netLpOut","type":"uint256"},{"internalType":"uint256","name":"netSyUsed","type":"uint256"},{"internalType":"uint256","name":"netPtUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"uint216","name":"lnImpliedRateCumulative","type":"uint216"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"secondsAgos","type":"uint32[]"}],"name":"observe","outputs":[{"internalType":"uint216[]","name":"lnImpliedRateCumulative","type":"uint216[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"readState","outputs":[{"components":[{"internalType":"int256","name":"totalPt","type":"int256"},{"internalType":"int256","name":"totalSy","type":"int256"},{"internalType":"int256","name":"totalLp","type":"int256"},{"internalType":"address","name":"treasury","type":"address"},{"internalType":"int256","name":"scalarRoot","type":"int256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"lnFeeRateRoot","type":"uint256"},{"internalType":"uint256","name":"reserveFeePercent","type":"uint256"},{"internalType":"uint256","name":"lastLnImpliedRate","type":"uint256"}],"internalType":"struct MarketState","name":"market","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"readTokens","outputs":[{"internalType":"contract IStandardizedYield","name":"_SY","type":"address"},{"internalType":"contract IPPrincipalToken","name":"_PT","type":"address"},{"internalType":"contract IPYieldToken","name":"_YT","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"redeemRewards","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reentrancyGuardEntered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardState","outputs":[{"internalType":"uint128","name":"index","type":"uint128"},{"internalType":"uint128","name":"lastBalance","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"exactPtIn","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapExactPtForSy","outputs":[{"internalType":"uint256","name":"netSyOut","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"exactPtOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapSyForExactPt","outputs":[{"internalType":"uint256","name":"netSyIn","type":"uint256"},{"internalType":"uint256","name":"netSyFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalActiveSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userReward","outputs":[{"internalType":"uint128","name":"index","type":"uint128"},{"internalType":"uint128","name":"accrued","type":"uint128"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6102206040523462000155576200002662000019620001c8565b94939093929192620002af565b604051615ef4908162000d0c82396080518161190a015260a051818181613c58015261519b015260c05181613c8c015260e05181818161291d01526157f2015261010051816151e00152610120518181816104380152818161166c015281816119f901528181611bd301528181612062015261258401526101405181818161046c01528181611461015281816119d401528181611b9b01528181611d7a015281816120a201526125460152610160518181816113b601528181611a2001528181611aea015261227901526101805181818161090201526131d801526101a05181818161074b015281816113820152818161196401528181611ab601528181612245015261327301526101c0518161325001526101e051816127430152610200518181816106f2015261322b0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b038211176200018957604052565b62000159565b601f909101601f19168101906001600160401b038211908210176200018957604052565b51906001600160a01b03821682036200015557565b62006c009060c08238039283604051948592620001e682856200018f565b8339810103126200015557620001fc82620001b3565b9060208301519260408101519260608201519260018060501b038416840362000155576200023b60a06200023360808601620001b3565b9401620001b3565b91959493929190565b9081602091031262000155576200025b90620001b3565b90565b6040513d5f823e3d90fd5b519061ffff821682036200015557565b919082604091031262000155576200025b6020620002978462000269565b930162000269565b9081602091031262000155575190565b939095949260018060a01b039283861693604093845163afd27bf560e01b918282526020996004998b848c818d5afa928315620004a15762000311945f9462000553575b506200030a620003038462000981565b9362000a5c565b9362000577565b6101208681528551918252888289818a5afa918215620004a157620003548a92858b9562000360945f9162000531575b501661014052516001600160a01b031690565b6001600160a01b031690565b865163781c18db60e01b815292839182905afa908115620004a1575f91620004fd575b501661016052825163329f6acb60e21b8152600c8682019081524263ffffffff1660208201528490829081906040010381737d20e644d2a9e149e5be9be9ad2ab243a7835d375af4908115620004a15762000424915f915f91620004c2575b50600b805461ffff60801b191660809290921b61ffff60801b169190911790555b600b805461ffff60701b191660709290921b61ffff60701b16919091179055565b5f881315620004a7578596976101c0969596526101e0526102005251928380926370c264df60e11b82525afa918215620004a1575f926200046d575b50506101a0523361018052565b620004919250803d1062000499575b6200048881836200018f565b8101906200029f565b5f8062000460565b503d6200047c565b6200025e565b825163483512b160e11b815280860189815281906020010390fd5b620004039250620004ed9150863d8811620004f5575b620004e481836200018f565b81019062000279565b9091620003e2565b503d620004d8565b620005229150883d8a1162000529575b6200051981836200018f565b81019062000244565b5f62000383565b503d6200050d565b6200054c9150863d881162000529576200051981836200018f565b5f62000341565b6200056f9194508d803d1062000529576200051981836200018f565b925f620002f3565b835191949293909291906001600160401b0382116200018957620005a882620005a260065462000712565b6200074d565b602090601f83116001146200067d57600493620005e884602098955f9a9895620005f1958c9262000671575b50508160011b915f199060031b1c19161790565b600655620007f7565b6012608052600580546001600160f81b0316600160f81b17905560a0526001600160a01b0392831660e052610100819052604051635162986d60e11b81529485938492165af1908115620004a1575f916200064d575b5060c052565b6200066a915060203d60201162000529576200051981836200018f565b5f62000647565b015190505f80620005d4565b60065f52601f19831691907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f925f5b818110620006f95750845f999794620005f1946004989460209b9860019510620006e0575b505050811b01600655620007f7565b01518b1960f88460031b161c191690555f8080620006d1565b92936020600181928786015181550195019301620006ac565b90600182811c9216801562000742575b60208310146200072e57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162000722565b601f81116200075a575050565b60065f5260205f20906020601f840160051c8301931062000797575b601f0160051c01905b8181106200078b575050565b5f81556001016200077f565b909150819062000776565b601f8111620007af575050565b60075f5260205f20906020601f840160051c83019310620007ec575b601f0160051c01905b818110620007e0575050565b5f8155600101620007d4565b9091508190620007cb565b80519091906001600160401b038111620001895762000823816200081d60075462000712565b620007a2565b602080601f83116001146200085b575081906200085693945f92620006715750508160011b915f199060031b1c19161790565b600755565b60075f52601f198316949091907fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688925f905b878210620008c7575050836001959610620008ae575b505050811b01600755565b01515f1960f88460031b161c191690555f8080620008a3565b806001859682949686015181550195019301906200088d565b6001600160401b0381116200018957601f01601f191660200190565b9190602092838183031262000155578051906001600160401b0382116200015557019281601f85011215620001555783516200093881620008e0565b926200094860405194856200018f565b81845282828701011162000155575f5b8181106200096d5750825f9394955001015290565b858101830151848201840152820162000958565b6004905f620009ad60405162000997816200016d565b848152630282628160e51b602082015262000b2c565b6040516306fdde0360e01b815293909284919082906001600160a01b03165afa918215620004a1576200025b9262000a2e915f9162000a35575b5062000a2762000a2060405192620009ff846200016d565b60038452620282a160ed1b602085015262000a1962000b12565b5062000b2c565b9162000b2c565b9062000bf6565b9062000b6a565b62000a5591503d805f833e62000a4c81836200018f565b810190620008fc565b5f620009e7565b6004905f62000a8860405162000a72816200016d565b84815263504c502d60e01b602082015262000b2c565b6040516395d89b4160e01b815293909284919082906001600160a01b03165afa918215620004a1576200025b9262000a2e915f9162000af4575b5062000a2762000a206040519262000ada846200016d565b600384526250542d60e81b602085015262000a1962000b12565b62000b0b91503d805f833e62000a4c81836200018f565b5f62000ac2565b6040519062000b21826200016d565b5f6020838281520152565b62000b3662000b12565b5060208151916040519262000b4b846200016d565b835201602082015290565b634e487b7160e01b5f52601160045260245ffd5b908151918151830180931162000be157602062000ba462000b8b85620008e0565b9462000b9b60405196876200018f565b808652620008e0565b8482019290601f190136843762000bc2828201518251908562000c86565b5184010180911162000be1578160206200025b93015190519162000c86565b62000b56565b5f1981019190821162000be157565b9062000c0162000b12565b5081519080519182811062000c70576001926020850193845182602086015180830362000c5f575b50505062000c39575b5050505090565b810390811162000be15783525190805191820180921162000be157525f80808062000c32565b81929350209120145f828162000c29565b50505090565b601f811162000be1576101000a90565b92905b60209384841062000cc3578151815284810180911162000be15793810180911162000be15791601f19810190811162000be1579162000c89565b929193505f19918062000cdf575b505182518216911916179052565b9091506020036020811162000be15762000cfd62000d039162000c76565b62000be7565b905f62000cd156fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314612c80575080630892cd8b14612c1d578063095ea7b314612bce578063156e29f6146121ab57806318160ddd1461214f5780631dd19cb414611fb657806323b872dd14611e28578063252c09d714611da657806329910b1114611a475780632c8ce6bc146119895780632f13b60c1461192e578063313ce567146118d357806337d45e3a146116c25780635b709f17146113135780635cbadbe41461127b57806370a082311461121257806372069264146111d7578063794052f314611124578063883bdbfd14610e875780639262187b14610c0c57806395d89b4114610ab9578063a9059cbb146109e5578063a9f8d181146109aa578063c3fb90d614610926578063c45a0155146108b8578063c4f59f9b14610828578063d2c725e0146107e7578063dd62ed3e1461076e578063e184c9be14610716578063e4f8b2e9146106b2578063ea64a82014610629578063f6b911bc146101c45763ffa1ad7414610187575f80fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160068152f35b5f80fd5b346101c0576101d236612e02565b9160058054906101e860028360f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809316178155610238336130d3565b30156105a55761024730614aed565b305f526020926003845260405f2054878110610521578790305f52600386520360405f20558087116101c0578254818816828216038281116104f457827fff00000000000000000000000000000000000000000000000000000000000000911691161783555f6040518881527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef863092a36102e130614bb0565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c05786156104ca578382019561031d875189613bd8565b9561032e6040850197885190613df0565b9761034561033d86518c613bd8565b895190613df0565b978915806104c2575b6104985761035d8b8251614ce7565b905261036a888651614ce7565b8552610377898251614ce7565b90525f88126101c0575f87126101c0577f4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f908860409a61040a7f01000000000000000000000000000000000000000000000000000000000000009773ffffffffffffffffffffffffffffffffffffffff8087169685308903610465575b50508c81891698308a03610432575b505050613527565b8b51908152602081019190915260408101899052606090a38254161790558351928352820152f35b61045d927f000000000000000000000000000000000000000000000000000000000000000016613755565b8e8c81610402565b61049191837f000000000000000000000000000000000000000000000000000000000000000016613755565b8f856103f3565b60046040517fccbf5468000000000000000000000000000000000000000000000000000000008152fd5b50881561034e565b60046040517f25daaec3000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b608485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff610675612dbc565b165f908152600260209081526040918290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b0390f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405169ffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576107a5612dbc565b6107ad612ddf565b9073ffffffffffffffffffffffffffffffffffffffff8091165f52600460205260405f2091165f52602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600260055460f81c14604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05761085e613c16565b604051809160208083016020845282518091526020604085019301915f5b82811061088b57505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff168552869550938101939281019260010161087c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760c0600a54600b5461ffff906040519280600f0b845260801d600f0b60208401526bffffffffffffffffffffffff81166040840152818160601c166060840152818160701c16608084015260801c1660a0820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600154604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0577f0100000000000000000000000000000000000000000000000000000000000000610a3d612dbc565b610aa660055491610a5460028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055560243590336138d2565b6005541617600555602060405160018152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576040515f9060075460018160011c9060018316928315610c02575b6020938484108114610bd557838652908115610b975750600114610b3d575b6106ae84610b3181880382612f4c565b60405191829182612d58565b60075f9081529294507fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885b828410610b8457505050816106ae93610b319282010193610b21565b8054858501870152928501928101610b68565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b8201019150610b31816106ae610b21565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610b02565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610c44612dbc565b90600554906002610c5b60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80941617600555610cac84614aed565b610cb584614bb0565b610cbd613c16565b93610cc885516146c5565b915f9573ffffffffffffffffffffffffffffffffffffffff96878416905b8251811015610e1a5788610cfa8285614714565b51165f525f875260405f20825f52875260405f205490608091821c610d1f8289614714565b52610d2a8188614714565b51610d3a575b6001915001610ce6565b89610d458286614714565b51165f525f885260405f20835f52885260405f20916fffffffffffffffffffffffffffffffff92838154169055610d7c8289614714565b51908382116101c0578b610d908488614714565b51165f52868a528360405f209216908254901c039283116104f457600192610df391906fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b610e158a610e018387614714565b511687610e0e848b614714565b5191614c41565b610d30565b6106ae867f01000000000000000000000000000000000000000000000000000000000000008a857f78d61a0c27b13f43911095f9f356f14daa3cd8b125eea1aa22421245e90e813d60405180610e708782612fa5565b0390a2600554161760055560405191829182612fa5565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05767ffffffffffffffff6004358181116101c057366023820112156101c0578060040135610ee381612f8d565b91610ef16040519384612f4c565b8183528483016024819360051b830101913683116101c057602401905b8282106111085750505063ffffffff91600b5461ffff906040519485937f1dd421ce00000000000000000000000000000000000000000000000000000000855260c4850190600c6004870152824216602487015260c060448701525180915260e4850195915f905b8a8383106110eb578a885f81808d8c8c6bffffffffffffffffffffffff81166064850152818160601c16608485015260701c1660a48301520381737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156110e0575f92611036575b505090604051918183928301818452825180915281604085019301915f5b82811061100257505050500390f35b83517affffffffffffffffffffffffffffffffffffffffffffffffffffff1685528695509381019392810192600101610ff3565b9091503d805f843e6110488184612f4c565b82019183818403126101c05780519182116101c057019080601f830112156101c057815161107581612f8d565b926110836040519485612f4c565b818452848085019260051b8201019283116101c0578401905b8282106110ad575050508280610fd5565b81517affffffffffffffffffffffffffffffffffffffffffffffffffffff811681036101c057815290840190840161109c565b6040513d5f823e3d90fd5b845182168952978801978997509093019260019190910190610f76565b813563ffffffff811681036101c0578152908601908601610f0e565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610120611166611161612dbc565b6130d3565b6040519080518252602081015160208301526040810151604083015273ffffffffffffffffffffffffffffffffffffffff60608201511660608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008091015190820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600854604051908152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602061127361124e612dbc565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f205490565b604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576112b2612dbc565b6112ba612ddf565b73ffffffffffffffffffffffffffffffffffffffff9182165f908152602081815260408083209390941682529182528290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b346101c05761132136612e62565b91929060055461133760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f00000000000000000000000000000000000000000000000000000000000000001115611698576113b0336130d3565b926113da7f0000000000000000000000000000000000000000000000000000000000000000613491565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081881190816101c05761141f96611415428b84846141bf565b9891999099613b87565b995f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978d308a03611665575b505061148e8a847f0000000000000000000000000000000000000000000000000000000000000000169460608701511685613755565b61149784613527565b816115ea575b505060206114aa826136f7565b9201918251905f82126101c057106115a15750506114da670de0b6b3a7640000916114d5888a613045565b613f76565b0415611577576101c05785116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c4857f01000000000000000000000000000000000000000000000000000000000000009561156061153d8a613b87565b8b5195865260208601526040850192909252606084015233929081906080820190565b0390a3600554161760055582519182526020820152f35b60046040517f880ccd2c000000000000000000000000000000000000000000000000000000008152fd5b6115aa906136f7565b9051905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090868c116101c0576115fe8c613b87565b333b156101c0578d61163f5f9360405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611656575b8061149d565b61165f90612eef565b8b611650565b61169191857f000000000000000000000000000000000000000000000000000000000000000016613755565b8d8d611458565b60046040517fb2094b59000000000000000000000000000000000000000000000000000000008152fd5b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576004359061ffff908183168093036101c0576005549161171860028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055580600b5460801c16604051947f587cdc06000000000000000000000000000000000000000000000000000000008652600c600487015281602487015260448601528285606481737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156110e0575f9261187d575b7f010000000000000000000000000000000000000000000000000000000000000095508216808203611804575b60058054861687179055005b7fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a936040937fffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffff71ffff00000000000000000000000000000000600b549260801b16911617600b558351928352820152a1828080806117f8565b91508285813d83116118cc575b6118948183612f4c565b810103126101c0576118c67f0100000000000000000000000000000000000000000000000000000000000000956130a3565b916117cb565b503d61188a565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020604051427f000000000000000000000000000000000000000000000000000000000000000011158152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057606060405173ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000000000000000000000000000000000000000000000168252807f00000000000000000000000000000000000000000000000000000000000000001660208301527f0000000000000000000000000000000000000000000000000000000000000000166040820152f35b346101c057611a5536612e62565b909192600554611a6b60028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f0000000000000000000000000000000000000000000000000000000000000000111561169857611ae4336130d3565b91611b0e7f0000000000000000000000000000000000000000000000000000000000000000613491565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9687821190816101c057611b4e42611b4785613b87565b83896141bf565b97919990985f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978c308a03611d73575b5050611bc08a84606087015116857f000000000000000000000000000000000000000000000000000000000000000016613755565b611bc984613527565b81611cf6575b50507f000000000000000000000000000000000000000000000000000000000000000016611bfc816136f7565b8251905f82126101c05710611cad575050611c24670de0b6b3a7640000916114d5888a613045565b0415611577576101c057611c3790613b87565b9585116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c46115607f0100000000000000000000000000000000000000000000000000000000000000958951918291898b3397859094939260609260808301968352602083015260408201520152565b611cb6906136f7565b9051905f82126101c057604491604051917f2c49ea0f00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090611d0387613b87565b908d8d116101c057333b156101c0575f91611d4d8e9260405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611d64575b80611bcf565b611d6d90612eef565b8b611d5e565b611d9f91857f000000000000000000000000000000000000000000000000000000000000000016613755565b8d8c611b8b565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760043561ffff8110156101c057606090600c01546040519063ffffffff811682527affffffffffffffffffffffffffffffffffffffffffffffffffffff8160201c16602083015260f81c15156040820152f35b346101c057611e3636612e02565b60055491611e4a60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055573ffffffffffffffffffffffffffffffffffffffff84165f52600460205260405f20335f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103611f14575b5090610aa6917f0100000000000000000000000000000000000000000000000000000000000000946138d2565b9190818310611f58577f010000000000000000000000000000000000000000000000000000000000000094611f4f83610aa695033383613320565b94509091611ee7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057600554611ff760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555612048336130d3565b9073ffffffffffffffffffffffffffffffffffffffff91827f0000000000000000000000000000000000000000000000000000000000000000169261208c846136f7565b82515f81126101c05761209e91613045565b90807f000000000000000000000000000000000000000000000000000000000000000016906120cc826136f7565b956020850151965f88126101c0577f01000000000000000000000000000000000000000000000000000000000000009761210591613045565b9380612137575b50508261211f5760058054861687179055005b606061212e9401511690613755565b828080806117f8565b612148918360608801511690613755565b868061210c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b346101c05760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576121e2612dbc565b602435604435917f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055461223d60028260f81c1415612fe0565b1617600555427f0000000000000000000000000000000000000000000000000000000000000000111561169857612273336130d3565b9061229d7f0000000000000000000000000000000000000000000000000000000000000000613491565b917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff938481116101c0578486116101c0575f9381158015612bc6575b6104ca5760a08301954287511115611698576040840195865180155f14612b295750506123068884613bd8565b905f82126101c0575f6003831115612b1a5750816001908060011c600181018091116104f45791905b848310612aff575050505b81116101c0577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1881019081136001166104f457956103e89297955b5f8813801590612af5575b8015612aeb575b61049857612399896020870151613edc565b60208601526123a9878651613edc565b85526123bf6123b8858a613edc565b8251613edc565b90525f83126101c0575f87126101c0575f88126101c0575f86126101c05782612719575b50505073ffffffffffffffffffffffffffffffffffffffff8083169283156126bb5761240e81614aed565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f4577fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61253a9416911617600555845f52600360205260405f2061250788825461351a565b9055845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206040518b8152a3614bb0565b61254382613527565b807f00000000000000000000000000000000000000000000000000000000000000001661256f816136f7565b6020840151905f82126101c0571061266e57507f0000000000000000000000000000000000000000000000000000000000000000166125ad816136f7565b8251905f82126101c05710611cad575050906106ae917fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb60405180612605858989846040919493926060820195825260208201520152565b0390a27f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6005541617600555604051938493846040919493926060820195825260208201520152565b602061267a84926136f7565b910151905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b42815111156116985761274061273761277593602087015190613ef7565b91429051613045565b907f00000000000000000000000000000000000000000000000000000000000000009061276d8387613f89565b90865161401a565b610100830152612783614940565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f45760205f927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef927fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001971691161760055584845260038252604084206128a082825461351a565b9055604051908152a33060011415806128bb575b80806123e3565b6128c4906155be565b60015f52600360205260405f20546040517fe268b3a4000000000000000000000000000000000000000000000000000000008152600160048201526040816024815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19081156110e0575f905f92612a92575b506fffffffffffffffffffffffffffffffff16826028810204602814831517156104f4576064602884020491816129fa575b505060207f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54916001938082105f146129f357505b6129d6816129d1600854875f526009865260405f205490613045565b61351a565b600855835f52600982528060405f2055604051908152a2856128b4565b90506129b5565b90612a3d6fffffffffffffffffffffffffffffffff612a4293167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b614010565b90603c820291808304603c14901517156104f457600192612a8a60209260647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5495049061351a565b925092612981565b9150506040813d604011612ae3575b81612aae60409383612f4c565b810103126101c0576fffffffffffffffffffffffffffffffff612adc6020612ad584615ea1565b9301615ea1565b919061294f565b3d9150612aa1565b505f871315612387565b505f891315612380565b90919350612b11846129d18184614010565b821c919061232f565b911561233a576001915061233a565b89989250612b3d90612b459295989a613bd8565b855190613df0565b612b50895188613bd8565b96612b616020870198895190613df0565b9081831215612b9b575050612b7c81612b9592999851613bd8565b612b90612b8b8b518093613edc565b613e72565b613df0565b97612375565b985098965050612bc0612bb18998998651613bd8565b612b90612b8b89518093613edc565b95612375565b5086156122d9565b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057612c12612c08612dbc565b6024359033613320565b602060405160018152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff612c69612dbc565b165f526009602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0575f9060065460018160011c9060018316928315612d4e575b6020938484108114610bd557838652908115610b975750600114612cf4576106ae84610b3181880382612f4c565b60065f9081529294507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b828410612d3b57505050816106ae93610b319282010193610b21565b8054858501870152928501928101612d1f565b91607f1691612cc6565b6020808252825181830181905293925f5b858110612da8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b818101830151848201604001528201612d69565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101c05773ffffffffffffffffffffffffffffffffffffffff9060043582811681036101c0579160243590811681036101c0579060443590565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101c05760043573ffffffffffffffffffffffffffffffffffffffff811681036101c057916024359160443567ffffffffffffffff928382116101c057806023830112156101c05781600401359384116101c057602484830101116101c0576024019190565b67ffffffffffffffff8111612f0357604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117612f0357604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612f0357604052565b67ffffffffffffffff8111612f035760051b60200190565b60209060206040818301928281528551809452019301915f5b828110612fcc575050505090565b835185529381019392810192600101612fbe565b15612fe757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b919082039182116104f457565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe094601f948752602087015260606040870152816060870152868601375f8582860101520116010190565b519061ffff821682036101c057565b519073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b906040805192610120840184811067ffffffffffffffff821117612f03578252606084015f815260808501905f825260a08601925f845260c08701915f835260e088015f81526101008901965f885289600a5480600f0b825260801d600f0b60208201527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82116101c05782015273ffffffffffffffffffffffffffffffffffffffff91828251957f5c098c11000000000000000000000000000000000000000000000000000000008752306004880152166024860152606085604481867f0000000000000000000000000000000000000000000000000000000000000000165afa8015613316575f925f965f926132ae575b505060ff16905216905269ffffffffffffffffffff908082166132a957507f00000000000000000000000000000000000000000000000000000000000000005b1690527f000000000000000000000000000000000000000000000000000000000000000090527f000000000000000000000000000000000000000000000000000000000000000090526bffffffffffffffffffffffff600b54169052565b61324b565b93509550506060823d60601161330e575b816132cc60609383612f4c565b810103126101c0576132dd826130b2565b9460208301519269ffffffffffffffffffff841684036101c05701519460ff861686036101c057919460ff5f61320b565b3d91506132bf565b82513d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff80911691821561340e571691821561338a5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591835f526004825260405f20855f5282528060405f2055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b602073ffffffffffffffffffffffffffffffffffffffff60045f9360405194859384927f1d52edc4000000000000000000000000000000000000000000000000000000008452165af19081156110e0575f916134eb575090565b90506020813d602011613512575b8161350660209383612f4c565b810103126101c0575190565b3d91506134f9565b919082018092116104f457565b610100810180516bffffffffffffffffffffffff8082116101c05761355960206135518651614170565b950151614170565b93600b549161ffff956040968751947f4af74b68000000000000000000000000000000000000000000000000000000008652600c6004870152818160601c16602487015263ffffffff421660448701528381166064870152818160701c16608487015260801c1660a4850152868460c481737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156136ed575f935f93613697575b6dffff0000000000000000000000009495507fffffffffffffffffffffffffffffffff00000000000000000000000000000000809360801b16906fffffffffffffffffffffffffffffffff1617600a556fffff0000000000000000000000000000600b549360701b1695169116179160601b161717600b555190519081527f5c0e21d57bb4cf91d8fe238d6f92e2685a695371b19209afcce6217b478f83e160204292a2565b925092508684813d83116136e6575b6136b08183612f4c565b810103126101c0576dffff000000000000000000000000926136dd60206136d6876130a3565b96016130a3565b928594506135f1565b503d6136a6565b87513d5f823e3d90fd5b602073ffffffffffffffffffffffffffffffffffffffff916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352306004840152165afa9081156110e0575f916134eb575090565b906040519060208201937fa9059cbb00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff8092166024840152604483015260448252608082019282841067ffffffffffffffff851117612f0357613815945f9283928660405216936137d786612f30565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082855af161380f614be4565b9161599a565b80519081159182156138af575b50501561382b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b81925090602091810103126101c0576020015180151581036101c0575f80613822565b91909173ffffffffffffffffffffffffffffffffffffffff808216908115613b03578416928315613a7f57838214613a215761390e8584614b41565b815f52600360205260405f205481811061399d577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208387948694855f52600384520360405f2055845f5260405f2061396982825461351a565b9055604051908152a3300361398e575b5030036139835750565b61398c9061578c565b565b6139979061578c565b5f613979565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f45524332303a207472616e7366657220746f2073656c660000000000000000006044820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b90815f03918083057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14901517156104f457565b90670de0b6b3a7640000918281029281840514901517156104f457565b81810292915f82127f80000000000000000000000000000000000000000000000000000000000000008214166104f45781840514901517156104f457565b6040517fc4f59f9b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff5f826004817f000000000000000000000000000000000000000000000000000000000000000085165afa9182156110e0575f92613d55575b507f000000000000000000000000000000000000000000000000000000000000000090613cb68284614755565b613d505782516001808201948583116104f4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613d0c613cf688612f8d565b97613d04604051998a612f4c565b808952612f8d565b013660208801375f5b838110613d3057505050613d299084614714565b9116905290565b8085613d3d859385614714565b5116613d49828a614714565b5201613d15565b505090565b9091503d805f833e613d678183612f4c565b81019060209081818403126101c05780519067ffffffffffffffff82116101c057019180601f840112156101c0578251613da081612f8d565b93613dae6040519586612f4c565b818552838086019260051b8201019283116101c0578301905b828210613dd95750505050905f613c89565b838091613de5846130b2565b815201910190613dc7565b8115613e45577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82147f80000000000000000000000000000000000000000000000000000000000000008214166104f4570590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182136001166104f457565b90670de0b6b3a76400005f83820393128184128116918413901516176104f457565b81810392915f1380158285131691841216176104f457565b9190915f83820193841291129080158216911516176104f457565b613f37670de0b6b3a7640000915f84125f14613f6b576114d57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff946147a5565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b90565b6114d56001946147a5565b818102929181159184041417156104f457565b60800151906301e13380918281029281840514901517156104f4577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613fd491613df0565b905f821315613fdf57565b602482604051907f1ca418760000000000000000000000000000000000000000000000000000000082526004820152fd5b8115613e45570490565b5f81126141125761403761403161403c9383613edc565b91613bbb565b6147f6565b670d529ae9e86000008082136140db575061406b929161403761406161406693614cf8565b613bbb565b613edc565b670de0b6b3a764000081126140aa5761408390614800565b905f82126101c0576301e13380918281029281840414901517156104f457613f6891614010565b602490604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917ffc68d09e00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e656761746976650000000000000000000000000000000000000000000000006044820152fd5b807fffffffffffffffffffffffffffffffff800000000000000000000000000000001315806141a5575b156101c057600f0b90565b506f7fffffffffffffffffffffffffffffff81131561419a565b939060a085019484865111156116985780518481131561468e5750604051916080830183811067ffffffffffffffff821117612f03576040525f83525f60208401525f60408401525f6060840152858751111561169857614221868851613045565b61422b8184613f89565b845261423b602084015183613ef7565b80602086015283519081158015614686575b61464f57505082516101008401516020860151916301e1338061427285895194613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c0576142a390615ac7565b92670de0b6b3a7640000841261461e57926142e06301e1338095936140376140616142db866140376140316142f59b6142e69a613edc565b614cf8565b90613ec4565b604087015260c0850151613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05761432690615ac7565b6060840152815160208401519061435585519161403761403160408901519561434f8c85614ce7565b93613edc565b670d529ae9e86000008082136140db575061437a929161403761406161406693614cf8565b94670de0b6b3a764000086126145ed5761439f61439a8761403784613bbb565b613b87565b606085015190965f8313156145cd57816140376143bb92613bbb565b670de0b6b3a764000081126140aa57506143e66143e0670de0b6b3a764000092613ea2565b88613bd8565b05915b60e08401517f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05783606461442561442c9383613bd8565b0598613ec4565b945f861280156145a35715614598576144657fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff966147a5565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156104f457826144909161351a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116104f457826144c491614010565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c0576145476145629561453061452861451e61451761450f6101009d61455598613bd8565b945b89615093565b9d88615093565b9c9d839f51613045565b968951614ce7565b88526145418b60208a015192613edc565b90614ce7565b806020880152865193613ef7565b604082519201519261401a565b91829101521561456e57565b60046040517faa69ae1e000000000000000000000000000000000000000000000000000000008152fd5b6144656001966147a5565b506145556145476145629561453061452861451e6145176145c76101009d8a615093565b94614511565b5061439a81612b906145e16145e794613ea2565b8a613bd8565b916143e9565b602486604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917fb1c4aefb00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50801561424d565b60449085604051917f8615426c00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b906146cf82612f8d565b6146dc6040519182612f4c565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061470a8294612f8d565b0190602036910137565b80518210156147285760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b908151915f5b83811061476a57505050505f90565b73ffffffffffffffffffffffffffffffffffffffff8061478a8385614714565b51169084161461479c5760010161475b565b50505050600190565b5f8113156147b05790565b7f800000000000000000000000000000000000000000000000000000000000000081146104f4575f0390565b8015613e45576ec097ce7bc90715b34b9f10000000000590565b8115613e45570590565b5f8113156148e25780670c7d713b49da000012806148d1575b156148c857670de0b6b3a7640000906ec097ce7bc90715b34b9f10000000009061486c908302828101907fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f0000000000183026147f6565b9080828002059181838202058284820205838582020591848684020593858786020595808888020597880205600f900596600d900595600b900594600990059360079005926005900591600390050101010101010160011b0590565b613f6890614d49565b50670f43fc2c04ee00008112614819565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e6473000000000000000000000000000000000000006044820152fd5b614948615124565b815115614ae957600191308314159182614963575b50505050565b61496d84936155be565b825f52602060098152604090815f2054935f955b61498e575b50505061495d565b8051861015614ae45773ffffffffffffffffffffffffffffffffffffffff6149b68783614714565b5116956149c38186614714565b5190875f525f8452845f20895f5284526fffffffffffffffffffffffffffffffff9182865f2054168015614add575b8a8282148015614ad5575b614ac95750670de0b6b3a7640000614a21614a1b614a3c9385613045565b8b613f76565b048a5f525f8752875f208c5f528752875f205460801c61351a565b908381116101c0578382116101c0578a9984614ac2938c968a5194614a6086612f30565b16845216878301525f525f8652865f20845f528652865f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b0195614981565b99505050879150614ac2565b5082156149fd565b50896149f2565b614986565b5050565b614af5615124565b90805115614b3c5773ffffffffffffffffffffffffffffffffffffffff83168015159081614b31575b50614b2857505050565b61398c926155f2565b90503014155f614b1e565b505050565b614b49615124565b9181511561495d5773ffffffffffffffffffffffffffffffffffffffff9083838383168015159081614ba5575b50614b94575b50505083168015159081614b315750614b2857505050565b614b9d926155f2565b5f8383614b7c565b90503014155f614b76565b73ffffffffffffffffffffffffffffffffffffffff81168015159081614bd9575b506139835750565b90503014155f614bd1565b3d15614c3c573d9067ffffffffffffffff8211612f035760405191614c3160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184612f4c565b82523d5f602084013e565b606090565b9190918115614b3c5773ffffffffffffffffffffffffffffffffffffffff169182614cde575f809350809281925af1614c78614be4565b5015614c8057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6574682073656e64206661696c656400000000000000000000000000000000006044820152fd5b61398c92613755565b9080821261411257613f6891613ec4565b670de0b6b3a76400008114614d1f57614d1a81614037614031613f6894613ea2565b614800565b60046040517fa9c8b14d000000000000000000000000000000000000000000000000000000008152fd5b670de0b6b3a7640000811261507d576064905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215615052575b73011798004d755d3c8bc8e03204cf44619e000000821215615031575b820290808302906e01855144814a7ff805980ff0084000908183121561500e575b50506b02df0ab5a80a22c61ab5a70080821215614fee575b50693f1fce3da636ea5cf85080821215614fce575b50690127fa27722cc06cc5e280821215614fae575b5068280e60114edb805d0380821215614f8e575b50680ebc5fb4174612111080821215614f77575b506808f00f760a4b2db55d80821215614f57575b506806f5f177578893793780821215614f37575b506806248f33704b28660380821215614f18575b506805c548670b9510e7ac80821215614ef9575b50614eb868056bc75e2d6310000091827ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000008183019201026147f6565b9080828002059181838202058284820205916003600560076009600b888a89020598808b8b02059a8b0205059805960594059205010101010160011b010590565b68056bc75e2d631000006756bc75e2d63100009202059101905f614e7c565b68056bc75e2d6310000067ad78ebc5ac6200009202059101905f614e68565b68056bc75e2d6310000068015af1d78b58c400009202059101905f614e54565b68056bc75e2d631000006802b5e3af16b18800009202059101905f614e40565b68056bc75e2d63100000809202059101905f614e2c565b68056bc75e2d63100000680ad78ebc5ac62000009202059101905f614e18565b68056bc75e2d631000006815af1d78b58c4000009202059101905f614e04565b68056bc75e2d63100000682b5e3af16b188000009202059101905f614def565b68056bc75e2d631000006856bc75e2d6310000009202059101905f614dda565b68ad78ebc5ac62000000925069021e19e0c9bab240000002059101905f80614dc2565b906b1425982cf597cd205cef73806803782dace9d900000091059101614da1565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b2000000614d84565b61508961508e916147dc565b614d49565b5f0390565b5f821215615119576150c57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff926147a5565b90670de0b6b3a7640000918281029281840414901517156104f4576150e991614010565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b6150c56001926147a5565b61512c613c16565b9061513782516146c5565b918051925f93156155b857600154431461555357436001556008546040517fef5cfb8c0000000000000000000000000000000000000000000000000000000081523060048201525f816024818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af180156110e0576154c9575b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b156101c0575f80916004604051809481937f58f3d1630000000000000000000000000000000000000000000000000000000083525af180156110e0576154b6575b50845b83518110156154ae5773ffffffffffffffffffffffffffffffffffffffff61526e8286614714565b5116908187526002602052604087206040519061528a82612f30565b54906fffffffffffffffffffffffffffffffff8216815260208260801c9101526fffffffffffffffffffffffffffffffff81169073ffffffffffffffffffffffffffffffffffffffff6152dd8489614714565b51168061542157506152f4475b8260801c90613045565b6fffffffffffffffffffffffffffffffff821615615418575b856153e4575b6fffffffffffffffffffffffffffffffff83116101c0576153369160801c61351a565b926fffffffffffffffffffffffffffffffff84116101c0576001936153d3916fffffffffffffffffffffffffffffffff6040519261537384612f30565b81861684521660208301528a52600260205260408a20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b6153dd8286614714565b5201615246565b91670de0b6b3a7640000908184029184830414841517156104f45761540c8761541293614010565b9061351a565b91615313565b6001925061530d565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa80156154a3578a9061546f575b6152f491506152ea565b506020813d60201161549b575b8161548960209383612f4c565b810103126101c0576152f49051615465565b3d915061547c565b6040513d8c823e3d90fd5b505091909250565b6154c1919550612eef565b5f935f615243565b3d805f833e6154d88183612f4c565b810160209182818303126101c05780519067ffffffffffffffff82116101c0570181601f820112156101c057805190838061551284612f8d565b61551f6040519182612f4c565b848152019260051b8201019283116101c0578301905b828210615544575050506151c8565b81518152908301908301615535565b9092505f5b83518110156155b4578073ffffffffffffffffffffffffffffffffffffffff61558360019387614714565b51165f5260026020526fffffffffffffffffffffffffffffffff60405f2054166155ad8285614714565b5201615558565b5090565b92509190565b156155c557565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9291909273ffffffffffffffffffffffffffffffffffffffff8091169081151580615782575b615621906155be565b815f5260209160098352604091825f2054965f5b815181101561577757826156498284614714565b5116906156568189614714565b5191805f525f8852865f20865f5288526fffffffffffffffffffffffffffffffff928b84895f205416801561576f575b8281148015615767575b61575a576156b46156cf926156ae670de0b6b3a76400009386613045565b90613f76565b04835f525f8b52895f20895f528b52895f205460801c61351a565b918482116101c0578483116101c05784615754936001968b51946156f286612f30565b168452168a8301525f525f8952875f20875f528952875f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b01615635565b5050505060019150615754565b508215615690565b506001615686565b505050505050509050565b5030821415615618565b73ffffffffffffffffffffffffffffffffffffffff5f6157bb8284169384151580615990575b61124e906155be565b6040809360248251809581937fe268b3a40000000000000000000000000000000000000000000000000000000083528960048401527f0000000000000000000000000000000000000000000000000000000000000000165af1918215615986575f905f93615949575b506fffffffffffffffffffffffffffffffff8091169060288302838104602814841517156104f4576064900493826158c5575b505050916020917f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54938082105f146158bd5750905b6158a7826129d1600854885f5260098752845f205490613045565b600855845f526009835281815f205551908152a2565b90509061588c565b6158fb9291612a3d91969496167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b92603c840293808504603c14901517156104f45761594060209360647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5496049061351a565b92819294615857565b809350848092503d831161597f575b6159628183612f4c565b810103126101c0576159786020612ad584615ea1565b915f615824565b503d615958565b83513d5f823e3d90fd5b50308514156157b2565b91929015615a1557508151156159ae575090565b3b156159b75790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015615a285750805190602001fd5b615a5e906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301612d58565b0390fd5b15615a6957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e74000000000000000000000000000000006044820152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc000081121580615e8e575b615afa90615a62565b5f8112615e7a576064906806f05b59d3b20000008112615e17577ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e0000000168056bc75e2d6310000082770195e54c5dd42177f53a27172fa9ec630262827000000000925b02819068ad78ebc5ac62000000811215615dde575b6856bc75e2d631000000811215615da4575b682b5e3af16b18800000811215615d6c575b6815af1d78b58c400000811215615d34575b680ad78ebc5ac6200000811215615cfd575b82811215615cc6575b6802b5e3af16b1880000811215615c8f575b68015af1d78b58c40000811215615c58575b60028382800205058360038184840205056004828583020505600583868302050560068487830205056007858883020505906008868984020505926009878a8602050594600a888b8802050596600b898c8a02050599600c8a8d8d0205059b0101010101010101010101010205020590565b6806f5f17757889379377ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000084920192020590615be6565b6808f00f760a4b2db55d7ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000084920192020590615bd4565b680ebc5fb417461211107ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000084920192020590615bc2565b68280e60114edb805d037ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e0000084920192020590615bb9565b690127fa27722cc06cc5e27fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c0000084920192020590615ba7565b693f1fce3da636ea5cf8507fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e780000084920192020590615b95565b6b02df0ab5a80a22c61ab5a7007fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf00000084920192020590615b83565b6e01855144814a7ff805980ff008400091507fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e00000001615b71565b6803782dace9d90000008112615e67577ffffffffffffffffffffffffffffffffffffffffffffffffc87d25316270000000168056bc75e2d63100000826b1425982cf597cd205cef738092615b5c565b68056bc75e2d6310000082600192615b5c565b615e85905f03615ac7565b613f68906147dc565b5068070c1cc73b00c80000811315615af1565b51906fffffffffffffffffffffffffffffffff821682036101c05756fea2646970667358221220057ba17c06d00bc766723abb82a4a8e82f42e80ca1820c4ccbe78d47ab45bba264736f6c63430008180033000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f50000000000000000000000000000000000000000000000017e47e1502df48bba0000000000000000000000000000000000000000000000000e5d988b3876edf0000000000000000000000000000000000000000000000000001ae6069e68807b0000000000000000000000001fbacedf510daa79dfd9b41227a7ba192d5d548d0000000000000000000000007e500c6efbb00fd3227888256e477171a1304721
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314612c80575080630892cd8b14612c1d578063095ea7b314612bce578063156e29f6146121ab57806318160ddd1461214f5780631dd19cb414611fb657806323b872dd14611e28578063252c09d714611da657806329910b1114611a475780632c8ce6bc146119895780632f13b60c1461192e578063313ce567146118d357806337d45e3a146116c25780635b709f17146113135780635cbadbe41461127b57806370a082311461121257806372069264146111d7578063794052f314611124578063883bdbfd14610e875780639262187b14610c0c57806395d89b4114610ab9578063a9059cbb146109e5578063a9f8d181146109aa578063c3fb90d614610926578063c45a0155146108b8578063c4f59f9b14610828578063d2c725e0146107e7578063dd62ed3e1461076e578063e184c9be14610716578063e4f8b2e9146106b2578063ea64a82014610629578063f6b911bc146101c45763ffa1ad7414610187575f80fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160068152f35b5f80fd5b346101c0576101d236612e02565b9160058054906101e860028360f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809316178155610238336130d3565b30156105a55761024730614aed565b305f526020926003845260405f2054878110610521578790305f52600386520360405f20558087116101c0578254818816828216038281116104f457827fff00000000000000000000000000000000000000000000000000000000000000911691161783555f6040518881527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef863092a36102e130614bb0565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c05786156104ca578382019561031d875189613bd8565b9561032e6040850197885190613df0565b9761034561033d86518c613bd8565b895190613df0565b978915806104c2575b6104985761035d8b8251614ce7565b905261036a888651614ce7565b8552610377898251614ce7565b90525f88126101c0575f87126101c0577f4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f908860409a61040a7f01000000000000000000000000000000000000000000000000000000000000009773ffffffffffffffffffffffffffffffffffffffff8087169685308903610465575b50508c81891698308a03610432575b505050613527565b8b51908152602081019190915260408101899052606090a38254161790558351928352820152f35b61045d927f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f516613755565b8e8c81610402565b61049191837f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e16613755565b8f856103f3565b60046040517fccbf5468000000000000000000000000000000000000000000000000000000008152fd5b50881561034e565b60046040517f25daaec3000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b608485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff610675612dbc565b165f908152600260209081526040918290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b0390f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405169ffffffffffffffffffff7f000000000000000000000000000000000000000000000000001ae6069e68807b168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760206040517f0000000000000000000000000000000000000000000000000000000069bb3c808152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576107a5612dbc565b6107ad612ddf565b9073ffffffffffffffffffffffffffffffffffffffff8091165f52600460205260405f2091165f52602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600260055460f81c14604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05761085e613c16565b604051809160208083016020845282518091526020604085019301915f5b82811061088b57505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff168552869550938101939281019260010161087c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5cd902cbef8461b8d6fa852f93784f090fd7beb168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760c0600a54600b5461ffff906040519280600f0b845260801d600f0b60208401526bffffffffffffffffffffffff81166040840152818160601c166060840152818160701c16608084015260801c1660a0820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600154604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0577f0100000000000000000000000000000000000000000000000000000000000000610a3d612dbc565b610aa660055491610a5460028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055560243590336138d2565b6005541617600555602060405160018152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576040515f9060075460018160011c9060018316928315610c02575b6020938484108114610bd557838652908115610b975750600114610b3d575b6106ae84610b3181880382612f4c565b60405191829182612d58565b60075f9081529294507fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885b828410610b8457505050816106ae93610b319282010193610b21565b8054858501870152928501928101610b68565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b8201019150610b31816106ae610b21565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610b02565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610c44612dbc565b90600554906002610c5b60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80941617600555610cac84614aed565b610cb584614bb0565b610cbd613c16565b93610cc885516146c5565b915f9573ffffffffffffffffffffffffffffffffffffffff96878416905b8251811015610e1a5788610cfa8285614714565b51165f525f875260405f20825f52875260405f205490608091821c610d1f8289614714565b52610d2a8188614714565b51610d3a575b6001915001610ce6565b89610d458286614714565b51165f525f885260405f20835f52885260405f20916fffffffffffffffffffffffffffffffff92838154169055610d7c8289614714565b51908382116101c0578b610d908488614714565b51165f52868a528360405f209216908254901c039283116104f457600192610df391906fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b610e158a610e018387614714565b511687610e0e848b614714565b5191614c41565b610d30565b6106ae867f01000000000000000000000000000000000000000000000000000000000000008a857f78d61a0c27b13f43911095f9f356f14daa3cd8b125eea1aa22421245e90e813d60405180610e708782612fa5565b0390a2600554161760055560405191829182612fa5565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05767ffffffffffffffff6004358181116101c057366023820112156101c0578060040135610ee381612f8d565b91610ef16040519384612f4c565b8183528483016024819360051b830101913683116101c057602401905b8282106111085750505063ffffffff91600b5461ffff906040519485937f1dd421ce00000000000000000000000000000000000000000000000000000000855260c4850190600c6004870152824216602487015260c060448701525180915260e4850195915f905b8a8383106110eb578a885f81808d8c8c6bffffffffffffffffffffffff81166064850152818160601c16608485015260701c1660a48301520381737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156110e0575f92611036575b505090604051918183928301818452825180915281604085019301915f5b82811061100257505050500390f35b83517affffffffffffffffffffffffffffffffffffffffffffffffffffff1685528695509381019392810192600101610ff3565b9091503d805f843e6110488184612f4c565b82019183818403126101c05780519182116101c057019080601f830112156101c057815161107581612f8d565b926110836040519485612f4c565b818452848085019260051b8201019283116101c0578401905b8282106110ad575050508280610fd5565b81517affffffffffffffffffffffffffffffffffffffffffffffffffffff811681036101c057815290840190840161109c565b6040513d5f823e3d90fd5b845182168952978801978997509093019260019190910190610f76565b813563ffffffff811681036101c0578152908601908601610f0e565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610120611166611161612dbc565b6130d3565b6040519080518252602081015160208301526040810151604083015273ffffffffffffffffffffffffffffffffffffffff60608201511660608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008091015190820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600854604051908152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602061127361124e612dbc565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f205490565b604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576112b2612dbc565b6112ba612ddf565b73ffffffffffffffffffffffffffffffffffffffff9182165f908152602081815260408083209390941682529182528290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b346101c05761132136612e62565b91929060055461133760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f0000000000000000000000000000000000000000000000000000000069bb3c801115611698576113b0336130d3565b926113da7f0000000000000000000000008e8df024cf6d3e916be0821ff3177db6981fcad2613491565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081881190816101c05761141f96611415428b84846141bf565b9891999099613b87565b995f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978d308a03611665575b505061148e8a847f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e169460608701511685613755565b61149784613527565b816115ea575b505060206114aa826136f7565b9201918251905f82126101c057106115a15750506114da670de0b6b3a7640000916114d5888a613045565b613f76565b0415611577576101c05785116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c4857f01000000000000000000000000000000000000000000000000000000000000009561156061153d8a613b87565b8b5195865260208601526040850192909252606084015233929081906080820190565b0390a3600554161760055582519182526020820152f35b60046040517f880ccd2c000000000000000000000000000000000000000000000000000000008152fd5b6115aa906136f7565b9051905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090868c116101c0576115fe8c613b87565b333b156101c0578d61163f5f9360405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611656575b8061149d565b61165f90612eef565b8b611650565b61169191857f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f516613755565b8d8d611458565b60046040517fb2094b59000000000000000000000000000000000000000000000000000000008152fd5b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576004359061ffff908183168093036101c0576005549161171860028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055580600b5460801c16604051947f587cdc06000000000000000000000000000000000000000000000000000000008652600c600487015281602487015260448601528285606481737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156110e0575f9261187d575b7f010000000000000000000000000000000000000000000000000000000000000095508216808203611804575b60058054861687179055005b7fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a936040937fffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffff71ffff00000000000000000000000000000000600b549260801b16911617600b558351928352820152a1828080806117f8565b91508285813d83116118cc575b6118948183612f4c565b810103126101c0576118c67f0100000000000000000000000000000000000000000000000000000000000000956130a3565b916117cb565b503d61188a565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020604051427f0000000000000000000000000000000000000000000000000000000069bb3c8011158152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057606060405173ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e168252807f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f51660208301527f0000000000000000000000008e8df024cf6d3e916be0821ff3177db6981fcad2166040820152f35b346101c057611a5536612e62565b909192600554611a6b60028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f0000000000000000000000000000000000000000000000000000000069bb3c80111561169857611ae4336130d3565b91611b0e7f0000000000000000000000008e8df024cf6d3e916be0821ff3177db6981fcad2613491565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9687821190816101c057611b4e42611b4785613b87565b83896141bf565b97919990985f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978c308a03611d73575b5050611bc08a84606087015116857f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e16613755565b611bc984613527565b81611cf6575b50507f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f516611bfc816136f7565b8251905f82126101c05710611cad575050611c24670de0b6b3a7640000916114d5888a613045565b0415611577576101c057611c3790613b87565b9585116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c46115607f0100000000000000000000000000000000000000000000000000000000000000958951918291898b3397859094939260609260808301968352602083015260408201520152565b611cb6906136f7565b9051905f82126101c057604491604051917f2c49ea0f00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090611d0387613b87565b908d8d116101c057333b156101c0575f91611d4d8e9260405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611d64575b80611bcf565b611d6d90612eef565b8b611d5e565b611d9f91857f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e16613755565b8d8c611b8b565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760043561ffff8110156101c057606090600c01546040519063ffffffff811682527affffffffffffffffffffffffffffffffffffffffffffffffffffff8160201c16602083015260f81c15156040820152f35b346101c057611e3636612e02565b60055491611e4a60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055573ffffffffffffffffffffffffffffffffffffffff84165f52600460205260405f20335f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103611f14575b5090610aa6917f0100000000000000000000000000000000000000000000000000000000000000946138d2565b9190818310611f58577f010000000000000000000000000000000000000000000000000000000000000094611f4f83610aa695033383613320565b94509091611ee7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057600554611ff760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555612048336130d3565b9073ffffffffffffffffffffffffffffffffffffffff91827f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f5169261208c846136f7565b82515f81126101c05761209e91613045565b90807f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e16906120cc826136f7565b956020850151965f88126101c0577f01000000000000000000000000000000000000000000000000000000000000009761210591613045565b9380612137575b50508261211f5760058054861687179055005b606061212e9401511690613755565b828080806117f8565b612148918360608801511690613755565b868061210c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b346101c05760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576121e2612dbc565b602435604435917f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055461223d60028260f81c1415612fe0565b1617600555427f0000000000000000000000000000000000000000000000000000000069bb3c80111561169857612273336130d3565b9061229d7f0000000000000000000000008e8df024cf6d3e916be0821ff3177db6981fcad2613491565b917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff938481116101c0578486116101c0575f9381158015612bc6575b6104ca5760a08301954287511115611698576040840195865180155f14612b295750506123068884613bd8565b905f82126101c0575f6003831115612b1a5750816001908060011c600181018091116104f45791905b848310612aff575050505b81116101c0577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1881019081136001166104f457956103e89297955b5f8813801590612af5575b8015612aeb575b61049857612399896020870151613edc565b60208601526123a9878651613edc565b85526123bf6123b8858a613edc565b8251613edc565b90525f83126101c0575f87126101c0575f88126101c0575f86126101c05782612719575b50505073ffffffffffffffffffffffffffffffffffffffff8083169283156126bb5761240e81614aed565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f4577fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61253a9416911617600555845f52600360205260405f2061250788825461351a565b9055845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206040518b8152a3614bb0565b61254382613527565b807f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e1661256f816136f7565b6020840151905f82126101c0571061266e57507f000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f5166125ad816136f7565b8251905f82126101c05710611cad575050906106ae917fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb60405180612605858989846040919493926060820195825260208201520152565b0390a27f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6005541617600555604051938493846040919493926060820195825260208201520152565b602061267a84926136f7565b910151905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b42815111156116985761274061273761277593602087015190613ef7565b91429051613045565b907f0000000000000000000000000000000000000000000000000e5d988b3876edf09061276d8387613f89565b90865161401a565b610100830152612783614940565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f45760205f927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef927fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001971691161760055584845260038252604084206128a082825461351a565b9055604051908152a33060011415806128bb575b80806123e3565b6128c4906155be565b60015f52600360205260405f20546040517fe268b3a4000000000000000000000000000000000000000000000000000000008152600160048201526040816024815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001fbacedf510daa79dfd9b41227a7ba192d5d548d165af19081156110e0575f905f92612a92575b506fffffffffffffffffffffffffffffffff16826028810204602814831517156104f4576064602884020491816129fa575b505060207f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54916001938082105f146129f357505b6129d6816129d1600854875f526009865260405f205490613045565b61351a565b600855835f52600982528060405f2055604051908152a2856128b4565b90506129b5565b90612a3d6fffffffffffffffffffffffffffffffff612a4293167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b614010565b90603c820291808304603c14901517156104f457600192612a8a60209260647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5495049061351a565b925092612981565b9150506040813d604011612ae3575b81612aae60409383612f4c565b810103126101c0576fffffffffffffffffffffffffffffffff612adc6020612ad584615ea1565b9301615ea1565b919061294f565b3d9150612aa1565b505f871315612387565b505f891315612380565b90919350612b11846129d18184614010565b821c919061232f565b911561233a576001915061233a565b89989250612b3d90612b459295989a613bd8565b855190613df0565b612b50895188613bd8565b96612b616020870198895190613df0565b9081831215612b9b575050612b7c81612b9592999851613bd8565b612b90612b8b8b518093613edc565b613e72565b613df0565b97612375565b985098965050612bc0612bb18998998651613bd8565b612b90612b8b89518093613edc565b95612375565b5086156122d9565b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057612c12612c08612dbc565b6024359033613320565b602060405160018152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff612c69612dbc565b165f526009602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0575f9060065460018160011c9060018316928315612d4e575b6020938484108114610bd557838652908115610b975750600114612cf4576106ae84610b3181880382612f4c565b60065f9081529294507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b828410612d3b57505050816106ae93610b319282010193610b21565b8054858501870152928501928101612d1f565b91607f1691612cc6565b6020808252825181830181905293925f5b858110612da8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b818101830151848201604001528201612d69565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101c05773ffffffffffffffffffffffffffffffffffffffff9060043582811681036101c0579160243590811681036101c0579060443590565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101c05760043573ffffffffffffffffffffffffffffffffffffffff811681036101c057916024359160443567ffffffffffffffff928382116101c057806023830112156101c05781600401359384116101c057602484830101116101c0576024019190565b67ffffffffffffffff8111612f0357604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117612f0357604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612f0357604052565b67ffffffffffffffff8111612f035760051b60200190565b60209060206040818301928281528551809452019301915f5b828110612fcc575050505090565b835185529381019392810192600101612fbe565b15612fe757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b919082039182116104f457565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe094601f948752602087015260606040870152816060870152868601375f8582860101520116010190565b519061ffff821682036101c057565b519073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b906040805192610120840184811067ffffffffffffffff821117612f03578252606084015f815260808501905f825260a08601925f845260c08701915f835260e088015f81526101008901965f885289600a5480600f0b825260801d600f0b60208201527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82116101c05782015273ffffffffffffffffffffffffffffffffffffffff91828251957f5c098c11000000000000000000000000000000000000000000000000000000008752306004880152166024860152606085604481867f000000000000000000000000b5cd902cbef8461b8d6fa852f93784f090fd7beb165afa8015613316575f925f965f926132ae575b505060ff16905216905269ffffffffffffffffffff908082166132a957507f000000000000000000000000000000000000000000000000001ae6069e68807b5b1690527f0000000000000000000000000000000000000000000000017e47e1502df48bba90527f0000000000000000000000000000000000000000000000000000000069bb3c8090526bffffffffffffffffffffffff600b54169052565b61324b565b93509550506060823d60601161330e575b816132cc60609383612f4c565b810103126101c0576132dd826130b2565b9460208301519269ffffffffffffffffffff841684036101c05701519460ff861686036101c057919460ff5f61320b565b3d91506132bf565b82513d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff80911691821561340e571691821561338a5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591835f526004825260405f20855f5282528060405f2055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b602073ffffffffffffffffffffffffffffffffffffffff60045f9360405194859384927f1d52edc4000000000000000000000000000000000000000000000000000000008452165af19081156110e0575f916134eb575090565b90506020813d602011613512575b8161350660209383612f4c565b810103126101c0575190565b3d91506134f9565b919082018092116104f457565b610100810180516bffffffffffffffffffffffff8082116101c05761355960206135518651614170565b950151614170565b93600b549161ffff956040968751947f4af74b68000000000000000000000000000000000000000000000000000000008652600c6004870152818160601c16602487015263ffffffff421660448701528381166064870152818160701c16608487015260801c1660a4850152868460c481737d20e644d2a9e149e5be9be9ad2ab243a7835d375af49182156136ed575f935f93613697575b6dffff0000000000000000000000009495507fffffffffffffffffffffffffffffffff00000000000000000000000000000000809360801b16906fffffffffffffffffffffffffffffffff1617600a556fffff0000000000000000000000000000600b549360701b1695169116179160601b161717600b555190519081527f5c0e21d57bb4cf91d8fe238d6f92e2685a695371b19209afcce6217b478f83e160204292a2565b925092508684813d83116136e6575b6136b08183612f4c565b810103126101c0576dffff000000000000000000000000926136dd60206136d6876130a3565b96016130a3565b928594506135f1565b503d6136a6565b87513d5f823e3d90fd5b602073ffffffffffffffffffffffffffffffffffffffff916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352306004840152165afa9081156110e0575f916134eb575090565b906040519060208201937fa9059cbb00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff8092166024840152604483015260448252608082019282841067ffffffffffffffff851117612f0357613815945f9283928660405216936137d786612f30565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082855af161380f614be4565b9161599a565b80519081159182156138af575b50501561382b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b81925090602091810103126101c0576020015180151581036101c0575f80613822565b91909173ffffffffffffffffffffffffffffffffffffffff808216908115613b03578416928315613a7f57838214613a215761390e8584614b41565b815f52600360205260405f205481811061399d577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208387948694855f52600384520360405f2055845f5260405f2061396982825461351a565b9055604051908152a3300361398e575b5030036139835750565b61398c9061578c565b565b6139979061578c565b5f613979565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f45524332303a207472616e7366657220746f2073656c660000000000000000006044820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b90815f03918083057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14901517156104f457565b90670de0b6b3a7640000918281029281840514901517156104f457565b81810292915f82127f80000000000000000000000000000000000000000000000000000000000000008214166104f45781840514901517156104f457565b6040517fc4f59f9b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff5f826004817f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e85165afa9182156110e0575f92613d55575b507f000000000000000000000000d6eb81136884713e843936843e286fd2a85a205a90613cb68284614755565b613d505782516001808201948583116104f4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613d0c613cf688612f8d565b97613d04604051998a612f4c565b808952612f8d565b013660208801375f5b838110613d3057505050613d299084614714565b9116905290565b8085613d3d859385614714565b5116613d49828a614714565b5201613d15565b505090565b9091503d805f833e613d678183612f4c565b81019060209081818403126101c05780519067ffffffffffffffff82116101c057019180601f840112156101c0578251613da081612f8d565b93613dae6040519586612f4c565b818552838086019260051b8201019283116101c0578301905b828210613dd95750505050905f613c89565b838091613de5846130b2565b815201910190613dc7565b8115613e45577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82147f80000000000000000000000000000000000000000000000000000000000000008214166104f4570590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182136001166104f457565b90670de0b6b3a76400005f83820393128184128116918413901516176104f457565b81810392915f1380158285131691841216176104f457565b9190915f83820193841291129080158216911516176104f457565b613f37670de0b6b3a7640000915f84125f14613f6b576114d57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff946147a5565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b90565b6114d56001946147a5565b818102929181159184041417156104f457565b60800151906301e13380918281029281840514901517156104f4577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613fd491613df0565b905f821315613fdf57565b602482604051907f1ca418760000000000000000000000000000000000000000000000000000000082526004820152fd5b8115613e45570490565b5f81126141125761403761403161403c9383613edc565b91613bbb565b6147f6565b670d529ae9e86000008082136140db575061406b929161403761406161406693614cf8565b613bbb565b613edc565b670de0b6b3a764000081126140aa5761408390614800565b905f82126101c0576301e13380918281029281840414901517156104f457613f6891614010565b602490604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917ffc68d09e00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e656761746976650000000000000000000000000000000000000000000000006044820152fd5b807fffffffffffffffffffffffffffffffff800000000000000000000000000000001315806141a5575b156101c057600f0b90565b506f7fffffffffffffffffffffffffffffff81131561419a565b939060a085019484865111156116985780518481131561468e5750604051916080830183811067ffffffffffffffff821117612f03576040525f83525f60208401525f60408401525f6060840152858751111561169857614221868851613045565b61422b8184613f89565b845261423b602084015183613ef7565b80602086015283519081158015614686575b61464f57505082516101008401516020860151916301e1338061427285895194613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c0576142a390615ac7565b92670de0b6b3a7640000841261461e57926142e06301e1338095936140376140616142db866140376140316142f59b6142e69a613edc565b614cf8565b90613ec4565b604087015260c0850151613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05761432690615ac7565b6060840152815160208401519061435585519161403761403160408901519561434f8c85614ce7565b93613edc565b670d529ae9e86000008082136140db575061437a929161403761406161406693614cf8565b94670de0b6b3a764000086126145ed5761439f61439a8761403784613bbb565b613b87565b606085015190965f8313156145cd57816140376143bb92613bbb565b670de0b6b3a764000081126140aa57506143e66143e0670de0b6b3a764000092613ea2565b88613bd8565b05915b60e08401517f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05783606461442561442c9383613bd8565b0598613ec4565b945f861280156145a35715614598576144657fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff966147a5565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156104f457826144909161351a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116104f457826144c491614010565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c0576145476145629561453061452861451e61451761450f6101009d61455598613bd8565b945b89615093565b9d88615093565b9c9d839f51613045565b968951614ce7565b88526145418b60208a015192613edc565b90614ce7565b806020880152865193613ef7565b604082519201519261401a565b91829101521561456e57565b60046040517faa69ae1e000000000000000000000000000000000000000000000000000000008152fd5b6144656001966147a5565b506145556145476145629561453061452861451e6145176145c76101009d8a615093565b94614511565b5061439a81612b906145e16145e794613ea2565b8a613bd8565b916143e9565b602486604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917fb1c4aefb00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50801561424d565b60449085604051917f8615426c00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b906146cf82612f8d565b6146dc6040519182612f4c565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061470a8294612f8d565b0190602036910137565b80518210156147285760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b908151915f5b83811061476a57505050505f90565b73ffffffffffffffffffffffffffffffffffffffff8061478a8385614714565b51169084161461479c5760010161475b565b50505050600190565b5f8113156147b05790565b7f800000000000000000000000000000000000000000000000000000000000000081146104f4575f0390565b8015613e45576ec097ce7bc90715b34b9f10000000000590565b8115613e45570590565b5f8113156148e25780670c7d713b49da000012806148d1575b156148c857670de0b6b3a7640000906ec097ce7bc90715b34b9f10000000009061486c908302828101907fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f0000000000183026147f6565b9080828002059181838202058284820205838582020591848684020593858786020595808888020597880205600f900596600d900595600b900594600990059360079005926005900591600390050101010101010160011b0590565b613f6890614d49565b50670f43fc2c04ee00008112614819565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e6473000000000000000000000000000000000000006044820152fd5b614948615124565b815115614ae957600191308314159182614963575b50505050565b61496d84936155be565b825f52602060098152604090815f2054935f955b61498e575b50505061495d565b8051861015614ae45773ffffffffffffffffffffffffffffffffffffffff6149b68783614714565b5116956149c38186614714565b5190875f525f8452845f20895f5284526fffffffffffffffffffffffffffffffff9182865f2054168015614add575b8a8282148015614ad5575b614ac95750670de0b6b3a7640000614a21614a1b614a3c9385613045565b8b613f76565b048a5f525f8752875f208c5f528752875f205460801c61351a565b908381116101c0578382116101c0578a9984614ac2938c968a5194614a6086612f30565b16845216878301525f525f8652865f20845f528652865f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b0195614981565b99505050879150614ac2565b5082156149fd565b50896149f2565b614986565b5050565b614af5615124565b90805115614b3c5773ffffffffffffffffffffffffffffffffffffffff83168015159081614b31575b50614b2857505050565b61398c926155f2565b90503014155f614b1e565b505050565b614b49615124565b9181511561495d5773ffffffffffffffffffffffffffffffffffffffff9083838383168015159081614ba5575b50614b94575b50505083168015159081614b315750614b2857505050565b614b9d926155f2565b5f8383614b7c565b90503014155f614b76565b73ffffffffffffffffffffffffffffffffffffffff81168015159081614bd9575b506139835750565b90503014155f614bd1565b3d15614c3c573d9067ffffffffffffffff8211612f035760405191614c3160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184612f4c565b82523d5f602084013e565b606090565b9190918115614b3c5773ffffffffffffffffffffffffffffffffffffffff169182614cde575f809350809281925af1614c78614be4565b5015614c8057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6574682073656e64206661696c656400000000000000000000000000000000006044820152fd5b61398c92613755565b9080821261411257613f6891613ec4565b670de0b6b3a76400008114614d1f57614d1a81614037614031613f6894613ea2565b614800565b60046040517fa9c8b14d000000000000000000000000000000000000000000000000000000008152fd5b670de0b6b3a7640000811261507d576064905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215615052575b73011798004d755d3c8bc8e03204cf44619e000000821215615031575b820290808302906e01855144814a7ff805980ff0084000908183121561500e575b50506b02df0ab5a80a22c61ab5a70080821215614fee575b50693f1fce3da636ea5cf85080821215614fce575b50690127fa27722cc06cc5e280821215614fae575b5068280e60114edb805d0380821215614f8e575b50680ebc5fb4174612111080821215614f77575b506808f00f760a4b2db55d80821215614f57575b506806f5f177578893793780821215614f37575b506806248f33704b28660380821215614f18575b506805c548670b9510e7ac80821215614ef9575b50614eb868056bc75e2d6310000091827ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000008183019201026147f6565b9080828002059181838202058284820205916003600560076009600b888a89020598808b8b02059a8b0205059805960594059205010101010160011b010590565b68056bc75e2d631000006756bc75e2d63100009202059101905f614e7c565b68056bc75e2d6310000067ad78ebc5ac6200009202059101905f614e68565b68056bc75e2d6310000068015af1d78b58c400009202059101905f614e54565b68056bc75e2d631000006802b5e3af16b18800009202059101905f614e40565b68056bc75e2d63100000809202059101905f614e2c565b68056bc75e2d63100000680ad78ebc5ac62000009202059101905f614e18565b68056bc75e2d631000006815af1d78b58c4000009202059101905f614e04565b68056bc75e2d63100000682b5e3af16b188000009202059101905f614def565b68056bc75e2d631000006856bc75e2d6310000009202059101905f614dda565b68ad78ebc5ac62000000925069021e19e0c9bab240000002059101905f80614dc2565b906b1425982cf597cd205cef73806803782dace9d900000091059101614da1565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b2000000614d84565b61508961508e916147dc565b614d49565b5f0390565b5f821215615119576150c57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff926147a5565b90670de0b6b3a7640000918281029281840414901517156104f4576150e991614010565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b6150c56001926147a5565b61512c613c16565b9061513782516146c5565b918051925f93156155b857600154431461555357436001556008546040517fef5cfb8c0000000000000000000000000000000000000000000000000000000081523060048201525f816024818373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000057fc55dff8ceca86ee94a6bf255af2f0ed90eb9e165af180156110e0576154c9575b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007e500c6efbb00fd3227888256e477171a130472116803b156101c0575f80916004604051809481937f58f3d1630000000000000000000000000000000000000000000000000000000083525af180156110e0576154b6575b50845b83518110156154ae5773ffffffffffffffffffffffffffffffffffffffff61526e8286614714565b5116908187526002602052604087206040519061528a82612f30565b54906fffffffffffffffffffffffffffffffff8216815260208260801c9101526fffffffffffffffffffffffffffffffff81169073ffffffffffffffffffffffffffffffffffffffff6152dd8489614714565b51168061542157506152f4475b8260801c90613045565b6fffffffffffffffffffffffffffffffff821615615418575b856153e4575b6fffffffffffffffffffffffffffffffff83116101c0576153369160801c61351a565b926fffffffffffffffffffffffffffffffff84116101c0576001936153d3916fffffffffffffffffffffffffffffffff6040519261537384612f30565b81861684521660208301528a52600260205260408a20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b6153dd8286614714565b5201615246565b91670de0b6b3a7640000908184029184830414841517156104f45761540c8761541293614010565b9061351a565b91615313565b6001925061530d565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa80156154a3578a9061546f575b6152f491506152ea565b506020813d60201161549b575b8161548960209383612f4c565b810103126101c0576152f49051615465565b3d915061547c565b6040513d8c823e3d90fd5b505091909250565b6154c1919550612eef565b5f935f615243565b3d805f833e6154d88183612f4c565b810160209182818303126101c05780519067ffffffffffffffff82116101c0570181601f820112156101c057805190838061551284612f8d565b61551f6040519182612f4c565b848152019260051b8201019283116101c0578301905b828210615544575050506151c8565b81518152908301908301615535565b9092505f5b83518110156155b4578073ffffffffffffffffffffffffffffffffffffffff61558360019387614714565b51165f5260026020526fffffffffffffffffffffffffffffffff60405f2054166155ad8285614714565b5201615558565b5090565b92509190565b156155c557565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9291909273ffffffffffffffffffffffffffffffffffffffff8091169081151580615782575b615621906155be565b815f5260209160098352604091825f2054965f5b815181101561577757826156498284614714565b5116906156568189614714565b5191805f525f8852865f20865f5288526fffffffffffffffffffffffffffffffff928b84895f205416801561576f575b8281148015615767575b61575a576156b46156cf926156ae670de0b6b3a76400009386613045565b90613f76565b04835f525f8b52895f20895f528b52895f205460801c61351a565b918482116101c0578483116101c05784615754936001968b51946156f286612f30565b168452168a8301525f525f8952875f20875f528952875f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b01615635565b5050505060019150615754565b508215615690565b506001615686565b505050505050509050565b5030821415615618565b73ffffffffffffffffffffffffffffffffffffffff5f6157bb8284169384151580615990575b61124e906155be565b6040809360248251809581937fe268b3a40000000000000000000000000000000000000000000000000000000083528960048401527f0000000000000000000000001fbacedf510daa79dfd9b41227a7ba192d5d548d165af1918215615986575f905f93615949575b506fffffffffffffffffffffffffffffffff8091169060288302838104602814841517156104f4576064900493826158c5575b505050916020917f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54938082105f146158bd5750905b6158a7826129d1600854885f5260098752845f205490613045565b600855845f526009835281815f205551908152a2565b90509061588c565b6158fb9291612a3d91969496167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b92603c840293808504603c14901517156104f45761594060209360647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5496049061351a565b92819294615857565b809350848092503d831161597f575b6159628183612f4c565b810103126101c0576159786020612ad584615ea1565b915f615824565b503d615958565b83513d5f823e3d90fd5b50308514156157b2565b91929015615a1557508151156159ae575090565b3b156159b75790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015615a285750805190602001fd5b615a5e906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301612d58565b0390fd5b15615a6957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e74000000000000000000000000000000006044820152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc000081121580615e8e575b615afa90615a62565b5f8112615e7a576064906806f05b59d3b20000008112615e17577ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e0000000168056bc75e2d6310000082770195e54c5dd42177f53a27172fa9ec630262827000000000925b02819068ad78ebc5ac62000000811215615dde575b6856bc75e2d631000000811215615da4575b682b5e3af16b18800000811215615d6c575b6815af1d78b58c400000811215615d34575b680ad78ebc5ac6200000811215615cfd575b82811215615cc6575b6802b5e3af16b1880000811215615c8f575b68015af1d78b58c40000811215615c58575b60028382800205058360038184840205056004828583020505600583868302050560068487830205056007858883020505906008868984020505926009878a8602050594600a888b8802050596600b898c8a02050599600c8a8d8d0205059b0101010101010101010101010205020590565b6806f5f17757889379377ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000084920192020590615be6565b6808f00f760a4b2db55d7ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000084920192020590615bd4565b680ebc5fb417461211107ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000084920192020590615bc2565b68280e60114edb805d037ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e0000084920192020590615bb9565b690127fa27722cc06cc5e27fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c0000084920192020590615ba7565b693f1fce3da636ea5cf8507fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e780000084920192020590615b95565b6b02df0ab5a80a22c61ab5a7007fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf00000084920192020590615b83565b6e01855144814a7ff805980ff008400091507fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e00000001615b71565b6803782dace9d90000008112615e67577ffffffffffffffffffffffffffffffffffffffffffffffffc87d25316270000000168056bc75e2d63100000826b1425982cf597cd205cef738092615b5c565b68056bc75e2d6310000082600192615b5c565b615e85905f03615ac7565b613f68906147dc565b5068070c1cc73b00c80000811315615af1565b51906fffffffffffffffffffffffffffffffff821682036101c05756fea2646970667358221220057ba17c06d00bc766723abb82a4a8e82f42e80ca1820c4ccbe78d47ab45bba264736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f50000000000000000000000000000000000000000000000017e47e1502df48bba0000000000000000000000000000000000000000000000000e5d988b3876edf0000000000000000000000000000000000000000000000000001ae6069e68807b0000000000000000000000001fbacedf510daa79dfd9b41227a7ba192d5d548d0000000000000000000000007e500c6efbb00fd3227888256e477171a1304721
-----Decoded View---------------
Arg [0] : _PT (address): 0xea84ca9849D9e76a78B91F221F84e9Ca065FC9f5
Arg [1] : _scalarRoot (int256): 27546233380319562682
Arg [2] : _initialAnchor (int256): 1035151213080276464
Arg [3] : _lnFeeRateRoot (uint80): 7571265496318075
Arg [4] : _vePendle (address): 0x1fBaCedf510dAA79dfD9B41227A7ba192d5D548D
Arg [5] : _gaugeController (address): 0x7e500c6efBb00FD3227888256E477171a1304721
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000ea84ca9849d9e76a78b91f221f84e9ca065fc9f5
Arg [1] : 0000000000000000000000000000000000000000000000017e47e1502df48bba
Arg [2] : 0000000000000000000000000000000000000000000000000e5d988b3876edf0
Arg [3] : 000000000000000000000000000000000000000000000000001ae6069e68807b
Arg [4] : 0000000000000000000000001fbacedf510daa79dfd9b41227a7ba192d5d548d
Arg [5] : 0000000000000000000000007e500c6efbb00fd3227888256e477171a1304721
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$18,920.60
Net Worth in HYPE
Token Allocations
PENDLE
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| HYPEREVM | 100.00% | $1.96 | 9,653.3691 | $18,920.6 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.