Overview
S Balance
0 S
S Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 13 from a total of 13 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Batch | 6572667 | 32 hrs ago | IN | 0 S | 0.08103837 | ||||
Batch | 6559712 | 34 hrs ago | IN | 0 S | 0.01098361 | ||||
Batch | 6556643 | 35 hrs ago | IN | 0 S | 0.01874554 | ||||
Batch | 6523100 | 41 hrs ago | IN | 0 S | 0.01230031 | ||||
Batch | 6523007 | 41 hrs ago | IN | 0 S | 0.01694563 | ||||
Batch | 6522808 | 41 hrs ago | IN | 0 S | 0.01655867 | ||||
Batch | 6522622 | 41 hrs ago | IN | 0 S | 0.01360424 | ||||
Batch | 6522465 | 41 hrs ago | IN | 0 S | 0.01310585 | ||||
Batch | 6463784 | 2 days ago | IN | 0 S | 0.51951916 | ||||
Batch | 5613185 | 9 days ago | IN | 0 S | 0.01218805 | ||||
Batch | 5612891 | 9 days ago | IN | 0 S | 0.00934963 | ||||
Batch | 5336202 | 11 days ago | IN | 0 S | 0.0270942 | ||||
Batch | 5324624 | 12 days ago | IN | 0 S | 0.04084956 |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
EthereumVaultConnector
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 20000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {Set, SetStorage} from "./Set.sol"; import {Events} from "./Events.sol"; import {Errors} from "./Errors.sol"; import {ExecutionContext, EC} from "./ExecutionContext.sol"; import {TransientStorage} from "./TransientStorage.sol"; import {IEVC} from "./interfaces/IEthereumVaultConnector.sol"; import {IVault} from "./interfaces/IVault.sol"; import {IERC1271} from "./interfaces/IERC1271.sol"; /// @title EthereumVaultConnector /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract implements the Ethereum Vault Connector. contract EthereumVaultConnector is Events, Errors, TransientStorage, IEVC { using ExecutionContext for EC; using Set for SetStorage; /////////////////////////////////////////////////////////////////////////////////////////////// // CONSTANTS // /////////////////////////////////////////////////////////////////////////////////////////////// /// @notice Name of the Ethereum Vault Connector. string public constant name = "Ethereum Vault Connector"; uint160 internal constant ACCOUNT_ID_OFFSET = 8; address internal constant EIP_7587_PRECOMPILES = 0x0000000000000000000000000000000000000100; address internal constant COMMON_PREDEPLOYS = 0x4200000000000000000000000000000000000000; bytes32 internal constant HASHED_NAME = keccak256(bytes(name)); bytes32 internal constant TYPE_HASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); bytes32 internal constant PERMIT_TYPEHASH = keccak256( "Permit(address signer,address sender,uint256 nonceNamespace,uint256 nonce,uint256 deadline,uint256 value,bytes data)" ); uint256 internal immutable CACHED_CHAIN_ID; bytes32 internal immutable CACHED_DOMAIN_SEPARATOR; /////////////////////////////////////////////////////////////////////////////////////////////// // STORAGE // /////////////////////////////////////////////////////////////////////////////////////////////// // EVC implements controller isolation, meaning that unless in transient state, only one controller per account can // be enabled. However, this can lead to a suboptimal user experience. In the event a user wants to have multiple // controllers enabled, a separate wallet must be created and funded. Although there is nothing wrong with having // many accounts within the same wallet, this can be a bad experience. In order to improve on this, EVC supports // the concept of an owner that owns 256 accounts within EVC. // Every Ethereum address has 256 accounts in the EVC (including the primary account - called the owner). // Each account has an account ID from 0-255, where 0 is the owner account's ID. In order to compute the account // addresses, the account ID is treated as a uint256 and XORed (exclusive ORed) with the Ethereum address. // In order to record the owner of a group of 256 accounts, the EVC uses a definition of an address prefix. // An address prefix is a part of an address having the first 19 bytes common with any of the 256 account // addresses belonging to the same group. // account/152 -> prefix/152 // To get an address prefix for the account, it's enough to take the account address and right shift it by 8 bits. // Yes, this reduces the security of addresses by 8 bits, but creating multiple addresses in the wallet also reduces // security: if somebody is trying to brute-force one of user's N>1 private keys, they have N times as many chances // of succeeding per guess. It has to be admitted that the EVC model is weaker because finding a private key for // an owner gives access to all accounts, but there is still a very comfortable security margin. // Internal data structure that stores the addressPrefix owner and mode flags struct OwnerStorage { // The addressPrefix owner address owner; // Flag indicating if the addressPrefix is in lockdown mode bool isLockdownMode; // Flag indicating if the permit function is disabled for the addressPrefix bool isPermitDisabledMode; } mapping(bytes19 addressPrefix => OwnerStorage) internal ownerLookup; mapping(bytes19 addressPrefix => mapping(address operator => uint256 operatorBitField)) internal operatorLookup; mapping(bytes19 addressPrefix => mapping(uint256 nonceNamespace => uint256 nonce)) internal nonceLookup; mapping(address account => SetStorage) internal accountCollaterals; mapping(address account => SetStorage) internal accountControllers; /////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR, FALLBACKS // /////////////////////////////////////////////////////////////////////////////////////////////// constructor() { CACHED_CHAIN_ID = block.chainid; CACHED_DOMAIN_SEPARATOR = calculateDomainSeparator(); } /// @notice Fallback function to receive Ether. receive() external payable { // only allows to receive value when checks are deferred if (!executionContext.areChecksDeferred()) { revert EVC_NotAuthorized(); } } /////////////////////////////////////////////////////////////////////////////////////////////// // MODIFIERS // /////////////////////////////////////////////////////////////////////////////////////////////// /// @notice A modifier that allows only the address recorded as an owner of the address prefix to call the function. /// @dev The owner of an address prefix is an address that matches the address that has previously been recorded (or /// will be) as an owner in the ownerLookup. /// @param addressPrefix The address prefix for which it is checked whether the caller is the owner. modifier onlyOwner(bytes19 addressPrefix) { authenticateCaller({addressPrefix: addressPrefix, allowOperator: false, checkLockdownMode: false}); _; } /// @notice A modifier that allows only the owner or an operator of the account to call the function. /// @dev The owner of an address prefix is an address that matches the address that has previously been recorded (or /// will be) as an owner in the ownerLookup. An operator of an account is an address that has been authorized by the /// owner of an account to perform operations on behalf of the owner. /// @param account The address of the account for which it is checked whether the caller is the owner or an /// operator. modifier onlyOwnerOrOperator(address account) { authenticateCaller({account: account, allowOperator: true, checkLockdownMode: true}); _; } /// @notice A modifier checks whether msg.sender is the only controller for the account. /// @dev The controller cannot use permit function in conjunction with this modifier. modifier onlyController(address account) { { uint256 numOfControllers = accountControllers[account].numElements; address controller = accountControllers[account].firstElement; if (numOfControllers != 1) { revert EVC_ControllerViolation(); } if (controller != msg.sender) { revert EVC_NotAuthorized(); } } _; } /// @notice A modifier that verifies whether account or vault status checks are re-entered. modifier nonReentrantChecks() { if (executionContext.areChecksInProgress()) { revert EVC_ChecksReentrancy(); } _; } /// @notice A modifier that verifies whether account or vault status checks are re-entered as well as checks for /// controlCollateral re-entrancy. modifier nonReentrantChecksAndControlCollateral() { { EC context = executionContext; if (context.areChecksInProgress()) { revert EVC_ChecksReentrancy(); } if (context.isControlCollateralInProgress()) { revert EVC_ControlCollateralReentrancy(); } } _; } /// @notice A modifier that verifies whether account or vault status checks are re-entered and sets the lock. /// @dev This modifier also clears the current account on behalf of which the operation is performed as it shouldn't /// be relied upon when the checks are in progress. modifier nonReentrantChecksAcquireLock() { EC contextCache = executionContext; if (contextCache.areChecksInProgress()) { revert EVC_ChecksReentrancy(); } executionContext = contextCache.setChecksInProgress().setOnBehalfOfAccount(address(0)); _; executionContext = contextCache; } /////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS // /////////////////////////////////////////////////////////////////////////////////////////////// // Execution internals /// @inheritdoc IEVC function getRawExecutionContext() external view returns (uint256 context) { context = EC.unwrap(executionContext); } /// @inheritdoc IEVC function getCurrentOnBehalfOfAccount(address controllerToCheck) external view returns (address onBehalfOfAccount, bool controllerEnabled) { onBehalfOfAccount = executionContext.getOnBehalfOfAccount(); // for safety, revert if no account has been authenticated if (onBehalfOfAccount == address(0)) { revert EVC_OnBehalfOfAccountNotAuthenticated(); } controllerEnabled = controllerToCheck == address(0) ? false : accountControllers[onBehalfOfAccount].contains(controllerToCheck); } /// @inheritdoc IEVC function areChecksDeferred() external view returns (bool) { return executionContext.areChecksDeferred(); } /// @inheritdoc IEVC function areChecksInProgress() external view returns (bool) { return executionContext.areChecksInProgress(); } /// @inheritdoc IEVC function isControlCollateralInProgress() external view returns (bool) { return executionContext.isControlCollateralInProgress(); } /// @inheritdoc IEVC function isOperatorAuthenticated() external view returns (bool) { return executionContext.isOperatorAuthenticated(); } /// @inheritdoc IEVC function isSimulationInProgress() external view returns (bool) { return executionContext.isSimulationInProgress(); } // Owners and operators /// @inheritdoc IEVC function haveCommonOwner(address account, address otherAccount) external pure returns (bool) { return haveCommonOwnerInternal(account, otherAccount); } /// @inheritdoc IEVC function getAddressPrefix(address account) external pure returns (bytes19) { return getAddressPrefixInternal(account); } /// @inheritdoc IEVC function getAccountOwner(address account) external view returns (address) { bytes19 addressPrefix = getAddressPrefixInternal(account); return ownerLookup[addressPrefix].owner; } /// @inheritdoc IEVC function isLockdownMode(bytes19 addressPrefix) external view returns (bool) { return ownerLookup[addressPrefix].isLockdownMode; } /// @inheritdoc IEVC function isPermitDisabledMode(bytes19 addressPrefix) external view returns (bool) { return ownerLookup[addressPrefix].isPermitDisabledMode; } /// @inheritdoc IEVC function getNonce(bytes19 addressPrefix, uint256 nonceNamespace) external view returns (uint256) { return nonceLookup[addressPrefix][nonceNamespace]; } /// @inheritdoc IEVC function getOperator(bytes19 addressPrefix, address operator) external view returns (uint256) { return operatorLookup[addressPrefix][operator]; } /// @inheritdoc IEVC function isAccountOperatorAuthorized(address account, address operator) external view returns (bool) { return isAccountOperatorAuthorizedInternal(account, operator); } /// @inheritdoc IEVC function setLockdownMode(bytes19 addressPrefix, bool enabled) public payable virtual onlyOwner(addressPrefix) { if (ownerLookup[addressPrefix].isLockdownMode != enabled) { // to increase user security, it is prohibited to disable this mode within the self-call of the permit // function or within a checks-deferrable call. to disable this mode, the setLockdownMode function must be // called directly if (!enabled && (executionContext.areChecksDeferred() || inPermitSelfCall())) { revert EVC_NotAuthorized(); } ownerLookup[addressPrefix].isLockdownMode = enabled; emit LockdownModeStatus(addressPrefix, enabled); } } /// @inheritdoc IEVC function setPermitDisabledMode( bytes19 addressPrefix, bool enabled ) public payable virtual onlyOwner(addressPrefix) { if (ownerLookup[addressPrefix].isPermitDisabledMode != enabled) { // to increase user security, it is prohibited to disable this mode within the self-call of the permit // function (verified in the permit function) or within a checks-deferrable call. to disable this mode the // setPermitDisabledMode function must be called directly if (!enabled && executionContext.areChecksDeferred()) { revert EVC_NotAuthorized(); } ownerLookup[addressPrefix].isPermitDisabledMode = enabled; emit PermitDisabledModeStatus(addressPrefix, enabled); } } /// @inheritdoc IEVC function setNonce( bytes19 addressPrefix, uint256 nonceNamespace, uint256 nonce ) public payable virtual onlyOwner(addressPrefix) { uint256 currentNonce = nonceLookup[addressPrefix][nonceNamespace]; if (currentNonce >= nonce) { revert EVC_InvalidNonce(); } nonceLookup[addressPrefix][nonceNamespace] = nonce; emit NonceStatus(addressPrefix, nonceNamespace, currentNonce, nonce); } /// @inheritdoc IEVC /// @dev Uses authenticateCaller() function instead of onlyOwner() modifier to authenticate and get the caller /// address at once. function setOperator(bytes19 addressPrefix, address operator, uint256 operatorBitField) public payable virtual { address msgSender = authenticateCaller({addressPrefix: addressPrefix, allowOperator: false, checkLockdownMode: false}); // the operator can neither be the EVC nor can be one of 256 accounts of the owner if (operator == address(this) || haveCommonOwnerInternal(msgSender, operator)) { revert EVC_InvalidAddress(); } if (operatorLookup[addressPrefix][operator] == operatorBitField) { revert EVC_InvalidOperatorStatus(); } else { operatorLookup[addressPrefix][operator] = operatorBitField; emit OperatorStatus(addressPrefix, operator, operatorBitField); } } /// @inheritdoc IEVC /// @dev Uses authenticateCaller() function instead of onlyOwnerOrOperator() modifier to authenticate and get the /// caller address at once. function setAccountOperator(address account, address operator, bool authorized) public payable virtual { address msgSender = authenticateCaller({account: account, allowOperator: true, checkLockdownMode: false}); bytes19 addressPrefix = getAddressPrefixInternal(account); // if the account and the caller have a common owner, the caller must be the owner. if the account and the // caller don't have a common owner, the caller must be an operator and the owner address is taken from the // storage. the caller authentication above guarantees that the account owner is already registered hence // non-zero address owner = haveCommonOwnerInternal(account, msgSender) ? msgSender : ownerLookup[addressPrefix].owner; // if it's an operator calling, it can only act for itself and must not be able to change other operators status if (owner != msgSender && operator != msgSender) { revert EVC_NotAuthorized(); } // the operator can neither be the EVC nor can be one of 256 accounts of the owner if (operator == address(this) || haveCommonOwnerInternal(owner, operator)) { revert EVC_InvalidAddress(); } // The bitMask defines which accounts the operator is authorized for. The bitMask is created from the account // number which is a number up to 2^8 in binary, or 256. 1 << (uint160(owner) ^ uint160(account)) transforms // that number in an 256-position binary array like 0...010...0, marking the account positionally in a uint256. uint256 bitMask = 1 << (uint160(owner) ^ uint160(account)); // The operatorBitField is a 256-position binary array, where each 1 signals by position the account that the // operator is authorized for. uint256 oldOperatorBitField = operatorLookup[addressPrefix][operator]; uint256 newOperatorBitField = authorized ? oldOperatorBitField | bitMask : oldOperatorBitField & ~bitMask; if (oldOperatorBitField == newOperatorBitField) { revert EVC_InvalidOperatorStatus(); } else { operatorLookup[addressPrefix][operator] = newOperatorBitField; emit OperatorStatus(addressPrefix, operator, newOperatorBitField); } } // Collaterals management /// @inheritdoc IEVC function getCollaterals(address account) external view returns (address[] memory) { return accountCollaterals[account].get(); } /// @inheritdoc IEVC function isCollateralEnabled(address account, address vault) external view returns (bool) { return accountCollaterals[account].contains(vault); } /// @inheritdoc IEVC function enableCollateral( address account, address vault ) public payable virtual nonReentrantChecksAndControlCollateral onlyOwnerOrOperator(account) { if (vault == address(this)) revert EVC_InvalidAddress(); if (accountCollaterals[account].insert(vault)) { emit CollateralStatus(account, vault, true); } requireAccountStatusCheck(account); } /// @inheritdoc IEVC function disableCollateral( address account, address vault ) public payable virtual nonReentrantChecksAndControlCollateral onlyOwnerOrOperator(account) { if (accountCollaterals[account].remove(vault)) { emit CollateralStatus(account, vault, false); } requireAccountStatusCheck(account); } /// @inheritdoc IEVC function reorderCollaterals( address account, uint8 index1, uint8 index2 ) public payable virtual nonReentrantChecksAndControlCollateral onlyOwnerOrOperator(account) { accountCollaterals[account].reorder(index1, index2); requireAccountStatusCheck(account); } // Controllers management /// @inheritdoc IEVC function getControllers(address account) external view returns (address[] memory) { return accountControllers[account].get(); } /// @inheritdoc IEVC function isControllerEnabled(address account, address vault) external view returns (bool) { return accountControllers[account].contains(vault); } /// @inheritdoc IEVC function enableController( address account, address vault ) public payable virtual nonReentrantChecksAndControlCollateral onlyOwnerOrOperator(account) { if (vault == address(this)) revert EVC_InvalidAddress(); if (accountControllers[account].insert(vault)) { emit ControllerStatus(account, vault, true); } requireAccountStatusCheck(account); } /// @inheritdoc IEVC function disableController(address account) public payable virtual nonReentrantChecksAndControlCollateral { if (accountControllers[account].remove(msg.sender)) { emit ControllerStatus(account, msg.sender, false); } requireAccountStatusCheck(account); } // Permit /// @inheritdoc IEVC function permit( address signer, address sender, uint256 nonceNamespace, uint256 nonce, uint256 deadline, uint256 value, bytes calldata data, bytes calldata signature ) public payable virtual nonReentrantChecksAndControlCollateral { // cannot be called within the self-call of the permit function; can occur for nested calls. // the permit function can be called only by the specified sender, unless address zero is specified in which // case anyone can call it if (inPermitSelfCall() || (sender != address(0) && sender != msg.sender)) { revert EVC_NotAuthorized(); } if (signer == address(0) || !isSignerValid(signer)) { revert EVC_InvalidAddress(); } bytes19 addressPrefix = getAddressPrefixInternal(signer); if (ownerLookup[addressPrefix].isPermitDisabledMode) { revert EVC_PermitDisabledMode(); } { uint256 currentNonce = nonceLookup[addressPrefix][nonceNamespace]; if (currentNonce == type(uint256).max || currentNonce != nonce) { revert EVC_InvalidNonce(); } } if (deadline < block.timestamp) { revert EVC_InvalidTimestamp(); } if (data.length == 0) { revert EVC_InvalidData(); } bytes32 permitHash = getPermitHash(signer, sender, nonceNamespace, nonce, deadline, value, data); if ( signer != recoverECDSASigner(permitHash, signature) && !isValidERC1271Signature(signer, permitHash, signature) ) { revert EVC_NotAuthorized(); } unchecked { nonceLookup[addressPrefix][nonceNamespace] = nonce + 1; } emit NonceUsed(addressPrefix, nonceNamespace, nonce); // EVC address becomes the msg.sender for the duration this self-call, no authentication is required here. // the signer will be later on authenticated as per data, depending on the functions that will be called (bool success, bytes memory result) = callWithContextInternal(address(this), signer, value, data); if (!success) revertBytes(result); } // Calls forwarding /// @inheritdoc IEVC function call( address targetContract, address onBehalfOfAccount, uint256 value, bytes calldata data ) public payable virtual nonReentrantChecksAndControlCollateral returns (bytes memory result) { EC contextCache = executionContext; executionContext = contextCache.setChecksDeferred(); bool success; (success, result) = callWithAuthenticationInternal(targetContract, onBehalfOfAccount, value, data); if (!success) revertBytes(result); restoreExecutionContext(contextCache); } /// @inheritdoc IEVC function controlCollateral( address targetCollateral, address onBehalfOfAccount, uint256 value, bytes calldata data ) public payable virtual nonReentrantChecksAndControlCollateral onlyController(onBehalfOfAccount) returns (bytes memory result) { if (!accountCollaterals[onBehalfOfAccount].contains(targetCollateral)) { revert EVC_NotAuthorized(); } EC contextCache = executionContext; executionContext = contextCache.setChecksDeferred().setControlCollateralInProgress(); bool success; (success, result) = callWithContextInternal(targetCollateral, onBehalfOfAccount, value, data); if (!success) revertBytes(result); restoreExecutionContext(contextCache); } /// @inheritdoc IEVC function batch(BatchItem[] calldata items) public payable virtual nonReentrantChecksAndControlCollateral { EC contextCache = executionContext; executionContext = contextCache.setChecksDeferred(); uint256 length = items.length; for (uint256 i; i < length; ++i) { BatchItem calldata item = items[i]; (bool success, bytes memory result) = callWithAuthenticationInternal(item.targetContract, item.onBehalfOfAccount, item.value, item.data); if (!success) revertBytes(result); } restoreExecutionContext(contextCache); } // Simulations /// @inheritdoc IEVC function batchRevert(BatchItem[] calldata items) public payable virtual nonReentrantChecksAndControlCollateral { BatchItemResult[] memory batchItemsResult; StatusCheckResult[] memory accountsStatusCheckResult; StatusCheckResult[] memory vaultsStatusCheckResult; EC contextCache = executionContext; if (contextCache.areChecksDeferred()) { revert EVC_SimulationBatchNested(); } executionContext = contextCache.setChecksDeferred().setSimulationInProgress(); uint256 length = items.length; batchItemsResult = new BatchItemResult[](length); for (uint256 i; i < length; ++i) { BatchItem calldata item = items[i]; (batchItemsResult[i].success, batchItemsResult[i].result) = callWithAuthenticationInternal(item.targetContract, item.onBehalfOfAccount, item.value, item.data); } executionContext = contextCache.setChecksInProgress().setOnBehalfOfAccount(address(0)); accountsStatusCheckResult = checkStatusAllWithResult(SetType.Account); vaultsStatusCheckResult = checkStatusAllWithResult(SetType.Vault); executionContext = contextCache; revert EVC_RevertedBatchResult(batchItemsResult, accountsStatusCheckResult, vaultsStatusCheckResult); } /// @inheritdoc IEVC function batchSimulation(BatchItem[] calldata items) external payable virtual returns ( BatchItemResult[] memory batchItemsResult, StatusCheckResult[] memory accountsStatusCheckResult, StatusCheckResult[] memory vaultsStatusCheckResult ) { (bool success, bytes memory result) = address(this).delegatecall(abi.encodeCall(this.batchRevert, items)); if (success) { revert EVC_BatchPanic(); } else if (result.length < 4 || bytes4(result) != EVC_RevertedBatchResult.selector) { revertBytes(result); } assembly { let length := mload(result) // skip 4-byte EVC_RevertedBatchResult selector result := add(result, 4) // write new array length = original length - 4-byte selector // cannot underflow as we require result.length >= 4 above mstore(result, sub(length, 4)) } (batchItemsResult, accountsStatusCheckResult, vaultsStatusCheckResult) = abi.decode(result, (BatchItemResult[], StatusCheckResult[], StatusCheckResult[])); } // Account Status Check /// @inheritdoc IEVC function getLastAccountStatusCheckTimestamp(address account) external view nonReentrantChecks returns (uint256) { return accountControllers[account].getMetadata(); } /// @inheritdoc IEVC function isAccountStatusCheckDeferred(address account) external view nonReentrantChecks returns (bool) { return accountStatusChecks.contains(account); } /// @inheritdoc IEVC function requireAccountStatusCheck(address account) public payable virtual { if (executionContext.areChecksDeferred()) { accountStatusChecks.insert(account); } else { requireAccountStatusCheckInternalNonReentrantChecks(account); } } /// @inheritdoc IEVC function forgiveAccountStatusCheck(address account) public payable virtual nonReentrantChecksAcquireLock onlyController(account) { accountStatusChecks.remove(account); } // Vault Status Check /// @inheritdoc IEVC function isVaultStatusCheckDeferred(address vault) external view nonReentrantChecks returns (bool) { return vaultStatusChecks.contains(vault); } /// @inheritdoc IEVC function requireVaultStatusCheck() public payable virtual { if (executionContext.areChecksDeferred()) { vaultStatusChecks.insert(msg.sender); } else { requireVaultStatusCheckInternalNonReentrantChecks(msg.sender); } } /// @inheritdoc IEVC function forgiveVaultStatusCheck() public payable virtual nonReentrantChecksAcquireLock { vaultStatusChecks.remove(msg.sender); } /// @inheritdoc IEVC function requireAccountAndVaultStatusCheck(address account) public payable virtual { if (executionContext.areChecksDeferred()) { accountStatusChecks.insert(account); vaultStatusChecks.insert(msg.sender); } else { requireAccountStatusCheckInternalNonReentrantChecks(account); requireVaultStatusCheckInternalNonReentrantChecks(msg.sender); } } /////////////////////////////////////////////////////////////////////////////////////////////// // INTERNAL FUNCTIONS // /////////////////////////////////////////////////////////////////////////////////////////////// /// @notice Authenticates the caller of a function. /// @dev This function checks if the caller is the owner or an authorized operator of the account, and if the /// account is not in lockdown mode. /// @param account The account address to authenticate the caller against. /// @param allowOperator A boolean indicating if operators are allowed to authenticate as the caller. /// @param checkLockdownMode A boolean indicating if the function should check for lockdown mode on the account. /// @return The address of the authenticated caller. function authenticateCaller( address account, bool allowOperator, bool checkLockdownMode ) internal virtual returns (address) { bytes19 addressPrefix = getAddressPrefixInternal(account); address owner = ownerLookup[addressPrefix].owner; bool lockdownMode = ownerLookup[addressPrefix].isLockdownMode; address msgSender = _msgSender(); bool authenticated = false; // check if the caller is the owner of the account if (haveCommonOwnerInternal(account, msgSender)) { // if the owner is not registered, register it if (owner == address(0)) { ownerLookup[addressPrefix].owner = owner = msgSender; emit OwnerRegistered(addressPrefix, msgSender); authenticated = true; } else if (owner == msgSender) { authenticated = true; } } // if the caller is not the owner, check if it is an operator if operators are allowed if (!authenticated && allowOperator && isAccountOperatorAuthorizedInternal(account, msgSender)) { authenticated = true; } // if the authenticated account is non-owner, prevent its account from being a smart contract if (authenticated && owner != account && account.code.length != 0) { authenticated = false; } // must revert if neither the owner nor the operator were authenticated if (!authenticated) { revert EVC_NotAuthorized(); } // revert if the account is in lockdown mode unless the lockdown mode is not being checked if (checkLockdownMode && lockdownMode) { revert EVC_LockdownMode(); } return msgSender; } /// @notice Authenticates the caller of a function. /// @dev This function either passes the address prefix owner address, if the address prefix owner is already /// registered, or converts the bytes19 address prefix into an account address which will belong to the owner when /// it's finally registered. /// @param addressPrefix The bytes19 address prefix to authenticate the caller against. /// @param allowOperator A boolean indicating if operators are allowed to authenticate as the caller. /// @param checkLockdownMode A boolean indicating if the function should check for lockdown mode on the account. /// @return The address of the authenticated caller. function authenticateCaller( bytes19 addressPrefix, bool allowOperator, bool checkLockdownMode ) internal virtual returns (address) { address owner = ownerLookup[addressPrefix].owner; return authenticateCaller({ account: owner == address(0) ? address(uint160(uint152(addressPrefix)) << ACCOUNT_ID_OFFSET) : owner, allowOperator: allowOperator, checkLockdownMode: checkLockdownMode }); } /// @notice Internal function to make a call to a target contract with a specific context. /// @dev This function sets the execution context for the duration of the call. /// @param targetContract The contract address to call. /// @param onBehalfOfAccount The account address on behalf of which the call is made. /// @param value The amount of value to send with the call. /// @param data The calldata to send with the call. function callWithContextInternal( address targetContract, address onBehalfOfAccount, uint256 value, bytes calldata data ) internal virtual returns (bool success, bytes memory result) { if (value == type(uint256).max) { value = address(this).balance; } else if (value > address(this).balance) { revert EVC_InvalidValue(); } EC contextCache = executionContext; address msgSender = _msgSender(); // set the onBehalfOfAccount in the execution context for the duration of the external call. // considering that the operatorAuthenticated is only meant to be observable by external // contracts, it is sufficient to set it here rather than in the authentication function. // apart from the usual scenario (when an owner operates on behalf of its account), // the operatorAuthenticated should be cleared when about to execute the permit self-call, when // target contract is equal to the msg.sender in call() and batch(), or when the controlCollateral is in // progress (in which case the operatorAuthenticated is not relevant) if ( haveCommonOwnerInternal(onBehalfOfAccount, msgSender) || targetContract == msg.sender || targetContract == address(this) || contextCache.isControlCollateralInProgress() ) { executionContext = contextCache.setOnBehalfOfAccount(onBehalfOfAccount).clearOperatorAuthenticated(); } else { executionContext = contextCache.setOnBehalfOfAccount(onBehalfOfAccount).setOperatorAuthenticated(); } emit CallWithContext( msgSender, getAddressPrefixInternal(onBehalfOfAccount), onBehalfOfAccount, targetContract, bytes4(data) ); (success, result) = targetContract.call{value: value}(data); executionContext = contextCache; } /// @notice Internal function to call a target contract with necessary authentication. /// @dev This function decides whether to use delegatecall or a regular call based on the target contract. /// If the target contract is this contract, it uses delegatecall to preserve msg.sender for authentication. /// Otherwise, it authenticates the caller if needed and proceeds with a regular call. /// @param targetContract The contract address to call. /// @param onBehalfOfAccount The account address on behalf of which the call is made. /// @param value The amount of value to send with the call. /// @param data The calldata to send with the call. /// @return success A boolean indicating if the call was successful. /// @return result The bytes returned from the call. function callWithAuthenticationInternal( address targetContract, address onBehalfOfAccount, uint256 value, bytes calldata data ) internal virtual returns (bool success, bytes memory result) { if (targetContract == address(this)) { if (onBehalfOfAccount != address(0)) { revert EVC_InvalidAddress(); } if (value != 0) { revert EVC_InvalidValue(); } // delegatecall is used here to preserve msg.sender in order to be able to perform authentication (success, result) = address(this).delegatecall(data); } else { // when the target contract is equal to the msg.sender, both in call() and batch(), authentication is not // required if (targetContract != msg.sender) { authenticateCaller({account: onBehalfOfAccount, allowOperator: true, checkLockdownMode: true}); } (success, result) = callWithContextInternal(targetContract, onBehalfOfAccount, value, data); } } /// @notice Restores the execution context from a cached state. /// @dev This function restores the execution context to a previously cached state, performing necessary status /// checks if they are no longer deferred. If checks are no longer deferred, it sets the execution context to /// indicate checks are in progress and clears the 'on behalf of' account. It then performs status checks for both /// accounts and vaults before restoring the execution context to the cached state. /// @param contextCache The cached execution context to restore from. function restoreExecutionContext(EC contextCache) internal virtual { if (!contextCache.areChecksDeferred()) { executionContext = contextCache.setChecksInProgress().setOnBehalfOfAccount(address(0)); checkStatusAll(SetType.Account); checkStatusAll(SetType.Vault); } executionContext = contextCache; } /// @notice Checks the status of an account internally. /// @dev This function first checks the number of controllers for the account. If there are no controllers enabled, /// it returns true immediately, indicating the account status is valid without further checks. If there is more /// than one controller, it reverts with an EVC_ControllerViolation error. For a single controller, it proceeds to /// call the controller to check the account status. /// @param account The account address to check the status for. /// @return isValid A boolean indicating if the account status is valid. /// @return result The bytes returned from the controller call, indicating the account status. function checkAccountStatusInternal(address account) internal virtual returns (bool isValid, bytes memory result) { SetStorage storage accountControllersStorage = accountControllers[account]; uint256 numOfControllers = accountControllersStorage.numElements; address controller = accountControllersStorage.firstElement; uint8 stamp = accountControllersStorage.stamp; if (numOfControllers == 0) return (true, ""); else if (numOfControllers > 1) return (false, abi.encodeWithSelector(EVC_ControllerViolation.selector)); bool success; (success, result) = controller.staticcall( abi.encodeCall(IVault.checkAccountStatus, (account, accountCollaterals[account].get())) ); isValid = success && result.length == 32 && abi.decode(result, (bytes32)) == bytes32(IVault.checkAccountStatus.selector); if (isValid) { accountControllersStorage.numElements = uint8(numOfControllers); accountControllersStorage.firstElement = controller; accountControllersStorage.metadata = uint80(block.timestamp); accountControllersStorage.stamp = stamp; } emit AccountStatusCheck(account, controller); } function requireAccountStatusCheckInternal(address account) internal virtual { (bool isValid, bytes memory result) = checkAccountStatusInternal(account); if (!isValid) { revertBytes(result); } } function requireAccountStatusCheckInternalNonReentrantChecks(address account) internal virtual nonReentrantChecksAcquireLock { requireAccountStatusCheckInternal(account); } /// @notice Checks the status of a vault internally. /// @dev This function makes an external call to the vault to check its status. /// @param vault The address of the vault to check the status for. /// @return isValid A boolean indicating if the vault status is valid. /// @return result The bytes returned from the vault call, indicating the vault status. function checkVaultStatusInternal(address vault) internal virtual returns (bool isValid, bytes memory result) { bool success; (success, result) = vault.call(abi.encodeCall(IVault.checkVaultStatus, ())); isValid = success && result.length == 32 && abi.decode(result, (bytes32)) == bytes32(IVault.checkVaultStatus.selector); emit VaultStatusCheck(vault); } function requireVaultStatusCheckInternal(address vault) internal virtual { (bool isValid, bytes memory result) = checkVaultStatusInternal(vault); if (!isValid) { revertBytes(result); } } function requireVaultStatusCheckInternalNonReentrantChecks(address vault) internal virtual nonReentrantChecksAcquireLock { requireVaultStatusCheckInternal(vault); } /// @notice Checks the status of all entities in a set, either accounts or vaults, and clears the checks. /// @dev Iterates over either accountStatusChecks or vaultStatusChecks based on the setType and performs status /// checks. /// Clears the checks while performing them. /// @param setType The type of set to perform the status checks on, either accounts or vaults. function checkStatusAll(SetType setType) internal virtual { setType == SetType.Account ? accountStatusChecks.forEachAndClear(requireAccountStatusCheckInternal) : vaultStatusChecks.forEachAndClear(requireVaultStatusCheckInternal); } function checkStatusAllWithResult(SetType setType) internal virtual returns (StatusCheckResult[] memory checksResult) { bytes[] memory callbackResult = setType == SetType.Account ? accountStatusChecks.forEachAndClearWithResult(checkAccountStatusInternal) : vaultStatusChecks.forEachAndClearWithResult(checkVaultStatusInternal); uint256 length = callbackResult.length; checksResult = new StatusCheckResult[](length); for (uint256 i; i < length; ++i) { (address checkedAddress, bool isValid, bytes memory result) = abi.decode(callbackResult[i], (address, bool, bytes)); checksResult[i] = StatusCheckResult({checkedAddress: checkedAddress, isValid: isValid, result: result}); } } // Permit-related functions /// @notice Determines if the signer address is valid. /// @dev It's important to revisit this logic when deploying on chains other than the Ethereum mainnet. If new /// precompiles had been added to the Ethereum mainnet, the current implementation of the function would not be /// future-proof and would need to be updated. /// @param signer The address of the signer to validate. /// @return bool Returns true if the signer is valid, false otherwise. function isSignerValid(address signer) internal pure virtual returns (bool) { // not valid if the signer address falls into any of the precompiles/predeploys // addresses space (depends on the chain ID). return !haveCommonOwnerInternal(signer, address(0)) && !haveCommonOwnerInternal(signer, EIP_7587_PRECOMPILES) && !haveCommonOwnerInternal(signer, COMMON_PREDEPLOYS); } /// @notice Computes the permit hash for a given set of parameters. /// @dev This function generates a permit hash using EIP712 typed data signing. /// @param signer The address of the signer. /// @param nonceNamespace The namespace of the nonce. /// @param nonce The nonce value, ensuring permits are used once. /// @param deadline The time until when the permit is valid. /// @param value The value associated with the permit. /// @param data Calldata associated with the permit. /// @return permitHash The computed permit hash. function getPermitHash( address signer, address sender, uint256 nonceNamespace, uint256 nonce, uint256 deadline, uint256 value, bytes calldata data ) internal view returns (bytes32 permitHash) { bytes32 domainSeparator = block.chainid == CACHED_CHAIN_ID ? CACHED_DOMAIN_SEPARATOR : calculateDomainSeparator(); bytes32 structHash = keccak256( abi.encode(PERMIT_TYPEHASH, signer, sender, nonceNamespace, nonce, deadline, value, keccak256(data)) ); // This code overwrites the two most significant bytes of the free memory pointer, // and restores them to 0 after assembly ("memory-safe") { mstore(0x00, "\x19\x01") mstore(0x02, domainSeparator) mstore(0x22, structHash) permitHash := keccak256(0x00, 0x42) mstore(0x22, 0) } } /// @notice Recovers the signer address from a hash and a signature. /// Based on: /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol /// Note that the function returns zero address if the signature is invalid hence the result always has to be /// checked against address zero. /// @param hash The hash of the signed data. /// @param signature The signature to recover the signer from. /// @return signer The address of the signer, or the zero address if signature recovery fails. function recoverECDSASigner(bytes32 hash, bytes memory signature) internal pure returns (address signer) { if (signature.length != 65) return address(0); bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } // return the signer address (note that it might be zero address) signer = ecrecover(hash, v, r, s); } /// @notice Checks if a given signature is valid according to ERC-1271 standard. /// @dev This function is based on the implementation found in OpenZeppelin's SignatureChecker. /// See: /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol /// It performs a static call to the signer's address with the signature data and checks if the returned value /// matches the expected valid signature selector. /// @param signer The address of the signer to validate the signature against. /// @param hash The hash of the data that was signed. /// @param signature The signature to validate. /// @return isValid True if the signature is valid according to ERC-1271, false otherwise. function isValidERC1271Signature( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool isValid) { if (signer.code.length == 0) return false; (bool success, bytes memory result) = signer.staticcall(abi.encodeCall(IERC1271.isValidSignature, (hash, signature))); isValid = success && result.length == 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector); } /// @notice Calculates the EIP-712 domain separator for the contract. /// @return The calculated EIP-712 domain separator as a bytes32 value. function calculateDomainSeparator() internal view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, HASHED_NAME, block.chainid, address(this))); } // Auxiliary functions /// @notice Returns the message sender's address. /// @dev In the context of a permit self-call, it returns the account on behalf of which the call is made. /// Otherwise, it returns `msg.sender`. /// @return The address of the message sender or the account on behalf of which the call is made. function _msgSender() internal view virtual returns (address) { return inPermitSelfCall() ? executionContext.getOnBehalfOfAccount() : msg.sender; } /// @notice Checks if the contract is in the context of a permit self-call. /// @dev EVC can only be `msg.sender` during the self-call in the permit function. /// @return True if the current call is a self-call within the permit function, false otherwise. function inPermitSelfCall() internal view returns (bool) { return address(this) == msg.sender; } /// @notice Determines if two accounts have a common owner by comparing their address prefixes. /// @param account The first account address to compare. /// @param otherAccount The second account address to compare. /// @return result True if the accounts have a common owner, false otherwise. function haveCommonOwnerInternal(address account, address otherAccount) internal pure returns (bool result) { assembly { result := lt(xor(account, otherAccount), 0x100) } } /// @notice Computes the address prefix for a given account address. /// @dev The address prefix is derived by right-shifting the account address by 8 bits which effectively reduces the /// address size to 19 bytes. /// @param account The account address to compute the prefix for. /// @return The computed address prefix as a bytes19 value. function getAddressPrefixInternal(address account) internal pure returns (bytes19) { return bytes19(uint152(uint160(account) >> ACCOUNT_ID_OFFSET)); } /// @notice Checks if an operator is authorized for a specific account. /// @dev Determines operator authorization by checking if the operator's bit is set in the operator's bit field for /// the account's address prefix. If the owner is not registered (address(0)), it implies the operator cannot be /// authorized, hence returns false. The bitMask is calculated by shifting 1 left by the XOR of the owner's and /// account's address, effectively checking the operator's authorization for the specific account. /// @param account The account address to check the operator authorization for. /// @param operator The operator address to check authorization status. /// @return isAuthorized True if the operator is authorized for the account, false otherwise. function isAccountOperatorAuthorizedInternal( address account, address operator ) internal view returns (bool isAuthorized) { bytes19 addressPrefix = getAddressPrefixInternal(account); address owner = ownerLookup[addressPrefix].owner; // if the owner is not registered yet, it means that the operator couldn't have been authorized if (owner == address(0)) return false; // The bitMask defines which accounts the operator is authorized for. The bitMask is created from the account // number which is a number up to 2^8 in binary, or 256. 1 << (uint160(owner) ^ uint160(account)) transforms // that number in an 256-position binary array like 0...010...0, marking the account positionally in a uint256. uint256 bitMask = 1 << (uint160(owner) ^ uint160(account)); return operatorLookup[addressPrefix][operator] & bitMask != 0; } /// @notice Reverts the transaction with a custom error message if provided, otherwise reverts with a generic empty /// error. /// @param errMsg The custom error message to revert the transaction with. function revertBytes(bytes memory errMsg) internal pure { if (errMsg.length != 0) { assembly { revert(add(32, errMsg), mload(errMsg)) } } revert EVC_EmptyError(); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @dev Represents the maximum number of elements that can be stored in the set. /// Must not exceed 255 due to the uint8 data type limit. uint8 constant SET_MAX_ELEMENTS = 10; /// @title ElementStorage /// @notice This struct is used to store the value and stamp of an element. /// @dev The stamp field is used to keep the storage slot non-zero when the element is removed. /// @dev It allows for cheaper SSTORE when an element is inserted. struct ElementStorage { /// @notice The value of the element. address value; /// @notice The stamp of the element. uint96 stamp; } /// @title SetStorage /// @notice This struct is used to store the set data. /// @dev To optimize the gas consumption, firstElement is stored in the same storage slot as the numElements /// @dev so that for sets with one element, only one storage slot has to be read/written. To keep the elements /// @dev array indexing consistent and because the first element is stored outside of the array, the elements[0] /// @dev is not utilized. The stamp field is used to keep the storage slot non-zero when the element is removed. /// @dev It allows for cheaper SSTORE when an element is inserted. struct SetStorage { /// @notice The number of elements in the set. uint8 numElements; /// @notice The first element in the set. address firstElement; /// @notice The metadata of the set. uint80 metadata; /// @notice The stamp of the set. uint8 stamp; /// @notice The array of elements in the set. Stores the elements starting from index 1. ElementStorage[SET_MAX_ELEMENTS] elements; } /// @title Set /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This library provides functions for managing sets of addresses. /// @dev The maximum number of elements in the set is defined by the constant SET_MAX_ELEMENTS. library Set { error TooManyElements(); error InvalidIndex(); uint8 internal constant EMPTY_ELEMENT_OFFSET = 1; // must be 1 uint8 internal constant DUMMY_STAMP = 1; /// @notice Initializes the set by setting the stamp field of the SetStorage and the stamp field of elements to /// DUMMY_STAMP. /// @dev The stamp field is used to keep the storage slot non-zero when the element is removed. It allows for /// cheaper SSTORE when an element is inserted. /// @param setStorage The set storage whose stamp fields will be initialized. function initialize(SetStorage storage setStorage) internal { setStorage.stamp = DUMMY_STAMP; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < SET_MAX_ELEMENTS; ++i) { setStorage.elements[i].stamp = DUMMY_STAMP; } } /// @notice Inserts an element and returns information whether the element was inserted or not. /// @dev Reverts if the set is full but the element is not in the set storage. /// @param setStorage The set storage to which the element will be inserted. /// @param element The element to be inserted. /// @return A boolean value that indicates whether the element was inserted or not. If the element was already in /// the set storage, it returns false. function insert(SetStorage storage setStorage, address element) internal returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; uint80 metadata = setStorage.metadata; if (numElements == 0) { // gas optimization: // on the first element insertion, set the stamp to non-zero value to keep the storage slot non-zero when // the element is removed. when a new element is inserted after the removal, it should be cheaper setStorage.numElements = 1; setStorage.firstElement = element; setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; return true; } if (firstElement == element) return false; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { if (setStorage.elements[i].value == element) return false; } if (numElements == SET_MAX_ELEMENTS) revert TooManyElements(); setStorage.elements[numElements].value = element; unchecked { setStorage.numElements = uint8(numElements + 1); } return true; } /// @notice Removes an element and returns information whether the element was removed or not. /// @dev This operation may affect the order of elements in the array of elements obtained using get() function. This /// function does not modify the metadata of the set, even if it becomes empty as a result of invoking this /// function. /// @param setStorage The set storage from which the element will be removed. /// @param element The element to be removed. /// @return A boolean value that indicates whether the element was removed or not. If the element was not in the set /// storage, it returns false. function remove(SetStorage storage setStorage, address element) internal returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; uint80 metadata = setStorage.metadata; if (numElements == 0) return false; uint256 searchIndex; if (firstElement != element) { for (searchIndex = EMPTY_ELEMENT_OFFSET; searchIndex < numElements; ++searchIndex) { if (setStorage.elements[searchIndex].value == element) break; } if (searchIndex == numElements) return false; } // write full slot at once to avoid SLOAD and bit masking if (numElements == 1) { setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; return true; } uint256 lastIndex; unchecked { lastIndex = numElements - 1; } // set numElements for every execution path to avoid SSTORE and bit masking when the element removed is // firstElement ElementStorage storage lastElement = setStorage.elements[lastIndex]; if (searchIndex != lastIndex) { if (searchIndex == 0) { setStorage.firstElement = lastElement.value; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } else { setStorage.elements[searchIndex].value = lastElement.value; setStorage.firstElement = firstElement; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } } else { setStorage.firstElement = firstElement; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } lastElement.value = address(0); return true; } /// @notice Swaps the position of two elements so that they appear switched in the array of elements obtained using /// get() function. /// @dev The first index must not be greater than or equal to the second index. Indices must not be out of bounds. /// The function will revert if the indices are invalid. /// @param setStorage The set storage for which the elements will be swapped. /// @param index1 The index of the first element to be swapped. /// @param index2 The index of the second element to be swapped. function reorder(SetStorage storage setStorage, uint8 index1, uint8 index2) internal { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; if (index1 >= index2 || index2 >= numElements) { revert InvalidIndex(); } if (index1 == 0) { (setStorage.firstElement, setStorage.elements[index2].value) = (setStorage.elements[index2].value, firstElement); } else { (setStorage.elements[index1].value, setStorage.elements[index2].value) = (setStorage.elements[index2].value, setStorage.elements[index1].value); } } /// @notice Sets the metadata for the set storage. /// @param setStorage The storage structure where metadata will be set. /// @param metadata The metadata value to set. function setMetadata(SetStorage storage setStorage, uint80 metadata) internal { setStorage.metadata = metadata; } /// @notice Returns an array of elements contained in the storage. /// @dev The order of the elements in the array may be affected by performing operations on the set. /// @param setStorage The set storage to be processed. /// @return An array that contains the same elements as the set storage. function get(SetStorage storage setStorage) internal view returns (address[] memory) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; address[] memory output = new address[](numElements); if (numElements == 0) return output; output[0] = firstElement; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { output[i] = setStorage.elements[i].value; } return output; } /// @notice Retrieves the metadata from the set storage. /// @param setStorage The storage structure from which metadata is retrieved. /// @return The metadata value. function getMetadata(SetStorage storage setStorage) internal view returns (uint80) { return setStorage.metadata; } /// @notice Checks if the set storage contains a given element and returns a boolean value that indicates the /// result. /// @param setStorage The set storage to be searched. /// @param element The element to be searched for. /// @return A boolean value that indicates whether the set storage includes the element or not. function contains(SetStorage storage setStorage, address element) internal view returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; if (numElements == 0) return false; if (firstElement == element) return true; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { if (setStorage.elements[i].value == element) return true; } return false; } /// @notice Iterates over each element in the set and applies the callback function to it. /// @dev The set is cleared as a result of this call. Considering that this function does not follow the /// Checks-Effects-Interactions pattern, the function using it must prevent re-entrancy. This function does not /// modify the metadata of the set. /// @param setStorage The set storage to be processed. /// @param callback The function to be applied to each element. function forEachAndClear(SetStorage storage setStorage, function(address) callback) internal { uint256 numElements = setStorage.numElements; address firstElement = setStorage.firstElement; uint80 metadata = setStorage.metadata; if (numElements == 0) return; setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; callback(firstElement); for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { address element = setStorage.elements[i].value; setStorage.elements[i] = ElementStorage({value: address(0), stamp: DUMMY_STAMP}); callback(element); } } /// @notice Iterates over each element in the set and applies the callback function to it, returning the array of /// callback results. /// @dev The set is cleared as a result of this call. Considering that this function does not follow the /// Checks-Effects-Interactions pattern, the function using it must prevent re-entrancy. This function does not /// modify the metadata of the set. /// @param setStorage The set storage to be processed. /// @param callback The function to be applied to each element. /// @return result An array of encoded bytes that are the addresses passed to the callback function and results of /// calling it. function forEachAndClearWithResult( SetStorage storage setStorage, function(address) returns (bool, bytes memory) callback ) internal returns (bytes[] memory) { uint256 numElements = setStorage.numElements; address firstElement = setStorage.firstElement; uint80 metadata = setStorage.metadata; bytes[] memory results = new bytes[](numElements); if (numElements == 0) return results; setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; (bool success, bytes memory result) = callback(firstElement); results[0] = abi.encode(firstElement, success, result); for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { address element = setStorage.elements[i].value; setStorage.elements[i] = ElementStorage({value: address(0), stamp: DUMMY_STAMP}); (success, result) = callback(element); results[i] = abi.encode(element, success, result); } return results; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title Events /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract implements the events for the Ethereum Vault Connector. contract Events { /// @notice Emitted when an owner is registered for an address prefix. /// @param addressPrefix The address prefix for which the owner is registered. /// @param owner The address of the owner registered. event OwnerRegistered(bytes19 indexed addressPrefix, address indexed owner); /// @notice Emitted when the lockdown mode status is changed for an address prefix. /// @param addressPrefix The address prefix for which the lockdown mode status is changed. /// @param enabled True if the lockdown mode is enabled, false otherwise. event LockdownModeStatus(bytes19 indexed addressPrefix, bool enabled); /// @notice Emitted when the permit disabled mode status is changed for an address prefix. /// @param addressPrefix The address prefix for which the permit disabled mode status is changed. /// @param enabled True if the permit disabled mode is enabled, false otherwise. event PermitDisabledModeStatus(bytes19 indexed addressPrefix, bool enabled); /// @notice Emitted when the nonce status is updated for a given address prefix and nonce namespace. /// @param addressPrefix The prefix of the address for which the nonce status is updated. /// @param nonceNamespace The namespace of the nonce being updated. /// @param oldNonce The previous nonce value before the update. /// @param newNonce The new nonce value after the update. event NonceStatus( bytes19 indexed addressPrefix, uint256 indexed nonceNamespace, uint256 oldNonce, uint256 newNonce ); /// @notice Emitted when a nonce is used for an address prefix and nonce namespace as part of permit execution. /// @param addressPrefix The address prefix for which the nonce is used. /// @param nonceNamespace The namespace of the nonce used. /// @param nonce The nonce that was used. event NonceUsed(bytes19 indexed addressPrefix, uint256 indexed nonceNamespace, uint256 nonce); /// @notice Emitted when the operator status is changed for an address prefix. /// @param addressPrefix The address prefix for which the operator status is changed. /// @param operator The address of the operator. /// @param accountOperatorAuthorized The new authorization bitfield of the operator. event OperatorStatus(bytes19 indexed addressPrefix, address indexed operator, uint256 accountOperatorAuthorized); /// @notice Emitted when the collateral status is changed for an account. /// @param account The account for which the collateral status is changed. /// @param collateral The address of the collateral. /// @param enabled True if the collateral is enabled, false otherwise. event CollateralStatus(address indexed account, address indexed collateral, bool enabled); /// @notice Emitted when the controller status is changed for an account. /// @param account The account for which the controller status is changed. /// @param controller The address of the controller. /// @param enabled True if the controller is enabled, false otherwise. event ControllerStatus(address indexed account, address indexed controller, bool enabled); /// @notice Emitted when an external call is made through the EVC. /// @param caller The address of the caller. /// @param onBehalfOfAddressPrefix The address prefix of the account on behalf of which the call is made. /// @param onBehalfOfAccount The account on behalf of which the call is made. /// @param targetContract The target contract of the call. /// @param selector The selector of the function called on the target contract. event CallWithContext( address indexed caller, bytes19 indexed onBehalfOfAddressPrefix, address onBehalfOfAccount, address indexed targetContract, bytes4 selector ); /// @notice Emitted when an account status check is performed. /// @param account The account for which the status check is performed. /// @param controller The controller performing the status check. event AccountStatusCheck(address indexed account, address indexed controller); /// @notice Emitted when a vault status check is performed. /// @param vault The vault for which the status check is performed. event VaultStatusCheck(address indexed vault); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {IEVC} from "./interfaces/IEthereumVaultConnector.sol"; /// @title Errors /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract implements the error messages for the Ethereum Vault Connector. contract Errors { /// @notice Error for when caller is not authorized to perform an operation. error EVC_NotAuthorized(); /// @notice Error for when no account has been authenticated to act on behalf of. error EVC_OnBehalfOfAccountNotAuthenticated(); /// @notice Error for when an operator's to be set is no different from the current one. error EVC_InvalidOperatorStatus(); /// @notice Error for when a nonce is invalid or already used. error EVC_InvalidNonce(); /// @notice Error for when an address parameter passed is invalid. error EVC_InvalidAddress(); /// @notice Error for when a timestamp parameter passed is expired. error EVC_InvalidTimestamp(); /// @notice Error for when a value parameter passed is invalid or exceeds current balance. error EVC_InvalidValue(); /// @notice Error for when data parameter passed is empty. error EVC_InvalidData(); /// @notice Error for when an action is prohibited due to the lockdown mode. error EVC_LockdownMode(); /// @notice Error for when permit execution is prohibited due to the permit disabled mode. error EVC_PermitDisabledMode(); /// @notice Error for when checks are in progress and reentrancy is not allowed. error EVC_ChecksReentrancy(); /// @notice Error for when control collateral is in progress and reentrancy is not allowed. error EVC_ControlCollateralReentrancy(); /// @notice Error for when there is a different number of controllers enabled than expected. error EVC_ControllerViolation(); /// @notice Error for when a simulation batch is nested within another simulation batch. error EVC_SimulationBatchNested(); /// @notice Auxiliary error to pass simulation batch results. error EVC_RevertedBatchResult( IEVC.BatchItemResult[] batchItemsResult, IEVC.StatusCheckResult[] accountsStatusResult, IEVC.StatusCheckResult[] vaultsStatusResult ); /// @notice Panic error for when simulation does not behave as expected. Should never be observed. error EVC_BatchPanic(); /// @notice Error for when an empty or undefined error is thrown. error EVC_EmptyError(); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; type EC is uint256; /// @title ExecutionContext /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This library provides functions for managing the execution context in the Ethereum Vault Connector. /// @dev The execution context is a bit field that stores the following information: /// @dev - on behalf of account - an account on behalf of which the currently executed operation is being performed /// @dev - checks deferred flag - used to indicate whether checks are deferred /// @dev - checks in progress flag - used to indicate that the account/vault status checks are in progress. This flag is /// used to prevent re-entrancy. /// @dev - control collateral in progress flag - used to indicate that the control collateral is in progress. This flag /// is used to prevent re-entrancy. /// @dev - operator authenticated flag - used to indicate that the currently executed operation is being performed by /// the account operator /// @dev - simulation flag - used to indicate that the currently executed batch call is a simulation /// @dev - stamp - dummy value for optimization purposes library ExecutionContext { uint256 internal constant ON_BEHALF_OF_ACCOUNT_MASK = 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; uint256 internal constant CHECKS_DEFERRED_MASK = 0x0000000000000000000000FF0000000000000000000000000000000000000000; uint256 internal constant CHECKS_IN_PROGRESS_MASK = 0x00000000000000000000FF000000000000000000000000000000000000000000; uint256 internal constant CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK = 0x000000000000000000FF00000000000000000000000000000000000000000000; uint256 internal constant OPERATOR_AUTHENTICATED_MASK = 0x0000000000000000FF0000000000000000000000000000000000000000000000; uint256 internal constant SIMULATION_MASK = 0x00000000000000FF000000000000000000000000000000000000000000000000; uint256 internal constant STAMP_OFFSET = 200; // None of the functions below modifies the state. All the functions operate on the copy // of the execution context and return its modified value as a result. In order to update // one should use the result of the function call as a new execution context value. function getOnBehalfOfAccount(EC self) internal pure returns (address result) { result = address(uint160(EC.unwrap(self) & ON_BEHALF_OF_ACCOUNT_MASK)); } function setOnBehalfOfAccount(EC self, address account) internal pure returns (EC result) { result = EC.wrap((EC.unwrap(self) & ~ON_BEHALF_OF_ACCOUNT_MASK) | uint160(account)); } function areChecksDeferred(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CHECKS_DEFERRED_MASK != 0; } function setChecksDeferred(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CHECKS_DEFERRED_MASK); } function areChecksInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CHECKS_IN_PROGRESS_MASK != 0; } function setChecksInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CHECKS_IN_PROGRESS_MASK); } function isControlCollateralInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK != 0; } function setControlCollateralInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK); } function isOperatorAuthenticated(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & OPERATOR_AUTHENTICATED_MASK != 0; } function setOperatorAuthenticated(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | OPERATOR_AUTHENTICATED_MASK); } function clearOperatorAuthenticated(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) & ~OPERATOR_AUTHENTICATED_MASK); } function isSimulationInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & SIMULATION_MASK != 0; } function setSimulationInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | SIMULATION_MASK); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {ExecutionContext, EC} from "./ExecutionContext.sol"; import {Set, SetStorage} from "./Set.sol"; /// @title TransientStorage /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract provides transient storage for the Ethereum Vault Connector. /// @dev All the variables in this contract are considered transient meaning that their state does not change between /// invocations. abstract contract TransientStorage { using ExecutionContext for EC; using Set for SetStorage; enum SetType { Account, Vault } EC internal executionContext; SetStorage internal accountStatusChecks; SetStorage internal vaultStatusChecks; constructor() { // set the execution context to non-zero value to always keep the storage slot in non-zero state. // it allows for cheaper SSTOREs when the execution context is in its default state executionContext = EC.wrap(1 << ExecutionContext.STAMP_OFFSET); // there are two types of data that are stored using SetStorage type: // - the data that is transient in nature (accountStatusChecks and vaultStatusChecks) // - the data that is permanent (accountControllers and accountCollaterals from the EthereumVaultConnector // contract) // for the permanent data, there's no need to care that much about optimizations. each account has its two sets. // usually, an address inserted to either of them won't be removed within the same transaction. the only // optimization applied (directly in the Set contract) is that on the first element insertion, the stamp is set // to non-zero value to always keep that storage slot in non-zero state. it allows for cheaper SSTORE when an // element is inserted again after clearing the set. // for the transient data, an address insertion should be as cheap as possible. hence on construction, we store // dummy values for all the storage slots where the elements will be stored later on. it is important // considering that both accountStatusChecks and vaultStatusChecks are always cleared at the end of the // transaction. with dummy values set, the transition from zero to non-zero and back to zero will be // significantly cheaper than it would be otherwise accountStatusChecks.initialize(); vaultStatusChecks.initialize(); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; /// @title IEVC /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This interface defines the methods for the Ethereum Vault Connector. interface IEVC { /// @notice A struct representing a batch item. /// @dev Each batch item represents a single operation to be performed within a checks deferred context. struct BatchItem { /// @notice The target contract to be called. address targetContract; /// @notice The account on behalf of which the operation is to be performed. msg.sender must be authorized to /// act on behalf of this account. Must be address(0) if the target contract is the EVC itself. address onBehalfOfAccount; /// @notice The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. Must be 0 if the target contract is the EVC itself. uint256 value; /// @notice The encoded data which is called on the target contract. bytes data; } /// @notice A struct representing the result of a batch item operation. /// @dev Used only for simulation purposes. struct BatchItemResult { /// @notice A boolean indicating whether the operation was successful. bool success; /// @notice The result of the operation. bytes result; } /// @notice A struct representing the result of the account or vault status check. /// @dev Used only for simulation purposes. struct StatusCheckResult { /// @notice The address of the account or vault for which the check was performed. address checkedAddress; /// @notice A boolean indicating whether the status of the account or vault is valid. bool isValid; /// @notice The result of the check. bytes result; } /// @notice Returns current raw execution context. /// @dev When checks in progress, on behalf of account is always address(0). /// @return context Current raw execution context. function getRawExecutionContext() external view returns (uint256 context); /// @notice Returns an account on behalf of which the operation is being executed at the moment and whether the /// controllerToCheck is an enabled controller for that account. /// @dev This function should only be used by external smart contracts if msg.sender is the EVC. Otherwise, the /// account address returned must not be trusted. /// @dev When checks in progress, on behalf of account is always address(0). When address is zero, the function /// reverts to protect the consumer from ever relying on the on behalf of account address which is in its default /// state. /// @param controllerToCheck The address of the controller for which it is checked whether it is an enabled /// controller for the account on behalf of which the operation is being executed at the moment. /// @return onBehalfOfAccount An account that has been authenticated and on behalf of which the operation is being /// executed at the moment. /// @return controllerEnabled A boolean value that indicates whether controllerToCheck is an enabled controller for /// the account on behalf of which the operation is being executed at the moment. Always false if controllerToCheck /// is address(0). function getCurrentOnBehalfOfAccount(address controllerToCheck) external view returns (address onBehalfOfAccount, bool controllerEnabled); /// @notice Checks if checks are deferred. /// @return A boolean indicating whether checks are deferred. function areChecksDeferred() external view returns (bool); /// @notice Checks if checks are in progress. /// @return A boolean indicating whether checks are in progress. function areChecksInProgress() external view returns (bool); /// @notice Checks if control collateral is in progress. /// @return A boolean indicating whether control collateral is in progress. function isControlCollateralInProgress() external view returns (bool); /// @notice Checks if an operator is authenticated. /// @return A boolean indicating whether an operator is authenticated. function isOperatorAuthenticated() external view returns (bool); /// @notice Checks if a simulation is in progress. /// @return A boolean indicating whether a simulation is in progress. function isSimulationInProgress() external view returns (bool); /// @notice Checks whether the specified account and the other account have the same owner. /// @dev The function is used to check whether one account is authorized to perform operations on behalf of the /// other. Accounts are considered to have a common owner if they share the first 19 bytes of their address. /// @param account The address of the account that is being checked. /// @param otherAccount The address of the other account that is being checked. /// @return A boolean flag that indicates whether the accounts have the same owner. function haveCommonOwner(address account, address otherAccount) external pure returns (bool); /// @notice Returns the address prefix of the specified account. /// @dev The address prefix is the first 19 bytes of the account address. /// @param account The address of the account whose address prefix is being retrieved. /// @return A bytes19 value that represents the address prefix of the account. function getAddressPrefix(address account) external pure returns (bytes19); /// @notice Returns the owner for the specified account. /// @dev The function returns address(0) if the owner is not registered. Registration of the owner happens on the /// initial /// interaction with the EVC that requires authentication of an owner. /// @param account The address of the account whose owner is being retrieved. /// @return owner The address of the account owner. An account owner is an EOA/smart contract which address matches /// the first 19 bytes of the account address. function getAccountOwner(address account) external view returns (address); /// @notice Checks if lockdown mode is enabled for a given address prefix. /// @param addressPrefix The address prefix to check for lockdown mode status. /// @return A boolean indicating whether lockdown mode is enabled. function isLockdownMode(bytes19 addressPrefix) external view returns (bool); /// @notice Checks if permit functionality is disabled for a given address prefix. /// @param addressPrefix The address prefix to check for permit functionality status. /// @return A boolean indicating whether permit functionality is disabled. function isPermitDisabledMode(bytes19 addressPrefix) external view returns (bool); /// @notice Returns the current nonce for a given address prefix and nonce namespace. /// @dev Each nonce namespace provides 256 bit nonce that has to be used sequentially. There's no requirement to use /// all the nonces for a given nonce namespace before moving to the next one which allows to use permit messages in /// a non-sequential manner. /// @param addressPrefix The address prefix for which the nonce is being retrieved. /// @param nonceNamespace The nonce namespace for which the nonce is being retrieved. /// @return nonce The current nonce for the given address prefix and nonce namespace. function getNonce(bytes19 addressPrefix, uint256 nonceNamespace) external view returns (uint256 nonce); /// @notice Returns the bit field for a given address prefix and operator. /// @dev The bit field is used to store information about authorized operators for a given address prefix. Each bit /// in the bit field corresponds to one account belonging to the same owner. If the bit is set, the operator is /// authorized for the account. /// @param addressPrefix The address prefix for which the bit field is being retrieved. /// @param operator The address of the operator for which the bit field is being retrieved. /// @return operatorBitField The bit field for the given address prefix and operator. The bit field defines which /// accounts the operator is authorized for. It is a 256-position binary array like 0...010...0, marking the account /// positionally in a uint256. The position in the bit field corresponds to the account ID (0-255), where 0 is the /// owner account's ID. function getOperator(bytes19 addressPrefix, address operator) external view returns (uint256 operatorBitField); /// @notice Returns whether a given operator has been authorized for a given account. /// @param account The address of the account whose operator is being checked. /// @param operator The address of the operator that is being checked. /// @return authorized A boolean value that indicates whether the operator is authorized for the account. function isAccountOperatorAuthorized(address account, address operator) external view returns (bool authorized); /// @notice Enables or disables lockdown mode for a given address prefix. /// @dev This function can only be called by the owner of the address prefix. To disable this mode, the EVC /// must be called directly. It is not possible to disable this mode by using checks-deferrable call or /// permit message. /// @param addressPrefix The address prefix for which the lockdown mode is being set. /// @param enabled A boolean indicating whether to enable or disable lockdown mode. function setLockdownMode(bytes19 addressPrefix, bool enabled) external payable; /// @notice Enables or disables permit functionality for a given address prefix. /// @dev This function can only be called by the owner of the address prefix. To disable this mode, the EVC /// must be called directly. It is not possible to disable this mode by using checks-deferrable call or (by /// definition) permit message. To support permit functionality by default, note that the logic was inverted here. To /// disable the permit functionality, one must pass true as the second argument. To enable the permit /// functionality, one must pass false as the second argument. /// @param addressPrefix The address prefix for which the permit functionality is being set. /// @param enabled A boolean indicating whether to enable or disable the disable-permit mode. function setPermitDisabledMode(bytes19 addressPrefix, bool enabled) external payable; /// @notice Sets the nonce for a given address prefix and nonce namespace. /// @dev This function can only be called by the owner of the address prefix. Each nonce namespace provides a 256 /// bit nonce that has to be used sequentially. There's no requirement to use all the nonces for a given nonce /// namespace before moving to the next one which allows the use of permit messages in a non-sequential manner. To /// invalidate signed permit messages, set the nonce for a given nonce namespace accordingly. To invalidate all the /// permit messages for a given nonce namespace, set the nonce to type(uint).max. /// @param addressPrefix The address prefix for which the nonce is being set. /// @param nonceNamespace The nonce namespace for which the nonce is being set. /// @param nonce The new nonce for the given address prefix and nonce namespace. function setNonce(bytes19 addressPrefix, uint256 nonceNamespace, uint256 nonce) external payable; /// @notice Sets the bit field for a given address prefix and operator. /// @dev This function can only be called by the owner of the address prefix. Each bit in the bit field corresponds /// to one account belonging to the same owner. If the bit is set, the operator is authorized for the account. /// @param addressPrefix The address prefix for which the bit field is being set. /// @param operator The address of the operator for which the bit field is being set. Can neither be the EVC address /// nor an address belonging to the same address prefix. /// @param operatorBitField The new bit field for the given address prefix and operator. Reverts if the provided /// value is equal to the currently stored value. function setOperator(bytes19 addressPrefix, address operator, uint256 operatorBitField) external payable; /// @notice Authorizes or deauthorizes an operator for the account. /// @dev Only the owner or authorized operator of the account can call this function. An operator is an address that /// can perform actions for an account on behalf of the owner. If it's an operator calling this function, it can /// only deauthorize itself. /// @param account The address of the account whose operator is being set or unset. /// @param operator The address of the operator that is being installed or uninstalled. Can neither be the EVC /// address nor an address belonging to the same owner as the account. /// @param authorized A boolean value that indicates whether the operator is being authorized or deauthorized. /// Reverts if the provided value is equal to the currently stored value. function setAccountOperator(address account, address operator, bool authorized) external payable; /// @notice Returns an array of collaterals enabled for an account. /// @dev A collateral is a vault for which an account's balances are under the control of the currently enabled /// controller vault. /// @param account The address of the account whose collaterals are being queried. /// @return An array of addresses that are enabled collaterals for the account. function getCollaterals(address account) external view returns (address[] memory); /// @notice Returns whether a collateral is enabled for an account. /// @dev A collateral is a vault for which account's balances are under the control of the currently enabled /// controller vault. /// @param account The address of the account that is being checked. /// @param vault The address of the collateral that is being checked. /// @return A boolean value that indicates whether the vault is an enabled collateral for the account or not. function isCollateralEnabled(address account, address vault) external view returns (bool); /// @notice Enables a collateral for an account. /// @dev A collaterals is a vault for which account's balances are under the control of the currently enabled /// controller vault. Only the owner or an operator of the account can call this function. Unless it's a duplicate, /// the collateral is added to the end of the array. There can be at most 10 unique collaterals enabled at a time. /// Account status checks are performed. /// @param account The account address for which the collateral is being enabled. /// @param vault The address being enabled as a collateral. function enableCollateral(address account, address vault) external payable; /// @notice Disables a collateral for an account. /// @dev This function does not preserve the order of collaterals in the array obtained using the getCollaterals /// function; the order may change. A collateral is a vault for which account’s balances are under the control of /// the currently enabled controller vault. Only the owner or an operator of the account can call this function. /// Disabling a collateral might change the order of collaterals in the array obtained using getCollaterals /// function. Account status checks are performed. /// @param account The account address for which the collateral is being disabled. /// @param vault The address of a collateral being disabled. function disableCollateral(address account, address vault) external payable; /// @notice Swaps the position of two collaterals so that they appear switched in the array of collaterals for a /// given account obtained by calling getCollaterals function. /// @dev A collateral is a vault for which account’s balances are under the control of the currently enabled /// controller vault. Only the owner or an operator of the account can call this function. The order of collaterals /// can be changed by specifying the indices of the two collaterals to be swapped. Indices are zero-based and must /// be in the range of 0 to the number of collaterals minus 1. index1 must be lower than index2. Account status /// checks are performed. /// @param account The address of the account for which the collaterals are being reordered. /// @param index1 The index of the first collateral to be swapped. /// @param index2 The index of the second collateral to be swapped. function reorderCollaterals(address account, uint8 index1, uint8 index2) external payable; /// @notice Returns an array of enabled controllers for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over the account's /// balances in enabled collaterals vaults. A user can have multiple controllers during a call execution, but at /// most one can be selected when the account status check is performed. /// @param account The address of the account whose controllers are being queried. /// @return An array of addresses that are the enabled controllers for the account. function getControllers(address account) external view returns (address[] memory); /// @notice Returns whether a controller is enabled for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. /// @param account The address of the account that is being checked. /// @param vault The address of the controller that is being checked. /// @return A boolean value that indicates whether the vault is enabled controller for the account or not. function isControllerEnabled(address account, address vault) external view returns (bool); /// @notice Enables a controller for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. Only the owner or an operator of the account can call this function. /// Unless it's a duplicate, the controller is added to the end of the array. Transiently, there can be at most 10 /// unique controllers enabled at a time, but at most one can be enabled after the outermost checks-deferrable /// call concludes. Account status checks are performed. /// @param account The address for which the controller is being enabled. /// @param vault The address of the controller being enabled. function enableController(address account, address vault) external payable; /// @notice Disables a controller for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. Only the vault itself can call this function. Disabling a controller /// might change the order of controllers in the array obtained using getControllers function. Account status checks /// are performed. /// @param account The address for which the calling controller is being disabled. function disableController(address account) external payable; /// @notice Executes signed arbitrary data by self-calling into the EVC. /// @dev Low-level call function is used to execute the arbitrary data signed by the owner or the operator on the /// EVC contract. During that call, EVC becomes msg.sender. /// @param signer The address signing the permit message (ECDSA) or verifying the permit message signature /// (ERC-1271). It's also the owner or the operator of all the accounts for which authentication will be needed /// during the execution of the arbitrary data call. /// @param sender The address of the msg.sender which is expected to execute the data signed by the signer. If /// address(0) is passed, the msg.sender is ignored. /// @param nonceNamespace The nonce namespace for which the nonce is being used. /// @param nonce The nonce for the given account and nonce namespace. A valid nonce value is considered to be the /// value currently stored and can take any value between 0 and type(uint256).max - 1. /// @param deadline The timestamp after which the permit is considered expired. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is self-called on the EVC contract. /// @param signature The signature of the data signed by the signer. function permit( address signer, address sender, uint256 nonceNamespace, uint256 nonce, uint256 deadline, uint256 value, bytes calldata data, bytes calldata signature ) external payable; /// @notice Calls into a target contract as per data encoded. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev This function can be used to interact with any contract while checks are deferred. If the target contract /// is msg.sender, msg.sender is called back with the calldata provided and the context set up according to the /// account provided. If the target contract is not msg.sender, only the owner or the operator of the account /// provided can call this function. /// @dev This function can be used to recover the remaining value from the EVC contract. /// @param targetContract The address of the contract to be called. /// @param onBehalfOfAccount If the target contract is msg.sender, the address of the account which will be set /// in the context. It assumes msg.sender has authenticated the account themselves. If the target contract is /// not msg.sender, the address of the account for which it is checked whether msg.sender is authorized to act /// on behalf of. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is called on the target contract. /// @return result The result of the call. function call( address targetContract, address onBehalfOfAccount, uint256 value, bytes calldata data ) external payable returns (bytes memory result); /// @notice For a given account, calls into one of the enabled collateral vaults from the currently enabled /// controller vault as per data encoded. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev This function can be used to interact with any contract while checks are deferred as long as the contract /// is enabled as a collateral of the account and the msg.sender is the only enabled controller of the account. /// @param targetCollateral The collateral address to be called. /// @param onBehalfOfAccount The address of the account for which it is checked whether msg.sender is authorized to /// act on behalf. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is called on the target collateral. /// @return result The result of the call. function controlCollateral( address targetCollateral, address onBehalfOfAccount, uint256 value, bytes calldata data ) external payable returns (bytes memory result); /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev The authentication rules for each batch item are the same as for the call function. /// @param items An array of batch items to be executed. function batch(BatchItem[] calldata items) external payable; /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function always reverts as it's only used for simulation purposes. This function cannot be called /// within a checks-deferrable call. /// @param items An array of batch items to be executed. function batchRevert(BatchItem[] calldata items) external payable; /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function does not modify state and should only be used for simulation purposes. This function cannot /// be called within a checks-deferrable call. /// @param items An array of batch items to be executed. /// @return batchItemsResult An array of batch item results for each item. /// @return accountsStatusCheckResult An array of account status check results for each account. /// @return vaultsStatusCheckResult An array of vault status check results for each vault. function batchSimulation(BatchItem[] calldata items) external payable returns ( BatchItemResult[] memory batchItemsResult, StatusCheckResult[] memory accountsStatusCheckResult, StatusCheckResult[] memory vaultsStatusCheckResult ); /// @notice Retrieves the timestamp of the last successful account status check performed for a specific account. /// @dev This function reverts if the checks are in progress. /// @dev The account status check is considered to be successful if it calls into the selected controller vault and /// obtains expected magic value. This timestamp does not change if the account status is considered valid when no /// controller enabled. When consuming, one might need to ensure that the account status check is not deferred at /// the moment. /// @param account The address of the account for which the last status check timestamp is being queried. /// @return The timestamp of the last status check as a uint256. function getLastAccountStatusCheckTimestamp(address account) external view returns (uint256); /// @notice Checks whether the status check is deferred for a given account. /// @dev This function reverts if the checks are in progress. /// @param account The address of the account for which it is checked whether the status check is deferred. /// @return A boolean flag that indicates whether the status check is deferred or not. function isAccountStatusCheckDeferred(address account) external view returns (bool); /// @notice Checks the status of an account and reverts if it is not valid. /// @dev If checks deferred, the account is added to the set of accounts to be checked at the end of the outermost /// checks-deferrable call. There can be at most 10 unique accounts added to the set at a time. Account status /// check is performed by calling into the selected controller vault and passing the array of currently enabled /// collaterals. If controller is not selected, the account is always considered valid. /// @param account The address of the account to be checked. function requireAccountStatusCheck(address account) external payable; /// @notice Forgives previously deferred account status check. /// @dev Account address is removed from the set of addresses for which status checks are deferred. This function /// can only be called by the currently enabled controller of a given account. Depending on the vault /// implementation, may be needed in the liquidation flow. /// @param account The address of the account for which the status check is forgiven. function forgiveAccountStatusCheck(address account) external payable; /// @notice Checks whether the status check is deferred for a given vault. /// @dev This function reverts if the checks are in progress. /// @param vault The address of the vault for which it is checked whether the status check is deferred. /// @return A boolean flag that indicates whether the status check is deferred or not. function isVaultStatusCheckDeferred(address vault) external view returns (bool); /// @notice Checks the status of a vault and reverts if it is not valid. /// @dev If checks deferred, the vault is added to the set of vaults to be checked at the end of the outermost /// checks-deferrable call. There can be at most 10 unique vaults added to the set at a time. This function can /// only be called by the vault itself. function requireVaultStatusCheck() external payable; /// @notice Forgives previously deferred vault status check. /// @dev Vault address is removed from the set of addresses for which status checks are deferred. This function can /// only be called by the vault itself. function forgiveVaultStatusCheck() external payable; /// @notice Checks the status of an account and a vault and reverts if it is not valid. /// @dev If checks deferred, the account and the vault are added to the respective sets of accounts and vaults to be /// checked at the end of the outermost checks-deferrable call. Account status check is performed by calling into /// selected controller vault and passing the array of currently enabled collaterals. If controller is not selected, /// the account is always considered valid. This function can only be called by the vault itself. /// @param account The address of the account to be checked. function requireAccountAndVaultStatusCheck(address account) external payable; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; /// @title IVault /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This interface defines the methods for the Vault for the purpose of integration with the Ethereum Vault /// Connector. interface IVault { /// @notice Disables a controller (this vault) for the authenticated account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. User calls this function in order for the vault to disable itself /// for the account if the conditions are met (i.e. user has repaid debt in full). If the conditions are not met, /// the function reverts. function disableController() external; /// @notice Checks the status of an account. /// @dev This function must only deliberately revert if the account status is invalid. If this function reverts due /// to any other reason, it may render the account unusable with possibly no way to recover funds. /// @param account The address of the account to be checked. /// @param collaterals The array of enabled collateral addresses to be considered for the account status check. /// @return magicValue Must return the bytes4 magic value 0xb168c58f (which is a selector of this function) when /// account status is valid, or revert otherwise. function checkAccountStatus( address account, address[] calldata collaterals ) external view returns (bytes4 magicValue); /// @notice Checks the status of the vault. /// @dev This function must only deliberately revert if the vault status is invalid. If this function reverts due to /// any other reason, it may render some accounts unusable with possibly no way to recover funds. /// @return magicValue Must return the bytes4 magic value 0x4b3d1223 (which is a selector of this function) when /// account status is valid, or revert otherwise. function checkVaultStatus() external returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity >=0.8.0; /// @dev Interface of the ERC1271 standard signature validation method for /// contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. interface IERC1271 { /// @dev Should return whether the signature provided is valid for the provided data /// @param hash Hash of the data to be signed /// @param signature Signature byte array associated with _data /// @return magicValue Must return the bytes4 magic value 0x1626ba7e (which is a selector of this function) when /// the signature is valid. function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
{ "remappings": [ "lib/euler-price-oracle:@openzeppelin/contracts/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/", "lib/native-token-transfers/evm:openzeppelin-contracts/contracts/=lib/native-token-transfers/evm/lib/openzeppelin-contracts/contracts/", "lib/euler-earn:@openzeppelin/=lib/euler-earn/lib/openzeppelin-contracts/", "lib/euler-earn:@openzeppelin-upgradeable/=lib/euler-earn/lib/openzeppelin-contracts-upgradeable/contracts/", "lib/euler-earn:ethereum-vault-connector/=lib/euler-earn/lib/ethereum-vault-connector/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "ethereum-vault-connector/=lib/ethereum-vault-connector/src/", "evc/=lib/ethereum-vault-connector/src/", "evk/=lib/euler-vault-kit/src/", "evk-test/=lib/euler-vault-kit/test/", "euler-price-oracle/=lib/euler-price-oracle/src/", "euler-price-oracle-test/=lib/euler-price-oracle/test/", "fee-flow/=lib/fee-flow/src/", "reward-streams/=lib/reward-streams/src/", "@openzeppelin/=lib/openzeppelin-contracts/contracts/", "euler-earn/=lib/euler-earn/src/", "native-token-transfers/=lib/native-token-transfers/evm/src/", "@openzeppelin-upgradeable/=lib/euler-earn/lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@pendle/core-v2/=lib/euler-price-oracle/lib/pendle-core-v2-public/contracts/", "@pyth/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "@redstone/evm-connector/=lib/euler-price-oracle/lib/redstone-oracles-monorepo/packages/evm-connector/contracts/", "@solady/=lib/euler-price-oracle/lib/solady/src/", "@uniswap/v3-core/=lib/euler-price-oracle/lib/v3-core/", "@uniswap/v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/", "ERC4626/=lib/euler-earn/lib/properties/lib/ERC4626/contracts/", "crytic-properties/=lib/euler-earn/lib/properties/contracts/", "ds-test/=lib/ethereum-vault-connector/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "euler-vault-kit/=lib/euler-vault-kit/", "forge-gas-snapshot/=lib/euler-vault-kit/lib/permit2/lib/forge-gas-snapshot/src/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/", "layerzero-devtools/=lib/layerzero-devtools/packages/toolbox-foundry/src/", "layerzero-v2/=lib/layerzero-v2/", "openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/", "pendle-core-v2-public/=lib/euler-price-oracle/lib/pendle-core-v2-public/contracts/", "permit2/=lib/euler-vault-kit/lib/permit2/", "properties/=lib/euler-earn/lib/properties/contracts/", "pyth-sdk-solidity/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "redstone-oracles-monorepo/=lib/euler-price-oracle/lib/", "solady/=lib/euler-price-oracle/lib/solady/src/", "solidity-bytes-utils/=lib/native-token-transfers/evm/lib/solidity-bytes-utils/contracts/", "solmate/=lib/fee-flow/lib/solmate/src/", "v3-core/=lib/euler-price-oracle/lib/v3-core/contracts/", "v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/contracts/", "wormhole-solidity-sdk/=lib/native-token-transfers/evm/lib/wormhole-solidity-sdk/src/" ], "optimizer": { "enabled": true, "runs": 20000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EVC_BatchPanic","type":"error"},{"inputs":[],"name":"EVC_ChecksReentrancy","type":"error"},{"inputs":[],"name":"EVC_ControlCollateralReentrancy","type":"error"},{"inputs":[],"name":"EVC_ControllerViolation","type":"error"},{"inputs":[],"name":"EVC_EmptyError","type":"error"},{"inputs":[],"name":"EVC_InvalidAddress","type":"error"},{"inputs":[],"name":"EVC_InvalidData","type":"error"},{"inputs":[],"name":"EVC_InvalidNonce","type":"error"},{"inputs":[],"name":"EVC_InvalidOperatorStatus","type":"error"},{"inputs":[],"name":"EVC_InvalidTimestamp","type":"error"},{"inputs":[],"name":"EVC_InvalidValue","type":"error"},{"inputs":[],"name":"EVC_LockdownMode","type":"error"},{"inputs":[],"name":"EVC_NotAuthorized","type":"error"},{"inputs":[],"name":"EVC_OnBehalfOfAccountNotAuthenticated","type":"error"},{"inputs":[],"name":"EVC_PermitDisabledMode","type":"error"},{"inputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.BatchItemResult[]","name":"batchItemsResult","type":"tuple[]"},{"components":[{"internalType":"address","name":"checkedAddress","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.StatusCheckResult[]","name":"accountsStatusResult","type":"tuple[]"},{"components":[{"internalType":"address","name":"checkedAddress","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.StatusCheckResult[]","name":"vaultsStatusResult","type":"tuple[]"}],"name":"EVC_RevertedBatchResult","type":"error"},{"inputs":[],"name":"EVC_SimulationBatchNested","type":"error"},{"inputs":[],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"TooManyElements","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"controller","type":"address"}],"name":"AccountStatusCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"bytes19","name":"onBehalfOfAddressPrefix","type":"bytes19"},{"indexed":false,"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"indexed":true,"internalType":"address","name":"targetContract","type":"address"},{"indexed":false,"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"CallWithContext","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"CollateralStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ControllerStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"LockdownModeStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":true,"internalType":"uint256","name":"nonceNamespace","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newNonce","type":"uint256"}],"name":"NonceStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":true,"internalType":"uint256","name":"nonceNamespace","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"NonceUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"accountOperatorAuthorized","type":"uint256"}],"name":"OperatorStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"PermitDisabledModeStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultStatusCheck","type":"event"},{"inputs":[],"name":"areChecksDeferred","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areChecksInProgress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IEVC.BatchItem[]","name":"items","type":"tuple[]"}],"name":"batch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IEVC.BatchItem[]","name":"items","type":"tuple[]"}],"name":"batchRevert","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IEVC.BatchItem[]","name":"items","type":"tuple[]"}],"name":"batchSimulation","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.BatchItemResult[]","name":"batchItemsResult","type":"tuple[]"},{"components":[{"internalType":"address","name":"checkedAddress","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.StatusCheckResult[]","name":"accountsStatusCheckResult","type":"tuple[]"},{"components":[{"internalType":"address","name":"checkedAddress","type":"address"},{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"internalType":"struct IEVC.StatusCheckResult[]","name":"vaultsStatusCheckResult","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"call","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"targetCollateral","type":"address"},{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"controlCollateral","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"disableCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"disableController","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"enableCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"enableController","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"forgiveAccountStatusCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"forgiveVaultStatusCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAddressPrefix","outputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCollaterals","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getControllers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controllerToCheck","type":"address"}],"name":"getCurrentOnBehalfOfAccount","outputs":[{"internalType":"address","name":"onBehalfOfAccount","type":"address"},{"internalType":"bool","name":"controllerEnabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getLastAccountStatusCheckTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"uint256","name":"nonceNamespace","type":"uint256"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"address","name":"operator","type":"address"}],"name":"getOperator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRawExecutionContext","outputs":[{"internalType":"uint256","name":"context","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"otherAccount","type":"address"}],"name":"haveCommonOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isAccountOperatorAuthorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAccountStatusCheckDeferred","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"isCollateralEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isControlCollateralInProgress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"isControllerEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"}],"name":"isLockdownMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOperatorAuthenticated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"}],"name":"isPermitDisabledMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSimulationInProgress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"isVaultStatusCheckDeferred","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonceNamespace","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"permit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint8","name":"index1","type":"uint8"},{"internalType":"uint8","name":"index2","type":"uint8"}],"name":"reorderCollaterals","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"requireAccountAndVaultStatusCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"requireAccountStatusCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requireVaultStatusCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"authorized","type":"bool"}],"name":"setAccountOperator","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setLockdownMode","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"uint256","name":"nonceNamespace","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"setNonce","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"operatorBitField","type":"uint256"}],"name":"setOperator","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes19","name":"addressPrefix","type":"bytes19"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setPermitDisabledMode","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60c060405234801562000010575f80fd5b50600160c81b5f55620000246001620000e2565b62000030600c620000e2565b466080818152604080518082018252601881527f457468657265756d205661756c7420436f6e6e6563746f72000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f040f0adc9d57e8bea9a4602b7065ad7261f63d709e7be772afe0ceb20a92d47381840152606081019490945230848401528151808503909301835260a09093019052805191012060a05262000162565b80546001600160f81b0316600160f81b17815560015b600a8110156200014a57600182810182600a81106200011b576200011b6200014e565b0180546001600160601b0392909216600160a01b026001600160a01b03909216919091179055600101620000f8565b5050565b634e487b7160e01b5f52603260045260245ffd5b60805160a051615622620001845f395f6134b901525f6133e601526156225ff3fe6080604052600436106102da575f3560e01c8063863789d71161017b578063c368516c116100d1578063df7c138411610087578063ebf1ea8611610062578063ebf1ea8614610915578063f4fc35701461091d578063fd6046d714610930575f80fd5b8063df7c1384146108b6578063e21e537c146108d5578063e920e8e014610902575f80fd5b8063cb29955a116100b7578063cb29955a1461082d578063cdd8ea7814610884578063d44fee5a146108a3575f80fd5b8063c368516c146107fb578063c760d9211461080e575f80fd5b8063a4d25d1e11610131578063b9b70ff51161010c578063b9b70ff5146107c2578063c14c11bf146107d5578063c16ae7a4146107e8575f80fd5b8063a4d25d1e14610730578063a829aaf51461075c578063b03c130d1461076f575f80fd5b80639e716d58116101615780639e716d58146106f65780639f5c462a14610715578063a37d54af14610728575f80fd5b8063863789d71461069857806392d2fc01146106c6575f80fd5b80633b2416be1161023057806347cfdac4116101e6578063642ea23f116101c1578063642ea23f146106505780637f17c377146106635780637f5c92f314610685575f80fd5b806347cfdac4146105e0578063506d8c92146105ff5780635bedd1cd1461063d575f80fd5b8063430292b311610216578063430292b314610542578063442b172c1461056e57806346591032146105cd575f80fd5b80633b2416be146104f457806342e5349914610523575f80fd5b80631647292a1161029057806330f316671161026b57806330f31667146104785780633a1a3a1d1461048b5780633b10f3ef1461049e575f80fd5b80631647292a146103f857806318503a1e146104275780631f8b521514610465575f80fd5b8063116d0e93116102c0578063116d0e93146103a5578063129d21a0146103b857806312d6c936146103cb575f80fd5b806306fdde031461033457806310a7519814610392575f80fd5b36610330575f5474ff00000000000000000000000000000000000000001661032e576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b5f80fd5b34801561033f575f80fd5b5061037c6040518060400160405280601881526020017f457468657265756d205661756c7420436f6e6e6563746f72000000000000000081525081565b6040516103899190614920565b60405180910390f35b61032e6103a0366004614946565b61094f565b61032e6103b336600461498b565b610aaa565b61032e6103c636600461498b565b610bfd565b3480156103d6575f80fd5b506103ea6103e53660046149c0565b610d49565b604051908152602001610389565b348015610403575f80fd5b506104176104123660046149e8565b610d7a565b6040519015158152602001610389565b348015610432575f80fd5b50610446610441366004614946565b610d8c565b604080516001600160a01b039093168352901515602083015201610389565b61037c610473366004614a59565b610e1f565b61032e610486366004614946565b610f17565b348015610496575f80fd5b505f546103ea565b3480156104a9575f80fd5b506104176104b8366004614ac7565b6cffffffffffffffffffffffffff19165f9081526017602052604090205474010000000000000000000000000000000000000000900460ff1690565b3480156104ff575f80fd5b505f5477ff0000000000000000000000000000000000000000000000161515610417565b34801561052e575f80fd5b5061041761053d366004614946565b610f65565b34801561054d575f80fd5b505f5474ff0000000000000000000000000000000000000000161515610417565b348015610579575f80fd5b506105b5610588366004614946565b60601b6cffffffffffffffffffffffffff19165f908152601760205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001610389565b61032e6105db366004614946565b610fca565b3480156105eb575f80fd5b506104176105fa3660046149e8565b610ffc565b34801561060a575f80fd5b5061061e610619366004614946565b61101d565b6040516cffffffffffffffffffffffffff199091168152602001610389565b61032e61064b366004614ae0565b611038565b61032e61065e366004614ba7565b611485565b610676610671366004614be9565b611568565b60405161038993929190614cd9565b61032e610693366004614be9565b611719565b3480156106a3575f80fd5b505f5476ff00000000000000000000000000000000000000000000161515610417565b3480156106d1575f80fd5b505f5478ff000000000000000000000000000000000000000000000000161515610417565b348015610701575f80fd5b506104176107103660046149e8565b6119ce565b61032e610723366004614d8c565b6119ef565b61032e611c0e565b34801561073b575f80fd5b5061074f61074a366004614946565b611c42565b6040516103899190614e17565b61032e61076a366004614e29565b611c65565b34801561077a575f80fd5b506103ea610789366004614e59565b6cffffffffffffffffffffffffff1982165f9081526018602090815260408083206001600160a01b038516845290915290205492915050565b61037c6107d0366004614a59565b611d42565b61032e6107e3366004614e73565b611f38565b61032e6107f6366004614be9565b612073565b61032e6108093660046149e8565b6121c5565b348015610819575f80fd5b506104176108283660046149e8565b612339565b348015610838575f80fd5b50610417610847366004614ac7565b6cffffffffffffffffffffffffff19165f908152601760205260409020547501000000000000000000000000000000000000000000900460ff1690565b34801561088f575f80fd5b5061041761089e366004614946565b612346565b61032e6108b13660046149e8565b6123a3565b3480156108c1575f80fd5b506103ea6108d0366004614946565b612505565b3480156108e0575f80fd5b505f5475ff000000000000000000000000000000000000000000161515610417565b61032e6109103660046149e8565b612597565b61032e6126b6565b61032e61092b366004614946565b61273c565b34801561093b575f80fd5b5061074f61094a366004614946565b612850565b5f5475ff0000000000000000000000000000000000000000008116156109a1576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109f05f75ff00000000000000000000000000000000000000000083175b7fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039091161790565b5f9081556001600160a01b038084168252601b602052604090912054839160ff82169161010090041660018214610a53576040517ff1be451900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610a95576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50610aa39050600184612873565b50505f5550565b81610ab6815f80612cee565b506cffffffffffffffffffffffffff1983165f9081526017602052604090205460ff750100000000000000000000000000000000000000000090910416151582151514610bf85781158015610b2257505f5474ff00000000000000000000000000000000000000001615155b15610b59576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1983165f818152601760205260409081902080548515157501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff909116179055517f6321df4e44267d425279195e7488fadba1a42d5cce9e84f596d5cf696f4449cd90610bef90851515815260200190565b60405180910390a25b505050565b81610c09815f80612cee565b506cffffffffffffffffffffffffff1983165f9081526017602052604090205460ff7401000000000000000000000000000000000000000090910416151582151514610bf85781158015610c7d57505f5474ff000000000000000000000000000000000000000016151580610c7d57503330145b15610cb4576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1983165f8181526017602052604090819020805485151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909116179055517faf5120bc58372f0063d8362c9bba9070c462c07ae24c24082d080a426432798b90610bef90851515815260200190565b6cffffffffffffffffffffffffff1982165f9081526019602090815260408083208484529091529020545b92915050565b5f610d858383612d51565b9392505050565b5f80610d9f5f546001600160a01b031690565b91506001600160a01b038216610de1576040517f5217b8ae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831615610e16576001600160a01b0382165f908152601b60205260409020610e119084612dda565b610e18565b5f5b9050915091565b5f5460609075ff000000000000000000000000000000000000000000811615610e74576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615610ec5576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5474ff000000000000000000000000000000000000000081175f908155610ef18888888888612e7e565b9350905080610f0357610f0383612f9f565b610f0c82612fe0565b505095945050505050565b5f5474ff00000000000000000000000000000000000000001615610f5057610f40600182613039565b50610f4c600c33613039565b5050565b610f598161329f565b610f6233613323565b50565b5f805475ff0000000000000000000000000000000000000000001615610fb7576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2600183612dda565b90505b919050565b5f5474ff00000000000000000000000000000000000000001615610ff357610f4c600182613039565b610f628161329f565b6001600160a01b0382165f908152601b60205260408120610d859083612dda565b5f606082901b6cffffffffffffffffffffffffff1916610fc2565b5f5475ff00000000000000000000000000000000000000000081161561108a576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156110db576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b503330148061110657506001600160a01b0389161580159061110657506001600160a01b0389163314155b1561113d576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038a16158061115957506111578a6133a2565b155b15611190576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608a901b6cffffffffffffffffffffffffff19165f818152601760205260409020547501000000000000000000000000000000000000000000900460ff1615611206576040517f4426359200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1981165f9081526019602090815260408083208c84529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81148061125e5750888114155b15611295576040517fa82b84bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50428710156112d0576040517f7c9bb1cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f84900361130a576040517fe85c620e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61131b8c8c8c8c8c8c8c8c6133e2565b905061135c8185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506135bc92505050565b6001600160a01b03168c6001600160a01b0316141580156113ba57506113b88c8286868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061367692505050565b155b156113f1576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1982165f8181526019602090815260408083208e845282529182902060018d01905590518b81528c92917fb0dcec731e48090736be6db10ad9f9581d0ec5fc0f1925a8e267b64b614b08d6910160405180910390a35f80611463308f8b8b8b6137ab565b91509150816114755761147581612f9f565b5050505050505050505050505050565b5f5475ff0000000000000000000000000000000000000000008116156114d7576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615611528576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508261153681600180613a11565b506001600160a01b0384165f908152601a60205260409020611559908484613c0d565b61156284610fca565b50505050565b60608060605f80306001600160a01b0316306001600160a01b0316637f5c92f3898960405160240161159b929190614ed8565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e09490941b9390931790925290516115e89250615024565b5f60405180830381855af49150503d805f8114611620576040519150601f19603f3d011682016040523d82523d5f602084013e611625565b606091505b50915091508115611662576040517f4cd2d4f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004815110806116bc57507fd4b0e4d3000000000000000000000000000000000000000000000000000000006116978261503f565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156116ca576116ca81612f9f565b80517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc81016004830190815291611709918101602001906024016152a6565b9199909850909650945050505050565b5f5475ff00000000000000000000000000000000000000000081161561176b576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156117bc576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f546060908190819074ff0000000000000000000000000000000000000000811615611815576040517fb83566c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b78ff000000ff000000000000000000000000000000000000000081175f55848067ffffffffffffffff81111561184d5761184d61508a565b60405190808252806020026020018201604052801561189257816020015b604080518082019091525f81526060602082015281526020019060019003908161186b5790505b5094505f5b8181101561194a57368888838181106118b2576118b26153d9565b90506020028101906118c49190615406565b90506118fd6118d66020830183614946565b6118e66040840160208501614946565b60408401356118f86060860186615438565b612e7e565b88848151811061190f5761190f6153d9565b60200260200101515f0189858151811061192b5761192b6153d9565b6020908102919091018101510191909152901515905250600101611897565b5061196d5f75ff00000000000000000000000000000000000000000084176109bf565b5f90815561197a90613dd8565b93506119866001613dd8565b5f8390556040517fd4b0e4d30000000000000000000000000000000000000000000000000000000081529093506119c590869086908690600401614cd9565b60405180910390fd5b6001600160a01b0382165f908152601a60205260408120610d859083612dda565b5f6119fc8460015f613a11565b90506cffffffffffffffffffffffffff19606085901b165f61010086841810611a4c576cffffffffffffffffffffffffff1982165f908152601760205260409020546001600160a01b0316611a4e565b825b9050826001600160a01b0316816001600160a01b031614158015611a845750826001600160a01b0316856001600160a01b031614155b15611abb576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516301480611ad55750610100858218105b15611b0c576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1982165f9081526018602090815260408083206001600160a01b038981168552925282205460018985189092169190911b9186611b5a5782198216611b5e565b8282175b9050808203611b99576040517f655156bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1985165f8181526018602090815260408083206001600160a01b038d1680855290835292819020859055518481529192917f7ba31654d8467e98b6bd4fc56ddde246de9ade831cf860c7ac695579aecb9564910160405180910390a3505050505050505050565b5f5474ff00000000000000000000000000000000000000001615611c3757610f62600c33613039565b611c4033613323565b565b6001600160a01b0381165f908152601a60205260409020606090610fc290613f18565b82611c71815f80612cee565b506cffffffffffffffffffffffffff1984165f908152601960209081526040808320868452909152902054828110611cd5576040517fa82b84bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1985165f81815260196020908152604080832088845282529182902086905581518481529081018690528692917f3b8510174a91acb36200f7427c1889f934941fd89ed86faf390749b4c2b46337910160405180910390a35050505050565b5f5460609075ff000000000000000000000000000000000000000000811615611d97576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615611de8576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b038086165f908152601b6020526040902054869160ff82169161010090041660018214611e49576040517ff1be451900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314611e8b576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506001600160a01b0386165f908152601a60205260409020611eae9088612dda565b611ee4576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5476ff00ff000000000000000000000000000000000000000081175f908155611f1189898989896137ab565b9450905080611f2357611f2384612f9f565b611f2c82612fe0565b50505095945050505050565b5f611f44845f80612cee565b90506001600160a01b038316301480611f605750610100838218105b15611f97576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1984165f9081526018602090815260408083206001600160a01b0387168452909152902054829003612003576040517f655156bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1984165f8181526018602090815260408083206001600160a01b03881680855290835292819020869055518581529192917f7ba31654d8467e98b6bd4fc56ddde246de9ade831cf860c7ac695579aecb9564910160405180910390a350505050565b5f5475ff0000000000000000000000000000000000000000008116156120c5576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612116576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5474ff000000000000000000000000000000000000000081175f90815582905b818110156121bb5736858583818110612153576121536153d9565b90506020028101906121659190615406565b90505f8061219b6121796020850185614946565b6121896040860160208701614946565b60408601356118f86060880188615438565b91509150816121ad576121ad81612f9f565b505050806001019050612138565b5061156282612fe0565b5f5475ff000000000000000000000000000000000000000000811615612217576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612268576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161227681600180613a11565b50306001600160a01b038316036122b9576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383165f908152601b602052604090206122da9083613039565b1561233057816001600160a01b0316836001600160a01b03167f9919d437ee612d4ec7bba88a7d9bc4fc36a0a23608ad6259252711a46b708af96001604051612327911515815260200190565b60405180910390a35b610bf883610fca565b5f61010082841810610d85565b5f805475ff0000000000000000000000000000000000000000001615612398576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2600c83612dda565b5f5475ff0000000000000000000000000000000000000000008116156123f5576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612446576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161245481600180613a11565b50306001600160a01b03831603612497576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383165f908152601a602052604090206124b89083613039565b1561233057816001600160a01b0316836001600160a01b03167ff022705c827017c972043d1984cfddc7958c9f4685b4d9ce8bd68696f4381cd26001604051612327911515815260200190565b5f805475ff0000000000000000000000000000000000000000001615612557576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03165f908152601b60205260409020547501000000000000000000000000000000000000000000900469ffffffffffffffffffff1690565b5f5475ff0000000000000000000000000000000000000000008116156125e9576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff0000000000000000000000000000000000000000000081161561263a576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161264881600180613a11565b506001600160a01b0383165f908152601a6020526040902061266a9083612873565b1561233057816001600160a01b0316836001600160a01b03167ff022705c827017c972043d1984cfddc7958c9f4685b4d9ce8bd68696f4381cd25f604051612327911515815260200190565b5f5475ff000000000000000000000000000000000000000000811615612708576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61272a5f75ff00000000000000000000000000000000000000000083176109bf565b5f55612737600c33612873565b505f55565b5f5475ff00000000000000000000000000000000000000000081161561278e576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156127df576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b0381165f908152601b602052604090206128019033612873565b15612847576040515f815233906001600160a01b038316907f9919d437ee612d4ec7bba88a7d9bc4fc36a0a23608ad6259252711a46b708af99060200160405180910390a35b610f6281610fca565b6001600160a01b0381165f908152601b60205260409020606090610fc290613f18565b81545f9061010081046001600160a01b03169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff168184036128c2575f9350505050610d74565b5f856001600160a01b0316846001600160a01b031614612932575060015b8281101561291f57856001600160a01b03168760010182600a8110612907576129076153d9565b01546001600160a01b03161461291f576001016128e0565b828103612932575f945050505050610d74565b826001036129b257507effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff909116750100000000000000000000000000000000000000000002167f01000000000000000000000000000000000000000000000000000000000000001785555060019150610d749050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83015f6001890182600a81106129eb576129eb6153d9565b019050818314612bef57825f03612ad157805489547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff000000000000000000000000000000000000000000000000000000000000009092166101006001600160a01b03909416939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16178955612cba565b80546001600160a01b031660018a0184600a8110612af157612af16153d9565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392831617905589547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff00000000000000000000000000000000000000000000000000000000000000909216610100938a16939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16178955612cba565b88547f010000000000000000000000000000000000000000000000000000000000000060ff84167fff000000000000000000000000000000000000000000000000000000000000009092166101006001600160a01b038a16027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00161791909117750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789555b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001979650505050505050565b6cffffffffffffffffffffffffff1983165f908152601760205260408120546001600160a01b0316612d488115612d255781612d41565b73ffffffffffffffffffffffffffffffffffffff00606087901c165b8585613a11565b95945050505050565b6cffffffffffffffffffffffffff19606083901b165f818152601760205260408120549091906001600160a01b031680612d8f575f92505050610d74565b6cffffffffffffffffffffffffff19919091165f9081526018602090815260408083206001600160a01b0396871684529091529020546001949091189092169290921b161515919050565b81545f906001600160a01b036101008204169060ff16808303612e01575f92505050610d74565b836001600160a01b0316826001600160a01b031603612e2557600192505050610d74565b60015b81811015612e7357846001600160a01b03168660010182600a8110612e4f57612e4f6153d9565b01546001600160a01b031603612e6b5760019350505050610d74565b600101612e28565b505f95945050505050565b5f6060306001600160a01b03881603612f64576001600160a01b03861615612ed2576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415612f0a576040517fbb6de1c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040513090612f1c9086908690615499565b5f60405180830381855af49150503d805f8114612f54576040519150601f19603f3d011682016040523d82523d5f602084013e612f59565b606091505b509092509050612f95565b6001600160a01b0387163314612f8257612f8086600180613a11565b505b612f8f87878787876137ab565b90925090505b9550959350505050565b805115612fae57805181602001fd5b6040517f38ae747c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b74ff000000000000000000000000000000000000000081166130355761301e5f75ff00000000000000000000000000000000000000000083176109bf565b5f90815561302b9061401a565b613035600161401a565b5f55565b81545f9061010081046001600160a01b03169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff1681840361314d5785547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9092167501000000000000000000000000000000000000000000029190911674ffffffffffffffffffffffffffffffffffffffffff6001600160a01b038716610100027fffffffffffffffffffffff00000000000000000000000000000000000000000090931692909217600190811792909216177f01000000000000000000000000000000000000000000000000000000000000001786559250610d74915050565b846001600160a01b0316836001600160a01b031603613171575f9350505050610d74565b60015b828110156131bf57856001600160a01b03168760010182600a811061319b5761319b6153d9565b01546001600160a01b0316036131b7575f945050505050610d74565b600101613174565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff68201613219576040517f3572cf8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848660010183600a811061322f5761322f6153d9565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555084547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600191820160ff1617909455509192915050565b5f5475ff0000000000000000000000000000000000000000008116156132f1576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133135f75ff00000000000000000000000000000000000000000083176109bf565b5f5561331e8261404c565b5f5550565b5f5475ff000000000000000000000000000000000000000000811615613375576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133975f75ff00000000000000000000000000000000000000000083176109bf565b5f5561331e82614069565b5f61010082101580156133b9575061010080831810155b8015610fc257506101007342000000000000000000000000000000000000008318101592915050565b5f807f000000000000000000000000000000000000000000000000000000000000000046146134b757604080518082018252601881527f457468657265756d205661756c7420436f6e6e6563746f72000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f040f0adc9d57e8bea9a4602b7065ad7261f63d709e7be772afe0ceb20a92d47381840152466060820152306080808301919091528351808303909101815260a090910190925281519101206134d9565b7f00000000000000000000000000000000000000000000000000000000000000005b90505f7f4ae56dd541cf527f212121ebe3756a7631631f85f66a3073e982c01a6e2ecbf28b8b8b8b8b8b8b8b604051613513929190615499565b6040805191829003822060208301999099526001600160a01b0397881690820152959094166060860152608085019290925260a084015260c083015260e0820152610100810191909152610120016040516020818303038152906040528051906020012090507f19010000000000000000000000000000000000000000000000000000000000005f52816002528060225260425f2092505f602252505098975050505050505050565b5f81516041146135cd57505f610d74565b6020820151604083015160608401515f1a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115613611575f9350505050610d74565b604080515f81526020810180835288905260ff831691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015613661573d5f803e3d5ffd5b5050604051601f190151979650505050505050565b5f836001600160a01b03163b5f0361368f57505f610d85565b5f80856001600160a01b031685856040516024016136ae9291906154a8565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e00000000000000000000000000000000000000000000000000000000179052516137119190615024565b5f60405180830381855afa9150503d805f8114613749576040519150601f19603f3d011682016040523d82523d5f602084013e61374e565b606091505b5091509150818015613761575080516020145b80156137a1575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061379f90830160209081019084016154c0565b145b9695505050505050565b5f60607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85036137dd57479450613817565b47851115613817576040517fbb6de1c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805490613823614074565b9050610100888218108061383f57506001600160a01b03891633145b8061385257506001600160a01b03891630145b80613875575076ff00000000000000000000000000000000000000000000821615155b156138af577fffffffffffffffff00ffffff000000000000000000000000000000000000000082166001600160a01b038916175f556138fa565b77ff00000000000000000000000000000000000000000000006001600160a01b0389167fffffffffffffffffffffffff0000000000000000000000000000000000000000841617175f555b6001600160a01b038916606089901b6cffffffffffffffffffffffffff19166cffffffffffffffffffffffffff19166001600160a01b0383167f6e9738e5aa38fe1517adbb480351ec386ece82947737b18badbcad1e911133ec8b61395f8a8c6154d7565b604080516001600160a01b0390931683527fffffffff0000000000000000000000000000000000000000000000000000000090911660208301520160405180910390a4886001600160a01b03168787876040516139bd929190615499565b5f6040518083038185875af1925050503d805f81146139f7576040519150601f19603f3d011682016040523d82523d5f602084013e6139fc565b606091505b505f9390935599919850909650505050505050565b606083901b6cffffffffffffffffffffffffff19165f818152601760205260408120549091906001600160a01b0381169074010000000000000000000000000000000000000000900460ff1683613a66614074565b90505f6101008983181015613b28576001600160a01b038416613b0b576cffffffffffffffffffffffffff1985165f8181526017602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038716908117909155905194965086949092917f67cb2734834e775d6db886bf16ac03d7273b290223ee5363354b385ec5b643b091a3506001613b28565b816001600160a01b0316846001600160a01b031603613b28575060015b80158015613b335750875b8015613b445750613b448983612d51565b15613b4d575060015b808015613b6c5750886001600160a01b0316846001600160a01b031614155b8015613b8157506001600160a01b0389163b15155b15613b8957505f5b80613bc0576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b868015613bca5750825b15613c01576040517fd80a9cac00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50979650505050505050565b82546001600160a01b036101008204169060ff90811690838116908516101580613c3a5750808360ff1610155b15613c71576040517f63df817100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff165f03613d0b57846001018360ff16600a8110613c9357613c936153d9565b01546001600160a01b03168286600181810160ff8816600a8110613cb957613cb96153d9565b0180546001600160a01b039485167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905581549383166101009190910a908102920219909216179055613dd1565b846001018360ff16600a8110613d2357613d236153d9565b01546001600160a01b03166001860160ff8616600a8110613d4657613d466153d9565b01546001600160a01b03166001870160ff8716600a8110613d6957613d696153d9565b015f6001890160ff8816600a8110613d8357613d836153d9565b0180546001600160a01b039485167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905581549383166101009190910a9081029202199092161790555b5050505050565b60605f80836001811115613dee57613dee61551f565b14613e0557613e00600c6140906141d1565b613e12565b613e1260016144326141d1565b80519091508067ffffffffffffffff811115613e3057613e3061508a565b604051908082528060200260200182016040528015613e7c57816020015b60408051606080820183525f808352602083015291810191909152815260200190600190039081613e4e5790505b5092505f5b81811015613f10575f805f858481518110613e9e57613e9e6153d9565b6020026020010151806020019051810190613eb9919061554c565b9250925092506040518060600160405280846001600160a01b03168152602001831515815260200182815250878581518110613ef757613ef76153d9565b6020026020010181905250505050806001019050613e81565b505050919050565b80546060906001600160a01b036101008204169060ff165f8167ffffffffffffffff811115613f4957613f4961508a565b604051908082528060200260200182016040528015613f72578160200160208202803683370190505b509050815f03613f8457949350505050565b82815f81518110613f9757613f976153d9565b6001600160a01b039092166020928302919091019091015260015b82811015614011578560010181600a8110613fcf57613fcf6153d9565b015482516001600160a01b0390911690839083908110613ff157613ff16153d9565b6001600160a01b0390921660209283029190910190910152600101613fb2565b50949350505050565b5f81600181111561402d5761402d61551f565b1461403f57610f62600c61406961475b565b610f62600161404c61475b565b5f8061405783614432565b9150915081610bf857610bf881612f9f565b5f8061405783614090565b5f33301461408157503390565b505f546001600160a01b031690565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4b3d12230000000000000000000000000000000000000000000000000000000017905290515f9160609183916001600160a01b038616916141049190615024565b5f604051808303815f865af19150503d805f811461413d576040519150601f19603f3d011682016040523d82523d5f602084013e614142565b606091505b5092509050808015614155575081516020145b8015614195575081517f4b3d1223000000000000000000000000000000000000000000000000000000009061419390840160209081019085016154c0565b145b6040519093506001600160a01b038516907faea973cfb51ea8ca328767d72f105b5b9d2360c65f5ac4110a2c4470434471c9905f90a250915091565b815460609060ff81169061010081046001600160a01b0316907501000000000000000000000000000000000000000000900469ffffffffffffffffffff165f8367ffffffffffffffff8111156142295761422961508a565b60405190808252806020026020018201604052801561425c57816020015b60608152602001906001900390816142475790505b509050835f03614271579350610d7492505050565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff8316750100000000000000000000000000000000000000000002167f01000000000000000000000000000000000000000000000000000000000000001787555f806142ea8563ffffffff8a16565b91509150848282604051602001614303939291906155a2565b604051602081830303815290604052835f81518110614324576143246153d9565b602090810291909101015260015b86811015614424575f8a60010182600a8110614350576143506153d9565b0154604080518082019091525f81526001602082018190526001600160a01b039092169250908c0183600a8110614389576143896153d9565b82516020909301516bffffffffffffffffffffffff1674010000000000000000000000000000000000000000026001600160a01b03909316929092179101556143d58163ffffffff8c16565b60405191955093506143ef908290869086906020016155a2565b604051602081830303815290604052858381518110614410576144106153d9565b602090810291909101015250600101614332565b509198975050505050505050565b6001600160a01b038082165f908152601b602052604081208054919260609260ff80821692610100830416917f01000000000000000000000000000000000000000000000000000000000000009004168286036144a757600160405180602001604052805f8152509550955050505050915091565b60018311156145155750506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff1be4519000000000000000000000000000000000000000000000000000000001790525f969095509350505050565b6001600160a01b038781165f908152601a602052604081209091841690899061453d90613f18565b60405160240161454e9291906155cb565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb168c58f00000000000000000000000000000000000000000000000000000000179052516145b19190615024565b5f60405180830381855afa9150503d805f81146145e9576040519150601f19603f3d011682016040523d82523d5f602084013e6145ee565b606091505b5096509050808015614601575085516020145b8015614641575085517fb168c58f000000000000000000000000000000000000000000000000000000009061463f90880160209081019089016154c0565b145b9650861561471157845460ff8581167fffffffffffffffffffffff000000000000000000000000000000000000000000909216919091176101006001600160a01b038616021774ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000004269ffffffffffffffffffff16027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177f0100000000000000000000000000000000000000000000000000000000000000918416919091021785555b826001600160a01b0316886001600160a01b03167f889a4d4628b31342e420737e2aeb45387087570710d26239aa8a5f13d3e829d460405160405180910390a35050505050915091565b815460ff81169061010081046001600160a01b0316907501000000000000000000000000000000000000000000900469ffffffffffffffffffff165f8390036147a5575050505050565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff8216750100000000000000000000000000000000000000000002167f010000000000000000000000000000000000000000000000000000000000000017855561481c8263ffffffff8616565b60015b838110156148cb575f8660010182600a811061483d5761483d6153d9565b0154604080518082019091525f81526001602082018190526001600160a01b03909216925090880183600a8110614876576148766153d9565b82516020909301516bffffffffffffffffffffffff1674010000000000000000000000000000000000000000026001600160a01b03909316929092179101556148c28163ffffffff8816565b5060010161481f565b505050505050565b5f5b838110156148ed5781810151838201526020016148d5565b50505f910152565b5f815180845261490c8160208601602086016148d3565b601f01601f19169290920160200192915050565b602081525f610d8560208301846148f5565b6001600160a01b0381168114610f62575f80fd5b5f60208284031215614956575f80fd5b8135610d8581614932565b80356cffffffffffffffffffffffffff1981168114610fc5575f80fd5b8015158114610f62575f80fd5b5f806040838503121561499c575f80fd5b6149a583614961565b915060208301356149b58161497e565b809150509250929050565b5f80604083850312156149d1575f80fd5b6149da83614961565b946020939093013593505050565b5f80604083850312156149f9575f80fd5b8235614a0481614932565b915060208301356149b581614932565b5f8083601f840112614a24575f80fd5b50813567ffffffffffffffff811115614a3b575f80fd5b602083019150836020828501011115614a52575f80fd5b9250929050565b5f805f805f60808688031215614a6d575f80fd5b8535614a7881614932565b94506020860135614a8881614932565b935060408601359250606086013567ffffffffffffffff811115614aaa575f80fd5b614ab688828901614a14565b969995985093965092949392505050565b5f60208284031215614ad7575f80fd5b610d8582614961565b5f805f805f805f805f806101008b8d031215614afa575f80fd5b8a35614b0581614932565b995060208b0135614b1581614932565b985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b013567ffffffffffffffff80821115614b4d575f80fd5b614b598e838f01614a14565b909650945060e08d0135915080821115614b71575f80fd5b50614b7e8d828e01614a14565b915080935050809150509295989b9194979a5092959850565b803560ff81168114610fc5575f80fd5b5f805f60608486031215614bb9575f80fd5b8335614bc481614932565b9250614bd260208501614b97565b9150614be060408501614b97565b90509250925092565b5f8060208385031215614bfa575f80fd5b823567ffffffffffffffff80821115614c11575f80fd5b818501915085601f830112614c24575f80fd5b813581811115614c32575f80fd5b8660208260051b8501011115614c46575f80fd5b60209290920196919550909350505050565b5f82825180855260208086019550808260051b8401018186015f5b84811015614ccc57858303601f19018952815180516001600160a01b0316845284810151151585850152604090810151606091850182905290614cb8818601836148f5565b9a86019a9450505090830190600101614c73565b5090979650505050505050565b5f606082016060835280865180835260808501915060808160051b860101925060208089015f5b83811015614d62578786037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001855281518051151587528301516040848801819052614d4e818901836148f5565b975050509382019390820190600101614d00565b505085840381870152505050614d788186614c58565b905082810360408401526137a18185614c58565b5f805f60608486031215614d9e575f80fd5b8335614da981614932565b92506020840135614db981614932565b91506040840135614dc98161497e565b809150509250925092565b5f815180845260208085019450602084015f5b83811015614e0c5781516001600160a01b031687529582019590820190600101614de7565b509495945050505050565b602081525f610d856020830184614dd4565b5f805f60608486031215614e3b575f80fd5b614e4484614961565b95602085013595506040909401359392505050565b5f8060408385031215614e6a575f80fd5b614a0483614961565b5f805f60608486031215614e85575f80fd5b614e8e84614961565b92506020840135614e9e81614932565b929592945050506040919091013590565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b60208082528181018390525f906040808401600586901b8501820187855b88811015615016577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818b3603018112614f56575f80fd5b8a0160808135614f6581614932565b6001600160a01b0390811686528289013590614f8082614932565b16858901528187013587860152606080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112614fc3575f80fd5b90920188810192903567ffffffffffffffff811115614fe0575f80fd5b803603841315614fee575f80fd5b82828801526150008388018286614eaf565b978a019796505050928701925050600101614ef6565b509098975050505050505050565b5f82516150358184602087016148d3565b9190910192915050565b5f815160208301517fffffffff0000000000000000000000000000000000000000000000000000000080821693506004831015613f105760049290920360031b82901b161692915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516060810167ffffffffffffffff811182821017156150da576150da61508a565b60405290565b6040805190810167ffffffffffffffff811182821017156150da576150da61508a565b604051601f8201601f1916810167ffffffffffffffff8111828210171561512c5761512c61508a565b604052919050565b5f67ffffffffffffffff82111561514d5761514d61508a565b5060051b60200190565b5f82601f830112615166575f80fd5b815167ffffffffffffffff8111156151805761518061508a565b6151936020601f19601f84011601615103565b8181528460208386010111156151a7575f80fd5b6151b88260208301602087016148d3565b949350505050565b5f82601f8301126151cf575f80fd5b815160206151e46151df83615134565b615103565b82815260059290921b84018101918181019086841115615202575f80fd5b8286015b8481101561529b57805167ffffffffffffffff80821115615225575f80fd5b8189019150606080601f19848d0301121561523e575f80fd5b6152466150b7565b8784015161525381614932565b81526040848101516152648161497e565b828a0152918401519183831115615279575f80fd5b6152878d8a85880101615157565b908201528652505050918301918301615206565b509695505050505050565b5f805f606084860312156152b8575f80fd5b835167ffffffffffffffff808211156152cf575f80fd5b818601915086601f8301126152e2575f80fd5b815160206152f26151df83615134565b82815260059290921b8401810191818101908a841115615310575f80fd5b8286015b848110156153885780518681111561532a575f80fd5b87016040818e03601f1901121561533f575f80fd5b6153476150e0565b858201516153548161497e565b8152604082015188811115615367575f80fd5b6153758f8883860101615157565b8288015250845250918301918301615314565b50918901519197509093505050808211156153a1575f80fd5b6153ad878388016151c0565b935060408601519150808211156153c2575f80fd5b506153cf868287016151c0565b9150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112615035575f80fd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261546b575f80fd5b83018035915067ffffffffffffffff821115615485575f80fd5b602001915036819003821315614a52575f80fd5b818382375f9101908152919050565b828152604060208201525f6151b860408301846148f5565b5f602082840312156154d0575f80fd5b5051919050565b7fffffffff0000000000000000000000000000000000000000000000000000000081358181169160048510156155175780818660040360031b1b83161692505b505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f805f6060848603121561555e575f80fd5b835161556981614932565b602085015190935061557a8161497e565b604085015190925067ffffffffffffffff811115615596575f80fd5b6153cf86828701615157565b6001600160a01b03841681528215156020820152606060408201525f612d4860608301846148f5565b6001600160a01b0383168152604060208201525f6151b86040830184614dd456fea26469706673582212205a870f33e8159c08635f7653cecaeb4d147a9788ef435d7f51a5f95d4ab3360064736f6c63430008180033
Deployed Bytecode
0x6080604052600436106102da575f3560e01c8063863789d71161017b578063c368516c116100d1578063df7c138411610087578063ebf1ea8611610062578063ebf1ea8614610915578063f4fc35701461091d578063fd6046d714610930575f80fd5b8063df7c1384146108b6578063e21e537c146108d5578063e920e8e014610902575f80fd5b8063cb29955a116100b7578063cb29955a1461082d578063cdd8ea7814610884578063d44fee5a146108a3575f80fd5b8063c368516c146107fb578063c760d9211461080e575f80fd5b8063a4d25d1e11610131578063b9b70ff51161010c578063b9b70ff5146107c2578063c14c11bf146107d5578063c16ae7a4146107e8575f80fd5b8063a4d25d1e14610730578063a829aaf51461075c578063b03c130d1461076f575f80fd5b80639e716d58116101615780639e716d58146106f65780639f5c462a14610715578063a37d54af14610728575f80fd5b8063863789d71461069857806392d2fc01146106c6575f80fd5b80633b2416be1161023057806347cfdac4116101e6578063642ea23f116101c1578063642ea23f146106505780637f17c377146106635780637f5c92f314610685575f80fd5b806347cfdac4146105e0578063506d8c92146105ff5780635bedd1cd1461063d575f80fd5b8063430292b311610216578063430292b314610542578063442b172c1461056e57806346591032146105cd575f80fd5b80633b2416be146104f457806342e5349914610523575f80fd5b80631647292a1161029057806330f316671161026b57806330f31667146104785780633a1a3a1d1461048b5780633b10f3ef1461049e575f80fd5b80631647292a146103f857806318503a1e146104275780631f8b521514610465575f80fd5b8063116d0e93116102c0578063116d0e93146103a5578063129d21a0146103b857806312d6c936146103cb575f80fd5b806306fdde031461033457806310a7519814610392575f80fd5b36610330575f5474ff00000000000000000000000000000000000000001661032e576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b5f80fd5b34801561033f575f80fd5b5061037c6040518060400160405280601881526020017f457468657265756d205661756c7420436f6e6e6563746f72000000000000000081525081565b6040516103899190614920565b60405180910390f35b61032e6103a0366004614946565b61094f565b61032e6103b336600461498b565b610aaa565b61032e6103c636600461498b565b610bfd565b3480156103d6575f80fd5b506103ea6103e53660046149c0565b610d49565b604051908152602001610389565b348015610403575f80fd5b506104176104123660046149e8565b610d7a565b6040519015158152602001610389565b348015610432575f80fd5b50610446610441366004614946565b610d8c565b604080516001600160a01b039093168352901515602083015201610389565b61037c610473366004614a59565b610e1f565b61032e610486366004614946565b610f17565b348015610496575f80fd5b505f546103ea565b3480156104a9575f80fd5b506104176104b8366004614ac7565b6cffffffffffffffffffffffffff19165f9081526017602052604090205474010000000000000000000000000000000000000000900460ff1690565b3480156104ff575f80fd5b505f5477ff0000000000000000000000000000000000000000000000161515610417565b34801561052e575f80fd5b5061041761053d366004614946565b610f65565b34801561054d575f80fd5b505f5474ff0000000000000000000000000000000000000000161515610417565b348015610579575f80fd5b506105b5610588366004614946565b60601b6cffffffffffffffffffffffffff19165f908152601760205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001610389565b61032e6105db366004614946565b610fca565b3480156105eb575f80fd5b506104176105fa3660046149e8565b610ffc565b34801561060a575f80fd5b5061061e610619366004614946565b61101d565b6040516cffffffffffffffffffffffffff199091168152602001610389565b61032e61064b366004614ae0565b611038565b61032e61065e366004614ba7565b611485565b610676610671366004614be9565b611568565b60405161038993929190614cd9565b61032e610693366004614be9565b611719565b3480156106a3575f80fd5b505f5476ff00000000000000000000000000000000000000000000161515610417565b3480156106d1575f80fd5b505f5478ff000000000000000000000000000000000000000000000000161515610417565b348015610701575f80fd5b506104176107103660046149e8565b6119ce565b61032e610723366004614d8c565b6119ef565b61032e611c0e565b34801561073b575f80fd5b5061074f61074a366004614946565b611c42565b6040516103899190614e17565b61032e61076a366004614e29565b611c65565b34801561077a575f80fd5b506103ea610789366004614e59565b6cffffffffffffffffffffffffff1982165f9081526018602090815260408083206001600160a01b038516845290915290205492915050565b61037c6107d0366004614a59565b611d42565b61032e6107e3366004614e73565b611f38565b61032e6107f6366004614be9565b612073565b61032e6108093660046149e8565b6121c5565b348015610819575f80fd5b506104176108283660046149e8565b612339565b348015610838575f80fd5b50610417610847366004614ac7565b6cffffffffffffffffffffffffff19165f908152601760205260409020547501000000000000000000000000000000000000000000900460ff1690565b34801561088f575f80fd5b5061041761089e366004614946565b612346565b61032e6108b13660046149e8565b6123a3565b3480156108c1575f80fd5b506103ea6108d0366004614946565b612505565b3480156108e0575f80fd5b505f5475ff000000000000000000000000000000000000000000161515610417565b61032e6109103660046149e8565b612597565b61032e6126b6565b61032e61092b366004614946565b61273c565b34801561093b575f80fd5b5061074f61094a366004614946565b612850565b5f5475ff0000000000000000000000000000000000000000008116156109a1576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109f05f75ff00000000000000000000000000000000000000000083175b7fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039091161790565b5f9081556001600160a01b038084168252601b602052604090912054839160ff82169161010090041660018214610a53576040517ff1be451900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610a95576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50610aa39050600184612873565b50505f5550565b81610ab6815f80612cee565b506cffffffffffffffffffffffffff1983165f9081526017602052604090205460ff750100000000000000000000000000000000000000000090910416151582151514610bf85781158015610b2257505f5474ff00000000000000000000000000000000000000001615155b15610b59576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1983165f818152601760205260409081902080548515157501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff909116179055517f6321df4e44267d425279195e7488fadba1a42d5cce9e84f596d5cf696f4449cd90610bef90851515815260200190565b60405180910390a25b505050565b81610c09815f80612cee565b506cffffffffffffffffffffffffff1983165f9081526017602052604090205460ff7401000000000000000000000000000000000000000090910416151582151514610bf85781158015610c7d57505f5474ff000000000000000000000000000000000000000016151580610c7d57503330145b15610cb4576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1983165f8181526017602052604090819020805485151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909116179055517faf5120bc58372f0063d8362c9bba9070c462c07ae24c24082d080a426432798b90610bef90851515815260200190565b6cffffffffffffffffffffffffff1982165f9081526019602090815260408083208484529091529020545b92915050565b5f610d858383612d51565b9392505050565b5f80610d9f5f546001600160a01b031690565b91506001600160a01b038216610de1576040517f5217b8ae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831615610e16576001600160a01b0382165f908152601b60205260409020610e119084612dda565b610e18565b5f5b9050915091565b5f5460609075ff000000000000000000000000000000000000000000811615610e74576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615610ec5576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5474ff000000000000000000000000000000000000000081175f908155610ef18888888888612e7e565b9350905080610f0357610f0383612f9f565b610f0c82612fe0565b505095945050505050565b5f5474ff00000000000000000000000000000000000000001615610f5057610f40600182613039565b50610f4c600c33613039565b5050565b610f598161329f565b610f6233613323565b50565b5f805475ff0000000000000000000000000000000000000000001615610fb7576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2600183612dda565b90505b919050565b5f5474ff00000000000000000000000000000000000000001615610ff357610f4c600182613039565b610f628161329f565b6001600160a01b0382165f908152601b60205260408120610d859083612dda565b5f606082901b6cffffffffffffffffffffffffff1916610fc2565b5f5475ff00000000000000000000000000000000000000000081161561108a576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156110db576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b503330148061110657506001600160a01b0389161580159061110657506001600160a01b0389163314155b1561113d576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038a16158061115957506111578a6133a2565b155b15611190576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608a901b6cffffffffffffffffffffffffff19165f818152601760205260409020547501000000000000000000000000000000000000000000900460ff1615611206576040517f4426359200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1981165f9081526019602090815260408083208c84529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81148061125e5750888114155b15611295576040517fa82b84bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50428710156112d0576040517f7c9bb1cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f84900361130a576040517fe85c620e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61131b8c8c8c8c8c8c8c8c6133e2565b905061135c8185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506135bc92505050565b6001600160a01b03168c6001600160a01b0316141580156113ba57506113b88c8286868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061367692505050565b155b156113f1576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1982165f8181526019602090815260408083208e845282529182902060018d01905590518b81528c92917fb0dcec731e48090736be6db10ad9f9581d0ec5fc0f1925a8e267b64b614b08d6910160405180910390a35f80611463308f8b8b8b6137ab565b91509150816114755761147581612f9f565b5050505050505050505050505050565b5f5475ff0000000000000000000000000000000000000000008116156114d7576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615611528576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508261153681600180613a11565b506001600160a01b0384165f908152601a60205260409020611559908484613c0d565b61156284610fca565b50505050565b60608060605f80306001600160a01b0316306001600160a01b0316637f5c92f3898960405160240161159b929190614ed8565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e09490941b9390931790925290516115e89250615024565b5f60405180830381855af49150503d805f8114611620576040519150601f19603f3d011682016040523d82523d5f602084013e611625565b606091505b50915091508115611662576040517f4cd2d4f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004815110806116bc57507fd4b0e4d3000000000000000000000000000000000000000000000000000000006116978261503f565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156116ca576116ca81612f9f565b80517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc81016004830190815291611709918101602001906024016152a6565b9199909850909650945050505050565b5f5475ff00000000000000000000000000000000000000000081161561176b576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156117bc576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f546060908190819074ff0000000000000000000000000000000000000000811615611815576040517fb83566c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b78ff000000ff000000000000000000000000000000000000000081175f55848067ffffffffffffffff81111561184d5761184d61508a565b60405190808252806020026020018201604052801561189257816020015b604080518082019091525f81526060602082015281526020019060019003908161186b5790505b5094505f5b8181101561194a57368888838181106118b2576118b26153d9565b90506020028101906118c49190615406565b90506118fd6118d66020830183614946565b6118e66040840160208501614946565b60408401356118f86060860186615438565b612e7e565b88848151811061190f5761190f6153d9565b60200260200101515f0189858151811061192b5761192b6153d9565b6020908102919091018101510191909152901515905250600101611897565b5061196d5f75ff00000000000000000000000000000000000000000084176109bf565b5f90815561197a90613dd8565b93506119866001613dd8565b5f8390556040517fd4b0e4d30000000000000000000000000000000000000000000000000000000081529093506119c590869086908690600401614cd9565b60405180910390fd5b6001600160a01b0382165f908152601a60205260408120610d859083612dda565b5f6119fc8460015f613a11565b90506cffffffffffffffffffffffffff19606085901b165f61010086841810611a4c576cffffffffffffffffffffffffff1982165f908152601760205260409020546001600160a01b0316611a4e565b825b9050826001600160a01b0316816001600160a01b031614158015611a845750826001600160a01b0316856001600160a01b031614155b15611abb576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516301480611ad55750610100858218105b15611b0c576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1982165f9081526018602090815260408083206001600160a01b038981168552925282205460018985189092169190911b9186611b5a5782198216611b5e565b8282175b9050808203611b99576040517f655156bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1985165f8181526018602090815260408083206001600160a01b038d1680855290835292819020859055518481529192917f7ba31654d8467e98b6bd4fc56ddde246de9ade831cf860c7ac695579aecb9564910160405180910390a3505050505050505050565b5f5474ff00000000000000000000000000000000000000001615611c3757610f62600c33613039565b611c4033613323565b565b6001600160a01b0381165f908152601a60205260409020606090610fc290613f18565b82611c71815f80612cee565b506cffffffffffffffffffffffffff1984165f908152601960209081526040808320868452909152902054828110611cd5576040517fa82b84bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1985165f81815260196020908152604080832088845282529182902086905581518481529081018690528692917f3b8510174a91acb36200f7427c1889f934941fd89ed86faf390749b4c2b46337910160405180910390a35050505050565b5f5460609075ff000000000000000000000000000000000000000000811615611d97576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615611de8576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b038086165f908152601b6020526040902054869160ff82169161010090041660018214611e49576040517ff1be451900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314611e8b576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506001600160a01b0386165f908152601a60205260409020611eae9088612dda565b611ee4576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5476ff00ff000000000000000000000000000000000000000081175f908155611f1189898989896137ab565b9450905080611f2357611f2384612f9f565b611f2c82612fe0565b50505095945050505050565b5f611f44845f80612cee565b90506001600160a01b038316301480611f605750610100838218105b15611f97576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1984165f9081526018602090815260408083206001600160a01b0387168452909152902054829003612003576040517f655156bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6cffffffffffffffffffffffffff1984165f8181526018602090815260408083206001600160a01b03881680855290835292819020869055518581529192917f7ba31654d8467e98b6bd4fc56ddde246de9ade831cf860c7ac695579aecb9564910160405180910390a350505050565b5f5475ff0000000000000000000000000000000000000000008116156120c5576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612116576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f5474ff000000000000000000000000000000000000000081175f90815582905b818110156121bb5736858583818110612153576121536153d9565b90506020028101906121659190615406565b90505f8061219b6121796020850185614946565b6121896040860160208701614946565b60408601356118f86060880188615438565b91509150816121ad576121ad81612f9f565b505050806001019050612138565b5061156282612fe0565b5f5475ff000000000000000000000000000000000000000000811615612217576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612268576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161227681600180613a11565b50306001600160a01b038316036122b9576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383165f908152601b602052604090206122da9083613039565b1561233057816001600160a01b0316836001600160a01b03167f9919d437ee612d4ec7bba88a7d9bc4fc36a0a23608ad6259252711a46b708af96001604051612327911515815260200190565b60405180910390a35b610bf883610fca565b5f61010082841810610d85565b5f805475ff0000000000000000000000000000000000000000001615612398576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc2600c83612dda565b5f5475ff0000000000000000000000000000000000000000008116156123f5576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff00000000000000000000000000000000000000000000811615612446576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161245481600180613a11565b50306001600160a01b03831603612497576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383165f908152601a602052604090206124b89083613039565b1561233057816001600160a01b0316836001600160a01b03167ff022705c827017c972043d1984cfddc7958c9f4685b4d9ce8bd68696f4381cd26001604051612327911515815260200190565b5f805475ff0000000000000000000000000000000000000000001615612557576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03165f908152601b60205260409020547501000000000000000000000000000000000000000000900469ffffffffffffffffffff1690565b5f5475ff0000000000000000000000000000000000000000008116156125e9576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff0000000000000000000000000000000000000000000081161561263a576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508161264881600180613a11565b506001600160a01b0383165f908152601a6020526040902061266a9083612873565b1561233057816001600160a01b0316836001600160a01b03167ff022705c827017c972043d1984cfddc7958c9f4685b4d9ce8bd68696f4381cd25f604051612327911515815260200190565b5f5475ff000000000000000000000000000000000000000000811615612708576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61272a5f75ff00000000000000000000000000000000000000000083176109bf565b5f55612737600c33612873565b505f55565b5f5475ff00000000000000000000000000000000000000000081161561278e576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b76ff000000000000000000000000000000000000000000008116156127df576040517f0ddfd8da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b0381165f908152601b602052604090206128019033612873565b15612847576040515f815233906001600160a01b038316907f9919d437ee612d4ec7bba88a7d9bc4fc36a0a23608ad6259252711a46b708af99060200160405180910390a35b610f6281610fca565b6001600160a01b0381165f908152601b60205260409020606090610fc290613f18565b81545f9061010081046001600160a01b03169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff168184036128c2575f9350505050610d74565b5f856001600160a01b0316846001600160a01b031614612932575060015b8281101561291f57856001600160a01b03168760010182600a8110612907576129076153d9565b01546001600160a01b03161461291f576001016128e0565b828103612932575f945050505050610d74565b826001036129b257507effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff909116750100000000000000000000000000000000000000000002167f01000000000000000000000000000000000000000000000000000000000000001785555060019150610d749050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83015f6001890182600a81106129eb576129eb6153d9565b019050818314612bef57825f03612ad157805489547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff000000000000000000000000000000000000000000000000000000000000009092166101006001600160a01b03909416939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16178955612cba565b80546001600160a01b031660018a0184600a8110612af157612af16153d9565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392831617905589547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff00000000000000000000000000000000000000000000000000000000000000909216610100938a16939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16178955612cba565b88547f010000000000000000000000000000000000000000000000000000000000000060ff84167fff000000000000000000000000000000000000000000000000000000000000009092166101006001600160a01b038a16027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00161791909117750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789555b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001979650505050505050565b6cffffffffffffffffffffffffff1983165f908152601760205260408120546001600160a01b0316612d488115612d255781612d41565b73ffffffffffffffffffffffffffffffffffffff00606087901c165b8585613a11565b95945050505050565b6cffffffffffffffffffffffffff19606083901b165f818152601760205260408120549091906001600160a01b031680612d8f575f92505050610d74565b6cffffffffffffffffffffffffff19919091165f9081526018602090815260408083206001600160a01b0396871684529091529020546001949091189092169290921b161515919050565b81545f906001600160a01b036101008204169060ff16808303612e01575f92505050610d74565b836001600160a01b0316826001600160a01b031603612e2557600192505050610d74565b60015b81811015612e7357846001600160a01b03168660010182600a8110612e4f57612e4f6153d9565b01546001600160a01b031603612e6b5760019350505050610d74565b600101612e28565b505f95945050505050565b5f6060306001600160a01b03881603612f64576001600160a01b03861615612ed2576040517f8133abd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415612f0a576040517fbb6de1c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040513090612f1c9086908690615499565b5f60405180830381855af49150503d805f8114612f54576040519150601f19603f3d011682016040523d82523d5f602084013e612f59565b606091505b509092509050612f95565b6001600160a01b0387163314612f8257612f8086600180613a11565b505b612f8f87878787876137ab565b90925090505b9550959350505050565b805115612fae57805181602001fd5b6040517f38ae747c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b74ff000000000000000000000000000000000000000081166130355761301e5f75ff00000000000000000000000000000000000000000083176109bf565b5f90815561302b9061401a565b613035600161401a565b5f55565b81545f9061010081046001600160a01b03169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff1681840361314d5785547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9092167501000000000000000000000000000000000000000000029190911674ffffffffffffffffffffffffffffffffffffffffff6001600160a01b038716610100027fffffffffffffffffffffff00000000000000000000000000000000000000000090931692909217600190811792909216177f01000000000000000000000000000000000000000000000000000000000000001786559250610d74915050565b846001600160a01b0316836001600160a01b031603613171575f9350505050610d74565b60015b828110156131bf57856001600160a01b03168760010182600a811061319b5761319b6153d9565b01546001600160a01b0316036131b7575f945050505050610d74565b600101613174565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff68201613219576040517f3572cf8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848660010183600a811061322f5761322f6153d9565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555084547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600191820160ff1617909455509192915050565b5f5475ff0000000000000000000000000000000000000000008116156132f1576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133135f75ff00000000000000000000000000000000000000000083176109bf565b5f5561331e8261404c565b5f5550565b5f5475ff000000000000000000000000000000000000000000811615613375576040517f7c1b290800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133975f75ff00000000000000000000000000000000000000000083176109bf565b5f5561331e82614069565b5f61010082101580156133b9575061010080831810155b8015610fc257506101007342000000000000000000000000000000000000008318101592915050565b5f807f000000000000000000000000000000000000000000000000000000000000009246146134b757604080518082018252601881527f457468657265756d205661756c7420436f6e6e6563746f72000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f040f0adc9d57e8bea9a4602b7065ad7261f63d709e7be772afe0ceb20a92d47381840152466060820152306080808301919091528351808303909101815260a090910190925281519101206134d9565b7f9f7d26e95495b8ea5a84553ed5215235e50221ac907be33e08ccf73960421f4f5b90505f7f4ae56dd541cf527f212121ebe3756a7631631f85f66a3073e982c01a6e2ecbf28b8b8b8b8b8b8b8b604051613513929190615499565b6040805191829003822060208301999099526001600160a01b0397881690820152959094166060860152608085019290925260a084015260c083015260e0820152610100810191909152610120016040516020818303038152906040528051906020012090507f19010000000000000000000000000000000000000000000000000000000000005f52816002528060225260425f2092505f602252505098975050505050505050565b5f81516041146135cd57505f610d74565b6020820151604083015160608401515f1a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115613611575f9350505050610d74565b604080515f81526020810180835288905260ff831691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015613661573d5f803e3d5ffd5b5050604051601f190151979650505050505050565b5f836001600160a01b03163b5f0361368f57505f610d85565b5f80856001600160a01b031685856040516024016136ae9291906154a8565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e00000000000000000000000000000000000000000000000000000000179052516137119190615024565b5f60405180830381855afa9150503d805f8114613749576040519150601f19603f3d011682016040523d82523d5f602084013e61374e565b606091505b5091509150818015613761575080516020145b80156137a1575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061379f90830160209081019084016154c0565b145b9695505050505050565b5f60607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85036137dd57479450613817565b47851115613817576040517fbb6de1c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805490613823614074565b9050610100888218108061383f57506001600160a01b03891633145b8061385257506001600160a01b03891630145b80613875575076ff00000000000000000000000000000000000000000000821615155b156138af577fffffffffffffffff00ffffff000000000000000000000000000000000000000082166001600160a01b038916175f556138fa565b77ff00000000000000000000000000000000000000000000006001600160a01b0389167fffffffffffffffffffffffff0000000000000000000000000000000000000000841617175f555b6001600160a01b038916606089901b6cffffffffffffffffffffffffff19166cffffffffffffffffffffffffff19166001600160a01b0383167f6e9738e5aa38fe1517adbb480351ec386ece82947737b18badbcad1e911133ec8b61395f8a8c6154d7565b604080516001600160a01b0390931683527fffffffff0000000000000000000000000000000000000000000000000000000090911660208301520160405180910390a4886001600160a01b03168787876040516139bd929190615499565b5f6040518083038185875af1925050503d805f81146139f7576040519150601f19603f3d011682016040523d82523d5f602084013e6139fc565b606091505b505f9390935599919850909650505050505050565b606083901b6cffffffffffffffffffffffffff19165f818152601760205260408120549091906001600160a01b0381169074010000000000000000000000000000000000000000900460ff1683613a66614074565b90505f6101008983181015613b28576001600160a01b038416613b0b576cffffffffffffffffffffffffff1985165f8181526017602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038716908117909155905194965086949092917f67cb2734834e775d6db886bf16ac03d7273b290223ee5363354b385ec5b643b091a3506001613b28565b816001600160a01b0316846001600160a01b031603613b28575060015b80158015613b335750875b8015613b445750613b448983612d51565b15613b4d575060015b808015613b6c5750886001600160a01b0316846001600160a01b031614155b8015613b8157506001600160a01b0389163b15155b15613b8957505f5b80613bc0576040517fe07f2e6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b868015613bca5750825b15613c01576040517fd80a9cac00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50979650505050505050565b82546001600160a01b036101008204169060ff90811690838116908516101580613c3a5750808360ff1610155b15613c71576040517f63df817100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff165f03613d0b57846001018360ff16600a8110613c9357613c936153d9565b01546001600160a01b03168286600181810160ff8816600a8110613cb957613cb96153d9565b0180546001600160a01b039485167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905581549383166101009190910a908102920219909216179055613dd1565b846001018360ff16600a8110613d2357613d236153d9565b01546001600160a01b03166001860160ff8616600a8110613d4657613d466153d9565b01546001600160a01b03166001870160ff8716600a8110613d6957613d696153d9565b015f6001890160ff8816600a8110613d8357613d836153d9565b0180546001600160a01b039485167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905581549383166101009190910a9081029202199092161790555b5050505050565b60605f80836001811115613dee57613dee61551f565b14613e0557613e00600c6140906141d1565b613e12565b613e1260016144326141d1565b80519091508067ffffffffffffffff811115613e3057613e3061508a565b604051908082528060200260200182016040528015613e7c57816020015b60408051606080820183525f808352602083015291810191909152815260200190600190039081613e4e5790505b5092505f5b81811015613f10575f805f858481518110613e9e57613e9e6153d9565b6020026020010151806020019051810190613eb9919061554c565b9250925092506040518060600160405280846001600160a01b03168152602001831515815260200182815250878581518110613ef757613ef76153d9565b6020026020010181905250505050806001019050613e81565b505050919050565b80546060906001600160a01b036101008204169060ff165f8167ffffffffffffffff811115613f4957613f4961508a565b604051908082528060200260200182016040528015613f72578160200160208202803683370190505b509050815f03613f8457949350505050565b82815f81518110613f9757613f976153d9565b6001600160a01b039092166020928302919091019091015260015b82811015614011578560010181600a8110613fcf57613fcf6153d9565b015482516001600160a01b0390911690839083908110613ff157613ff16153d9565b6001600160a01b0390921660209283029190910190910152600101613fb2565b50949350505050565b5f81600181111561402d5761402d61551f565b1461403f57610f62600c61406961475b565b610f62600161404c61475b565b5f8061405783614432565b9150915081610bf857610bf881612f9f565b5f8061405783614090565b5f33301461408157503390565b505f546001600160a01b031690565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4b3d12230000000000000000000000000000000000000000000000000000000017905290515f9160609183916001600160a01b038616916141049190615024565b5f604051808303815f865af19150503d805f811461413d576040519150601f19603f3d011682016040523d82523d5f602084013e614142565b606091505b5092509050808015614155575081516020145b8015614195575081517f4b3d1223000000000000000000000000000000000000000000000000000000009061419390840160209081019085016154c0565b145b6040519093506001600160a01b038516907faea973cfb51ea8ca328767d72f105b5b9d2360c65f5ac4110a2c4470434471c9905f90a250915091565b815460609060ff81169061010081046001600160a01b0316907501000000000000000000000000000000000000000000900469ffffffffffffffffffff165f8367ffffffffffffffff8111156142295761422961508a565b60405190808252806020026020018201604052801561425c57816020015b60608152602001906001900390816142475790505b509050835f03614271579350610d7492505050565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff8316750100000000000000000000000000000000000000000002167f01000000000000000000000000000000000000000000000000000000000000001787555f806142ea8563ffffffff8a16565b91509150848282604051602001614303939291906155a2565b604051602081830303815290604052835f81518110614324576143246153d9565b602090810291909101015260015b86811015614424575f8a60010182600a8110614350576143506153d9565b0154604080518082019091525f81526001602082018190526001600160a01b039092169250908c0183600a8110614389576143896153d9565b82516020909301516bffffffffffffffffffffffff1674010000000000000000000000000000000000000000026001600160a01b03909316929092179101556143d58163ffffffff8c16565b60405191955093506143ef908290869086906020016155a2565b604051602081830303815290604052858381518110614410576144106153d9565b602090810291909101015250600101614332565b509198975050505050505050565b6001600160a01b038082165f908152601b602052604081208054919260609260ff80821692610100830416917f01000000000000000000000000000000000000000000000000000000000000009004168286036144a757600160405180602001604052805f8152509550955050505050915091565b60018311156145155750506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ff1be4519000000000000000000000000000000000000000000000000000000001790525f969095509350505050565b6001600160a01b038781165f908152601a602052604081209091841690899061453d90613f18565b60405160240161454e9291906155cb565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb168c58f00000000000000000000000000000000000000000000000000000000179052516145b19190615024565b5f60405180830381855afa9150503d805f81146145e9576040519150601f19603f3d011682016040523d82523d5f602084013e6145ee565b606091505b5096509050808015614601575085516020145b8015614641575085517fb168c58f000000000000000000000000000000000000000000000000000000009061463f90880160209081019089016154c0565b145b9650861561471157845460ff8581167fffffffffffffffffffffff000000000000000000000000000000000000000000909216919091176101006001600160a01b038616021774ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000004269ffffffffffffffffffff16027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177f0100000000000000000000000000000000000000000000000000000000000000918416919091021785555b826001600160a01b0316886001600160a01b03167f889a4d4628b31342e420737e2aeb45387087570710d26239aa8a5f13d3e829d460405160405180910390a35050505050915091565b815460ff81169061010081046001600160a01b0316907501000000000000000000000000000000000000000000900469ffffffffffffffffffff165f8390036147a5575050505050565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff8216750100000000000000000000000000000000000000000002167f010000000000000000000000000000000000000000000000000000000000000017855561481c8263ffffffff8616565b60015b838110156148cb575f8660010182600a811061483d5761483d6153d9565b0154604080518082019091525f81526001602082018190526001600160a01b03909216925090880183600a8110614876576148766153d9565b82516020909301516bffffffffffffffffffffffff1674010000000000000000000000000000000000000000026001600160a01b03909316929092179101556148c28163ffffffff8816565b5060010161481f565b505050505050565b5f5b838110156148ed5781810151838201526020016148d5565b50505f910152565b5f815180845261490c8160208601602086016148d3565b601f01601f19169290920160200192915050565b602081525f610d8560208301846148f5565b6001600160a01b0381168114610f62575f80fd5b5f60208284031215614956575f80fd5b8135610d8581614932565b80356cffffffffffffffffffffffffff1981168114610fc5575f80fd5b8015158114610f62575f80fd5b5f806040838503121561499c575f80fd5b6149a583614961565b915060208301356149b58161497e565b809150509250929050565b5f80604083850312156149d1575f80fd5b6149da83614961565b946020939093013593505050565b5f80604083850312156149f9575f80fd5b8235614a0481614932565b915060208301356149b581614932565b5f8083601f840112614a24575f80fd5b50813567ffffffffffffffff811115614a3b575f80fd5b602083019150836020828501011115614a52575f80fd5b9250929050565b5f805f805f60808688031215614a6d575f80fd5b8535614a7881614932565b94506020860135614a8881614932565b935060408601359250606086013567ffffffffffffffff811115614aaa575f80fd5b614ab688828901614a14565b969995985093965092949392505050565b5f60208284031215614ad7575f80fd5b610d8582614961565b5f805f805f805f805f806101008b8d031215614afa575f80fd5b8a35614b0581614932565b995060208b0135614b1581614932565b985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b013567ffffffffffffffff80821115614b4d575f80fd5b614b598e838f01614a14565b909650945060e08d0135915080821115614b71575f80fd5b50614b7e8d828e01614a14565b915080935050809150509295989b9194979a5092959850565b803560ff81168114610fc5575f80fd5b5f805f60608486031215614bb9575f80fd5b8335614bc481614932565b9250614bd260208501614b97565b9150614be060408501614b97565b90509250925092565b5f8060208385031215614bfa575f80fd5b823567ffffffffffffffff80821115614c11575f80fd5b818501915085601f830112614c24575f80fd5b813581811115614c32575f80fd5b8660208260051b8501011115614c46575f80fd5b60209290920196919550909350505050565b5f82825180855260208086019550808260051b8401018186015f5b84811015614ccc57858303601f19018952815180516001600160a01b0316845284810151151585850152604090810151606091850182905290614cb8818601836148f5565b9a86019a9450505090830190600101614c73565b5090979650505050505050565b5f606082016060835280865180835260808501915060808160051b860101925060208089015f5b83811015614d62578786037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001855281518051151587528301516040848801819052614d4e818901836148f5565b975050509382019390820190600101614d00565b505085840381870152505050614d788186614c58565b905082810360408401526137a18185614c58565b5f805f60608486031215614d9e575f80fd5b8335614da981614932565b92506020840135614db981614932565b91506040840135614dc98161497e565b809150509250925092565b5f815180845260208085019450602084015f5b83811015614e0c5781516001600160a01b031687529582019590820190600101614de7565b509495945050505050565b602081525f610d856020830184614dd4565b5f805f60608486031215614e3b575f80fd5b614e4484614961565b95602085013595506040909401359392505050565b5f8060408385031215614e6a575f80fd5b614a0483614961565b5f805f60608486031215614e85575f80fd5b614e8e84614961565b92506020840135614e9e81614932565b929592945050506040919091013590565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b60208082528181018390525f906040808401600586901b8501820187855b88811015615016577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818b3603018112614f56575f80fd5b8a0160808135614f6581614932565b6001600160a01b0390811686528289013590614f8082614932565b16858901528187013587860152606080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112614fc3575f80fd5b90920188810192903567ffffffffffffffff811115614fe0575f80fd5b803603841315614fee575f80fd5b82828801526150008388018286614eaf565b978a019796505050928701925050600101614ef6565b509098975050505050505050565b5f82516150358184602087016148d3565b9190910192915050565b5f815160208301517fffffffff0000000000000000000000000000000000000000000000000000000080821693506004831015613f105760049290920360031b82901b161692915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516060810167ffffffffffffffff811182821017156150da576150da61508a565b60405290565b6040805190810167ffffffffffffffff811182821017156150da576150da61508a565b604051601f8201601f1916810167ffffffffffffffff8111828210171561512c5761512c61508a565b604052919050565b5f67ffffffffffffffff82111561514d5761514d61508a565b5060051b60200190565b5f82601f830112615166575f80fd5b815167ffffffffffffffff8111156151805761518061508a565b6151936020601f19601f84011601615103565b8181528460208386010111156151a7575f80fd5b6151b88260208301602087016148d3565b949350505050565b5f82601f8301126151cf575f80fd5b815160206151e46151df83615134565b615103565b82815260059290921b84018101918181019086841115615202575f80fd5b8286015b8481101561529b57805167ffffffffffffffff80821115615225575f80fd5b8189019150606080601f19848d0301121561523e575f80fd5b6152466150b7565b8784015161525381614932565b81526040848101516152648161497e565b828a0152918401519183831115615279575f80fd5b6152878d8a85880101615157565b908201528652505050918301918301615206565b509695505050505050565b5f805f606084860312156152b8575f80fd5b835167ffffffffffffffff808211156152cf575f80fd5b818601915086601f8301126152e2575f80fd5b815160206152f26151df83615134565b82815260059290921b8401810191818101908a841115615310575f80fd5b8286015b848110156153885780518681111561532a575f80fd5b87016040818e03601f1901121561533f575f80fd5b6153476150e0565b858201516153548161497e565b8152604082015188811115615367575f80fd5b6153758f8883860101615157565b8288015250845250918301918301615314565b50918901519197509093505050808211156153a1575f80fd5b6153ad878388016151c0565b935060408601519150808211156153c2575f80fd5b506153cf868287016151c0565b9150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112615035575f80fd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261546b575f80fd5b83018035915067ffffffffffffffff821115615485575f80fd5b602001915036819003821315614a52575f80fd5b818382375f9101908152919050565b828152604060208201525f6151b860408301846148f5565b5f602082840312156154d0575f80fd5b5051919050565b7fffffffff0000000000000000000000000000000000000000000000000000000081358181169160048510156155175780818660040360031b1b83161692505b505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f805f6060848603121561555e575f80fd5b835161556981614932565b602085015190935061557a8161497e565b604085015190925067ffffffffffffffff811115615596575f80fd5b6153cf86828701615157565b6001600160a01b03841681528215156020820152606060408201525f612d4860608301846148f5565b6001600160a01b0383168152604060208201525f6151b86040830184614dd456fea26469706673582212205a870f33e8159c08635f7653cecaeb4d147a9788ef435d7f51a5f95d4ab3360064736f6c63430008180033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.