Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Initialize | 20929937 | 133 days ago | IN | 0 ETH | 0.00570594 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
TimeBasedCollateralPool
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 999999 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; import "./interfaces/ICollateral.sol"; import "./interfaces/ITimeBasedCollateralPool.sol"; import "./Pricing.sol"; import "./interfaces/ICollateralPool.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @notice Defines a Collateral Pool contract that makes staked tokens available for claim by accounts with the * Claimant role. This contract is especially beneficial for claimants who frequently take on risk for some duration * during which staked collateral is guaranteed to be available for claim for a certain configurable duration (an epoch). * * This contract allows stakers to pool their collateral tokens such that they have a proportional amount of the overall * pool risk. Stakers are able to withdraw (release) their stake from the pool when their unstake vests at end of the * epoch following the one in which they unstake (`releaseEpoch` = `unstakeEpoch + 1`). Tokens are claimable up until * the point that they are eligible for release (in `releaseEpoch`). * * @custom:security-contact [email protected] */ contract TimeBasedCollateralPool is ITimeBasedCollateralPool, ICollateralPool, ERC165, AccessControl { using SafeERC20 for IERC20; /****************** * CONTRACT STATE * ******************/ bytes32 public constant ADMIN_ROLE = 0xa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775; // keccak256("ADMIN_ROLE") bytes32 public constant CLAIM_ROUTER_ROLE = 0x9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e8; // keccak256("CLAIM_ROUTER_ROLE") bytes32 public constant CLAIMANT_ROLE = 0xde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc6; // keccak256("CLAIMANT_ROLE") bytes32 public constant RESETTER_ROLE = 0x007d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d47; // keccak256("RESETTER_ROLE") ICollateral public collateral; bool private initialized; uint256 public epochPeriodSeconds; uint256 public firstEpochStartTimeSeconds; address public defaultClaimDestinationAccount; mapping(IERC20 => address) public tokenClaimDestinationAccountOverrides; /// account address => token address => AccountState. /// Note: this is public but 3rd parties should not depend on the units fields to be accurate, as vested unstakes /// may be present. Use `getAccountPoolUnits(...)` instead if looking for up-to-date information on units. mapping(address => mapping(address => AccountState)) public accountTokenState; /// token address => ContractState. mapping(address => ContractState) public tokenContractState; /// token address => epoch => ExitBalance. This contains the remaining units & tokens to exit for a specific epoch. /// This allows the contract to set an exchange rate from AccountState units to tokens for a specific epoch. mapping(address => mapping(uint256 => ExitBalance)) public tokenEpochExitBalances; /// token address => reset nonce => ExitBalance. This contains the remaining units & tokens to exit for a unit reset. /// See `resetPool(...)` for more information. mapping(address => mapping(uint256 => ExitBalance)) public tokenResetExitBalances; /*********** * STRUCTS * ***********/ struct AccountState { uint32 resetNonce; uint32 firstPendingUnstakeEpoch; uint32 secondPendingUnstakeEpoch; uint256 firstPendingUnstakeUnits; uint256 secondPendingUnstakeUnits; uint256 totalUnits; } struct ContractState { uint32 resetNonce; uint96 collateralReservationId; uint32 firstPendingUnstakeEpoch; uint32 secondPendingUnstakeEpoch; uint256 firstPendingUnstakeUnits; uint256 secondPendingUnstakeUnits; uint256 totalUnits; } /// ExitBalance is used to store tokens and units that are releasable for an epoch or reset nonce. struct ExitBalance { uint256 unitsLeft; uint256 tokensLeft; } /// PoolUnits is a breakdown of units, where `pendingUnstake` and `releasable` are mutually exclusive and `total` is /// the sum of staked, pending unstake, and releasable. Staked is not included here because it is derivable. struct PoolUnits { uint256 total; uint256 pendingUnstake; uint256 releasable; } /************* * MODIFIERS * *************/ /** * Modifier to guarantee eligible account tokens are released ahead of an operation. This is required for any * operation that may modify account unit/token state to make sure resets are processed. * @param _account The account address for which eligible tokens will be released. * @param _tokenAddress The address of the ERC-20 token that will be released, if eligible. */ modifier withEligibleAccountTokensReleased(address _account, address _tokenAddress) { _releaseEligibleAccountTokens(_account, _tokenAddress); _; } /**************** * PUBLIC VIEWS * ****************/ /** * @notice Calculates what the future ExitBalance would be at this moment, given the token and epoch. * @dev This assumes the caller can and will use the tokenEpochExitBalances mapping if an entry exists for the epoch * in question, so it operates on contract-level pending unstakes, assuming an entry does not exist. * @param _tokenAddress The address of the token for the ExitBalance. * @param _epoch The epoch of the ExitBalance. * @return _units The units of the ExitBalance, 0 if there is no pending unstake for the epoch in question. * @return _tokens The tokens of the ExitBalance, 0 if there is no pending unstake for the epoch in question. */ function calculateEpochExitBalance( address _tokenAddress, uint256 _epoch ) public view returns (uint256 _units, uint256 _tokens) { ContractState storage contractState = tokenContractState[_tokenAddress]; if (contractState.firstPendingUnstakeEpoch == _epoch) { _units = contractState.firstPendingUnstakeUnits; _tokens = collateral.getCollateralReservation(contractState.collateralReservationId).tokenAmount; _tokens = Pricing.calculateProportionOfTotal(_units, contractState.totalUnits, _tokens); } else if (contractState.secondPendingUnstakeEpoch == _epoch) { uint256 totalTokens = collateral .getCollateralReservation(contractState.collateralReservationId) .tokenAmount; uint256 totalUnits = contractState.totalUnits; uint256 firstUnits = contractState.firstPendingUnstakeUnits; _tokens = Pricing.calculateProportionOfTotal(firstUnits, totalUnits, totalTokens); _units = contractState.secondPendingUnstakeUnits; _tokens = Pricing.calculateProportionOfTotal(_units, totalUnits - firstUnits, totalTokens - _tokens); } // NB: default return (0,0) } /** * @notice Gets the account-level unstake units and tokens for the provided token, epoch, and unstake units. * @dev This will fetch the relevant information from the `tokenEpochExitBalances` mapping, and if it has not yet * been populated, will calculate what would be in that mapping using `calculateEpochExitBalance(...)` if told to do so. * @param _tokenAddress The address of the token of the unstake. * @param _epoch The epoch in question. * @param _unstakeUnits The units of the pending unstake. * @param _processContractUnstakes Whether or not the contract-level pending unstakes need to be calculated. * @return _units The units for the unstake in question, 0 if there is no unstake. * @return _tokens The tokens for the unstake in question. Note: 0 tokens but positive units is possible. */ function getAccountExitUnitsAndTokens( address _tokenAddress, uint256 _epoch, uint256 _unstakeUnits, bool _processContractUnstakes ) public view returns (uint256 _units, uint256 _tokens) { uint256 exitUnits = tokenEpochExitBalances[_tokenAddress][_epoch].unitsLeft; if (exitUnits > 0) { _units = _unstakeUnits; _tokens = Pricing.calculateProportionOfTotal( _unstakeUnits, exitUnits, tokenEpochExitBalances[_tokenAddress][_epoch].tokensLeft ); } else if (_processContractUnstakes) { uint256 exitTokens; (exitUnits, exitTokens) = calculateEpochExitBalance(_tokenAddress, _epoch); if (exitUnits > 0) { _units = _unstakeUnits; _tokens = Pricing.calculateProportionOfTotal(_unstakeUnits, exitUnits, exitTokens); } } // NB: default return (0,0) } /** * @inheritdoc ICollateralPool */ function getAccountPoolBalance( address _accountAddress, address _tokenAddress ) external view returns (uint256 _balance) { AccountState memory accountState = accountTokenState[_accountAddress][_tokenAddress]; uint256 accountUnitsLeft = accountState.totalUnits; // Calculate balance from processed contract-level unstakes. { uint256 vestedUnits; uint256 currentEpoch = getCurrentEpoch(); uint256 epoch = accountState.firstPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { (vestedUnits, _balance) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountState.firstPendingUnstakeUnits, false ); epoch = accountState.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { (uint256 units, uint256 tokens) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountState.secondPendingUnstakeUnits, false ); vestedUnits += units; _balance += tokens; } } accountUnitsLeft -= vestedUnits; } if (accountState.resetNonce < tokenContractState[_tokenAddress].resetNonce) { // The account has a reset to process. Add the account's share of the reset balance to _balance. ExitBalance memory exit = tokenResetExitBalances[_tokenAddress][accountState.resetNonce]; _balance += Pricing.calculateProportionOfTotal(accountUnitsLeft, exit.unitsLeft, exit.tokensLeft); } else { // Add in balance from staked tokens. This includes vested pending unstakes without tokenEpochExitBalances // (not yet processed at the contract level). uint256 stakedBalance = collateral .getCollateralReservation(tokenContractState[_tokenAddress].collateralReservationId) .tokenAmount; _balance += Pricing.calculateProportionOfTotal( accountUnitsLeft, tokenContractState[_tokenAddress].totalUnits, stakedBalance ); } } /** * @notice Calculates the total pool units at this point in time for the provided token address. * @dev Total units are those that are staked plus those that are releasable but have yet to be released. * @param _accountAddress The address of the account for which pool units are being requested. * @param _tokenAddress The address of the token for which account pool units are being requested. * @return _accountPoolUnits The PoolUnits object detailing total, pending unstake, and releasable units. */ function getAccountPoolUnits( address _accountAddress, address _tokenAddress ) public view returns (PoolUnits memory _accountPoolUnits) { AccountState memory accountState = accountTokenState[_accountAddress][_tokenAddress]; _accountPoolUnits.total = accountState.totalUnits; // If a pool reset has happened for this token, all units are releasable. if (accountState.resetNonce < tokenContractState[_tokenAddress].resetNonce) { _accountPoolUnits.releasable = _accountPoolUnits.total; return _accountPoolUnits; } uint256 currentEpoch = getCurrentEpoch(); uint256 epoch = accountState.firstPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { (_accountPoolUnits.releasable, ) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountState.firstPendingUnstakeUnits, true ); epoch = accountState.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { (uint256 units, ) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountState.secondPendingUnstakeUnits, true ); _accountPoolUnits.releasable += units; } } _accountPoolUnits.pendingUnstake = accountState.firstPendingUnstakeUnits + accountState.secondPendingUnstakeUnits - _accountPoolUnits.releasable; } /** * @notice Gets the accountTokenState entry for the provided account and token. * @param _account The account in question. * @param _tokenAddress The address of the token in question. * @return The AccountState for the provided account and token. */ function getAccountTokenState(address _account, address _tokenAddress) public view returns (AccountState memory) { return accountTokenState[_account][_tokenAddress]; } /** * @inheritdoc ITimeBasedCollateralPool */ function getClaimableCollateral( IERC20[] calldata _tokens ) external view returns (ClaimableCollateral[] memory _claimableCollateral) { uint256 currentEpoch = getCurrentEpoch(); uint256 endOfCurrentEpoch = getEpochEndTimestamp(currentEpoch); uint256 endOfNextEpoch = getEpochEndTimestamp(currentEpoch + 1); _claimableCollateral = new ClaimableCollateral[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { address tokenAddress = address(_tokens[i]); _claimableCollateral[i].endOfCurrentEpochTimestampSeconds = endOfCurrentEpoch; _claimableCollateral[i].endOfNextEpochTimestampSeconds = endOfNextEpoch; ContractState storage contractState = tokenContractState[tokenAddress]; uint256 totalUnits = contractState.totalUnits; if (totalUnits == 0) { // Nothing is claimable continue; } ICollateral.CollateralReservation memory reservation = collateral.getCollateralReservation( contractState.collateralReservationId ); uint256 unitsUnstaked; uint256 secondEpoch = contractState.secondPendingUnstakeEpoch; uint256 firstEpoch = contractState.firstPendingUnstakeEpoch; for (uint256 epochAdjustment = 0; epochAdjustment < 2; epochAdjustment++) { // claimable this epoch if (_nonZeroAndLessThan(secondEpoch, currentEpoch + epochAdjustment)) { unitsUnstaked = contractState.firstPendingUnstakeUnits + contractState.secondPendingUnstakeUnits; } else if (_nonZeroAndLessThan(firstEpoch, currentEpoch + epochAdjustment)) { unitsUnstaked = contractState.firstPendingUnstakeUnits; } uint256 claimable; uint256 tokensUnstaked = Pricing.calculateProportionOfTotal( unitsUnstaked, totalUnits, reservation.tokenAmount ); if (tokensUnstaked == 0) { claimable = reservation.claimableTokenAmount; } else { // NB: Need to calculate claimable amount from reserved amount because that's how CollateralVault does it. // Otherwise truncation can cause off by one issues in which a higher claimable amount is advertised. uint256 stakedAmountRemaining = reservation.tokenAmount - tokensUnstaked; claimable = Pricing.amountBeforeFee(stakedAmountRemaining, reservation.feeBasisPoints); } if (epochAdjustment == 0) { _claimableCollateral[i].amountClaimableUntilEndOfCurrentEpoch = claimable; } else { _claimableCollateral[i].amountClaimableUntilEndOfNextEpoch = claimable; } } } } /** * @notice Gets the epoch number of the current epoch, according to this block's timestamp. * @return The current epoch. */ function getCurrentEpoch() public view returns (uint256) { return ((block.timestamp - firstEpochStartTimeSeconds) / epochPeriodSeconds); } /** * @notice Gets the timestamp after which the current epoch ends. * @param _epoch The epoch for which the end timestamp is being returned. * @return The timestamp after which the current epoch ends. */ function getEpochEndTimestamp(uint256 _epoch) public view returns (uint256) { return ((_epoch + 1) * epochPeriodSeconds) + firstEpochStartTimeSeconds - 1; } /** * @notice Calculates the pool units at this point in time for the provided token address. * @dev Total units are those that are staked plus those that are releasable but have yet to be released. * @param _tokenAddress The address of the token. * @return _poolUnits The PoolUnits object detailing total, pending unstake, and releasable units. */ function getPoolUnits(address _tokenAddress) public view returns (PoolUnits memory _poolUnits) { ContractState storage contractState = tokenContractState[_tokenAddress]; _poolUnits.total = contractState.totalUnits; uint256 currentEpoch = getCurrentEpoch(); uint256 epoch = contractState.firstPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { epoch = contractState.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { _poolUnits.releasable = contractState.firstPendingUnstakeUnits + contractState.secondPendingUnstakeUnits; } else { _poolUnits.releasable = contractState.firstPendingUnstakeUnits; if (epoch > 0) { _poolUnits.pendingUnstake = contractState.secondPendingUnstakeUnits; } } } else { if (epoch > 0) { _poolUnits.pendingUnstake = contractState.firstPendingUnstakeUnits + contractState.secondPendingUnstakeUnits; } } } /** * @notice Gets the tokenContractState entry for the provided token. * @param _tokenAddress The address of the token in question. * @return The ContractState for the provided token. */ function getTokenContractState(address _tokenAddress) public view returns (ContractState memory) { return tokenContractState[_tokenAddress]; } /** * @notice Gets the tokenEpochExitBalances entry for the provided token and epoch. * @param _tokenAddress The address of the token in question. * @param _epoch The epoch in question. * @return _exitBalance The ExitBalance for the provided token and epoch. */ function getTokenEpochExitBalance(address _tokenAddress, uint256 _epoch) public view returns (ExitBalance memory) { return tokenEpochExitBalances[_tokenAddress][_epoch]; } /** * @notice Gets the tokenResetExitBalances entry for the provided token and reset nonce. * @param _tokenAddress The address of the token in question. * @param _resetNonce The reset nonce in question. * @return The ExitBalance for the provided token and reset nonce. */ function getTokenResetExitBalance( address _tokenAddress, uint256 _resetNonce ) public view returns (ExitBalance memory) { return tokenResetExitBalances[_tokenAddress][_resetNonce]; } /** * @notice Gets the total number of account-level units pending unstake for the account and token in question. * Note: this includes units for which the unstake has vested (releasable units). * @param _account The account for which the pending unstake units will be returned. * @param _tokenAddress The token for which the pending unstake units will be returned. * @return The total units pending unstake. */ function getTotalAccountUnitsPendingUnstake(address _account, address _tokenAddress) public view returns (uint256) { uint256 firstUnits = accountTokenState[_account][_tokenAddress].firstPendingUnstakeUnits; if (firstUnits == 0) { return 0; } return firstUnits + accountTokenState[_account][_tokenAddress].secondPendingUnstakeUnits; } /** * @notice Gets the total number of contract-level units pending unstake for the token in question. * Note: this includes units for which the unstake has vested (releasable units). * @param _tokenAddress The token for which the pending unstake units will be returned. * @return The total number of units pending unstake. */ function getTotalContractUnitsPendingUnstake(address _tokenAddress) public view returns (uint256) { uint256 firstUnits = tokenContractState[_tokenAddress].firstPendingUnstakeUnits; if (firstUnits == 0) { return 0; } return firstUnits + tokenContractState[_tokenAddress].secondPendingUnstakeUnits; } /** * Indicates support for IERC165, ICollateralPool, and ITimeBasedCollateralPool. * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceID) public view override(ERC165, AccessControl) returns (bool) { return interfaceID == type(ICollateralPool).interfaceId || interfaceID == type(ITimeBasedCollateralPool).interfaceId || super.supportsInterface(interfaceID); } /***************************** * STATE-MODIFYING FUNCTIONS * *****************************/ /** * @notice Initializes a TimeBasedCollateralPool using the provided configuration parameters. * @dev Please take note of: * - _epochPeriodSeconds and its implications for claimable collateral guarantee windows * - RBAC roles defined at the top of this file and the corresponding addresses to be assigned those roles * * @param _collateral The ICollateral contract to use to access collateral. * @param _epochPeriodSeconds The number of seconds in each epoch. This is the minimum amount of time that staked * tokens will be claimable if an UnstakeInitiated event has not been observed for them. * @param _defaultClaimDestination The address to which tokens will be claimed if no token-based override is set. * @param _admin The address that will be granted the ADMIN_ROLE, allowing it to administer RBAC roles. * @param _claimant (optional) The address that will be granted the CLAIMANT_ROLE, allowing it to call `claim(...)`. * @param _claimRouter (optional) The address that will be granted the CLAIM_ROUTER_ROLE, allowing it to update * the defaultClaimDestinationAccount and tokenClaimDestinationAccountOverrides mapping. * @param _resetter (optional) The address that will be granted the RESETTER_ROLE, allowing it to call `reset(...)`. */ function initialize( ICollateral _collateral, uint256 _epochPeriodSeconds, address _defaultClaimDestination, address _admin, address _claimant, address _claimRouter, address _resetter ) public { if (initialized) revert AlreadyInitialized(); initialized = true; if (_epochPeriodSeconds == 0) revert InvalidZeroAmount(); if (_defaultClaimDestination == address(0)) revert InvalidZeroAddress(); if (_admin == address(0)) revert InvalidZeroAddress(); // NB: all other parameter addresses may be zero, as admin can set them later. firstEpochStartTimeSeconds = block.timestamp; collateral = _collateral; epochPeriodSeconds = _epochPeriodSeconds; defaultClaimDestinationAccount = _defaultClaimDestination; _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _grantRole(ADMIN_ROLE, _admin); _setRoleAdmin(CLAIMANT_ROLE, ADMIN_ROLE); if (_claimant != address(0)) { _grantRole(CLAIMANT_ROLE, _claimant); } _setRoleAdmin(CLAIM_ROUTER_ROLE, ADMIN_ROLE); if (_claimRouter != address(0)) { _grantRole(CLAIM_ROUTER_ROLE, _claimRouter); } _setRoleAdmin(RESETTER_ROLE, ADMIN_ROLE); if (_resetter != address(0)) { _grantRole(RESETTER_ROLE, _resetter); } } /** * @inheritdoc ITimeBasedCollateralPool */ function claim(IERC20[] calldata _tokens, uint256[] calldata _amounts) external onlyRole(CLAIMANT_ROLE) { if (_tokens.length != _amounts.length) revert RelatedArraysLengthMismatch(_tokens.length, _amounts.length); for (uint256 i = 0; i < _tokens.length; i++) { uint256 amount = _amounts[i]; if (amount == 0) { // we could revert, but there may be some valid claims. continue; } address tokenAddress = address(_tokens[i]); { bool poolWasReset = _unlockEligibleTokenContractPendingUnstakes(tokenAddress); if (poolWasReset) { revert InsufficientClaimable(amount, 0); } } address destinationAccount = tokenClaimDestinationAccountOverrides[_tokens[i]]; if (destinationAccount == address(0)) { destinationAccount = defaultClaimDestinationAccount; } (uint256 remainingReserved, ) = collateral.claimCollateral( tokenContractState[tokenAddress].collateralReservationId, amount, destinationAccount, false // NB: never intentionally release remainder. It will be released if 0 claimable remains. ); emit CollateralClaimed(IERC20(tokenAddress), amount, destinationAccount); // If the whole amount is claimed, units are worthless, so we need to delete them on next account action. if (remainingReserved == 0) { tokenContractState[tokenAddress].collateralReservationId = 0; _resetPool(tokenAddress); } } } /** * @inheritdoc ITimeBasedCollateralPool */ function depositAndStake( IERC20 _token, uint256 _amount, bytes calldata _collateralizableDepositApprovalSignature ) external withEligibleAccountTokensReleased(msg.sender, address(_token)) returns (uint256) { collateral.depositFromAccount(msg.sender, address(_token), _amount, _collateralizableDepositApprovalSignature); return _stake(_token, _amount); } /** * @inheritdoc ITimeBasedCollateralPool */ function releaseEligibleTokens(address _account, IERC20[] calldata _tokens) external { for (uint256 i = 0; i < _tokens.length; i++) { _releaseEligibleAccountTokens(_account, address(_tokens[i])); } } /** * @inheritdoc ITimeBasedCollateralPool */ function resetPool(IERC20[] calldata _tokens) external onlyRole(RESETTER_ROLE) { for (uint256 i = 0; i < _tokens.length; i++) { _resetPool(address(_tokens[i])); } } /** * @inheritdoc ITimeBasedCollateralPool */ function setDefaultClaimDestinationAccount( address _defaultClaimDestinationAccount ) external onlyRole(CLAIM_ROUTER_ROLE) { if (_defaultClaimDestinationAccount == address(0)) revert InvalidZeroAddress(); address oldAccount = defaultClaimDestinationAccount; defaultClaimDestinationAccount = _defaultClaimDestinationAccount; emit DefaultClaimDestinationAccountUpdated(oldAccount, _defaultClaimDestinationAccount); } /** * @inheritdoc ITimeBasedCollateralPool */ function setTokenClaimDestinationAccountOverride( IERC20 _token, address _destinationAccount ) external onlyRole(CLAIM_ROUTER_ROLE) { address oldAccount = tokenClaimDestinationAccountOverrides[_token]; tokenClaimDestinationAccountOverrides[_token] = _destinationAccount; emit TokenClaimDestinationAccountOverrideUpdated(_token, oldAccount, _destinationAccount); } /** * @inheritdoc ITimeBasedCollateralPool */ function stake( IERC20 _token, uint256 _amount, bytes calldata _collateralizableApprovalSignature ) external withEligibleAccountTokensReleased(msg.sender, address(_token)) returns (uint256) { if (_collateralizableApprovalSignature.length > 0) { collateral.modifyCollateralizableTokenAllowanceWithSignature( msg.sender, address(this), address(_token), Pricing.safeCastToInt256(_amount), _collateralizableApprovalSignature ); } return _stake(_token, _amount); } /** * @inheritdoc ITimeBasedCollateralPool */ function stakeReleasableTokensFrom( ITimeBasedCollateralPool _pool, IERC20 _token, uint256 _amount, bytes calldata _collateralizableApprovalSignature ) external withEligibleAccountTokensReleased(msg.sender, address(_token)) returns (uint256) { if (address(_pool) != address(this)) { IERC20[] memory tokens = new IERC20[](1); tokens[0] = _token; _pool.releaseEligibleTokens(msg.sender, tokens); } if (_collateralizableApprovalSignature.length > 0) { collateral.modifyCollateralizableTokenAllowanceWithSignature( msg.sender, address(this), address(_token), Pricing.safeCastToInt256(_amount), _collateralizableApprovalSignature ); } return _stake(_token, _amount); } /** * @inheritdoc ITimeBasedCollateralPool */ function unstake(IERC20 _token, uint256 _poolUnits) external { if (_poolUnits == 0) revert UnstakeAmountZero(); address tokenAddress = address(_token); if ( accountTokenState[msg.sender][tokenAddress].totalUnits - getTotalAccountUnitsPendingUnstake(msg.sender, tokenAddress) < _poolUnits ) { revert InsufficientBalance( _poolUnits, accountTokenState[msg.sender][tokenAddress].totalUnits - getTotalAccountUnitsPendingUnstake(msg.sender, tokenAddress) ); } { // Release eligible account tokens. If a reset occurred, there is nothing left to unstake, so return. bool accountWasReset = _releaseEligibleAccountTokens(msg.sender, tokenAddress); if (accountWasReset) { // Do not revert because the user wanted tokens unstaked, and those were unstaked and released. return; } } _addToAccountPendingUnstakeNextEpoch(tokenAddress, msg.sender, _poolUnits); _addToContractPendingUnstakeNextEpoch(tokenAddress, _poolUnits); emit UnstakeInitiated(msg.sender, _token, _poolUnits, getEpochEndTimestamp(getCurrentEpoch() + 1)); } /******************************** * PRIVATE / INTERNAL FUNCTIONS * ********************************/ /** * @dev Finds the entry for the provided token and account that corresponds to the epoch following the current epoch * in the `accountTokenState` mapping and adds the provided pool units to it. * NOTE: This assumes that vested pending unstakes for the account and token in question were processed within this * transaction prior to this call. * @param _tokenAddress The address of the ERC-20 token for the pending unstake. * @param _account The account address for the pending unstake. * @param _unstakeUnitsToAdd The number of units to add to the pending unstake for next epoch. */ function _addToAccountPendingUnstakeNextEpoch( address _tokenAddress, address _account, uint256 _unstakeUnitsToAdd ) internal { AccountState storage state = accountTokenState[_account][_tokenAddress]; uint32 epoch = uint32(getCurrentEpoch() + 1); uint32 firstUnstakeEpoch = state.firstPendingUnstakeEpoch; if (firstUnstakeEpoch == 0) { state.firstPendingUnstakeEpoch = epoch; state.firstPendingUnstakeUnits = _unstakeUnitsToAdd; return; } if (firstUnstakeEpoch == epoch) { state.firstPendingUnstakeUnits += _unstakeUnitsToAdd; return; } if (state.secondPendingUnstakeEpoch == epoch) { state.secondPendingUnstakeUnits += _unstakeUnitsToAdd; } else { state.secondPendingUnstakeEpoch = epoch; state.secondPendingUnstakeUnits = _unstakeUnitsToAdd; } } /** * @dev Finds the entry for the provided token that corresponds to the epoch following the current epoch in the * `tokenContractState` mapping and adds the provided pool units to it. * NOTE: This assumes that `_unlockEligibleTokenContractPendingUnstakes(...)` was invoked within this transaction prior to this call. * @param _tokenAddress The address of the ERC-20 token for the pending unstake. * @param _unstakeUnitsToAdd The number of units to add to the pending unstake for next epoch. */ function _addToContractPendingUnstakeNextEpoch(address _tokenAddress, uint256 _unstakeUnitsToAdd) internal { ContractState storage state = tokenContractState[_tokenAddress]; uint32 epoch = uint32(getCurrentEpoch() + 1); uint32 firstUnstakeEpoch = state.firstPendingUnstakeEpoch; if (firstUnstakeEpoch == 0) { state.firstPendingUnstakeEpoch = epoch; state.firstPendingUnstakeUnits = _unstakeUnitsToAdd; return; } if (firstUnstakeEpoch == epoch) { state.firstPendingUnstakeUnits += _unstakeUnitsToAdd; return; } if (state.secondPendingUnstakeEpoch == epoch) { state.secondPendingUnstakeUnits += _unstakeUnitsToAdd; } else { state.secondPendingUnstakeEpoch = epoch; state.secondPendingUnstakeUnits = _unstakeUnitsToAdd; } } /** * Returns true if the provided number is non-zero and less than the number to compare to. * @param _numberToCheck The number being evaluated. * @param _numberToCompareTo The number that _numberToCheck is evaluated against. * @return True if _numberToCheck is non-zero and less than _numberToCompareTo, false otherwise. */ function _nonZeroAndLessThan(uint256 _numberToCheck, uint256 _numberToCompareTo) private pure returns (bool) { return _numberToCheck > 0 && _numberToCheck < _numberToCompareTo; } /** * @dev Gets the releasable units and tokens for the provided account and token by processing its pending unstakes. * NOTE: THIS FUNCTION UPDATES THE STATE. CALLER MUST RELEASE THE RETURNED UNITS & TOKENS OR REVERT. * NOTE: This function assumes that contract-level unstakes have already been processed within this transaction. * @param _account The address of the account in question. * @param _tokenAddress The address of the token in question. * @return _unitsToRelease The number of units to release from processing pending unstakes. * @return _tokensToRelease The number of tokens to release from processing pending unstakes. */ function _processAccountTokenUnstakes( address _account, address _tokenAddress ) internal returns (uint256 _unitsToRelease, uint256 _tokensToRelease) { AccountState storage accountStateStorage = accountTokenState[_account][_tokenAddress]; uint256 epoch = accountStateStorage.firstPendingUnstakeEpoch; if (epoch == 0) { return (0, 0); } uint256 currentEpoch = getCurrentEpoch(); if (epoch >= currentEpoch) { return (0, 0); } // NB: contract-level unstakes were already processed, so do not reprocess. (_unitsToRelease, _tokensToRelease) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountStateStorage.firstPendingUnstakeUnits, false ); /** Update epoch-exit state **/ tokenEpochExitBalances[_tokenAddress][epoch].unitsLeft -= _unitsToRelease; tokenEpochExitBalances[_tokenAddress][epoch].tokensLeft -= _tokensToRelease; epoch = accountStateStorage.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { // Process 2nd unstake (uint256 vestedUnits, uint256 vestedTokens) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountStateStorage.secondPendingUnstakeUnits, false ); _unitsToRelease += vestedUnits; _tokensToRelease += vestedTokens; tokenEpochExitBalances[_tokenAddress][epoch].unitsLeft -= vestedUnits; tokenEpochExitBalances[_tokenAddress][epoch].tokensLeft -= vestedTokens; accountStateStorage.firstPendingUnstakeEpoch = 0; accountStateStorage.secondPendingUnstakeEpoch = 0; accountStateStorage.firstPendingUnstakeUnits = 0; accountStateStorage.secondPendingUnstakeUnits = 0; } else if (epoch > 0) { // a second unstake exists; it's just not vested. accountStateStorage.firstPendingUnstakeEpoch = uint32(epoch); accountStateStorage.secondPendingUnstakeEpoch = 0; accountStateStorage.firstPendingUnstakeUnits = accountTokenState[_account][_tokenAddress] .secondPendingUnstakeUnits; accountStateStorage.secondPendingUnstakeUnits = 0; } else { // there is only 1 unstake. accountStateStorage.firstPendingUnstakeEpoch = 0; accountStateStorage.firstPendingUnstakeUnits = 0; } accountStateStorage.totalUnits -= _unitsToRelease; } /** * @dev Releases all vested unstakes from the `accountTokenState` struct for the provided token and account. * @param _account The address of the account for which vested unstakes will be released. * @param _tokenAddress The address of the ERC-20 token to be released. * @return _poolWasReset Whether or not the pool was reset as a result of this call. */ function _releaseEligibleAccountTokens( address _account, address _tokenAddress ) internal returns (bool _poolWasReset) { _poolWasReset = _unlockEligibleTokenContractPendingUnstakes(_tokenAddress); (uint256 totalUnitsToRelease, uint256 totalTokensToRelease) = _resetAccountTokenStateIfApplicable( _account, _tokenAddress ); if (totalUnitsToRelease == 0) { (uint256 units, uint256 tokens) = _processAccountTokenUnstakes(_account, _tokenAddress); totalUnitsToRelease += units; totalTokensToRelease += tokens; } if (totalUnitsToRelease == 0) { return _poolWasReset; } collateral.transferCollateral(_tokenAddress, totalTokensToRelease, _account); emit CollateralReleased(IERC20(_tokenAddress), totalTokensToRelease, totalUnitsToRelease, _account); } /** * @dev Gets the releasable units and tokens for the provided account and token by processing a pool token reset * if there is one. * NOTE: THIS FUNCTION UPDATES THE STATE. CALLER MUST RELEASE THE RETURNED UNITS & TOKENS OR REVERT. * @param _account The address of the account in question. * @param _tokenAddress The address of the token in question. * @return _unitsToRelease The number of units to release from processing a pool token reset. * @return _tokensToRelease The number of tokens to release from processing a pool token reset. */ function _resetAccountTokenStateIfApplicable( address _account, address _tokenAddress ) internal returns (uint256 _unitsToRelease, uint256 _tokensToRelease) { uint32 accountResetNonce = accountTokenState[_account][_tokenAddress].resetNonce; uint32 contractResetNonce = tokenContractState[_tokenAddress].resetNonce; if (accountResetNonce >= contractResetNonce) { // There is no account reset to process. return (0, 0); } // If we got here, the pool has been reset for this token and account. AccountState storage accountStateStorage = accountTokenState[_account][_tokenAddress]; // 1. Update account reset nonce so it is up to date. accountStateStorage.resetNonce = contractResetNonce; emit AccountResetNonceUpdated(_account, accountResetNonce, contractResetNonce); _unitsToRelease = accountStateStorage.totalUnits; if (_unitsToRelease == 0) { return (0, 0); } // Reset total units. accountStateStorage.totalUnits = 0; // 2. Process and purge all account pending unstake state. There are two possibilities: // a. A pending unstake is vested or unvested but no vested contract unstake was processed prior to pool // reset. That contract state was purged in _resetPool(...), the account pool units still exist in the // vault, and we can release them via the standard "everything has been unstaked" logic below. // b. A pending unstake, was vested and the contract unstake was already processed at the time of pool reset. // In this case, the exchange rate was captured in tokenEpochExitBalances, and it's unsafe to process // this unstake any different than the standard release process. uint256 unstakeUnitsToRelease; { uint256 epoch = accountStateStorage.firstPendingUnstakeEpoch; if (epoch > 0) { uint256 currentEpoch = getCurrentEpoch(); if (epoch < currentEpoch) { // NB: This is case b. from above -- do not process contract-level unstakes that have not already been processed. (unstakeUnitsToRelease, _tokensToRelease) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountStateStorage.firstPendingUnstakeUnits, false ); epoch = accountStateStorage.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(epoch, currentEpoch)) { (uint256 units, uint256 tokens) = getAccountExitUnitsAndTokens( _tokenAddress, epoch, accountStateStorage.secondPendingUnstakeUnits, false ); unstakeUnitsToRelease += units; _tokensToRelease += tokens; } } accountStateStorage.firstPendingUnstakeEpoch = 0; accountStateStorage.secondPendingUnstakeEpoch = 0; accountStateStorage.firstPendingUnstakeUnits = 0; accountStateStorage.secondPendingUnstakeUnits = 0; } } // 3. Process reset exit units. if (_unitsToRelease == unstakeUnitsToRelease) { // If we got here, it means that all units were handled above. return (_unitsToRelease, _tokensToRelease); } // _unitsToRelease includes pending unstakes above, so we need to remove them to process reset. uint256 accountResetUnits = _unitsToRelease - unstakeUnitsToRelease; uint256 exitTokensLeft = tokenResetExitBalances[_tokenAddress][accountResetNonce].tokensLeft; // If there are no tokens left, we're releasing all account units for zero tokens. No need to do the math. if (exitTokensLeft > 0) { uint256 exitUnitsLeft = tokenResetExitBalances[_tokenAddress][accountResetNonce].unitsLeft; // accountResetTokens / exitTokensLeft = accountResetUnits / exitUnitsLeft uint256 accountResetTokens = Pricing.calculateProportionOfTotal( accountResetUnits, exitUnitsLeft, exitTokensLeft ); _tokensToRelease += accountResetTokens; if (accountResetTokens == exitTokensLeft) { delete tokenResetExitBalances[_tokenAddress][accountResetNonce]; } else { tokenResetExitBalances[_tokenAddress][accountResetNonce].tokensLeft = exitTokensLeft - accountResetTokens; tokenResetExitBalances[_tokenAddress][accountResetNonce].unitsLeft = exitUnitsLeft - accountResetUnits; } } } /** * @notice Exact same as the similarly-named external function except operates on a single token. */ function _resetPool(address _tokenAddress) internal { // Note: we are not calling _unlockEligibleTokenContractPendingUnstakes because it can call this function. ContractState storage contractStateStorage = tokenContractState[_tokenAddress]; uint256 unitsToReset = contractStateStorage.totalUnits; if (unitsToReset == 0) { // This already has the state that a reset would achieve, so it's not required. return; } // NB: must be resetNonce++, NOT ++resetNonce uint256 resetNonce = contractStateStorage.resetNonce++; uint256 tokensToReset; { uint96 reservationId = contractStateStorage.collateralReservationId; if (reservationId != 0) { // Unlock all pool tokens so they are releasable. tokensToReset = collateral.releaseAllCollateral(reservationId); contractStateStorage.collateralReservationId = 0; } } // Only set an Exit balance if there is one. If all tokens were claimed, then effectively set (0,0). if (tokensToReset > 0) { // Create the reset ExitBalance so stakers can exit their tokens (see: _resetAccountTokenStateIfApplicable(...)) tokenResetExitBalances[_tokenAddress][resetNonce] = ExitBalance(unitsToReset, tokensToReset); } // Delete all contract-level pending unstake state. if (contractStateStorage.firstPendingUnstakeEpoch > 0) { contractStateStorage.firstPendingUnstakeEpoch = 0; contractStateStorage.firstPendingUnstakeUnits = 0; if (contractStateStorage.secondPendingUnstakeEpoch > 0) { contractStateStorage.secondPendingUnstakeEpoch = 0; contractStateStorage.secondPendingUnstakeUnits = 0; } } contractStateStorage.totalUnits = 0; emit PoolReset(IERC20(_tokenAddress), resetNonce + 1, tokensToReset, unitsToReset); } /** * @notice Internal function to pool collateral and stake. * @dev Note: that this function requires the collateral has already been received and properly associated with this * contract within the ICollateral contract. * NOTE: This assumes that all eligible account and contract level unstakes and resets have been processed * prior to calling this function. * @param _token The ERC-20 token to pool and stake. * @param _amount The amount of the ERC-20 token to pool and stake. */ function _stake(IERC20 _token, uint256 _amount) internal returns (uint256 _poolUnitsIssued) { if (_amount == 0) revert InvalidZeroAmount(); collateral.poolCollateral(msg.sender, address(_token), _amount); uint96 reservationId = tokenContractState[address(_token)].collateralReservationId; uint256 poolTotalUnitsAfter; uint256 poolTotalTokensAfter; if (reservationId == 0) { _poolUnitsIssued = _amount; (reservationId, ) = collateral.reserveCollateral(address(this), address(_token), _amount); tokenContractState[address(_token)].collateralReservationId = reservationId; tokenContractState[address(_token)].totalUnits = _poolUnitsIssued; accountTokenState[msg.sender][address(_token)].totalUnits = _poolUnitsIssued; poolTotalUnitsAfter = _poolUnitsIssued; poolTotalTokensAfter = _amount; } else { (poolTotalTokensAfter, ) = collateral.modifyCollateralReservation( reservationId, Pricing.safeCastToInt256(_amount) ); uint256 poolUnits = tokenContractState[address(_token)].totalUnits; _poolUnitsIssued = Pricing.calculateProportionOfTotal(_amount, poolTotalTokensAfter - _amount, poolUnits); poolTotalUnitsAfter = poolUnits + _poolUnitsIssued; tokenContractState[address(_token)].totalUnits = poolTotalUnitsAfter; accountTokenState[msg.sender][address(_token)].totalUnits += _poolUnitsIssued; } { uint256 product; unchecked { product = (poolTotalTokensAfter * poolTotalUnitsAfter) / poolTotalUnitsAfter; } // This means that the contract will not be able to process withdrawals without overflowing, so must revert. // This can happen because the pool is empty, but the number of tokens being deposited is > 2**128 - 1 // or if pool units have been diluted enough to make the units issued for this deposit very large. // If the latter, the pool should be reset. if (product != poolTotalTokensAfter) revert DepositTooLarge(); } emit CollateralStaked(msg.sender, _token, _amount, _poolUnitsIssued); } /** * @dev Removes applicable units and tokens from contract-level unstake state for the provided token address * creating entries in the tokenEpochExitBalances mapping as applicable. * This may end up resetting the pool for the token in question if processing pending unstakes leaves the associated * CollateralReservation in a state in which it has collateral but none is claimable. See try/catch below. * @param _tokenAddress The address of the ERC-20 token to be released. * @return Whether or not the pool was reset as a result of this call. */ function _unlockEligibleTokenContractPendingUnstakes(address _tokenAddress) internal returns (bool) { ContractState storage contractStateStorage = tokenContractState[_tokenAddress]; uint256 currentEpoch = getCurrentEpoch(); uint256 firstEpoch = contractStateStorage.firstPendingUnstakeEpoch; if (firstEpoch == 0 || firstEpoch >= currentEpoch) { return false; } uint256 totalPoolUnits = contractStateStorage.totalUnits; uint256 stakedPoolTokens = collateral .getCollateralReservation(tokenContractState[_tokenAddress].collateralReservationId) .tokenAmount; uint256 firstVestedUnits = contractStateStorage.firstPendingUnstakeUnits; uint256 firstVestedTokens = Pricing.calculateProportionOfTotal( firstVestedUnits, totalPoolUnits, stakedPoolTokens ); uint256 secondVestedUnits; uint256 secondVestedTokens; uint256 secondEpoch = contractStateStorage.secondPendingUnstakeEpoch; if (_nonZeroAndLessThan(secondEpoch, currentEpoch)) { secondVestedUnits = contractStateStorage.secondPendingUnstakeUnits; secondVestedTokens = Pricing.calculateProportionOfTotal( secondVestedUnits, totalPoolUnits - firstVestedUnits, stakedPoolTokens - firstVestedTokens ); } if ((firstVestedUnits + secondVestedUnits) == totalPoolUnits) { collateral.releaseAllCollateral(contractStateStorage.collateralReservationId); contractStateStorage.collateralReservationId = 0; } else { uint256 tokensToRelease = firstVestedTokens + secondVestedTokens; if (tokensToRelease > 0) { try collateral.modifyCollateralReservation( contractStateStorage.collateralReservationId, -Pricing.safeCastToInt256(tokensToRelease) ) {} catch (bytes memory reason) { if (bytes4(reason) == ICollateral.ClaimableAmountZero.selector) { // If we're here, it means that the result of lowering the collateral amount is a reservation with // 0 claimable balance. The only way to get this collateral out is to release less or more. Less // invalidates the unstaking logic, so we choose to release more, resetting the dust that remains. _resetPool(_tokenAddress); return true; } else { assembly { revert(add(reason, 0x20), mload(reason)) } } } } } /*** Update storage to "process" the vested unstakes ***/ tokenEpochExitBalances[_tokenAddress][firstEpoch] = ExitBalance(firstVestedUnits, firstVestedTokens); contractStateStorage.totalUnits = totalPoolUnits - (firstVestedUnits + secondVestedUnits); emit UnstakeProcessed( IERC20(_tokenAddress), firstVestedUnits + secondVestedUnits, firstVestedTokens + secondVestedTokens ); if (secondVestedUnits == 0) { if (secondEpoch > 0) { contractStateStorage.firstPendingUnstakeEpoch = uint32(secondEpoch); contractStateStorage.secondPendingUnstakeEpoch = 0; contractStateStorage.firstPendingUnstakeUnits = contractStateStorage.secondPendingUnstakeUnits; contractStateStorage.secondPendingUnstakeUnits = 0; return false; } contractStateStorage.firstPendingUnstakeEpoch = 0; contractStateStorage.firstPendingUnstakeUnits = 0; return false; } // If we got here, there were 2 vested unstakes. tokenEpochExitBalances[_tokenAddress][secondEpoch] = ExitBalance(secondVestedUnits, secondVestedTokens); contractStateStorage.firstPendingUnstakeEpoch = 0; contractStateStorage.secondPendingUnstakeEpoch = 0; contractStateStorage.firstPendingUnstakeUnits = 0; contractStateStorage.secondPendingUnstakeUnits = 0; return false; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @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. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ 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]. * * CAUTION: See Security Considerations above. */ 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 v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../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 An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @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.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) 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 FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @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; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; import "./ICollateralDepositTarget.sol"; /** * @title The Collateral interface that must be exposed to make stored collateral useful to a Collateralizable contract. */ interface ICollateral is ICollateralDepositTarget { /*************** * ERROR TYPES * ***************/ error CollateralReservationNotFound(uint96 _id); error ContractNotApprovedByProtocol(address _contract); error ClaimAmountZero(); error ClaimableAmountZero(); error InsufficientAllowance( address _contract, address _accountAddress, address _tokenAddress, uint256 _need, uint256 _have ); error InsufficientCollateral(uint256 _need, uint256 _have); error InvalidSignature(address _accountAddress); error InvalidTargetAddress(address _address); error InvalidUpgradeTarget(address _contract); error InvalidZeroAmount(); error RelatedArraysLengthMismatch(uint256 _firstLength, uint256 _secondLength); error TokenNotAllowed(address _address); error Unauthorized(address _address); error WithdrawalFeeTooHigh(uint16 _wouldBeValue, uint16 _max); /********** * EVENTS * **********/ // common protocol events event AccountCollateralizableContractAllowanceUpdated( address indexed account, address indexed contractAddress, address indexed tokenAddress, int256 modifiedByAmount, uint256 newTotal ); event AccountInitiatedUpgrade( address indexed account, address indexed toCollateralContract, address[] tokenAddresses, uint256[] amounts ); event CollateralClaimed( uint96 indexed reservationId, uint256 amountWithFee, uint256 feeAmount, bool remainderReleased ); event CollateralReleased(uint96 indexed reservationId, uint256 amount); event CollateralReservationModified( uint96 indexed reservationId, uint256 oldAmount, uint256 newAmount, uint256 oldClaimableAmount, uint256 newClaimableAmount ); event CollateralReserved( uint96 indexed reservationId, address indexed account, address reservingContract, address tokenAddress, uint256 amount, uint256 claimableAmount, uint16 claimFeeBasisPoints ); event CollateralTransferred( address indexed fromAccount, address indexed tokenAddress, address indexed toAccount, uint256 tokenAmount ); event FundsDeposited(address indexed from, address indexed toAccount, address tokenAddress, uint256 amount); event FundsWithdrawn( address indexed fromAccount, address tokenAddress, uint256 amountWithFee, uint256 feeAmount, address beneficiary ); // governance events event CollateralizableContractApprovalUpdated(bool approved, address contractAddress, bool isCollateralPool); event CollateralTokenUpdated(bool enabled, address tokenAddress); event CollateralUpgradeContractApprovalUpdated(bool approved, address upgradeContractAddress); event ProtocolBalanceWithdrawn(address indexed destination, address[] tokenAddresses, uint256[] amounts); event WithdrawalFeeUpdated(uint16 oldFeeBasisPoints, uint16 newFeeBasisPoints); /*********** * STRUCTS * ***********/ struct CollateralBalance { uint256 available; uint256 reserved; } struct CollateralToken { // total deposits for all users for this token. uint256 cumulativeUserBalance; bool enabled; } struct CollateralReservation { address collateralizableContract; address account; address tokenAddress; uint16 feeBasisPoints; uint256 tokenAmount; uint256 claimableTokenAmount; } /************* * FUNCTIONS * *************/ /*** Views ***/ /** * @notice Gets the CollateralToken with the provided address. If this collateral token does not exist, it will * not revert but return a CollateralToken with default values for every field. * @param _tokenAddress The address of the CollateralToken being fetched. * @return _token The populated CollateralToken if found, empty otherwise. */ function getCollateralToken(address _tokenAddress) external view returns (CollateralToken memory _token); /** * @notice Gets the CollateralBalance for the provided account and token. * @param _accountAddress The account for which the CollateralBalance will be returned. * @param _tokenAddress The address of the token for which the account's CollateralBalance will be returned. * @return _balance The CollateralBalance for the account and token. */ function getAccountCollateralBalance( address _accountAddress, address _tokenAddress ) external view returns (CollateralBalance memory _balance); /** * @notice Gets the CollateralReservation for the provided ID. * @dev NOTE: If a reservation does not exist for the provided ID, an empty CollateralReservation will be returned. * @param _reservationId The ID of the CollateralReservation to be returned. * @return _reservation The CollateralReservation. */ function getCollateralReservation( uint96 _reservationId ) external view returns (CollateralReservation memory _reservation); /** * @notice Gets the claimable amount for the provided CollateralReservation ID. * @dev NOTE: If a reservation does not exist for the provided ID, 0 will be returned. * @param _reservationId The ID of the CollateralReservation to be returned. * @return _claimable The claimable amount. */ function getClaimableAmount(uint96 _reservationId) external view returns (uint256 _claimable); /** * @notice Gets amount of the account's assets in the provided token that the Collateralizable contract may use * through this contract. * @param _accountAddress The address of the account in question. * @param _collateralizableContract The address of the Collateralizable contract. * @param _tokenAddress The address of the token to which the allowance pertains. * @return _allowance The allowance for the account-collateralizable-token combination. Note: If collateral is * released, it is added to the allowance, so negative allowances are allowed to disable future collateral use. */ function getCollateralizableTokenAllowance( address _accountAddress, address _collateralizableContract, address _tokenAddress ) external view returns (uint256 _allowance); /** * @notice Gets the fee for withdrawing funds from this vault, either directly or through claim. * @return The fee in basis points. */ function getWithdrawalFeeBasisPoints() external view returns (uint16); /*** State-modifying functions ***/ /** * @notice Claims reserved collateral, withdrawing it from the ICollateral contract. * @dev The ICollateral contract will handle fee calculation and transfer _amountToReceive, supposing there is * sufficient collateral reserved to cover _amountToReceive and the _reservationId's _claimFeeBasisPoints. * @param _reservationId The ID of the collateral reservation in question. * @param _amountToReceive The amount of collateral needed. * @param _toAddress The address to which the `_amountToReceive` will be sent. * @param _releaseRemainder Whether or not the remaining collateral should be released. * Note: if the full amount is claimed, regardless of this value, the reservation is deleted. * @return _remainingReservedCollateral The amount of collateral that remains reserved, if not released. * @return _remainingClaimableCollateral The portion of the remaining collateral that may be claimed. */ function claimCollateral( uint96 _reservationId, uint256 _amountToReceive, address _toAddress, bool _releaseRemainder ) external returns (uint256 _remainingReservedCollateral, uint256 _remainingClaimableCollateral); /** * @notice Deposits the provided amount of the specified token into the specified account. Assets are sourced from * the specified account's ERC-20 token balance. * * Note: Even if an account has previously approved a collateralizable to use its collateral, it must provide a * deposit signature allowing it to deposit on its behalf. If the account-collateralizable allowance is less than * the amount being deposited, the result of this call will be that the account-collateraliazble allowance is equal * to the amount being deposited. If the allowance was already sufficient to use this newly deposited amount, the * allowance will remain the same. * * @param _accountAddress The account address from which assets will be deposited and with which deposited assets will * be associated in this contract. * @param _tokenAddress The address of the token to be deposited. * @param _amount The amount of the token to be deposited. * @param _collateralizableDepositApprovalSignature Deposit approval signature permitting the calling collateralizable * to deposit the account's collateral. This enables deposit-approve-and-use functionality in a single transaction. */ function depositFromAccount( address _accountAddress, address _tokenAddress, uint256 _amount, bytes calldata _collateralizableDepositApprovalSignature ) external; /** * @notice Modifies the amount of the calling account's assets the Collateralizable contract may use through this contract. * @param _collateralizableContractAddress The address of the Collateralizable contract `msg.sender` is [dis]allowing. * @param _tokenAddress The address of the token for which the allowance is being checked and updated. * @param _byAmount The signed number by which the approved amount will be modified. Negative approved amounts * function the same as 0 when attempting to reserve collateral. An account may choose to modify such that the allowance * is negative since reservations, once released, add to the approved amount since those assets were previously approved. */ function modifyCollateralizableTokenAllowance( address _collateralizableContractAddress, address _tokenAddress, int256 _byAmount ) external; /** * @notice Approves the provided collateralizable contract on behalf of the provided account address using the * account's signature. * @dev The signature is the EIP-712 signature formatted according to the following type hash variable: * bytes32 public constant COLLATERALIZABLE_TOKEN_ALLOWANCE_ADJUSTMENT_TYPEHASH = * keccak256("CollateralizableTokenAllowanceAdjustment(uint256 chainId,address approver,address collateralizableAddress,address tokenAddress,int256 allowanceAdjustment,uint256 approverNonce)"); * * If this call is not successful, it will revert. If it succeeds, the caller may assume the modification succeeded. * @param _accountAddress The account for which approval will take place. * @param _collateralizableContractAddress The address of the collateralizable to approve. * @param _allowanceAdjustment The allowance adjustment to approve. Note: this is a relative amount. * @param _signature The signature to prove the account has authorized the approval. */ function modifyCollateralizableTokenAllowanceWithSignature( address _accountAddress, address _collateralizableContractAddress, address _tokenAddress, int256 _allowanceAdjustment, bytes calldata _signature ) external; /** * @notice Adds/removes collateral to/from the reservation in question, leaving the reservation intact. * @dev This call will revert if the modification is not successful. * @param _reservationId The ID of the collateral reservation. * @param _byAmount The amount by which the reservation will be modified (adding if positive, removing if negative). * @return _reservedCollateral The total resulting reserved collateral. * @return _claimableCollateral The total resulting claimable collateral. */ function modifyCollateralReservation( uint96 _reservationId, int256 _byAmount ) external returns (uint256 _reservedCollateral, uint256 _claimableCollateral); /** * @notice Pools assets from the provided account within the collateral contract into the calling Pool's account. * This allows the caller to use assets from one or more accounts as a pool of assets. * @dev This assumes the `_fromAccount` has given `msg.sender` permission to pool the provided amount of the token. * @param _fromAccount The account from which collateral assets will be pooled. * @param _tokenAddress The address of the token to pool. * @param _tokensToPool The number of tokens to pool from the provided account. */ function poolCollateral(address _fromAccount, address _tokenAddress, uint256 _tokensToPool) external; /** * @notice Releases all collateral from the reservation in question, releasing the reservation. * @param _reservationId The ID of the collateral reservation. * @return _totalCollateralReleased The collateral amount that was released. */ function releaseAllCollateral(uint96 _reservationId) external returns (uint256 _totalCollateralReleased); /** * @notice Reserves collateral from the storing contract so that it may not be rehypothecated. * @dev This call reserves the requisite amount of collateral such that the full `_amount` may be claimed. That is * to say that `_amount` + `_claimFeeBasisPoints` will actually be reserved. * @param _accountAddress The address of the account whose assets are being reserved. * @param _tokenAddress The address of the Token being reserved as collateral. * @param _claimableAmount The amount of the Token that must be claimable. * @return _reservationId The ID that can be used to refer to this reservation when claiming or releasing collateral. * @return _totalAmountReserved The total amount reserved from the account in question. */ function reserveClaimableCollateral( address _accountAddress, address _tokenAddress, uint256 _claimableAmount ) external returns (uint96 _reservationId, uint256 _totalAmountReserved); /** * @notice Reserves collateral from the storing contract so that it may not be rehypothecated. * @dev Note that the full _amount reserved will not be received when claimed due to _claimFeeBasisPoints. Supposing * the whole amount is claimed, _amount * (1000 - _claimFeeBasisPoints) / 1000 will be received if claimed. * @param _accountAddress The address of the account whose assets are being reserved. * @param _tokenAddress The address of the Token being reserved as collateral. * @param _amount The amount of the Token being reserved as collateral. * @return _reservationId The ID that can be used to refer to this reservation when claiming or releasing collateral. * @return _claimableCollateral The collateral that may be claimed (factoring in the withdrawal fee). */ function reserveCollateral( address _accountAddress, address _tokenAddress, uint256 _amount ) external returns (uint96 _reservationId, uint256 _claimableCollateral); /** * @notice Transfers the provided amount of the caller's available collateral to the provided destination address. * @param _tokenAddress The address of the collateral token being transferred. * @param _amount The number of collateral tokens being transferred. * @param _destinationAddress The address of the account to which assets will be released. */ function transferCollateral(address _tokenAddress, uint256 _amount, address _destinationAddress) external; /** * @notice Withdraws an ERC-20 token from this `Collateral` vault to the provided address on behalf of the sender, * provided the requester has sufficient available balance. * @notice There is a protocol fee for withdrawals, so a successful withdrawal of `_amount` will entail the * account's balance being lowered by `_amount`, but the `_destination` address receiving `_amount` less the fee. * @param _tokenAddress The token address of the ERC-20 token to withdraw. * @param _amount The amount of the ERC-20 token to withdraw. * @param _destinationAddress The address that will receive the assets. Note: cannot be 0. */ function withdraw(address _tokenAddress, uint256 _amount, address _destinationAddress) external; }
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; /** * @title An interface allowing the deposit of assets into a new ICollateral contract for benefit of a specified account. * @dev This function may be used to transfer account assets from one ICollateral contract to another as an upgrade. */ interface ICollateralDepositTarget { /** * @notice Deposits assets from the calling contract into the implementing target on behalf of users. * @dev The calling contract should iterate and approve _amounts of all Tokens in _tokenAddresses to be transferred * by the implementing contract. * @dev The implementing contract MUST iterate and transfer each of the Tokens in _tokenAddresses and transfer the * _amounts to itself from the calling contract or revert if that is not possible. * @param _accountAddress The address of the account to be credited assets in the implementing contract. * @param _tokenAddresses The list of addresses of the Tokens to transfer. Indexes must correspond to _amounts. * @param _amounts The list of amounts of the Tokens to transfer. Indexes must correspond to _tokenAddresses. */ function depositToAccount( address _accountAddress, address[] calldata _tokenAddresses, uint256[] calldata _amounts ) external; }
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; /** * @title The interface that should be implemented by all collateral pools using ICollateral's pooling functions. */ interface ICollateralPool { /** * @notice Gets the provided account's pool balance in the provided token. This should be calculated based on the * account's stake in the pool, multiplied by the pool's balance of the token in question. * In many cases, some or all of this amount will be staked, locked, or otherwise inaccessible by the account at the * time of the call, but the account's current portion of the pool will still be returned. * * Note: if staked, locked, or otherwise inaccessible, the account's pool balance may be at risk of future seizure. * That is to say that the value returned from this function may not be the future withdrawable balance for the account. * @param _accountAddress The address of the account for which the pool balance will be returned. * @param _tokenAddress The address of the token for which the account pool balance will be returned. * @return _balance The balance of the account in the pool at this moment in time. */ function getAccountPoolBalance( address _accountAddress, address _tokenAddress ) external view returns (uint256 _balance); }
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ITimeBasedCollateralPool { /*************** * ERROR TYPES * ***************/ error AlreadyInitialized(); error InvalidZeroAddress(); error InvalidZeroAmount(); error DepositTooLarge(); error InsufficientBalance(uint256 need, uint256 have); error InsufficientClaimable(uint256 need, uint256 have); error RelatedArraysLengthMismatch(uint256 firstLength, uint256 secondLength); error UnstakeAmountZero(); /********** * EVENTS * **********/ /// Emitted when an account processes a reset and becomes up-to-date with the latest reset nonce. event AccountResetNonceUpdated(address indexed account, uint256 oldNonce, uint256 newNonce); /// Emitted when tokens are claimed from the pool by a claimant. event CollateralClaimed(IERC20 indexed token, uint256 tokenAmount, address destinationAccount); /// Emitted when collateral is released back to an account through the ICollateral interface (the opposite of the stake operation). event CollateralReleased(IERC20 indexed token, uint256 tokenAmount, uint256 poolUnits, address destinationAccount); /// Emitted when tokens are staked via `depositAndStake(...)` or `stake(...)` event CollateralStaked(address indexed account, IERC20 indexed token, uint256 amount, uint256 poolUnitsIssued); /// Emitted when the default account to receive claimed tokens gets updated by the authorized updater role. event DefaultClaimDestinationAccountUpdated(address oldAccount, address newAccount); /// Emitted when the pool is reset for a token. See `resetPool(...)` below. event PoolReset(IERC20 indexed token, uint256 newResetNonce, uint256 totalTokens, uint256 totalUnits); /// Emitted when the claim destination account account override for the provided token is updated by the authorized updater role. event TokenClaimDestinationAccountOverrideUpdated(IERC20 indexed token, address oldAccount, address newAccount); /// Emitted when an account calls `unstake(...)` to initiate unstaking. /// Note: The tokens will still be claimable until `willCompleteAtTimestampSeconds`. event UnstakeInitiated( address indexed account, IERC20 indexed token, uint256 unitsToUnstake, uint256 willCompleteAtTimestampSeconds ); /// Emitted when the contract processes unstakes for an epoch. /// Note: This event will be emitted after the referenced tokens are no longer claimable, but it may be well after that point in time. event UnstakeProcessed(IERC20 indexed token, uint256 poolUnitsUnstaked, uint256 poolTokensUnstaked); /*********** * STRUCTS * ***********/ struct ClaimableCollateral { uint256 amountClaimableUntilEndOfCurrentEpoch; uint256 endOfCurrentEpochTimestampSeconds; uint256 amountClaimableUntilEndOfNextEpoch; uint256 endOfNextEpochTimestampSeconds; } /************************** * CLAIM ROUTER FUNCTIONS * **************************/ /** * @notice Updates the default claim destination account that will receive all claimed tokens if there is no * destination account override for the claimed token. * @param _defaultClaimDestinationAccount The account that will be set as the default destination for claims. */ function setDefaultClaimDestinationAccount(address _defaultClaimDestinationAccount) external; /** * @notice Updates the claim destination account override for the provided token. If this is not the zero address, * it will receive claimed tokens for the provided token. Set to the zero address to fall back to the default. * @param _token The token for which the account will receive claimed tokens. * @param _destinationAccount The account that will receive claimed tokens. */ function setTokenClaimDestinationAccountOverride(IERC20 _token, address _destinationAccount) external; /********************** * CLAIMANT FUNCTIONS * **********************/ /** * @notice Claims the provided token amounts to the destination account configured in the contract. * @dev This function may only be called by the beneficiary of this contract. * @param _tokens The addresses of the tokens to claim. Indexes in this array correspond to those of _amounts. * @param _amounts The amounts of the tokens to claim. Indexes in this array correspond to those of _tokens. */ function claim(IERC20[] calldata _tokens, uint256[] calldata _amounts) external; /** * @notice Gets the amounts of the provided tokens that are guaranteed to be claimable this epoch and next epoch. * @param _tokens The ERC-20 tokens for which claimable tokens are being requested. * @return _claimableCollateral The array of claimable tokens corresponding to the _tokens array in the same order. */ function getClaimableCollateral( IERC20[] calldata _tokens ) external view returns (ClaimableCollateral[] memory _claimableCollateral); /********************** * RESETTER FUNCTIONS * **********************/ /** * @notice Resets the pool, unstaking all staked tokens and requiring stakers to call `stake(...)` again if they wish * to stake again. All staked tokens at the time of this call are immediately releasable via a call to * `releaseEligibleTokens(...)`. * NOTE: This function may only be invoked by the resetter role. * @dev The main reason a resetter would invoke this function is due to unit dilution. Over time, claims of some * but not all of the pool tokens make it so one token corresponds to more and more pool units. Given the fact that * tokens may have many decimals of precision, it's plausible that total units approaches the limits of the uint256 * datatype, making future deposits fail and pool token value to be capped at a low number. * Extreme Example: * 1. 1e25 tokens (1MM ERC-20 tokens with 18 decimals) are in the contract, which is 1e25 units. * 2. 9.999...e24 tokens get claimed. There are still 1e25 units. * 3. Add another 1e25 tokens. 1 token is worth 1e25 units, so you get 1e50 units. * 4. Repeat once more (uint256 can support ~77 digits). * @param _tokens The tokens for which the pool will be reset to a balance of 0 tokens and 0 units. */ function resetPool(IERC20[] calldata _tokens) external; /******************** * STAKER FUNCTIONS * ********************/ /** * @notice Deposits the sender's provided token amount into this contract and stakes it using the CollateralVault. * @dev This requires that the caller has called IERC20.approve(...), permitting the CollateralVault to transfer its tokens. * @param _token The ERC-20 token to deposit and stake. * @param _amount The amount of the ERC-20 token to deposit and stake. * @param _collateralizableDepositApprovalSignature The signature to allow this collateralizable to deposit on * behalf of the calling account within the `ICollateral` contract. * @return _poolUnitsIssued The number of pool units issued to the sender as a result of this call. */ function depositAndStake( IERC20 _token, uint256 _amount, bytes calldata _collateralizableDepositApprovalSignature ) external returns (uint256 _poolUnitsIssued); /** * @notice Releases all of the provided tokens that are eligible for release for the sender. * @dev This will be a no-op rather than an error if there are no releasable tokens. * @param _account The account for which eligible tokens will be released. * @param _tokens The ERC-20 tokens that should be released. */ function releaseEligibleTokens(address _account, IERC20[] calldata _tokens) external; /** * @notice Adds the sender's existing available Collateral to this pool and stakes it. * @param _token The ERC-20 token to pool and stake. * @param _amount The amount of the ERC-20 token to pool and stake. * @param _collateralizableApprovalSignature [Optional] The signature to approve the use of this collateralizable * within the `ICollateral` contract. * @return _poolUnitsIssued The number of pool units issued to the sender as a result of this call. */ function stake( IERC20 _token, uint256 _amount, bytes calldata _collateralizableApprovalSignature ) external returns (uint256 _poolUnitsIssued); /** * @notice Releases the sender's tokens from the provided stake pool and stakes the provided amount in this pool. * Note: If releasing from this contract (i.e. restaking), it is more efficient to call stake(...) directly, since * eligible tokens are released before that operation is processed. * @param _pool The pool from which tokens will be released in order to stake in this contract. * @param _token The ERC-20 token to pool and stake. * @param _amount The amount of the ERC-20 token to pool and stake. * @param _collateralizableApprovalSignature [Optional] The signature to approve the use of this collateralizable * within the `ICollateralPool` contract. * @return _poolUnitsIssued The number of pool units issued to the sender as a result of this call. */ function stakeReleasableTokensFrom( ITimeBasedCollateralPool _pool, IERC20 _token, uint256 _amount, bytes calldata _collateralizableApprovalSignature ) external returns (uint256 _poolUnitsIssued); /** * @notice Starts the unstake vesting period for the caller's provided staked tokens. * Note: staked tokens are locked for at least one full epoch and are releasable at the end of the epoch in which * this unstake period lapses. This is to guarantee that the tokens are available to the claimant for at least a * full epoch. * @dev This function operates on the account state of the msg.sender. * @param _token The ERC-20 token to unstake. * @param _poolUnits The amount of the ERC-20 token to unstake. */ function unstake(IERC20 _token, uint256 _poolUnits) external; }
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; /** * @title Library with often used math-related helper functions related to the Anvil protocol. * * @custom:security-contact [email protected] */ library Pricing { error CastOverflow(uint256 input); /// Example: human-readable price is 25000, {price: 25, exponent: 3, ...} /// Example: human-readable price is 0.00004, {price: 4, exponent: -5, ...} struct OraclePrice { // Price uint256 price; // The exchange rate may be a decimal, but it will always be represented as a uint256. // The price should be multiplied by 10**exponent to get the proper scale. int32 exponent; // Unix timestamp describing when the price was published uint256 publishTime; } /** * @notice Calculates the collateral factor implied by the provided amounts of collateral and credited tokens. * @param _collateralTokenAmount The amount of the collateral token. * @param _creditedTokenAmount The amount of the credited token. * @param _price The price of the market in which the collateral is the input token and credited is the output token. * @return The calculated collateral factor in basis points. */ function collateralFactorInBasisPoints( uint256 _collateralTokenAmount, uint256 _creditedTokenAmount, OraclePrice memory _price ) internal pure returns (uint16) { uint256 collateralInCredited = collateralAmountInCreditedToken(_collateralTokenAmount, _price); // Don't divide by 0 if (collateralInCredited == 0) { return 0; } return uint16((_creditedTokenAmount * 10_000) / collateralInCredited); } /** * @notice Calculates the amount of the credited token the provided collateral would yield, given the provided price. * @param _collateralTokenAmount The amount of the collateral token. * @param _price The price of the market in which the collateral is the input token and credited is the output token. * @return _creditedTokenAmount The calculated amount of the credited token. */ function collateralAmountInCreditedToken( uint256 _collateralTokenAmount, OraclePrice memory _price ) internal pure returns (uint256) { if (_price.exponent < 0) { return (_collateralTokenAmount * _price.price) / (10 ** uint256(int256(-1 * _price.exponent))); } else { return _collateralTokenAmount * _price.price * (10 ** uint256(int256(_price.exponent))); } } /** * @notice Calculates the provided percentage of the provided amount. * @param _amount The base amount for which the percentage will be calculated. * @param _percentageBasisPoints The percentage, represented in basis points. For example, 10_000 is 100%. * @return The resulting percentage. */ function percentageOf(uint256 _amount, uint256 _percentageBasisPoints) internal pure returns (uint256) { return (_amount * _percentageBasisPoints) / 10_000; } /** * @notice Gets the result of the provided amount being increased by a relative fee. * @dev This is the exact reverse of the `amountBeforeFee` function. Please note that calling one * and then the other is not guaranteed to produce the starting value due to integer math. * @param _amount The amount, to which the fee will be added. * @param _feeBasisPoints The relative basis points value that amount should be increased by. * @return The resulting amount with the relative fee applied. */ function amountWithFee(uint256 _amount, uint16 _feeBasisPoints) internal pure returns (uint256) { return _amount + percentageOf(_amount, uint256(_feeBasisPoints)); } /** * @notice Given an amount with a relative fee baked in, returns the amount before the fee was added. * @dev This is the exact reverse of the `amountWithFee` function. Please note that calling one * and then the other is not guaranteed to produce the starting value due to integer math. * @param _amountWithFee The amount that includes the provided fee in its value. * @param _feeBasisPoints The basis points value of the fee baked into the provided amount. * @return The value of _amountWithFee before the _feeBasisPoints was added to it. */ function amountBeforeFee(uint256 _amountWithFee, uint16 _feeBasisPoints) internal pure returns (uint256) { return (_amountWithFee * 10_000) / (10_000 + _feeBasisPoints); } /** * @dev Calculates the amount that is proportional to the provided fraction, given the denominator of the amount. * For instance if a1/a2 = b1/b2, then b1 = calculateProportionOfTotal(a1, a2, b2). * @param _aPortion The numerator of the reference proportion used to calculate the other numerator. * @param _aTotal The numerator of the reference proportion used to calculate the other numerator. * @param _bTotal The denominator for which we are calculating the numerator such that aPortion/aTotal = bPortion/bTotal. * @param _bPortion The numerator that is an equal proportion of _bTotal that _aPortion is to _aTotal. */ function calculateProportionOfTotal( uint256 _aPortion, uint256 _aTotal, uint256 _bTotal ) internal pure returns (uint256 _bPortion) { if (_aTotal == 0) return 0; // NB: It is a conscious choice to not catch overflows before they happen. This means that callers need to // handle possible overflow reverts, but it saves gas for the great majority of cases. // _bPortion / _bTotal = _aPortion / _aTotal; // _bPortion = _bTotal * _aPortion / _aTotal _bPortion = (_bTotal * _aPortion) / _aTotal; } /** * @dev Safely casts the provided uint256 to an int256, reverting with CastOverflow on overflow. * @param _input The input uint256 to cast. * @return The safely casted uint256. */ function safeCastToInt256(uint256 _input) internal pure returns (int256) { if (_input > uint256(type(int256).max)) { revert CastOverflow(_input); } return int256(_input); } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"name":"CastOverflow","type":"error"},{"inputs":[],"name":"DepositTooLarge","type":"error"},{"inputs":[{"internalType":"uint256","name":"need","type":"uint256"},{"internalType":"uint256","name":"have","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"need","type":"uint256"},{"internalType":"uint256","name":"have","type":"uint256"}],"name":"InsufficientClaimable","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"InvalidZeroAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"firstLength","type":"uint256"},{"internalType":"uint256","name":"secondLength","type":"uint256"}],"name":"RelatedArraysLengthMismatch","type":"error"},{"inputs":[],"name":"UnstakeAmountZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newNonce","type":"uint256"}],"name":"AccountResetNonceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"destinationAccount","type":"address"}],"name":"CollateralClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"poolUnits","type":"uint256"},{"indexed":false,"internalType":"address","name":"destinationAccount","type":"address"}],"name":"CollateralReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"poolUnitsIssued","type":"uint256"}],"name":"CollateralStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAccount","type":"address"},{"indexed":false,"internalType":"address","name":"newAccount","type":"address"}],"name":"DefaultClaimDestinationAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newResetNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalTokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalUnits","type":"uint256"}],"name":"PoolReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"oldAccount","type":"address"},{"indexed":false,"internalType":"address","name":"newAccount","type":"address"}],"name":"TokenClaimDestinationAccountOverrideUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"unitsToUnstake","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"willCompleteAtTimestampSeconds","type":"uint256"}],"name":"UnstakeInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolUnitsUnstaked","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"poolTokensUnstaked","type":"uint256"}],"name":"UnstakeProcessed","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLAIMANT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLAIM_ROUTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESETTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"accountTokenState","outputs":[{"internalType":"uint32","name":"resetNonce","type":"uint32"},{"internalType":"uint32","name":"firstPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint32","name":"secondPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint256","name":"firstPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"secondPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"totalUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"calculateEpochExitBalance","outputs":[{"internalType":"uint256","name":"_units","type":"uint256"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ICollateral","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultClaimDestinationAccount","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_collateralizableDepositApprovalSignature","type":"bytes"}],"name":"depositAndStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epochPeriodSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstEpochStartTimeSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_unstakeUnits","type":"uint256"},{"internalType":"bool","name":"_processContractUnstakes","type":"bool"}],"name":"getAccountExitUnitsAndTokens","outputs":[{"internalType":"uint256","name":"_units","type":"uint256"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_accountAddress","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getAccountPoolBalance","outputs":[{"internalType":"uint256","name":"_balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_accountAddress","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getAccountPoolUnits","outputs":[{"components":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"pendingUnstake","type":"uint256"},{"internalType":"uint256","name":"releasable","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.PoolUnits","name":"_accountPoolUnits","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getAccountTokenState","outputs":[{"components":[{"internalType":"uint32","name":"resetNonce","type":"uint32"},{"internalType":"uint32","name":"firstPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint32","name":"secondPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint256","name":"firstPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"secondPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"totalUnits","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.AccountState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"}],"name":"getClaimableCollateral","outputs":[{"components":[{"internalType":"uint256","name":"amountClaimableUntilEndOfCurrentEpoch","type":"uint256"},{"internalType":"uint256","name":"endOfCurrentEpochTimestampSeconds","type":"uint256"},{"internalType":"uint256","name":"amountClaimableUntilEndOfNextEpoch","type":"uint256"},{"internalType":"uint256","name":"endOfNextEpochTimestampSeconds","type":"uint256"}],"internalType":"struct ITimeBasedCollateralPool.ClaimableCollateral[]","name":"_claimableCollateral","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getPoolUnits","outputs":[{"components":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"pendingUnstake","type":"uint256"},{"internalType":"uint256","name":"releasable","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.PoolUnits","name":"_poolUnits","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getTokenContractState","outputs":[{"components":[{"internalType":"uint32","name":"resetNonce","type":"uint32"},{"internalType":"uint96","name":"collateralReservationId","type":"uint96"},{"internalType":"uint32","name":"firstPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint32","name":"secondPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint256","name":"firstPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"secondPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"totalUnits","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.ContractState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getTokenEpochExitBalance","outputs":[{"components":[{"internalType":"uint256","name":"unitsLeft","type":"uint256"},{"internalType":"uint256","name":"tokensLeft","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.ExitBalance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_resetNonce","type":"uint256"}],"name":"getTokenResetExitBalance","outputs":[{"components":[{"internalType":"uint256","name":"unitsLeft","type":"uint256"},{"internalType":"uint256","name":"tokensLeft","type":"uint256"}],"internalType":"struct TimeBasedCollateralPool.ExitBalance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getTotalAccountUnitsPendingUnstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getTotalContractUnitsPendingUnstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ICollateral","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_epochPeriodSeconds","type":"uint256"},{"internalType":"address","name":"_defaultClaimDestination","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_claimant","type":"address"},{"internalType":"address","name":"_claimRouter","type":"address"},{"internalType":"address","name":"_resetter","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"}],"name":"releaseEligibleTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"}],"name":"resetPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_defaultClaimDestinationAccount","type":"address"}],"name":"setDefaultClaimDestinationAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"_destinationAccount","type":"address"}],"name":"setTokenClaimDestinationAccountOverride","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_collateralizableApprovalSignature","type":"bytes"}],"name":"stake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ITimeBasedCollateralPool","name":"_pool","type":"address"},{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_collateralizableApprovalSignature","type":"bytes"}],"name":"stakeReleasableTokensFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenClaimDestinationAccountOverrides","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenContractState","outputs":[{"internalType":"uint32","name":"resetNonce","type":"uint32"},{"internalType":"uint96","name":"collateralReservationId","type":"uint96"},{"internalType":"uint32","name":"firstPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint32","name":"secondPendingUnstakeEpoch","type":"uint32"},{"internalType":"uint256","name":"firstPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"secondPendingUnstakeUnits","type":"uint256"},{"internalType":"uint256","name":"totalUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenEpochExitBalances","outputs":[{"internalType":"uint256","name":"unitsLeft","type":"uint256"},{"internalType":"uint256","name":"tokensLeft","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenResetExitBalances","outputs":[{"internalType":"uint256","name":"unitsLeft","type":"uint256"},{"internalType":"uint256","name":"tokensLeft","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_poolUnits","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6080604052348015600f57600080fd5b50614eb48061001f6000396000f3fe608060405234801561001057600080fd5b50600436106102f45760003560e01c80637b21881811610191578063b97dd9e2116100e3578063d8dfeb4511610097578063e7f79fa111610071578063e7f79fa114610bcc578063f223ace514610bfe578063fc08874214610c1157600080fd5b8063d8dfeb4514610b64578063da984e8214610b84578063e0b1a29a14610bb957600080fd5b8063c93c5b40116100c8578063c93c5b4014610b35578063d5305e8114610b48578063d547741f14610b5157600080fd5b8063b97dd9e214610b1a578063c2a672e014610b2257600080fd5b80639c88d00e11610145578063a0d2dd4c1161011f578063a0d2dd4c14610a98578063a217fddf14610aff578063b37497e214610b0757600080fd5b80639c88d00e14610a3e5780639da5e95914610a5e578063a0254de414610a7157600080fd5b806391d148541161017657806391d14854146109de5780639530cbc814610a2257806396cf77e214610a3557600080fd5b80637b218818146109a45780638a867368146109b757600080fd5b806341c8b2681161024a5780636a811d01116101fe57806375b238fc116101d857806375b238fc14610944578063765a32861461096b57806379ca66201461097e57600080fd5b80636a811d011461077b5780636b10bcac146108ea578063747250011461093157600080fd5b80634d6dffac1161022f5780634d6dffac146106fa5780635e8de33d1461075557806362fb5b3e1461076857600080fd5b806341c8b2681461064b5780634c274aca1461065e57600080fd5b806325c4f050116102ac57806336568abe1161028657806336568abe1461054d5780633e12170f1461056057806341ab52e71461057357600080fd5b806325c4f050146103fa5780632f2ff15d14610525578063340485841461053a57600080fd5b80630475ad03116102dd5780630475ad03146103a3578063247ccf6e146103c4578063248a9ca3146103d757600080fd5b806301ffc9a7146102f957806303dd271914610321575b600080fd5b61030c61030736600461453c565b610c31565b60405190151581526020015b60405180910390f35b61038861032f3660046145a0565b6040805180820182526000808252602091820181905273ffffffffffffffffffffffffffffffffffffffff9490941684526009815281842092845291825291829020825180840190935280548352600101549082015290565b60408051825181526020928301519281019290925201610318565b6103b66103b136600461460e565b610cd9565b604051908152602001610318565b6103b66103d236600461466a565b610d8d565b6103b66103e5366004614687565b60009081526020819052604090206001015490565b6104cd6104083660046146a0565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525073ffffffffffffffffffffffffffffffffffffffff9182166000908152600660209081526040808320939094168252918252829020825160c081018452815463ffffffff8082168352640100000000820481169483019490945268010000000000000000900490921692820192909252600182015460608201526002820154608082015260039091015460a082015290565b6040516103189190600060c08201905063ffffffff80845116835280602085015116602084015280604085015116604084015250606083015160608301526080830151608083015260a083015160a083015292915050565b6105386105333660046146d9565b610e00565b005b6103b66105483660046146fe565b610e2b565b61053861055b3660046146d9565b610fc0565b6103b661056e36600461460e565b61101e565b6105f861058136600461466a565b600760205260009081526040902080546001820154600283015460039093015463ffffffff808416946bffffffffffffffffffffffff640100000000860416947001000000000000000000000000000000008104831694740100000000000000000000000000000000000000009091049092169287565b6040805163ffffffff98891681526bffffffffffffffffffffffff909716602088015294871694860194909452949091166060840152608083015260a082019290925260c081019190915260e001610318565b610538610659366004614771565b61108a565b6106bf61066c3660046146a0565b6006602090815260009283526040808420909152908252902080546001820154600283015460039093015463ffffffff808416946401000000008504821694680100000000000000009004909116929186565b6040805163ffffffff9788168152958716602087015293909516928401929092526060830152608082015260a081019190915260c001610318565b61073061070836600461466a565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610318565b6103b6610763366004614687565b611463565b610538610776366004614843565b611498565b61087a61078936600461466a565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091525073ffffffffffffffffffffffffffffffffffffffff16600090815260076020908152604091829020825160e081018452815463ffffffff80821683526bffffffffffffffffffffffff64010000000083041694830194909452700100000000000000000000000000000000810484169482019490945274010000000000000000000000000000000000000000909304909116606083015260018101546080830152600281015460a08301526003015460c082015290565b6040516103189190600060e08201905063ffffffff8084511683526bffffffffffffffffffffffff6020850151166020840152806040850151166040840152806060850151166060840152506080830151608083015260a083015160a083015260c083015160c083015292915050565b61091c6108f83660046145a0565b60096020908152600092835260408084209091529082529020805460019091015482565b60408051928352602083019190915201610318565b61053861093f366004614885565b611503565b6103b67fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b6105386109793660046146a0565b61183b565b6103b67e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d4781565b6105386109b236600461466a565b6118f9565b6103b67fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc681565b61030c6109ec3660046146d9565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103b6610a303660046146a0565b6119f7565b6103b660025481565b610a51610a4c366004614843565b611a88565b60405161031891906148e5565b610538610a6c366004614949565b611deb565b6103b67f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e881565b610388610aa63660046145a0565b6040805180820182526000808252602091820181905273ffffffffffffffffffffffffffffffffffffffff9490941684526008815281842092845291825291829020825180840190935280548352600101549082015290565b6103b6600081565b61091c610b1536600461499e565b611e2f565b6103b6611eed565b610538610b303660046145a0565b611f0f565b6103b6610b433660046146a0565b6120ba565b6103b660035481565b610538610b5f3660046146d9565b6123a3565b6001546107309073ffffffffffffffffffffffffffffffffffffffff1681565b610b97610b9236600461466a565b6123c8565b6040805182518152602080840151908201529181015190820152606001610318565b61091c610bc73660046145a0565b6124e5565b61091c610bda3660046145a0565b60086020908152600092835260408084209091529082529020805460019091015482565b610b97610c0c3660046146a0565b61271f565b6004546107309073ffffffffffffffffffffffffffffffffffffffff1681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fc93c5b40000000000000000000000000000000000000000000000000000000001480610cc457507fffffffff0000000000000000000000000000000000000000000000000000000082167fd61ac59e00000000000000000000000000000000000000000000000000000000145b80610cd35750610cd3826128b0565b92915050565b60003385610ce78282612947565b506001546040517fc1814ea400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c1814ea490610d469033908b908b908b908b90600401614a36565b600060405180830381600087803b158015610d6057600080fd5b505af1158015610d74573d6000803e3d6000fd5b50505050610d828787612a9f565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260076020526040812060010154808203610dc65750600092915050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260076020526040902060020154610df99082614aa5565b9392505050565b600082815260208190526040902060010154610e1b81612ef7565b610e258383612f04565b50505050565b60003385610e398282612947565b5073ffffffffffffffffffffffffffffffffffffffff88163014610f2457604080516001808252818301909252600091602080830190803683370190505090508781600081518110610e8d57610e8d614ae7565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101526040517f9da5e959000000000000000000000000000000000000000000000000000000008152908a1690639da5e95990610ef09033908590600401614b16565b600060405180830381600087803b158015610f0a57600080fd5b505af1158015610f1e573d6000803e3d6000fd5b50505050505b8315610faa5760015473ffffffffffffffffffffffffffffffffffffffff1663a3e07cd833308a610f548b613000565b8a8a6040518763ffffffff1660e01b8152600401610f7796959493929190614b80565b600060405180830381600087803b158015610f9157600080fd5b505af1158015610fa5573d6000803e3d6000fd5b505050505b610fb48787612a9f565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331461100f576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110198282613063565b505050565b6000338561102c8282612947565b5083156110805760015473ffffffffffffffffffffffffffffffffffffffff1663a3e07cd833308a61105d8b613000565b8a8a6040518763ffffffff1660e01b8152600401610d4696959493929190614b80565b610d828787612a9f565b60015474010000000000000000000000000000000000000000900460ff16156110df576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556000869003611159576040517fdd484e7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166111a6576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166111f3576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426003556001805473ffffffffffffffffffffffffffffffffffffffff808a167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556002889055600480549288169290911691909117905561127b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758061311e565b6112a57fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177585612f04565b506112f07fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc67fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff831615611338576113367fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc684612f04565b505b6113827f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e87fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff8216156113ca576113c87f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e883612f04565b505b6114137e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d477fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff81161561145a576114587e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d4782612f04565b505b50505050505050565b6000600160035460025484600161147a9190614aa5565b6114849190614bc8565b61148e9190614aa5565b610cd39190614bdf565b7e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d476114c181612ef7565b60005b82811015610e25576114fb8484838181106114e1576114e1614ae7565b90506020020160208101906114f6919061466a565b613169565b6001016114c4565b7fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc661152d81612ef7565b838214611575576040517f37f0621f00000000000000000000000000000000000000000000000000000000815260048101859052602481018390526044015b60405180910390fd5b60005b8481101561183357600084848381811061159457611594614ae7565b905060200201359050806000036115ab575061182b565b60008787848181106115bf576115bf614ae7565b90506020020160208101906115d4919061466a565b905060006115e182613427565b90508015611625576040517f6565c4ec000000000000000000000000000000000000000000000000000000008152600481018490526000602482015260440161156c565b506000600560008a8a8781811061163e5761163e614ae7565b9050602002016020810190611653919061466a565b73ffffffffffffffffffffffffffffffffffffffff90811682526020820192909252604001600020541690508061169f575060045473ffffffffffffffffffffffffffffffffffffffff165b60015473ffffffffffffffffffffffffffffffffffffffff8381166000908152600760205260408082205490517f7500616d0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff1660048201526024810187905284831660448201526064810182905290929190911690637500616d9060840160408051808303816000875af115801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190614bf2565b506040805186815273ffffffffffffffffffffffffffffffffffffffff8581166020830152929350918516917f9fd4fc858839483d97ea0ee6cea1b5f753e9a146f3c690d446d690dfcad91338910160405180910390a2806000036118265773ffffffffffffffffffffffffffffffffffffffff8316600090815260076020526040902080547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff16905561182683613169565b505050505b600101611578565b505050505050565b7f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e861186581612ef7565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526005602090815260409182902080548786167fffffffffffffffffffffffff0000000000000000000000000000000000000000821681179092558351951680865291850152927f24876a5af173e66ca909a8bc49cf65e6fb5fb0eea7a472673114e02d2419de23910160405180910390a250505050565b7f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e861192381612ef7565b73ffffffffffffffffffffffffffffffffffffffff8216611970576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527fa948ea39d1926f7af60a46d00105f869f06c5dae7a85303d773bda64396b661f910160405180910390a1505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600660209081526040808320938516835292905290812060010154808203611a40576000915050610cd3565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260066020908152604080832093871683529290522060020154611a809082614aa5565b949350505050565b60606000611a94611eed565b90506000611aa182611463565b90506000611ab3610763846001614aa5565b90508467ffffffffffffffff811115611ace57611ace614ab8565b604051908082528060200260200182016040528015611b2a57816020015b611b176040518060800160405280600081526020016000815260200160008152602001600081525090565b815260200190600190039081611aec5790505b50935060005b85811015611de1576000878783818110611b4c57611b4c614ae7565b9050602002016020810190611b61919061466a565b905083868381518110611b7657611b76614ae7565b6020026020010151602001818152505082868381518110611b9957611b99614ae7565b6020908102919091018101516060019190915273ffffffffffffffffffffffffffffffffffffffff8216600090815260079091526040812060038101549091819003611be757505050611dd9565b60015482546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015260009173ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614c38565b835490915060009063ffffffff740100000000000000000000000000000000000000008204811691700100000000000000000000000000000000900416825b6002811015611dd057611ced83611ce8838f614aa5565b613abe565b15611d0d5786600201548760010154611d069190614aa5565b9350611d28565b611d1b82611ce8838f614aa5565b15611d2857866001015493505b600080611d3a86898960800151613acf565b905080600003611d50578660a001519150611d76565b6000818860800151611d629190614bdf565b9050611d72818960600151613af6565b9250505b82600003611da257818f8c81518110611d9157611d91614ae7565b602090810291909101015152611dc6565b818f8c81518110611db557611db5614ae7565b602002602001015160400181815250505b5050600101611cd1565b50505050505050505b600101611b30565b5050505092915050565b60005b81811015610e2557611e2684848484818110611e0c57611e0c614ae7565b9050602002016020810190611e21919061466a565b612947565b50600101611dee565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260086020908152604080832086845290915281205481908015611eb35773ffffffffffffffffffffffffffffffffffffffff87166000908152600860209081526040808320898452909152902060010154859350611eac9084908390613acf565b9150611ee3565b8315611ee3576000611ec588886124e5565b90925090508115611ee157859350611ede868383613acf565b92505b505b5094509492505050565b600060025460035442611f009190614bdf565b611f0a9190614d22565b905090565b80600003611f49576040517fe4a98f7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181611f5533836119f7565b33600090815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902060030154611f939190614bdf565b101561201d5781611fa433836119f7565b33600090815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902060030154611fe29190614bdf565b6040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810192909252602482015260440161156c565b60006120293383612947565b905080156120375750505050565b50612043813384613b1e565b61204d8183613c79565b73ffffffffffffffffffffffffffffffffffffffff8316337f282129d404496635cd18d83022451839006a0623bada56a71d3b1e204231dbe08461209d612092611eed565b610763906001614aa5565b6040805192835260208301919091520160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526006602090815260408083209385168352928152828220835160c081018552815463ffffffff8082168352640100000000820481169483019490945268010000000000000000900490921693820193909352600183015460608201526002830154608082015260039092015460a083018190529091908280612157611eed565b602085015190915063ffffffff1661216f8183613abe565b156121db57612185878287606001516000611e2f565b604087015190975090935063ffffffff1690506121a28183613abe565b156121db576000806121bb898489608001516000611e2f565b90925090506121ca8286614aa5565b94506121d68189614aa5565b975050505b6121e58385614bdf565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260076020526040902054865191955063ffffffff9081169116101592506122949150505773ffffffffffffffffffffffffffffffffffffffff84166000908152600960209081526040808320855163ffffffff1684528252918290208251808401909352805480845260019091015491830182905261228291849190613acf565b61228c9085614aa5565b93505061239b565b60015473ffffffffffffffffffffffffffffffffffffffff8581166000908152600760205260408082205490517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015290929190911690631931f57c9060240160c060405180830381865afa15801561232d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123519190614c38565b6080015173ffffffffffffffffffffffffffffffffffffffff861660009081526007602052604090206003015490915061238d90839083613acf565b6123979085614aa5565b9350505b505092915050565b6000828152602081905260409020600101546123be81612ef7565b610e258383613063565b6123ec60405180606001604052806000815260200160008152602001600081525090565b73ffffffffffffffffffffffffffffffffffffffff821660009081526007602052604081206003810154835290612421611eed565b8254909150700100000000000000000000000000000000900463ffffffff1661244a8183613abe565b156124bd5750815474010000000000000000000000000000000000000000900463ffffffff1661247a8183613abe565b1561249d57826002015483600101546124939190614aa5565b60408501526124dd565b6001830154604085015280156124b857600283015460208501525b6124dd565b80156124dd57826002015483600101546124d79190614aa5565b60208501525b505050919050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526007602052604081208054829190700100000000000000000000000000000000900463ffffffff168490036125fc57600181810154905482546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015291945073ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e09190614c38565b6080015191506125f583826003015484613acf565b9150612717565b805474010000000000000000000000000000000000000000900463ffffffff168490036127175760015481546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015260009173ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa1580156126aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ce9190614c38565b6080015160038301546001840154919250906126eb818385613acf565b600285015496509450612711866127028385614bdf565b61270c8887614bdf565b613acf565b94505050505b509250929050565b61274360405180606001604052806000815260200160008152602001600081525090565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600660209081526040808320938616808452938252808320815160c081018352815463ffffffff8082168352640100000000820481168387015268010000000000000000909104811682850152600183015460608301526002830154608083015260039092015460a0820181905287529484526007909252909120548251908216911610156127f6575080516040820152610cd3565b6000612800611eed565b602083015190915063ffffffff166128188183613abe565b1561287f5761282e858285606001516001611e2f565b5060408086019190915283015163ffffffff16905061284d8183613abe565b1561287f576000612865868386608001516001611e2f565b509050808560400181815161287a9190614aa5565b905250505b8360400151836080015184606001516128989190614aa5565b6128a29190614bdf565b602085015250505092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610cd357507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610cd3565b600061295282613427565b90506000806129618585613df5565b915091508160000361299a5760008061297a878761417b565b90925090506129898285614aa5565b93506129958184614aa5565b925050505b816000036129a9575050610cd3565b6001546040517f3fe3e41a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201849052878116604483015290911690633fe3e41a90606401600060405180830381600087803b158015612a2557600080fd5b505af1158015612a39573d6000803e3d6000fd5b5050604080518481526020810186905273ffffffffffffffffffffffffffffffffffffffff89811682840152915191881693507f57c331065dc2c7134df6a583ce03d96deb0b890232daf7be65176ed11531b141925081900360600190a2505092915050565b600081600003612adb576040517fdd484e7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546040517fba27a43500000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff8581166024830152604482018590529091169063ba27a43590606401600060405180830381600087803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff831660009081526007602052604081205464010000000090046bffffffffffffffffffffffff169080828103612ce9576001546040517fa2e115e900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8881166024830152604482018890528796509091169063a2e115e99060640160408051808303816000875af1158015612c33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c579190614d5d565b5073ffffffffffffffffffffffffffffffffffffffff8716600081815260076020908152604080832080547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff166401000000006bffffffffffffffffffffffff88160217815560039081018a905533845260068352818420948452939091529020018590559250839150849050612e4b565b60015473ffffffffffffffffffffffffffffffffffffffff166360b05e7184612d1188613000565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526bffffffffffffffffffffffff9092166004830152602482015260440160408051808303816000875af1158015612d78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d9c9190614bf2565b5073ffffffffffffffffffffffffffffffffffffffff8716600090815260076020526040902060030154909150612ddd86612dd78185614bdf565b83613acf565b9450612de98582614aa5565b73ffffffffffffffffffffffffffffffffffffffff8816600081815260076020908152604080832060039081018690553384526006835281842094845293909152812090910180549295508792909190612e44908490614aa5565b9091555050505b60008283830281612e5e57612e5e614cf3565b049050818114612e9a576040517fc56d46d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff88169133917fa7b456599fe289da1e1af41ace1eaafeb22eb6daaf83cb8c545bb631963aa373910160405180910390a350505092915050565b612f0181336144b2565b50565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16612ff85760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055612f963390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610cd3565b506000610cd3565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561305f576040517fddc5fd250000000000000000000000000000000000000000000000000000000081526004810183905260240161156c565b5090565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615612ff85760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610cd3565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600760205260408120600381015490918190036131a157505050565b815460009063ffffffff1683826131b783614d9c565b82546101009290920a63ffffffff81810219909316918316021790915584549116915060009064010000000090046bffffffffffffffffffffffff1680156132c4576001546040517f20d3bd2c0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff909116906320d3bd2c906024016020604051808303816000875af1158015613277573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329b9190614dbf565b85547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff16865591505b50801561331857604080518082018252848152602080820184815273ffffffffffffffffffffffffffffffffffffffff89166000908152600983528481208782529092529290209051815590516001909101555b8354700100000000000000000000000000000000900463ffffffff16156133b85783547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff168085556000600186015574010000000000000000000000000000000000000000900463ffffffff16156133b85783547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff168455600060028501555b6000600385015573ffffffffffffffffffffffffffffffffffffffff85167fc327509b9b282e0686594a9c38417284099a458182f80ee3f46d34b9150b53f0613402846001614aa5565b6040805191825260208201859052810186905260600160405180910390a25050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260076020526040812081613455611eed565b8254909150700100000000000000000000000000000000900463ffffffff168015806134815750818110155b1561349157506000949350505050565b600383015460015473ffffffffffffffffffffffffffffffffffffffff8781166000908152600760205260408082205490517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015290929190911690631931f57c9060240160c060405180830381865afa15801561352f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135539190614c38565b608001516001860154909150600061356c828585613acf565b8754909150600090819074010000000000000000000000000000000000000000900463ffffffff1661359e818a613abe565b156135c55760028a015492506135c2836135b8878a614bdf565b61270c878a614bdf565b91505b866135d08487614aa5565b036136ad576001548a546040517f20d3bd2c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015273ffffffffffffffffffffffffffffffffffffffff909116906320d3bd2c906024016020604051808303816000875af115801561365d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136819190614dbf565b5089547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff168a5561385d565b60006136b98386614aa5565b9050801561385b576001548b5473ffffffffffffffffffffffffffffffffffffffff909116906360b05e719064010000000090046bffffffffffffffffffffffff1661370484613000565b61370d90614dd8565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526bffffffffffffffffffffffff9092166004830152602482015260440160408051808303816000875af19250505080156137ae575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526137ab91810190614bf2565b60015b613858573d8080156137dc576040519150601f19603f3d011682016040523d82523d6000602084013e6137e1565b606091505b507fbdbde9580000000000000000000000000000000000000000000000000000000061380c82614e10565b7fffffffff0000000000000000000000000000000000000000000000000000000016036138505761383c8e613169565b5060019d9c50505050505050505050505050565b805160208201fd5b50505b505b604051806040016040528086815260200185815250600860008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a8152602001908152602001600020600082015181600001556020820151816001015590505082856138e59190614aa5565b6138ef9088614bdf565b60038b015573ffffffffffffffffffffffffffffffffffffffff8c167f8afe5678fa641d4c7b1e0e9f5c781e370d85a57d57e7aca780178762158d62436139368588614aa5565b6139408588614aa5565b6040805192835260208301919091520160405180910390a282600003613a325780156139f25789547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff92909216919091027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1617895550505060028601805460019097019690965550506000938490555091949350505050565b505087547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff16885550506000600190960186905550939695505050505050565b604080518082018252938452602080850193845273ffffffffffffffffffffffffffffffffffffffff909d16600090815260088e5281812092815291909c529a8b20915182555160019182015587547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1688558701899055505050506002909201849055509192915050565b60008083118015610df95750501190565b600082600003613ae157506000610df9565b82613aec8584614bc8565b611a809190614d22565b6000613b0482612710614e5c565b61ffff16613b1484612710614bc8565b610df99190614d22565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600660209081526040808320938716835292905290812090613b5b611eed565b613b66906001614aa5565b8254909150640100000000900463ffffffff166000819003613bc35750815463ffffffff909116640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff909116178155600101555050565b8163ffffffff168163ffffffff1603613bf75783836001016000828254613bea9190614aa5565b9091555050505050505050565b825463ffffffff808416680100000000000000009092041603613c335783836002016000828254613c289190614aa5565b909155506118339050565b50815463ffffffff9190911668010000000000000000027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff909116178155600201555050565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260076020526040812090613ca7611eed565b613cb2906001614aa5565b8254909150700100000000000000000000000000000000900463ffffffff166000819003613d265750815463ffffffff909116700100000000000000000000000000000000027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9091161781556001015550565b8163ffffffff168163ffffffff1603613d595783836001016000828254613d4d9190614aa5565b90915550505050505050565b825463ffffffff808416740100000000000000000000000000000000000000009092041603613da15783836002016000828254613d969190614aa5565b90915550613dee9050565b82547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff841602178355600283018490555b5050505050565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526006602090815260408083209385168352928152828220546007909152918120549091829163ffffffff9182169116808210613e5657600080935093505050614174565b73ffffffffffffffffffffffffffffffffffffffff8681166000818152600660209081526040808320948a1683529381529083902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff86811691821783558551908816815292830152927fed3fc0cca9650a34e2236a0f6c97b03aabd06a5ef098a3cb24e97560a5680631910160405180910390a28060030154945084600003613f115760008094509450505050614174565b6000600382018190558154640100000000900463ffffffff168015613fea576000613f3a611eed565b905080821015613fb457613f55898386600101546000611e2f565b855490985090935068010000000000000000900463ffffffff169150613f7b8282613abe565b15613fb457600080613f948b8588600201546000611e2f565b9092509050613fa38286614aa5565b9450613faf818a614aa5565b985050505b5082547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff16835560006001840181905560028401555b50808603613ffb5750505050614174565b60006140078288614bdf565b73ffffffffffffffffffffffffffffffffffffffff8916600090815260096020908152604080832063ffffffff8a168452909152902060010154909150801561416d5773ffffffffffffffffffffffffffffffffffffffff8916600090815260096020908152604080832063ffffffff8a1684529091528120549061408d848385613acf565b9050614099818a614aa5565b98508281036140e45773ffffffffffffffffffffffffffffffffffffffff8b16600090815260096020908152604080832063ffffffff8c16845290915281208181556001015561416a565b6140ee8184614bdf565b73ffffffffffffffffffffffffffffffffffffffff8c16600090815260096020908152604080832063ffffffff8d1684529091529020600101556141328483614bdf565b73ffffffffffffffffffffffffffffffffffffffff8c16600090815260096020908152604080832063ffffffff8d1684529091529020555b50505b5050505050505b9250929050565b73ffffffffffffffffffffffffffffffffffffffff828116600090815260066020908152604080832093851683529290529081208054829190640100000000900463ffffffff168083036141d757600080935093505050614174565b60006141e1611eed565b90508082106141f95760008094509450505050614174565b61420a868385600101546000611e2f565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260086020908152604080832087845290915281208054939850919650879261424f908490614bdf565b909155505073ffffffffffffffffffffffffffffffffffffffff8616600090815260086020908152604080832085845290915281206001018054869290614297908490614bdf565b9091555050825468010000000000000000900463ffffffff1691506142bc8282613abe565b156143bd576000806142d5888587600201546000611e2f565b90925090506142e48288614aa5565b96506142f08187614aa5565b73ffffffffffffffffffffffffffffffffffffffff89166000908152600860209081526040808320888452909152812080549298508492909190614335908490614bdf565b909155505073ffffffffffffffffffffffffffffffffffffffff881660009081526008602090815260408083208784529091528120600101805483929061437d908490614bdf565b909155505084547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1685555050600060018401819055600284015561448f565b81156144615782547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1664010000000063ffffffff8416027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1617835573ffffffffffffffffffffffffffffffffffffffff8781166000908152600660209081526040808320938a168352929052908120600290810154600186015584015561448f565b82547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff168355600060018401555b848360030160008282546144a39190614bdf565b90915550505050509250929050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16614538576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024810183905260440161156c565b5050565b60006020828403121561454e57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610df957600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114612f0157600080fd5b600080604083850312156145b357600080fd5b82356145be8161457e565b946020939093013593505050565b60008083601f8401126145de57600080fd5b50813567ffffffffffffffff8111156145f657600080fd5b60208301915083602082850101111561417457600080fd5b6000806000806060858703121561462457600080fd5b843561462f8161457e565b935060208501359250604085013567ffffffffffffffff81111561465257600080fd5b61465e878288016145cc565b95989497509550505050565b60006020828403121561467c57600080fd5b8135610df98161457e565b60006020828403121561469957600080fd5b5035919050565b600080604083850312156146b357600080fd5b82356146be8161457e565b915060208301356146ce8161457e565b809150509250929050565b600080604083850312156146ec57600080fd5b8235915060208301356146ce8161457e565b60008060008060006080868803121561471657600080fd5b85356147218161457e565b945060208601356147318161457e565b935060408601359250606086013567ffffffffffffffff81111561475457600080fd5b614760888289016145cc565b969995985093965092949392505050565b600080600080600080600060e0888a03121561478c57600080fd5b87356147978161457e565b96506020880135955060408801356147ae8161457e565b945060608801356147be8161457e565b935060808801356147ce8161457e565b925060a08801356147de8161457e565b915060c08801356147ee8161457e565b8091505092959891949750929550565b60008083601f84011261481057600080fd5b50813567ffffffffffffffff81111561482857600080fd5b6020830191508360208260051b850101111561417457600080fd5b6000806020838503121561485657600080fd5b823567ffffffffffffffff81111561486d57600080fd5b614879858286016147fe565b90969095509350505050565b6000806000806040858703121561489b57600080fd5b843567ffffffffffffffff808211156148b357600080fd5b6148bf888389016147fe565b909650945060208701359150808211156148d857600080fd5b5061465e878288016147fe565b602080825282518282018190526000919060409081850190868401855b8281101561493c57815180518552868101518786015285810151868601526060908101519085015260809093019290850190600101614902565b5091979650505050505050565b60008060006040848603121561495e57600080fd5b83356149698161457e565b9250602084013567ffffffffffffffff81111561498557600080fd5b614991868287016147fe565b9497909650939450505050565b600080600080608085870312156149b457600080fd5b84356149bf8161457e565b93506020850135925060408501359150606085013580151581146149e257600080fd5b939692955090935050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152610d826080830184866149ed565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610cd357610cd3614a76565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006040820173ffffffffffffffffffffffffffffffffffffffff808616845260206040602086015282865180855260608701915060208801945060005b81811015614b72578551851683529483019491830191600101614b54565b509098975050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a06080830152610fb460a0830184866149ed565b8082028115828204841417610cd357610cd3614a76565b81810381811115610cd357610cd3614a76565b60008060408385031215614c0557600080fd5b505080516020909101519092909150565b8051614c218161457e565b919050565b805161ffff81168114614c2157600080fd5b600060c08284031215614c4a57600080fd5b60405160c0810181811067ffffffffffffffff82111715614c94577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614ca083614c16565b8152614cae60208401614c16565b6020820152614cbf60408401614c16565b6040820152614cd060608401614c26565b60608201526080830151608082015260a083015160a08201528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614d58577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008060408385031215614d7057600080fd5b82516bffffffffffffffffffffffff81168114614d8c57600080fd5b6020939093015192949293505050565b600063ffffffff808316818103614db557614db5614a76565b6001019392505050565b600060208284031215614dd157600080fd5b5051919050565b60007f80000000000000000000000000000000000000000000000000000000000000008203614e0957614e09614a76565b5060000390565b6000815160208301517fffffffff00000000000000000000000000000000000000000000000000000000808216935060048310156124dd5760049290920360031b82901b161692915050565b61ffff818116838216019080821115614e7757614e77614a76565b509291505056fea2646970667358221220d4057ec4ba39ee544d5a0c58aae28bc1351960b981be0200d013bbf4d7ccc9f164736f6c63430008190033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102f45760003560e01c80637b21881811610191578063b97dd9e2116100e3578063d8dfeb4511610097578063e7f79fa111610071578063e7f79fa114610bcc578063f223ace514610bfe578063fc08874214610c1157600080fd5b8063d8dfeb4514610b64578063da984e8214610b84578063e0b1a29a14610bb957600080fd5b8063c93c5b40116100c8578063c93c5b4014610b35578063d5305e8114610b48578063d547741f14610b5157600080fd5b8063b97dd9e214610b1a578063c2a672e014610b2257600080fd5b80639c88d00e11610145578063a0d2dd4c1161011f578063a0d2dd4c14610a98578063a217fddf14610aff578063b37497e214610b0757600080fd5b80639c88d00e14610a3e5780639da5e95914610a5e578063a0254de414610a7157600080fd5b806391d148541161017657806391d14854146109de5780639530cbc814610a2257806396cf77e214610a3557600080fd5b80637b218818146109a45780638a867368146109b757600080fd5b806341c8b2681161024a5780636a811d01116101fe57806375b238fc116101d857806375b238fc14610944578063765a32861461096b57806379ca66201461097e57600080fd5b80636a811d011461077b5780636b10bcac146108ea578063747250011461093157600080fd5b80634d6dffac1161022f5780634d6dffac146106fa5780635e8de33d1461075557806362fb5b3e1461076857600080fd5b806341c8b2681461064b5780634c274aca1461065e57600080fd5b806325c4f050116102ac57806336568abe1161028657806336568abe1461054d5780633e12170f1461056057806341ab52e71461057357600080fd5b806325c4f050146103fa5780632f2ff15d14610525578063340485841461053a57600080fd5b80630475ad03116102dd5780630475ad03146103a3578063247ccf6e146103c4578063248a9ca3146103d757600080fd5b806301ffc9a7146102f957806303dd271914610321575b600080fd5b61030c61030736600461453c565b610c31565b60405190151581526020015b60405180910390f35b61038861032f3660046145a0565b6040805180820182526000808252602091820181905273ffffffffffffffffffffffffffffffffffffffff9490941684526009815281842092845291825291829020825180840190935280548352600101549082015290565b60408051825181526020928301519281019290925201610318565b6103b66103b136600461460e565b610cd9565b604051908152602001610318565b6103b66103d236600461466a565b610d8d565b6103b66103e5366004614687565b60009081526020819052604090206001015490565b6104cd6104083660046146a0565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525073ffffffffffffffffffffffffffffffffffffffff9182166000908152600660209081526040808320939094168252918252829020825160c081018452815463ffffffff8082168352640100000000820481169483019490945268010000000000000000900490921692820192909252600182015460608201526002820154608082015260039091015460a082015290565b6040516103189190600060c08201905063ffffffff80845116835280602085015116602084015280604085015116604084015250606083015160608301526080830151608083015260a083015160a083015292915050565b6105386105333660046146d9565b610e00565b005b6103b66105483660046146fe565b610e2b565b61053861055b3660046146d9565b610fc0565b6103b661056e36600461460e565b61101e565b6105f861058136600461466a565b600760205260009081526040902080546001820154600283015460039093015463ffffffff808416946bffffffffffffffffffffffff640100000000860416947001000000000000000000000000000000008104831694740100000000000000000000000000000000000000009091049092169287565b6040805163ffffffff98891681526bffffffffffffffffffffffff909716602088015294871694860194909452949091166060840152608083015260a082019290925260c081019190915260e001610318565b610538610659366004614771565b61108a565b6106bf61066c3660046146a0565b6006602090815260009283526040808420909152908252902080546001820154600283015460039093015463ffffffff808416946401000000008504821694680100000000000000009004909116929186565b6040805163ffffffff9788168152958716602087015293909516928401929092526060830152608082015260a081019190915260c001610318565b61073061070836600461466a565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610318565b6103b6610763366004614687565b611463565b610538610776366004614843565b611498565b61087a61078936600461466a565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091525073ffffffffffffffffffffffffffffffffffffffff16600090815260076020908152604091829020825160e081018452815463ffffffff80821683526bffffffffffffffffffffffff64010000000083041694830194909452700100000000000000000000000000000000810484169482019490945274010000000000000000000000000000000000000000909304909116606083015260018101546080830152600281015460a08301526003015460c082015290565b6040516103189190600060e08201905063ffffffff8084511683526bffffffffffffffffffffffff6020850151166020840152806040850151166040840152806060850151166060840152506080830151608083015260a083015160a083015260c083015160c083015292915050565b61091c6108f83660046145a0565b60096020908152600092835260408084209091529082529020805460019091015482565b60408051928352602083019190915201610318565b61053861093f366004614885565b611503565b6103b67fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b6105386109793660046146a0565b61183b565b6103b67e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d4781565b6105386109b236600461466a565b6118f9565b6103b67fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc681565b61030c6109ec3660046146d9565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b6103b6610a303660046146a0565b6119f7565b6103b660025481565b610a51610a4c366004614843565b611a88565b60405161031891906148e5565b610538610a6c366004614949565b611deb565b6103b67f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e881565b610388610aa63660046145a0565b6040805180820182526000808252602091820181905273ffffffffffffffffffffffffffffffffffffffff9490941684526008815281842092845291825291829020825180840190935280548352600101549082015290565b6103b6600081565b61091c610b1536600461499e565b611e2f565b6103b6611eed565b610538610b303660046145a0565b611f0f565b6103b6610b433660046146a0565b6120ba565b6103b660035481565b610538610b5f3660046146d9565b6123a3565b6001546107309073ffffffffffffffffffffffffffffffffffffffff1681565b610b97610b9236600461466a565b6123c8565b6040805182518152602080840151908201529181015190820152606001610318565b61091c610bc73660046145a0565b6124e5565b61091c610bda3660046145a0565b60086020908152600092835260408084209091529082529020805460019091015482565b610b97610c0c3660046146a0565b61271f565b6004546107309073ffffffffffffffffffffffffffffffffffffffff1681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fc93c5b40000000000000000000000000000000000000000000000000000000001480610cc457507fffffffff0000000000000000000000000000000000000000000000000000000082167fd61ac59e00000000000000000000000000000000000000000000000000000000145b80610cd35750610cd3826128b0565b92915050565b60003385610ce78282612947565b506001546040517fc1814ea400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c1814ea490610d469033908b908b908b908b90600401614a36565b600060405180830381600087803b158015610d6057600080fd5b505af1158015610d74573d6000803e3d6000fd5b50505050610d828787612a9f565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260076020526040812060010154808203610dc65750600092915050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260076020526040902060020154610df99082614aa5565b9392505050565b600082815260208190526040902060010154610e1b81612ef7565b610e258383612f04565b50505050565b60003385610e398282612947565b5073ffffffffffffffffffffffffffffffffffffffff88163014610f2457604080516001808252818301909252600091602080830190803683370190505090508781600081518110610e8d57610e8d614ae7565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101526040517f9da5e959000000000000000000000000000000000000000000000000000000008152908a1690639da5e95990610ef09033908590600401614b16565b600060405180830381600087803b158015610f0a57600080fd5b505af1158015610f1e573d6000803e3d6000fd5b50505050505b8315610faa5760015473ffffffffffffffffffffffffffffffffffffffff1663a3e07cd833308a610f548b613000565b8a8a6040518763ffffffff1660e01b8152600401610f7796959493929190614b80565b600060405180830381600087803b158015610f9157600080fd5b505af1158015610fa5573d6000803e3d6000fd5b505050505b610fb48787612a9f565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331461100f576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110198282613063565b505050565b6000338561102c8282612947565b5083156110805760015473ffffffffffffffffffffffffffffffffffffffff1663a3e07cd833308a61105d8b613000565b8a8a6040518763ffffffff1660e01b8152600401610d4696959493929190614b80565b610d828787612a9f565b60015474010000000000000000000000000000000000000000900460ff16156110df576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556000869003611159576040517fdd484e7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166111a6576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166111f3576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426003556001805473ffffffffffffffffffffffffffffffffffffffff808a167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556002889055600480549288169290911691909117905561127b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758061311e565b6112a57fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177585612f04565b506112f07fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc67fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff831615611338576113367fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc684612f04565b505b6113827f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e87fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff8216156113ca576113c87f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e883612f04565b505b6114137e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d477fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561311e565b73ffffffffffffffffffffffffffffffffffffffff81161561145a576114587e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d4782612f04565b505b50505050505050565b6000600160035460025484600161147a9190614aa5565b6114849190614bc8565b61148e9190614aa5565b610cd39190614bdf565b7e7d5786643ee140934c78e29f0883c40e3db9ce7b5c23251d35d01bbe838d476114c181612ef7565b60005b82811015610e25576114fb8484838181106114e1576114e1614ae7565b90506020020160208101906114f6919061466a565b613169565b6001016114c4565b7fde60452b7e5ef525564a29469a0bce46dbce1bcfb88f883dcbd957a9cb50ddc661152d81612ef7565b838214611575576040517f37f0621f00000000000000000000000000000000000000000000000000000000815260048101859052602481018390526044015b60405180910390fd5b60005b8481101561183357600084848381811061159457611594614ae7565b905060200201359050806000036115ab575061182b565b60008787848181106115bf576115bf614ae7565b90506020020160208101906115d4919061466a565b905060006115e182613427565b90508015611625576040517f6565c4ec000000000000000000000000000000000000000000000000000000008152600481018490526000602482015260440161156c565b506000600560008a8a8781811061163e5761163e614ae7565b9050602002016020810190611653919061466a565b73ffffffffffffffffffffffffffffffffffffffff90811682526020820192909252604001600020541690508061169f575060045473ffffffffffffffffffffffffffffffffffffffff165b60015473ffffffffffffffffffffffffffffffffffffffff8381166000908152600760205260408082205490517f7500616d0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff1660048201526024810187905284831660448201526064810182905290929190911690637500616d9060840160408051808303816000875af115801561174f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117739190614bf2565b506040805186815273ffffffffffffffffffffffffffffffffffffffff8581166020830152929350918516917f9fd4fc858839483d97ea0ee6cea1b5f753e9a146f3c690d446d690dfcad91338910160405180910390a2806000036118265773ffffffffffffffffffffffffffffffffffffffff8316600090815260076020526040902080547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff16905561182683613169565b505050505b600101611578565b505050505050565b7f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e861186581612ef7565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526005602090815260409182902080548786167fffffffffffffffffffffffff0000000000000000000000000000000000000000821681179092558351951680865291850152927f24876a5af173e66ca909a8bc49cf65e6fb5fb0eea7a472673114e02d2419de23910160405180910390a250505050565b7f9642b0ce4086cbdb0258c5d14b2ca081d0f79a110447a9cea56a25db41d615e861192381612ef7565b73ffffffffffffffffffffffffffffffffffffffff8216611970576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527fa948ea39d1926f7af60a46d00105f869f06c5dae7a85303d773bda64396b661f910160405180910390a1505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600660209081526040808320938516835292905290812060010154808203611a40576000915050610cd3565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260066020908152604080832093871683529290522060020154611a809082614aa5565b949350505050565b60606000611a94611eed565b90506000611aa182611463565b90506000611ab3610763846001614aa5565b90508467ffffffffffffffff811115611ace57611ace614ab8565b604051908082528060200260200182016040528015611b2a57816020015b611b176040518060800160405280600081526020016000815260200160008152602001600081525090565b815260200190600190039081611aec5790505b50935060005b85811015611de1576000878783818110611b4c57611b4c614ae7565b9050602002016020810190611b61919061466a565b905083868381518110611b7657611b76614ae7565b6020026020010151602001818152505082868381518110611b9957611b99614ae7565b6020908102919091018101516060019190915273ffffffffffffffffffffffffffffffffffffffff8216600090815260079091526040812060038101549091819003611be757505050611dd9565b60015482546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015260009173ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614c38565b835490915060009063ffffffff740100000000000000000000000000000000000000008204811691700100000000000000000000000000000000900416825b6002811015611dd057611ced83611ce8838f614aa5565b613abe565b15611d0d5786600201548760010154611d069190614aa5565b9350611d28565b611d1b82611ce8838f614aa5565b15611d2857866001015493505b600080611d3a86898960800151613acf565b905080600003611d50578660a001519150611d76565b6000818860800151611d629190614bdf565b9050611d72818960600151613af6565b9250505b82600003611da257818f8c81518110611d9157611d91614ae7565b602090810291909101015152611dc6565b818f8c81518110611db557611db5614ae7565b602002602001015160400181815250505b5050600101611cd1565b50505050505050505b600101611b30565b5050505092915050565b60005b81811015610e2557611e2684848484818110611e0c57611e0c614ae7565b9050602002016020810190611e21919061466a565b612947565b50600101611dee565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260086020908152604080832086845290915281205481908015611eb35773ffffffffffffffffffffffffffffffffffffffff87166000908152600860209081526040808320898452909152902060010154859350611eac9084908390613acf565b9150611ee3565b8315611ee3576000611ec588886124e5565b90925090508115611ee157859350611ede868383613acf565b92505b505b5094509492505050565b600060025460035442611f009190614bdf565b611f0a9190614d22565b905090565b80600003611f49576040517fe4a98f7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181611f5533836119f7565b33600090815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902060030154611f939190614bdf565b101561201d5781611fa433836119f7565b33600090815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902060030154611fe29190614bdf565b6040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810192909252602482015260440161156c565b60006120293383612947565b905080156120375750505050565b50612043813384613b1e565b61204d8183613c79565b73ffffffffffffffffffffffffffffffffffffffff8316337f282129d404496635cd18d83022451839006a0623bada56a71d3b1e204231dbe08461209d612092611eed565b610763906001614aa5565b6040805192835260208301919091520160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526006602090815260408083209385168352928152828220835160c081018552815463ffffffff8082168352640100000000820481169483019490945268010000000000000000900490921693820193909352600183015460608201526002830154608082015260039092015460a083018190529091908280612157611eed565b602085015190915063ffffffff1661216f8183613abe565b156121db57612185878287606001516000611e2f565b604087015190975090935063ffffffff1690506121a28183613abe565b156121db576000806121bb898489608001516000611e2f565b90925090506121ca8286614aa5565b94506121d68189614aa5565b975050505b6121e58385614bdf565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260076020526040902054865191955063ffffffff9081169116101592506122949150505773ffffffffffffffffffffffffffffffffffffffff84166000908152600960209081526040808320855163ffffffff1684528252918290208251808401909352805480845260019091015491830182905261228291849190613acf565b61228c9085614aa5565b93505061239b565b60015473ffffffffffffffffffffffffffffffffffffffff8581166000908152600760205260408082205490517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015290929190911690631931f57c9060240160c060405180830381865afa15801561232d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123519190614c38565b6080015173ffffffffffffffffffffffffffffffffffffffff861660009081526007602052604090206003015490915061238d90839083613acf565b6123979085614aa5565b9350505b505092915050565b6000828152602081905260409020600101546123be81612ef7565b610e258383613063565b6123ec60405180606001604052806000815260200160008152602001600081525090565b73ffffffffffffffffffffffffffffffffffffffff821660009081526007602052604081206003810154835290612421611eed565b8254909150700100000000000000000000000000000000900463ffffffff1661244a8183613abe565b156124bd5750815474010000000000000000000000000000000000000000900463ffffffff1661247a8183613abe565b1561249d57826002015483600101546124939190614aa5565b60408501526124dd565b6001830154604085015280156124b857600283015460208501525b6124dd565b80156124dd57826002015483600101546124d79190614aa5565b60208501525b505050919050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526007602052604081208054829190700100000000000000000000000000000000900463ffffffff168490036125fc57600181810154905482546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015291945073ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e09190614c38565b6080015191506125f583826003015484613acf565b9150612717565b805474010000000000000000000000000000000000000000900463ffffffff168490036127175760015481546040517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015260009173ffffffffffffffffffffffffffffffffffffffff1690631931f57c9060240160c060405180830381865afa1580156126aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ce9190614c38565b6080015160038301546001840154919250906126eb818385613acf565b600285015496509450612711866127028385614bdf565b61270c8887614bdf565b613acf565b94505050505b509250929050565b61274360405180606001604052806000815260200160008152602001600081525090565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600660209081526040808320938616808452938252808320815160c081018352815463ffffffff8082168352640100000000820481168387015268010000000000000000909104811682850152600183015460608301526002830154608083015260039092015460a0820181905287529484526007909252909120548251908216911610156127f6575080516040820152610cd3565b6000612800611eed565b602083015190915063ffffffff166128188183613abe565b1561287f5761282e858285606001516001611e2f565b5060408086019190915283015163ffffffff16905061284d8183613abe565b1561287f576000612865868386608001516001611e2f565b509050808560400181815161287a9190614aa5565b905250505b8360400151836080015184606001516128989190614aa5565b6128a29190614bdf565b602085015250505092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610cd357507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610cd3565b600061295282613427565b90506000806129618585613df5565b915091508160000361299a5760008061297a878761417b565b90925090506129898285614aa5565b93506129958184614aa5565b925050505b816000036129a9575050610cd3565b6001546040517f3fe3e41a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201849052878116604483015290911690633fe3e41a90606401600060405180830381600087803b158015612a2557600080fd5b505af1158015612a39573d6000803e3d6000fd5b5050604080518481526020810186905273ffffffffffffffffffffffffffffffffffffffff89811682840152915191881693507f57c331065dc2c7134df6a583ce03d96deb0b890232daf7be65176ed11531b141925081900360600190a2505092915050565b600081600003612adb576040517fdd484e7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546040517fba27a43500000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff8581166024830152604482018590529091169063ba27a43590606401600060405180830381600087803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff831660009081526007602052604081205464010000000090046bffffffffffffffffffffffff169080828103612ce9576001546040517fa2e115e900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8881166024830152604482018890528796509091169063a2e115e99060640160408051808303816000875af1158015612c33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c579190614d5d565b5073ffffffffffffffffffffffffffffffffffffffff8716600081815260076020908152604080832080547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff166401000000006bffffffffffffffffffffffff88160217815560039081018a905533845260068352818420948452939091529020018590559250839150849050612e4b565b60015473ffffffffffffffffffffffffffffffffffffffff166360b05e7184612d1188613000565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526bffffffffffffffffffffffff9092166004830152602482015260440160408051808303816000875af1158015612d78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d9c9190614bf2565b5073ffffffffffffffffffffffffffffffffffffffff8716600090815260076020526040902060030154909150612ddd86612dd78185614bdf565b83613acf565b9450612de98582614aa5565b73ffffffffffffffffffffffffffffffffffffffff8816600081815260076020908152604080832060039081018690553384526006835281842094845293909152812090910180549295508792909190612e44908490614aa5565b9091555050505b60008283830281612e5e57612e5e614cf3565b049050818114612e9a576040517fc56d46d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff88169133917fa7b456599fe289da1e1af41ace1eaafeb22eb6daaf83cb8c545bb631963aa373910160405180910390a350505092915050565b612f0181336144b2565b50565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16612ff85760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055612f963390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610cd3565b506000610cd3565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561305f576040517fddc5fd250000000000000000000000000000000000000000000000000000000081526004810183905260240161156c565b5090565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615612ff85760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610cd3565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600760205260408120600381015490918190036131a157505050565b815460009063ffffffff1683826131b783614d9c565b82546101009290920a63ffffffff81810219909316918316021790915584549116915060009064010000000090046bffffffffffffffffffffffff1680156132c4576001546040517f20d3bd2c0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff909116906320d3bd2c906024016020604051808303816000875af1158015613277573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329b9190614dbf565b85547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff16865591505b50801561331857604080518082018252848152602080820184815273ffffffffffffffffffffffffffffffffffffffff89166000908152600983528481208782529092529290209051815590516001909101555b8354700100000000000000000000000000000000900463ffffffff16156133b85783547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff168085556000600186015574010000000000000000000000000000000000000000900463ffffffff16156133b85783547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff168455600060028501555b6000600385015573ffffffffffffffffffffffffffffffffffffffff85167fc327509b9b282e0686594a9c38417284099a458182f80ee3f46d34b9150b53f0613402846001614aa5565b6040805191825260208201859052810186905260600160405180910390a25050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260076020526040812081613455611eed565b8254909150700100000000000000000000000000000000900463ffffffff168015806134815750818110155b1561349157506000949350505050565b600383015460015473ffffffffffffffffffffffffffffffffffffffff8781166000908152600760205260408082205490517f1931f57c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015290929190911690631931f57c9060240160c060405180830381865afa15801561352f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135539190614c38565b608001516001860154909150600061356c828585613acf565b8754909150600090819074010000000000000000000000000000000000000000900463ffffffff1661359e818a613abe565b156135c55760028a015492506135c2836135b8878a614bdf565b61270c878a614bdf565b91505b866135d08487614aa5565b036136ad576001548a546040517f20d3bd2c0000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff16600482015273ffffffffffffffffffffffffffffffffffffffff909116906320d3bd2c906024016020604051808303816000875af115801561365d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136819190614dbf565b5089547fffffffffffffffffffffffffffffffff000000000000000000000000ffffffff168a5561385d565b60006136b98386614aa5565b9050801561385b576001548b5473ffffffffffffffffffffffffffffffffffffffff909116906360b05e719064010000000090046bffffffffffffffffffffffff1661370484613000565b61370d90614dd8565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526bffffffffffffffffffffffff9092166004830152602482015260440160408051808303816000875af19250505080156137ae575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526137ab91810190614bf2565b60015b613858573d8080156137dc576040519150601f19603f3d011682016040523d82523d6000602084013e6137e1565b606091505b507fbdbde9580000000000000000000000000000000000000000000000000000000061380c82614e10565b7fffffffff0000000000000000000000000000000000000000000000000000000016036138505761383c8e613169565b5060019d9c50505050505050505050505050565b805160208201fd5b50505b505b604051806040016040528086815260200185815250600860008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a8152602001908152602001600020600082015181600001556020820151816001015590505082856138e59190614aa5565b6138ef9088614bdf565b60038b015573ffffffffffffffffffffffffffffffffffffffff8c167f8afe5678fa641d4c7b1e0e9f5c781e370d85a57d57e7aca780178762158d62436139368588614aa5565b6139408588614aa5565b6040805192835260208301919091520160405180910390a282600003613a325780156139f25789547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff92909216919091027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1617895550505060028601805460019097019690965550506000938490555091949350505050565b505087547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff16885550506000600190960186905550939695505050505050565b604080518082018252938452602080850193845273ffffffffffffffffffffffffffffffffffffffff909d16600090815260088e5281812092815291909c529a8b20915182555160019182015587547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1688558701899055505050506002909201849055509192915050565b60008083118015610df95750501190565b600082600003613ae157506000610df9565b82613aec8584614bc8565b611a809190614d22565b6000613b0482612710614e5c565b61ffff16613b1484612710614bc8565b610df99190614d22565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600660209081526040808320938716835292905290812090613b5b611eed565b613b66906001614aa5565b8254909150640100000000900463ffffffff166000819003613bc35750815463ffffffff909116640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff909116178155600101555050565b8163ffffffff168163ffffffff1603613bf75783836001016000828254613bea9190614aa5565b9091555050505050505050565b825463ffffffff808416680100000000000000009092041603613c335783836002016000828254613c289190614aa5565b909155506118339050565b50815463ffffffff9190911668010000000000000000027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff909116178155600201555050565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260076020526040812090613ca7611eed565b613cb2906001614aa5565b8254909150700100000000000000000000000000000000900463ffffffff166000819003613d265750815463ffffffff909116700100000000000000000000000000000000027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9091161781556001015550565b8163ffffffff168163ffffffff1603613d595783836001016000828254613d4d9190614aa5565b90915550505050505050565b825463ffffffff808416740100000000000000000000000000000000000000009092041603613da15783836002016000828254613d969190614aa5565b90915550613dee9050565b82547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff841602178355600283018490555b5050505050565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526006602090815260408083209385168352928152828220546007909152918120549091829163ffffffff9182169116808210613e5657600080935093505050614174565b73ffffffffffffffffffffffffffffffffffffffff8681166000818152600660209081526040808320948a1683529381529083902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff86811691821783558551908816815292830152927fed3fc0cca9650a34e2236a0f6c97b03aabd06a5ef098a3cb24e97560a5680631910160405180910390a28060030154945084600003613f115760008094509450505050614174565b6000600382018190558154640100000000900463ffffffff168015613fea576000613f3a611eed565b905080821015613fb457613f55898386600101546000611e2f565b855490985090935068010000000000000000900463ffffffff169150613f7b8282613abe565b15613fb457600080613f948b8588600201546000611e2f565b9092509050613fa38286614aa5565b9450613faf818a614aa5565b985050505b5082547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff16835560006001840181905560028401555b50808603613ffb5750505050614174565b60006140078288614bdf565b73ffffffffffffffffffffffffffffffffffffffff8916600090815260096020908152604080832063ffffffff8a168452909152902060010154909150801561416d5773ffffffffffffffffffffffffffffffffffffffff8916600090815260096020908152604080832063ffffffff8a1684529091528120549061408d848385613acf565b9050614099818a614aa5565b98508281036140e45773ffffffffffffffffffffffffffffffffffffffff8b16600090815260096020908152604080832063ffffffff8c16845290915281208181556001015561416a565b6140ee8184614bdf565b73ffffffffffffffffffffffffffffffffffffffff8c16600090815260096020908152604080832063ffffffff8d1684529091529020600101556141328483614bdf565b73ffffffffffffffffffffffffffffffffffffffff8c16600090815260096020908152604080832063ffffffff8d1684529091529020555b50505b5050505050505b9250929050565b73ffffffffffffffffffffffffffffffffffffffff828116600090815260066020908152604080832093851683529290529081208054829190640100000000900463ffffffff168083036141d757600080935093505050614174565b60006141e1611eed565b90508082106141f95760008094509450505050614174565b61420a868385600101546000611e2f565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260086020908152604080832087845290915281208054939850919650879261424f908490614bdf565b909155505073ffffffffffffffffffffffffffffffffffffffff8616600090815260086020908152604080832085845290915281206001018054869290614297908490614bdf565b9091555050825468010000000000000000900463ffffffff1691506142bc8282613abe565b156143bd576000806142d5888587600201546000611e2f565b90925090506142e48288614aa5565b96506142f08187614aa5565b73ffffffffffffffffffffffffffffffffffffffff89166000908152600860209081526040808320888452909152812080549298508492909190614335908490614bdf565b909155505073ffffffffffffffffffffffffffffffffffffffff881660009081526008602090815260408083208784529091528120600101805483929061437d908490614bdf565b909155505084547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1685555050600060018401819055600284015561448f565b81156144615782547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1664010000000063ffffffff8416027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1617835573ffffffffffffffffffffffffffffffffffffffff8781166000908152600660209081526040808320938a168352929052908120600290810154600186015584015561448f565b82547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff168355600060018401555b848360030160008282546144a39190614bdf565b90915550505050509250929050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16614538576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024810183905260440161156c565b5050565b60006020828403121561454e57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610df957600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114612f0157600080fd5b600080604083850312156145b357600080fd5b82356145be8161457e565b946020939093013593505050565b60008083601f8401126145de57600080fd5b50813567ffffffffffffffff8111156145f657600080fd5b60208301915083602082850101111561417457600080fd5b6000806000806060858703121561462457600080fd5b843561462f8161457e565b935060208501359250604085013567ffffffffffffffff81111561465257600080fd5b61465e878288016145cc565b95989497509550505050565b60006020828403121561467c57600080fd5b8135610df98161457e565b60006020828403121561469957600080fd5b5035919050565b600080604083850312156146b357600080fd5b82356146be8161457e565b915060208301356146ce8161457e565b809150509250929050565b600080604083850312156146ec57600080fd5b8235915060208301356146ce8161457e565b60008060008060006080868803121561471657600080fd5b85356147218161457e565b945060208601356147318161457e565b935060408601359250606086013567ffffffffffffffff81111561475457600080fd5b614760888289016145cc565b969995985093965092949392505050565b600080600080600080600060e0888a03121561478c57600080fd5b87356147978161457e565b96506020880135955060408801356147ae8161457e565b945060608801356147be8161457e565b935060808801356147ce8161457e565b925060a08801356147de8161457e565b915060c08801356147ee8161457e565b8091505092959891949750929550565b60008083601f84011261481057600080fd5b50813567ffffffffffffffff81111561482857600080fd5b6020830191508360208260051b850101111561417457600080fd5b6000806020838503121561485657600080fd5b823567ffffffffffffffff81111561486d57600080fd5b614879858286016147fe565b90969095509350505050565b6000806000806040858703121561489b57600080fd5b843567ffffffffffffffff808211156148b357600080fd5b6148bf888389016147fe565b909650945060208701359150808211156148d857600080fd5b5061465e878288016147fe565b602080825282518282018190526000919060409081850190868401855b8281101561493c57815180518552868101518786015285810151868601526060908101519085015260809093019290850190600101614902565b5091979650505050505050565b60008060006040848603121561495e57600080fd5b83356149698161457e565b9250602084013567ffffffffffffffff81111561498557600080fd5b614991868287016147fe565b9497909650939450505050565b600080600080608085870312156149b457600080fd5b84356149bf8161457e565b93506020850135925060408501359150606085013580151581146149e257600080fd5b939692955090935050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152610d826080830184866149ed565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610cd357610cd3614a76565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006040820173ffffffffffffffffffffffffffffffffffffffff808616845260206040602086015282865180855260608701915060208801945060005b81811015614b72578551851683529483019491830191600101614b54565b509098975050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a06080830152610fb460a0830184866149ed565b8082028115828204841417610cd357610cd3614a76565b81810381811115610cd357610cd3614a76565b60008060408385031215614c0557600080fd5b505080516020909101519092909150565b8051614c218161457e565b919050565b805161ffff81168114614c2157600080fd5b600060c08284031215614c4a57600080fd5b60405160c0810181811067ffffffffffffffff82111715614c94577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614ca083614c16565b8152614cae60208401614c16565b6020820152614cbf60408401614c16565b6040820152614cd060608401614c26565b60608201526080830151608082015260a083015160a08201528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614d58577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008060408385031215614d7057600080fd5b82516bffffffffffffffffffffffff81168114614d8c57600080fd5b6020939093015192949293505050565b600063ffffffff808316818103614db557614db5614a76565b6001019392505050565b600060208284031215614dd157600080fd5b5051919050565b60007f80000000000000000000000000000000000000000000000000000000000000008203614e0957614e09614a76565b5060000390565b6000815160208301517fffffffff00000000000000000000000000000000000000000000000000000000808216935060048310156124dd5760049290920360031b82901b161692915050565b61ffff818116838216019080821115614e7757614e77614a76565b509291505056fea2646970667358221220d4057ec4ba39ee544d5a0c58aae28bc1351960b981be0200d013bbf4d7ccc9f164736f6c63430008190033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.