Overview
ETH Balance
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Loading...
Loading
Contract Name:
TezoroLendingAgent
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
No with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; // import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; // import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/access/Ownable.sol"; import "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol"; import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; import "openzeppelin-contracts/contracts/utils/introspection/IERC165.sol"; import "./interfaces/ILendingAdapter.sol"; import "./interfaces/ILendingAdapterWithWithdraw.sol"; import "./interfaces/ILendingAdapterWithRewards.sol"; import "./interfaces/IDistributionStrategy.sol"; import "./interfaces/IAutomationChecker.sol"; import "./TezoroLendingLens.sol"; import "./TezoroLendingAgentFactory.sol"; contract TezoroLendingAgent is Ownable, ReentrancyGuard, IAutomationChecker { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; error InvalidAddress(); error InvalidOwner(); error InvalidLens(); error InvalidStrategy(); error InvalidStrategyCacheStorage(); error InvalidAdapter(); error NoAdaptersProvided(); error AdapterAlreadyRegistered(); error AdapterNotFound(); error MismatchedInputLengths(); error NoTokensToWithdraw(); error ExternalCallFailed(bytes reason); event TokenDistributed( address indexed token, address indexed adapter, uint256 amount ); event Rebalanced( address indexed token, address indexed fromAdapter, address indexed toAdapter, uint256 withdrawnAmount, uint256 depositedAmount ); event InsufficientBalance(address indexed token); event NoAdapterFound(address indexed token); event ProtocolAdapterRegistered(address indexed adapter); event ProtocolAdapterUnregistered(address indexed adapter); event TokenWithdrawn(address indexed token, uint256 amount); event StrategyChanged(address oldStrategy, address newStrategy); event StrategyStorageChanged( address oldStrategyStorage, address newStrategyStorage ); event PerformanceFeeCollected( address indexed token, address indexed adapter, uint256 amount ); struct Allocation { address adapter; uint256 timestamp; } mapping(address adapter => mapping(address token => uint256 principal)) public supplied; mapping(address adapter => mapping(address token => uint256 interestAmount)) public accruedInterests; mapping(address token => Allocation[] allocation) public allocation; EnumerableSet.AddressSet private adapters; address public lens; uint256 public deploymentTimestamp; address public strategy; address public strategyCacheStorage; address public factory; bool public initialized; function initialize( address _owner, address _lens, address[] memory _adapters, address _strategy, address _strategyCacheStorage, address _factory ) external { if (initialized) revert(); initialized = true; if (_owner == address(0)) revert InvalidOwner(); _transferOwnership(_owner); if (_factory == address(0)) revert InvalidAddress(); if (_lens == address(0)) revert InvalidLens(); if (_strategy == address(0)) revert InvalidStrategy(); if (_strategyCacheStorage == address(0)) revert InvalidStrategyCacheStorage(); if (_adapters.length == 0) revert NoAdaptersProvided(); lens = _lens; deploymentTimestamp = block.timestamp; strategy = _strategy; strategyCacheStorage = _strategyCacheStorage; for (uint256 i = 0; i < _adapters.length; i++) { if (_adapters[i] == address(0)) revert InvalidAdapter(); adapters.add(_adapters[i]); } } function _safeCall(address target, bytes memory data) internal { (bool success, bytes memory ret) = target.call(data); if (!success) revert ExternalCallFailed(ret); } function singletonAddressArray( address value ) internal pure returns (address[] memory arr) { arr = new address[](1); arr[0] = value; } function recoverERC20( address tokenAddress, uint256 tokenAmount ) external onlyOwner { IERC20(tokenAddress).safeTransfer(owner(), tokenAmount); } function setStrategy(address _strategy) external onlyOwner { if (_strategy == address(0)) revert InvalidStrategy(); emit StrategyChanged(strategy, _strategy); strategy = _strategy; } function setStrategyCacheStorage( address _strategyCacheStorage ) external onlyOwner { if (_strategyCacheStorage == address(0)) revert InvalidStrategyCacheStorage(); emit StrategyStorageChanged( strategyCacheStorage, _strategyCacheStorage ); strategyCacheStorage = _strategyCacheStorage; } //-------------------------------------------------------------------------- // Adapters management //-------------------------------------------------------------------------- function registerProtocolAdapter(address adapter) external onlyOwner { if (adapter == address(0)) revert InvalidAdapter(); if (adapters.contains(adapter)) revert AdapterAlreadyRegistered(); adapters.add(adapter); emit ProtocolAdapterRegistered(adapter); } function unregisterProtocolAdapter(address adapter) external onlyOwner { if (!adapters.contains(adapter)) revert AdapterNotFound(); adapters.remove(adapter); emit ProtocolAdapterUnregistered(adapter); } function getAdapters() external view returns (address[] memory) { return adapters.values(); } //-------------------------------------------------------------------------- // Distribution logic //-------------------------------------------------------------------------- function _approveIfNeeded( IERC20 token, address spender, uint256 amount ) internal { if (token.allowance(address(this), spender) < amount) { token.approve(spender, 0); token.approve(spender, amount); } } function _transferTokenIfNonZero( address token, address recipient ) internal { uint256 amount = IERC20(token).balanceOf(address(this)); if (amount == 0) revert NoTokensToWithdraw(); IERC20(token).safeTransfer(recipient, amount); emit TokenWithdrawn(token, amount); } function _getCurrentAdapterAndAmount( address token ) internal view returns (address currentAdapter, uint256 currentAmount) { address[] memory currentAdapters = adapters.values(); for (uint256 i = 0; i < currentAdapters.length; i++) { address adapter = currentAdapters[i]; uint256 amount = supplied[adapter][token]; if (amount > 0) return (adapter, amount); } return (address(0), 0); } function _calculateRebalancePlan( address[] memory tokens, address[] memory strategyAdapters ) internal view returns ( address[] memory tokensToMove, address[] memory fromAdapters, address[] memory toAdapters, uint256[] memory withdrawAmounts, uint256[] memory depositAmounts, uint256 moveCount ) { tokensToMove = new address[](tokens.length); fromAdapters = new address[](tokens.length); toAdapters = new address[](tokens.length); withdrawAmounts = new uint256[](tokens.length); depositAmounts = new uint256[](tokens.length); moveCount = 0; for (uint256 i = 0; i < tokens.length; i++) { address token = tokens[i]; address strategyAdapter = strategyAdapters[i]; if (strategyAdapter == address(0)) { continue; } ( address currentAdapter, uint256 currentAmount ) = _getCurrentAdapterAndAmount(token); if (currentAdapter != strategyAdapter) { tokensToMove[moveCount] = token; fromAdapters[moveCount] = currentAdapter; toAdapters[moveCount] = strategyAdapter; withdrawAmounts[moveCount] = currentAmount; moveCount++; } } } function calculateRebalance( address[] calldata tokens ) external view returns ( address[] memory tokensToMove, address[] memory fromAdapters, address[] memory toAdapters, uint256[] memory withdrawAmounts, uint256[] memory depositAmounts ) { address[] memory currentAdapters = adapters.values(); address[] memory strategyAdapters = IDistributionStrategy(strategy) .computeDistributionPlan( currentAdapters, tokens, strategyCacheStorage ); ( tokensToMove, fromAdapters, toAdapters, withdrawAmounts, depositAmounts, ) = _calculateRebalancePlan(tokens, strategyAdapters); } function _saveAccruedInterest(address adapter, address token) internal { uint256 principal = supplied[adapter][token]; if (principal == 0) return; (, uint256 interest, ) = ILendingAdapter(adapter).viewAccruedInterest( token, address(this), principal ); accruedInterests[adapter][token] += interest; } function _recordAllocation(address token, address adapter) internal { allocation[token].push(Allocation(adapter, block.timestamp)); } function rebalanceUsingStrategy() external nonReentrant { address[] memory tokens = _getActiveTokens(); address[] memory currentAdapters = adapters.values(); address[] memory strategyAdapters = IDistributionStrategy(strategy) .computeDistributionPlan( currentAdapters, tokens, strategyCacheStorage ); ( address[] memory tokensToMove, address[] memory fromAdapters, address[] memory toAdapters, uint256[] memory withdrawAmounts, uint256[] memory depositAmounts, uint256 moveCount ) = _calculateRebalancePlan(tokens, strategyAdapters); if (moveCount == 0) return; for (uint256 i = 0; i < moveCount; i++) { address token = tokensToMove[i]; address from = fromAdapters[i]; address to = toAdapters[i]; if (from != address(0)) { _saveAccruedInterest(from, token); _withdrawAndTransfer( from, token, false, true, false, address(this) ); } if (to != address(0) && depositAmounts[i] > 0) { _distributeOne(token, to, depositAmounts[i]); _recordAllocation(token, to); } emit Rebalanced( token, from, to, withdrawAmounts[i], depositAmounts[i] ); } } function getAllocationHistory( address token ) external view returns (Allocation[] memory) { return allocation[token]; } function _distributeOne( address token, address adapter, uint256 amount ) internal { if (token == address(0) || adapter == address(0) || amount == 0) return; IERC20 erc20 = IERC20(token); erc20.safeTransferFrom(msg.sender, address(this), amount); _approveIfNeeded(erc20, adapter, amount); ILendingAdapter(adapter).supply(token, amount, address(this)); supplied[adapter][token] += amount; emit TokenDistributed(token, adapter, amount); } function _distribute( address[] memory _tokens, address[] memory _adapters, uint256[] memory _amounts ) internal { if ( _tokens.length != _adapters.length || _tokens.length != _amounts.length ) revert MismatchedInputLengths(); for (uint256 i = 0; i < _tokens.length; i++) { if (_tokens[i] == address(0)) continue; IERC20 erc20 = IERC20(_tokens[i]); uint256 amount = _amounts[i]; address adapter = _adapters[i]; erc20.safeTransferFrom(msg.sender, address(this), amount); _approveIfNeeded(erc20, adapter, amount); ILendingAdapter(adapter).supply( address(erc20), amount, address(this) ); supplied[adapter][address(erc20)] += amount; emit TokenDistributed(address(erc20), adapter, amount); } } function distribute( address[] calldata _tokens, address[] calldata _adapters, uint256[] calldata _amounts ) external onlyOwner nonReentrant { if (!isAdaptersValid(_adapters)) revert InvalidAdapter(); _distribute(_tokens, _adapters, _amounts); } function distributeUsingStrategy( address[] calldata tokens, uint256[] calldata amounts ) external onlyOwner nonReentrant { address[] memory currentAdapters = adapters.values(); address[] memory bestAdapters = IDistributionStrategy(strategy) .computeDistributionPlan( currentAdapters, tokens, strategyCacheStorage ); _distribute(tokens, bestAdapters, amounts); } function isAdaptersValid( address[] calldata _adapters ) internal view returns (bool) { for (uint256 i = 0; i < _adapters.length; i++) { if (!adapters.contains(_adapters[i])) return false; } return true; } function viewAccruedInterest( address adapter, address token, bool includeAccumulated ) external view returns ( uint256 principal, uint256 current, uint256 interest, bool isAccurate ) { principal = supplied[adapter][token]; if (principal == 0) return (0, 0, 0, true); ( uint256 _current, uint256 _interest, bool _isAccurate ) = ILendingAdapter(adapter).viewAccruedInterest( token, address(this), principal ); uint256 totalInterest = includeAccumulated ? _interest + accruedInterests[adapter][token] : _interest; return (principal, _current, totalInterest, _isAccurate); } function rebalanceIsNeeded() public view returns (bool) { address[] memory tokens = _getActiveTokens(); address[] memory currentAdapters = adapters.values(); address[] memory strategyAdapters = IDistributionStrategy(strategy) .computeDistributionPlan( currentAdapters, tokens, strategyCacheStorage ); (, , , , , uint256 moveCount) = _calculateRebalancePlan( tokens, strategyAdapters ); return moveCount > 0; } function _getActiveTokens() internal view returns (address[] memory) { return TezoroLendingLens(lens).getActiveTokensForAgent( address(this), adapters.values() ); } function checker() external view returns (bool canExec, bytes memory execPayload) { canExec = rebalanceIsNeeded(); execPayload = abi.encodeWithSelector( this.rebalanceUsingStrategy.selector ); } //-------------------------------------------------------------------------- // Withdraw logic //-------------------------------------------------------------------------- function withdraw( address adapter, address token, bool isTransferRequired, bool isClaimRewardsRequired ) external onlyOwner nonReentrant { _withdrawAndTransfer( adapter, token, isTransferRequired, isClaimRewardsRequired, true, msg.sender ); } function batchWithdraw( address[] calldata _adapters, address[] calldata _tokens, bool isTransferRequired, bool isClaimRewardsRequired ) external onlyOwner nonReentrant { if (_adapters.length != _tokens.length) revert MismatchedInputLengths(); for (uint256 i = 0; i < _adapters.length; i++) { _withdrawAndTransfer( _adapters[i], _tokens[i], isTransferRequired, isClaimRewardsRequired, true, msg.sender ); } } function _claimAndForwardRewards( address adapter, address token, address recipient ) internal { address[] memory tokensArray = singletonAddressArray(token); if ( IERC165(adapter).supportsInterface( type(ILendingAdapterWithRewards).interfaceId ) ) { ( address rewardTarget, bytes memory rewardCallData ) = ILendingAdapterWithRewards(adapter).getClaimRewardsCallData( tokensArray, address(this) ); if (rewardTarget == address(0) || rewardCallData.length == 0) return; address[] memory rewardTokens = ILendingAdapterWithRewards(adapter) .getRewardTokensForAsset(token); uint256[] memory balancesBefore = new uint256[]( rewardTokens.length ); for (uint256 i = 0; i < rewardTokens.length; i++) { balancesBefore[i] = IERC20(rewardTokens[i]).balanceOf( address(this) ); } _safeCall(rewardTarget, rewardCallData); for (uint256 i = 0; i < rewardTokens.length; i++) { uint256 balanceAfter = IERC20(rewardTokens[i]).balanceOf( address(this) ); uint256 delta = balanceAfter > balancesBefore[i] ? balanceAfter - balancesBefore[i] : 0; if (delta > 0) { IERC20(rewardTokens[i]).safeTransfer(recipient, delta); } } } } function _divUp(uint256 a, uint256 b) internal pure returns (uint256) { return a == 0 ? 0 : ((a - 1) / b) + 1; } function _collectPerformanceFee( address token, address adapter ) internal returns (uint256 feeAmount) { (, , uint256 interest, ) = this.viewAccruedInterest( adapter, token, true ); if (interest == 0) return 0; uint256 feeRate = TezoroLendingAgentFactory(factory).feeRateBps(); feeAmount = _divUp(interest * feeRate, 10000); if (feeAmount > 0) { IERC20(token).safeTransfer(factory, feeAmount); emit PerformanceFeeCollected(token, adapter, feeAmount); } return feeAmount; } function _withdrawAndTransfer( address adapter, address token, bool isTransferRequired, bool isClaimRewardsRequired, bool resetAccrued, address rewardRecipient ) internal { ( address target, bytes memory data, address[] memory tokensToTransfer, uint256[] memory amounts ) = ILendingAdapterWithWithdraw(adapter).getWithdrawCallData( token, address(this) ); for (uint256 i = 0; i < tokensToTransfer.length; i++) { IERC20(tokensToTransfer[i]).safeTransfer(adapter, amounts[i]); } _safeCall(target, data); delete supplied[adapter][token]; if (resetAccrued) { delete accruedInterests[adapter][token]; } if (isClaimRewardsRequired) { _claimAndForwardRewards(adapter, token, rewardRecipient); } if (isTransferRequired) { _collectPerformanceFee(token, adapter); _transferTokenIfNonZero(token, msg.sender); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; /// @title ILendingAdapter - Universal interface for lending protocols (Aave, Compound, etc.) interface ILendingAdapter { /// @notice Metadata about a supported token in the lending protocol struct TokenInfo { address token; bool isCollateral; // Whether the token can be used as collateral uint256 ltvBps; // Loan-to-value ratio (optional: 0 if unsupported, or equal to liquidationThresholdBps) uint256 liquidationThresholdBps; // Liquidation threshold (optional: 0 if unsupported) uint256 rate; // Interest rate uint8 rateDecimals; // Interest rate decimals } /// @notice Returns all tokens supported by this adapter, with metadata function getTokens() external view returns (TokenInfo[] memory); /// @notice Returns the token info for a specific token function getTokenInfo( address token ) external view returns (TokenInfo memory); /// @notice Supplies a token into the protocol function supply(address token, uint256 amount, address onBehalfOf) external; /// @notice Returns current borrow/supply caps for a token function getSupplyCaps( address token ) external view returns (uint256 borrowCap, uint256 supplyCap); /// @notice Returns the raw supply rate of a token (in 1e18 scale) function getSupplyBalance( address token, address user ) external returns (uint256); /// @notice Returns current supply balance of a user in underlying token units function getSupplyBalanceView( address token, address user ) external view returns (uint256); /// @notice Returns USD price of a token, scaled to `decimals` function getTokenPriceUsd( address token ) external view returns (uint256 price, uint8 decimals); /// @notice Returns the protocol-specific "deposit token" (like aToken or cToken) function getDepositToken(address asset) external view returns (address); /// @notice Returns all deposit tokens supported by this adapter function getAllDepositTokens() external view returns (address[] memory); /// @notice Human-readable name of the adapter function name() external view returns (string memory); /// @notice Returns the accrued interest (requires current on-chain state) function viewAccruedInterest( address token, address user, uint256 principal ) external view returns (uint256 current, uint256 interest, bool isAccurate); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; interface ILendingAdapterWithWithdraw { /// @notice Returns calldata needed to perform withdrawal via external agent /// @dev Used in protocols like Aave that require withdrawals to be initiated by the depositor contract function getWithdrawCallData( address token, address user ) external view returns ( address target, bytes memory callData, address[] memory tokensToTransfer, uint256[] memory amounts ); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; interface ILendingAdapterWithRewards { // @notice Returns calldata needed to perform claim rewards via external agent // @dev Used in protocols like Aave that require rewards to be claimed by the depositor contract function getClaimRewardsCallData( address[] memory tokens, address user ) external view returns (address target, bytes memory callData); /// @notice Returns list of reward tokens for given asset function getRewardTokensForAsset( address asset ) external view returns (address[] memory); /// @notice Returns list of reward tokens for given user and assets function getAccruedRewards( address user, address[] memory assets ) external view returns ( address[] memory rewardTokens, uint256[] memory unclaimedAmounts ); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; interface IDistributionStrategy { struct Observation { uint256 value; uint256 timestamp; } function computeDistributionPlan( address[] calldata _adapters, address[] calldata _tokens, address cacheStorageAddress ) external view returns (address[] memory optimalAdapters); function getEMA( Observation memory lastObservation, address adapter, address token ) external view returns (uint256 ema); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; interface IAutomationChecker { function checker() external view returns (bool canExec, bytes memory execPayload); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./interfaces/ILendingAdapter.sol"; import "./interfaces/ILendingAdapterWithRewards.sol"; import "./interfaces/IDistributionStrategy.sol"; contract TezoroLendingLens { struct TokenMetadata { address token; string symbol; uint8 decimals; uint256 priceUsd; uint8 priceUsdDecimals; } struct AdapterTokenInfo { address adapter; address token; uint256 rate; uint8 rateDecimals; uint256 ltvBps; address depositToken; uint8 depositTokenDecimals; } struct RewardInfo { address token; uint256 amount; uint256 amountUsd; } struct PositionInfo { TokenMetadata metadata; AdapterTokenInfo adapterTokenInfo; uint256 depositAmount; RewardInfo[] rewards; } struct TokenWithRate { TokenMetadata metadata; uint256 rate; uint8 rateDecimals; } error TokenUnsupported(address token); error NoPriceAvailable(address token); function _getDecimals(address token) internal view returns (uint8) { if (token == address(0)) return 0; try IERC20Metadata(token).decimals() returns (uint8 dec) { return dec; } catch { return 18; } } function _getSymbol(address token) internal view returns (string memory) { try IERC20Metadata(token).symbol() returns (string memory sym) { return sym; } catch { return "?"; } } function getActiveTokensForAgent( address agent, address[] calldata adapters ) external view returns (address[] memory) { AdapterTokenInfo[] memory all = _collectAllAdapterTokenInfos(adapters); address[] memory buffer = new address[](all.length); uint256 count = 0; for (uint256 i = 0; i < all.length; i++) { uint256 balance = ILendingAdapter(all[i].adapter) .getSupplyBalanceView(all[i].token, agent); if (balance > 0) { buffer[count++] = all[i].token; } } address[] memory result = new address[](count); for (uint256 i = 0; i < count; i++) { result[i] = buffer[i]; } return result; } function getSupportedTokenMetadata( address[] memory adapters ) external view returns (TokenWithRate[] memory) { AdapterTokenInfo[] memory adapterTokens = _collectAllAdapterTokenInfos( adapters ); TokenWithRate[] memory buffer = new TokenWithRate[]( adapterTokens.length ); uint256 count = 0; for (uint256 i = 0; i < adapterTokens.length; i++) { uint256 rate = adapterTokens[i].rate; if (rate == 0) continue; address adapter = adapterTokens[i].adapter; address assetToken = adapterTokens[i].token; (uint256 priceUsd, uint8 priceUsdDecimals) = _tryGetTokenPrice( ILendingAdapter(adapter), assetToken ); buffer[count++] = TokenWithRate({ metadata: TokenMetadata({ token: assetToken, symbol: _getSymbol(assetToken), decimals: _getDecimals(assetToken), priceUsd: priceUsd, priceUsdDecimals: priceUsdDecimals }), rate: rate, rateDecimals: adapterTokens[i].rateDecimals }); } TokenWithRate[] memory result = new TokenWithRate[](count); for (uint256 i = 0; i < count; i++) { result[i] = buffer[i]; } return result; } function findMinimumSupplyCap( address[] memory adapters, address token ) external view returns (uint256) { uint256 lowestCap = type(uint256).max; bool found = false; for (uint256 i = 0; i < adapters.length; i++) { (, uint256 supplyCap) = ILendingAdapter(adapters[i]).getSupplyCaps( token ); if (supplyCap == 0) continue; found = true; if (supplyCap < lowestCap) { lowestCap = supplyCap; } } if (!found) revert TokenUnsupported(token); return lowestCap; } function _collectAllAdapterTokenInfos( address[] memory adapters ) internal view returns (AdapterTokenInfo[] memory) { uint256 totalCount = 0; ILendingAdapter.TokenInfo[][] memory all = new ILendingAdapter.TokenInfo[][](adapters.length); for (uint256 i = 0; i < adapters.length; i++) { all[i] = ILendingAdapter(adapters[i]).getTokens(); totalCount += all[i].length; } AdapterTokenInfo[] memory result = new AdapterTokenInfo[](totalCount); uint256 index = 0; for (uint256 i = 0; i < adapters.length; i++) { ILendingAdapter adapter = ILendingAdapter(adapters[i]); for (uint256 j = 0; j < all[i].length; j++) { address token = all[i][j].token; address depositToken = _tryGetDepositToken(adapter, token); result[index++] = AdapterTokenInfo({ adapter: adapters[i], token: token, rate: all[i][j].rate, rateDecimals: all[i][j].rateDecimals, ltvBps: all[i][j].ltvBps, depositToken: depositToken, depositTokenDecimals: _getDecimals(depositToken) }); } } return result; } function getPaginatedAdapterTokenInfos( address[] memory adapters, uint256 offset, uint256 limit ) external view returns (AdapterTokenInfo[] memory) { AdapterTokenInfo[] memory all = _collectAllAdapterTokenInfos(adapters); if (offset >= all.length) return new AdapterTokenInfo[](0); uint256 end = offset + limit; if (end > all.length) end = all.length; AdapterTokenInfo[] memory result = new AdapterTokenInfo[](end - offset); for (uint256 i = offset; i < end; i++) { result[i - offset] = all[i]; } return result; } function getPositionInfo( address agent, address adapter, address token ) external view returns (PositionInfo memory) { ILendingAdapter ad = ILendingAdapter(adapter); ILendingAdapter.TokenInfo[] memory tokens = ad.getTokens(); for (uint256 i = 0; i < tokens.length; i++) { if (tokens[i].token == token) { address depositToken = _tryGetDepositToken(ad, token); return _buildPositionInfo( agent, AdapterTokenInfo({ adapter: adapter, token: token, rate: tokens[i].rate, rateDecimals: tokens[i].rateDecimals, ltvBps: tokens[i].ltvBps, depositToken: depositToken, depositTokenDecimals: _getDecimals(depositToken) }) ); } } revert TokenUnsupported(token); } function _getRewards( ILendingAdapter adapter, address user, address token ) internal view returns (RewardInfo[] memory) { address[] memory tokensArr = new address[](1); tokensArr[0] = token; try ILendingAdapterWithRewards(address(adapter)).getAccruedRewards( user, tokensArr ) returns ( address[] memory rewardTokens, uint256[] memory unclaimedAmounts ) { RewardInfo[] memory rewards = new RewardInfo[](rewardTokens.length); for (uint256 j = 0; j < rewardTokens.length; j++) { uint256 amountUsd = 0; try adapter.getTokenPriceUsd(rewardTokens[j]) returns ( uint256 price, uint8 decimals ) { amountUsd = (unclaimedAmounts[j] * price) / (10 ** decimals); } catch {} rewards[j] = RewardInfo({ token: rewardTokens[j], amount: unclaimedAmounts[j], amountUsd: amountUsd }); } return rewards; } catch { return new RewardInfo[](0); } } function _buildPositionInfo( address agent, AdapterTokenInfo memory info ) internal view returns (PositionInfo memory) { ILendingAdapter adapter = ILendingAdapter(info.adapter); address token = info.token; uint256 bal = adapter.getSupplyBalanceView(token, agent); if (bal == 0) { return PositionInfo({ metadata: TokenMetadata({ token: address(0), symbol: "", decimals: 0, priceUsd: 0, priceUsdDecimals: 0 }), adapterTokenInfo: info, depositAmount: 0, rewards: new RewardInfo[](0) }); } (uint256 priceUsd, uint8 priceUsdDecimals) = _tryGetTokenPrice( adapter, token ); return PositionInfo({ metadata: TokenMetadata({ token: token, symbol: _getSymbol(token), decimals: _getDecimals(token), priceUsd: priceUsd, priceUsdDecimals: priceUsdDecimals }), adapterTokenInfo: info, depositAmount: bal, rewards: _getRewards(adapter, agent, token) }); } function _tryGetTokenPrice( ILendingAdapter adapter, address token ) internal view returns (uint256, uint8) { try adapter.getTokenPriceUsd(token) returns ( uint256 price, uint8 decimals ) { return (price, decimals); } catch { return (0, 0); } } function _tryGetDepositToken( ILendingAdapter adapter, address token ) internal view returns (address) { try adapter.getDepositToken(token) returns (address depositToken) { return depositToken; } catch { return address(0); } } function getPositionsForTokens( address agent, AdapterTokenInfo[] memory tokens ) external view returns (PositionInfo[] memory) { PositionInfo[] memory buffer = new PositionInfo[](tokens.length); uint256 count = 0; for (uint256 i = 0; i < tokens.length; i++) { PositionInfo memory info = _buildPositionInfo(agent, tokens[i]); if (info.metadata.token == address(0)) continue; buffer[count++] = info; } PositionInfo[] memory result = new PositionInfo[](count); for (uint256 i = 0; i < count; i++) { result[i] = buffer[i]; } return result; } function getTokenPriceUsdFromAdapters( address[] memory adapters, address token ) external view returns (uint256 price, uint8 decimals) { for (uint256 i = 0; i < adapters.length; i++) { try ILendingAdapter(adapters[i]).getTokenPriceUsd(token) returns ( uint256 _price, uint8 _dec ) { if (_price > 0) return (_price, _dec); } catch {} } revert NoPriceAvailable(token); } function computeDistributionPlan( address[] calldata tokens, address[] calldata adapters, address strategy, address cacheStorageAddress ) external view returns (address[] memory _adapters) { return IDistributionStrategy(strategy).computeDistributionPlan( adapters, tokens, cacheStorageAddress ); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/access/Ownable.sol"; import "openzeppelin-contracts/contracts/proxy/Clones.sol"; import "./TezoroLendingAgent.sol"; contract TezoroLendingAgentFactory is Ownable { using Clones for address; error NoAdaptersProvided(); error ZeroAddressAdapter(); error AgentIndexOutOfBounds(); error FeeRateTooHigh(); event FeeWithdrawn(address token, uint256 amount); event AgentCreated( address indexed agent, address indexed creator, address[] adapters ); address public immutable implementation; address[] public allAgents; uint256 public feeRateBps = 1000; // 10% mapping(address => address[]) private _agentsByCreator; mapping(address => address) public agentCreator; constructor(address _implementation) { implementation = _implementation; } function createAgent( address lensAddress, address strategyAddress, address strategyCacheStorageAddress, address[] memory adapters ) external returns (address agent) { if (adapters.length == 0) revert NoAdaptersProvided(); for (uint256 i = 0; i < adapters.length; ++i) { if (adapters[i] == address(0)) revert ZeroAddressAdapter(); } agent = implementation.clone(); TezoroLendingAgent(agent).initialize( msg.sender, lensAddress, adapters, strategyAddress, strategyCacheStorageAddress, address(this) ); allAgents.push(agent); _agentsByCreator[msg.sender].push(agent); agentCreator[agent] = msg.sender; emit AgentCreated(agent, msg.sender, adapters); } function _setFeeRateBps(uint256 newFeeRateBps) internal { if (newFeeRateBps > 2000) revert FeeRateTooHigh(); feeRateBps = newFeeRateBps; } function setFeeRateBps(uint256 newFeeRateBps) external onlyOwner { _setFeeRateBps(newFeeRateBps); } function withdrawFee( address token, uint256 amount, address to ) external onlyOwner { if (amount == 0) revert NoAdaptersProvided(); if (to == address(0)) revert ZeroAddressAdapter(); IERC20(token).transfer(to, amount); emit FeeWithdrawn(token, amount); } function allAgentsLength() external view returns (uint256) { return allAgents.length; } function agentAt(uint256 index) external view returns (address) { if (index >= allAgents.length) revert AgentIndexOutOfBounds(); return allAgents[index]; } function agentsOf( address creator ) external view returns (address[] memory) { return _agentsByCreator[creator]; } function agentsLengthOf(address creator) external view returns (uint256) { return _agentsByCreator[creator].length; } function isAgent(address agent) external view returns (bool) { return agentCreator[agent] != address(0); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
{ "remappings": [ "aave-v3-core/=lib/aave-v3-core/", "aave-v3-periphery/=lib/aave-v3-periphery/contracts/", "chainlink-brownie-contracts/=lib/chainlink-brownie-contracts/", "compound-protocol/=lib/compound-protocol/", "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": false, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"AdapterAlreadyRegistered","type":"error"},{"inputs":[],"name":"AdapterNotFound","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"ExternalCallFailed","type":"error"},{"inputs":[],"name":"InvalidAdapter","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidLens","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidStrategy","type":"error"},{"inputs":[],"name":"InvalidStrategyCacheStorage","type":"error"},{"inputs":[],"name":"MismatchedInputLengths","type":"error"},{"inputs":[],"name":"NoAdaptersProvided","type":"error"},{"inputs":[],"name":"NoTokensToWithdraw","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"InsufficientBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"NoAdapterFound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PerformanceFeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adapter","type":"address"}],"name":"ProtocolAdapterRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adapter","type":"address"}],"name":"ProtocolAdapterUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"fromAdapter","type":"address"},{"indexed":true,"internalType":"address","name":"toAdapter","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositedAmount","type":"uint256"}],"name":"Rebalanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldStrategy","type":"address"},{"indexed":false,"internalType":"address","name":"newStrategy","type":"address"}],"name":"StrategyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldStrategyStorage","type":"address"},{"indexed":false,"internalType":"address","name":"newStrategyStorage","type":"address"}],"name":"StrategyStorageChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenWithdrawn","type":"event"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"accruedInterests","outputs":[{"internalType":"uint256","name":"interestAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"allocation","outputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"bool","name":"isTransferRequired","type":"bool"},{"internalType":"bool","name":"isClaimRewardsRequired","type":"bool"}],"name":"batchWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"calculateRebalance","outputs":[{"internalType":"address[]","name":"tokensToMove","type":"address[]"},{"internalType":"address[]","name":"fromAdapters","type":"address[]"},{"internalType":"address[]","name":"toAdapters","type":"address[]"},{"internalType":"uint256[]","name":"withdrawAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"depositAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checker","outputs":[{"internalType":"bool","name":"canExec","type":"bool"},{"internalType":"bytes","name":"execPayload","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deploymentTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"address[]","name":"_adapters","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"distributeUsingStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAdapters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getAllocationHistory","outputs":[{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct TezoroLendingAgent.Allocation[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_lens","type":"address"},{"internalType":"address[]","name":"_adapters","type":"address[]"},{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"address","name":"_strategyCacheStorage","type":"address"},{"internalType":"address","name":"_factory","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalanceIsNeeded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalanceUsingStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"}],"name":"registerProtocolAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"setStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyCacheStorage","type":"address"}],"name":"setStrategyCacheStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyCacheStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"supplied","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"}],"name":"unregisterProtocolAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"includeAccumulated","type":"bool"}],"name":"viewAccruedInterest","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"current","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"bool","name":"isAccurate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isTransferRequired","type":"bool"},{"internalType":"bool","name":"isClaimRewardsRequired","type":"bool"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5062000032620000266200003f60201b60201c565b6200004760201b60201c565b600180819055506200010b565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b6158a2806200011b6000396000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638a6b4cc911610104578063bfc12c05116100a2578063cf5303cf11610071578063cf5303cf146104e6578063eb14528214610505578063f2fde38b14610539578063fa72968514610555576101cf565b8063bfc12c051461045e578063c21fcdd81461047c578063c45a0155146104ac578063c5e0164a146104ca576101cf565b806395147783116100de57806395147783146103e8578063a8c62e7614610404578063a9d376ef14610422578063b82e16e314610440576101cf565b80638a6b4cc9146103925780638da5cb5b146103ae5780638f7b91c5146103cc576101cf565b806359383489116101715780637ca20fc01161014b5780637ca20fc01461030e5780637cf2510c1461032a5780637ecb7bed146103465780638980f11f14610376576101cf565b806359383489146102c9578063715018a6146102d35780637be7e96e146102dd576101cf565b80631f655b69116101ad5780631f655b691461022e578063274833631461025e5780632aeace391461027a57806333a100ca146102ad576101cf565b80630fd78152146101d4578063112666b7146101f2578063158ef93e14610210575b600080fd5b6101dc610571565b6040516101e99190613f0b565b60405180910390f35b6101fa610677565b6040516102079190613f67565b60405180910390f35b61021861069d565b6040516102259190613f0b565b60405180910390f35b61024860048036038101906102439190613fc2565b6106b0565b604051610255919061401b565b60405180910390f35b61027860048036038101906102739190614036565b6106d5565b005b610294600480360381019061028f919061408f565b6107e9565b6040516102a494939291906140e2565b60405180910390f35b6102c760048036038101906102c29190614036565b6109c5565b005b6102d1610ad2565b005b6102db610e15565b005b6102f760048036038101906102f29190614153565b610e29565b604051610305929190614193565b60405180910390f35b61032860048036038101906103239190614277565b610e8a565b005b610344600480360381019061033f919061432b565b610fb8565b005b610360600480360381019061035b9190614036565b611140565b60405161036d91906144a8565b60405180910390f35b610390600480360381019061038b9190614153565b61123e565b005b6103ac60048036038101906103a791906144ca565b61127c565b005b6103b66112a9565b6040516103c39190613f67565b60405180910390f35b6103e660048036038101906103e19190614680565b6112d2565b005b61040260048036038101906103fd9190614036565b6116ea565b005b61040c611797565b6040516104199190613f67565b60405180910390f35b61042a6117bd565b6040516104379190613f67565b60405180910390f35b6104486117e3565b60405161045591906147d8565b60405180910390f35b6104666117f4565b604051610473919061401b565b60405180910390f35b61049660048036038101906104919190613fc2565b6117fa565b6040516104a3919061401b565b60405180910390f35b6104b461181f565b6040516104c19190613f67565b60405180910390f35b6104e460048036038101906104df91906147fa565b611845565b005b6104ee611923565b6040516104fc929190614920565b60405180910390f35b61051f600480360381019061051a9190614950565b6119a2565b604051610530959493929190614a4c565b60405180910390f35b610553600480360381019061054e9190614036565b611af1565b005b61056f600480360381019061056a9190614036565b611b74565b005b60008061057c611c81565b9050600061058a6005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf8385600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff1660e01b815260040161060f93929190614ac2565b600060405180830381865afa15801561062c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906106559190614bb3565b905060006106638483611d55565b955050505050506000811194505050505090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600b60149054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6106dd6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610743576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61075781600561216190919063ffffffff16565b1561078e576040517f757d2ccf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107a281600561219190919063ffffffff16565b508073ffffffffffffffffffffffffffffffffffffffff167fbc4a525f9c0127e6139bae298a07809d85a2d907b95942ebf8f11ae80c19162d60405160405180910390a250565b600080600080600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205493506000840361088a576000806000600193509350935093506109bc565b60008060008973ffffffffffffffffffffffffffffffffffffffff16638bba2e378a308a6040518463ffffffff1660e01b81526004016108cc93929190614bfc565b606060405180830381865afa1580156108e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090d9190614c5d565b92509250925060008861092057826109a9565b600360008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054836109a89190614cdf565b5b9050878482849750975097509750505050505b93509350935093565b6109cd6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610a33576040517f4e236e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f254c88e7a2ea123aeeb89b7cc413fb949188fefcdb7584c4f3d493294daf65c5600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682604051610a86929190614d13565b60405180910390a180600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610ada6121c1565b6000610ae4611c81565b90506000610af26005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf8385600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff1660e01b8152600401610b7793929190614ac2565b600060405180830381865afa158015610b94573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610bbd9190614bb3565b9050600080600080600080610bd28988611d55565b95509550955095509550955060008103610bf457505050505050505050610e0b565b60005b81811015610e00576000878281518110610c1457610c13614d3c565b5b602002602001015190506000878381518110610c3357610c32614d3c565b5b602002602001015190506000878481518110610c5257610c51614d3c565b5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610cac57610c9a8284612210565b610cab8284600060016000306123bd565b5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015610d0357506000868581518110610cf957610cf8614d3c565b5b6020026020010151115b15610d3857610d2d8382888781518110610d2057610d1f614d3c565b5b6020026020010151612616565b610d378382612837565b5b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fd58635ff12f09ca41b1f8ea85ef83e3452b032687149d2be108b17130addd7ee8a8881518110610db157610db0614d3c565b5b60200260200101518a8981518110610dcc57610dcb614d3c565b5b6020026020010151604051610de2929190614d6b565b60405180910390a45050508080610df890614d94565b915050610bf7565b505050505050505050505b610e13612921565b565b610e1d6120e3565b610e27600061292a565b565b60046020528160005260406000208181548110610e4557600080fd5b9060005260206000209060020201600091509150508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010154905082565b610e926120e3565b610e9a6121c1565b610ea484846129ee565b610eda576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fa8868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050848480806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612a69565b610fb0612921565b505050505050565b610fc06120e3565b610fc86121c1565b6000610fd46005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf838888600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518563ffffffff1660e01b815260040161105b9493929190614e67565b600060405180830381865afa158015611078573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906110a19190614bb3565b9050611130868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505082868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612a69565b505061113a612921565b50505050565b6060600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b8282101561123357838290600052602060002090600202016040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015481525050815260200190600101906111a1565b505050509050919050565b6112466120e3565b6112786112516112a9565b828473ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b5050565b6112846120e3565b61128c6121c1565b61129b848484846001336123bd565b6112a3612921565b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600b60149054906101000a900460ff16156112ec57600080fd5b6001600b60146101000a81548160ff021916908315150217905550600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff160361136d576040517f49e27cff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113768661292a565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036113dc576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603611442576040517f2b7d09bc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036114a8576040517f4e236e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361150e576040517f2e7c914400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000845103611549576040517f332b53ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055504260088190555082600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060005b84518110156116e157600073ffffffffffffffffffffffffffffffffffffffff1685828151811061164a57611649614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160361169f576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cd8582815181106116b5576116b4614d3c565b5b6020026020010151600561219190919063ffffffff16565b5080806116d990614d94565b915050611616565b50505050505050565b6116f26120e3565b61170681600561216190919063ffffffff16565b61173c576040517ff7b1bf8e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611750816005612dad90919063ffffffff16565b508073ffffffffffffffffffffffffffffffffffffffff167f6044b319b7468595a28d1a8c817e22028afb652faec55c709426f75584fc8a4060405160405180910390a250565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60606117ef6005611d34565b905090565b60085481565b6003602052816000526040600020602052806000526040600020600091509150505481565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61184d6120e3565b6118556121c1565b838390508686905014611894576040517fa0b1d72d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b86869050811015611912576118ff8787838181106118b8576118b7614d3c565b5b90506020020160208101906118cd9190614036565b8686848181106118e0576118df614d3c565b5b90506020020160208101906118f59190614036565b85856001336123bd565b808061190a90614d94565b915050611897565b5061191b612921565b505050505050565b6000606061192f610571565b9150635938348960e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090509091565b606080606080606060006119b66005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf838b8b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518563ffffffff1660e01b8152600401611a3d9493929190614e67565b600060405180830381865afa158015611a5a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190611a839190614bb3565b9050611ad0898980806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505082611d55565b50809750819850829950839a50849b50505050505050509295509295909350565b611af96120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611b68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b5f90614f31565b60405180910390fd5b611b718161292a565b50565b611b7c6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611be2576040517f2e7c914400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fe6e02d5ab813935e6bcfd549ca115804cb5e7812d961d826c8bebe05a0b9a68a600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682604051611c35929190614d13565b60405180910390a180600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eff0fca930611ccc6005611d34565b6040518363ffffffff1660e01b8152600401611ce9929190614f51565b600060405180830381865afa158015611d06573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190611d2f9190614bb3565b905090565b60606000611d4483600001612ddd565b905060608190508092505050919050565b60608060608060606000875167ffffffffffffffff811115611d7a57611d79614542565b5b604051908082528060200260200182016040528015611da85781602001602082028036833780820191505090505b509550875167ffffffffffffffff811115611dc657611dc5614542565b5b604051908082528060200260200182016040528015611df45781602001602082028036833780820191505090505b509450875167ffffffffffffffff811115611e1257611e11614542565b5b604051908082528060200260200182016040528015611e405781602001602082028036833780820191505090505b509350875167ffffffffffffffff811115611e5e57611e5d614542565b5b604051908082528060200260200182016040528015611e8c5781602001602082028036833780820191505090505b509250875167ffffffffffffffff811115611eaa57611ea9614542565b5b604051908082528060200260200182016040528015611ed85781602001602082028036833780820191505090505b5091506000905060005b88518110156120d8576000898281518110611f0057611eff614d3c565b5b602002602001015190506000898381518110611f1f57611f1e614d3c565b5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611f645750506120c5565b600080611f7084612e39565b915091508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146120c057838b8781518110611fbb57611fba614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050818a878151811061200957612008614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508289878151811061205757612056614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050808887815181106120a5576120a4614d3c565b5b60200260200101818152505085806120bc90614d94565b9650505b505050505b80806120d090614d94565b915050611ee2565b509295509295509295565b6120eb612f32565b73ffffffffffffffffffffffffffffffffffffffff166121096112a9565b73ffffffffffffffffffffffffffffffffffffffff161461215f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161215690614fcd565b60405180910390fd5b565b6000612189836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612f3a565b905092915050565b60006121b9836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612f5d565b905092915050565b600260015403612206576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121fd90615039565b60405180910390fd5b6002600181905550565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506000810361229f57506123b9565b60008373ffffffffffffffffffffffffffffffffffffffff16638bba2e378430856040518463ffffffff1660e01b81526004016122de93929190614bfc565b606060405180830381865afa1580156122fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231f9190614c5d565b5091505080600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546123af9190614cdf565b9250508190555050505b5050565b6000806000808973ffffffffffffffffffffffffffffffffffffffff1663bd4e53318a306040518363ffffffff1660e01b81526004016123fe929190614d13565b600060405180830381865afa15801561241b573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061244491906151c2565b935093509350935060005b82518110156124ca576124b78b83838151811061246f5761246e614d3c565b5b602002602001015185848151811061248a57612489614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b80806124c290614d94565b91505061244f565b506124d58484612fcd565b600260008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000905585156125dc57600360008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b86156125ee576125ed8a8a87613084565b5b871561260a576125fe898b613516565b506126098933613722565b5b50505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16148061267d5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b806126885750600081145b6128325760008390506126be3330848473ffffffffffffffffffffffffffffffffffffffff16613858909392919063ffffffff16565b6126c98184846138e1565b8273ffffffffffffffffffffffffffffffffffffffff16638b2a4df58584306040518463ffffffff1660e01b81526004016127069392919061527d565b600060405180830381600087803b15801561272057600080fd5b505af1158015612734573d6000803e3d6000fd5b5050505081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546127c49190614cdf565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac884604051612828919061401b565b60405180910390a3505b505050565b600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180604001604052808373ffffffffffffffffffffffffffffffffffffffff16815260200142815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015550505050565b60018081905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080600090505b83839050811015612a5d57612a3c848483818110612a1757612a16614d3c565b5b9050602002016020810190612a2c9190614036565b600561216190919063ffffffff16565b612a4a576000915050612a63565b8080612a5590614d94565b9150506129f6565b50600190505b92915050565b81518351141580612a7c57508051835114155b15612ab3576040517fa0b1d72d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8351811015612d2157600073ffffffffffffffffffffffffffffffffffffffff16848281518110612aea57612ae9614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160315612d0e576000848281518110612b2357612b22614d3c565b5b602002602001015190506000838381518110612b4257612b41614d3c565b5b602002602001015190506000858481518110612b6157612b60614d3c565b5b60200260200101519050612b983330848673ffffffffffffffffffffffffffffffffffffffff16613858909392919063ffffffff16565b612ba38382846138e1565b8073ffffffffffffffffffffffffffffffffffffffff16638b2a4df58484306040518463ffffffff1660e01b8152600401612be09392919061527d565b600060405180830381600087803b158015612bfa57600080fd5b505af1158015612c0e573d6000803e3d6000fd5b5050505081600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612c9e9190614cdf565b925050819055508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac884604051612d02919061401b565b60405180910390a35050505b8080612d1990614d94565b915050612ab6565b50505050565b612da88363a9059cbb60e01b8484604051602401612d46929190614193565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613a69565b505050565b6000612dd5836000018373ffffffffffffffffffffffffffffffffffffffff1660001b613b30565b905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015612e2d57602002820191906000526020600020905b815481526020019060010190808311612e19575b50505050509050919050565b6000806000612e486005611d34565b905060005b8151811015612f23576000828281518110612e6b57612e6a614d3c565b5b602002602001015190506000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506000811115612f0e5781819550955050505050612f2d565b50508080612f1b90614d94565b915050612e4d565b5060008092509250505b915091565b600033905090565b600080836001016000848152602001908152602001600020541415905092915050565b6000612f698383612f3a565b612fc2578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050612fc7565b600090505b92915050565b6000808373ffffffffffffffffffffffffffffffffffffffff1683604051612ff591906152f0565b6000604051808303816000865af19150503d8060008114613032576040519150601f19603f3d011682016040523d82523d6000602084013e613037565b606091505b50915091508161307e57806040517f59afd6c60000000000000000000000000000000000000000000000000000000081526004016130759190615307565b60405180910390fd5b50505050565b600061308f83613c44565b90508373ffffffffffffffffffffffffffffffffffffffff166301ffc9a77e5f985e000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016130e99190615364565b602060405180830381865afa158015613106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312a919061537f565b1561350f576000808573ffffffffffffffffffffffffffffffffffffffff1663c01a654084306040518363ffffffff1660e01b815260040161316d9291906153ac565b600060405180830381865afa15801561318a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906131b391906153dc565b91509150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614806131f3575060008151145b1561320057505050613511565b60008673ffffffffffffffffffffffffffffffffffffffff16634c9f7c37876040518263ffffffff1660e01b815260040161323b9190613f67565b600060405180830381865afa158015613258573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906132819190614bb3565b90506000815167ffffffffffffffff8111156132a05761329f614542565b5b6040519080825280602002602001820160405280156132ce5781602001602082028036833780820191505090505b50905060005b82518110156133a3578281815181106132f0576132ef614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016133309190613f67565b602060405180830381865afa15801561334d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133719190615438565b82828151811061338457613383614d3c565b5b602002602001018181525050808061339b90614d94565b9150506132d4565b506133ae8484612fcd565b60005b82518110156135095760008382815181106133cf576133ce614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161340f9190613f67565b602060405180830381865afa15801561342c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134509190615438565b9050600083838151811061346757613466614d3c565b5b6020026020010151821161347c5760006134a3565b83838151811061348f5761348e614d3c565b5b6020026020010151826134a29190615465565b5b905060008111156134f4576134f389828786815181106134c6576134c5614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b5b5050808061350190614d94565b9150506133b1565b50505050505b505b505050565b6000803073ffffffffffffffffffffffffffffffffffffffff16632aeace39848660016040518463ffffffff1660e01b815260040161355793929190615499565b608060405180830381865afa158015613574573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359891906154d0565b5092505050600081036135af57600091505061371c565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388c7fff36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136429190615438565b905061365b81836136539190615537565b612710613ce6565b92506000831115613719576136b3600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16848773ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f9e1eaea49c3a4fb24beb4c9b2df59d3fbf568710fe1f5c6ed2d0d1d0dd4dac3e85604051613710919061401b565b60405180910390a35b50505b92915050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161375d9190613f67565b602060405180830381865afa15801561377a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379e9190615438565b9050600081036137da576040517ffba5154e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61380582828573ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff167fa2bd9fcfcdba69f52bcd9a520846ad4bd685b187483f53efc42d035b2ddebff08260405161384b919061401b565b60405180910390a2505050565b6138db846323b872dd60e01b85858560405160240161387993929190614bfc565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613a69565b50505050565b808373ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30856040518363ffffffff1660e01b815260040161391d929190614d13565b602060405180830381865afa15801561393a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061395e9190615438565b1015613a64578273ffffffffffffffffffffffffffffffffffffffff1663095ea7b38360006040518363ffffffff1660e01b81526004016139a09291906155be565b6020604051808303816000875af11580156139bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e3919061537f565b508273ffffffffffffffffffffffffffffffffffffffff1663095ea7b383836040518363ffffffff1660e01b8152600401613a1f929190614193565b6020604051808303816000875af1158015613a3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a62919061537f565b505b505050565b6000613acb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613d239092919063ffffffff16565b9050600081511115613b2b5780806020019051810190613aeb919061537f565b613b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613b2190615659565b60405180910390fd5b5b505050565b60008083600101600084815260200190815260200160002054905060008114613c38576000600182613b629190615465565b9050600060018660000180549050613b7a9190615465565b9050818114613be9576000866000018281548110613b9b57613b9a614d3c565b5b9060005260206000200154905080876000018481548110613bbf57613bbe614d3c565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b85600001805480613bfd57613bfc615679565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050613c3e565b60009150505b92915050565b6060600167ffffffffffffffff811115613c6157613c60614542565b5b604051908082528060200260200182016040528015613c8f5781602001602082028036833780820191505090505b5090508181600081518110613ca757613ca6614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050919050565b6000808314613d1857600182600185613cff9190615465565b613d0991906156d7565b613d139190614cdf565b613d1b565b60005b905092915050565b6060613d328484600085613d3b565b90509392505050565b606082471015613d80576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613d779061577a565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613da991906152f0565b60006040518083038185875af1925050503d8060008114613de6576040519150601f19603f3d011682016040523d82523d6000602084013e613deb565b606091505b5091509150613dfc87838387613e08565b92505050949350505050565b60608315613e6a576000835103613e6257613e2285613e7d565b613e61576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613e58906157e6565b60405180910390fd5b5b829050613e75565b613e748383613ea0565b5b949350505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600082511115613eb35781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613ee7919061584a565b60405180910390fd5b60008115159050919050565b613f0581613ef0565b82525050565b6000602082019050613f206000830184613efc565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613f5182613f26565b9050919050565b613f6181613f46565b82525050565b6000602082019050613f7c6000830184613f58565b92915050565b6000604051905090565b600080fd5b600080fd5b613f9f81613f46565b8114613faa57600080fd5b50565b600081359050613fbc81613f96565b92915050565b60008060408385031215613fd957613fd8613f8c565b5b6000613fe785828601613fad565b9250506020613ff885828601613fad565b9150509250929050565b6000819050919050565b61401581614002565b82525050565b6000602082019050614030600083018461400c565b92915050565b60006020828403121561404c5761404b613f8c565b5b600061405a84828501613fad565b91505092915050565b61406c81613ef0565b811461407757600080fd5b50565b60008135905061408981614063565b92915050565b6000806000606084860312156140a8576140a7613f8c565b5b60006140b686828701613fad565b93505060206140c786828701613fad565b92505060406140d88682870161407a565b9150509250925092565b60006080820190506140f7600083018761400c565b614104602083018661400c565b614111604083018561400c565b61411e6060830184613efc565b95945050505050565b61413081614002565b811461413b57600080fd5b50565b60008135905061414d81614127565b92915050565b6000806040838503121561416a57614169613f8c565b5b600061417885828601613fad565b92505060206141898582860161413e565b9150509250929050565b60006040820190506141a86000830185613f58565b6141b5602083018461400c565b9392505050565b600080fd5b600080fd5b600080fd5b60008083601f8401126141e1576141e06141bc565b5b8235905067ffffffffffffffff8111156141fe576141fd6141c1565b5b60208301915083602082028301111561421a576142196141c6565b5b9250929050565b60008083601f840112614237576142366141bc565b5b8235905067ffffffffffffffff811115614254576142536141c1565b5b6020830191508360208202830111156142705761426f6141c6565b5b9250929050565b6000806000806000806060878903121561429457614293613f8c565b5b600087013567ffffffffffffffff8111156142b2576142b1613f91565b5b6142be89828a016141cb565b9650965050602087013567ffffffffffffffff8111156142e1576142e0613f91565b5b6142ed89828a016141cb565b9450945050604087013567ffffffffffffffff8111156143105761430f613f91565b5b61431c89828a01614221565b92509250509295509295509295565b6000806000806040858703121561434557614344613f8c565b5b600085013567ffffffffffffffff81111561436357614362613f91565b5b61436f878288016141cb565b9450945050602085013567ffffffffffffffff81111561439257614391613f91565b5b61439e87828801614221565b925092505092959194509250565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6143e181613f46565b82525050565b6143f081614002565b82525050565b60408201600082015161440c60008501826143d8565b50602082015161441f60208501826143e7565b50505050565b600061443183836143f6565b60408301905092915050565b6000602082019050919050565b6000614455826143ac565b61445f81856143b7565b935061446a836143c8565b8060005b8381101561449b5781516144828882614425565b975061448d8361443d565b92505060018101905061446e565b5085935050505092915050565b600060208201905081810360008301526144c2818461444a565b905092915050565b600080600080608085870312156144e4576144e3613f8c565b5b60006144f287828801613fad565b945050602061450387828801613fad565b93505060406145148782880161407a565b92505060606145258782880161407a565b91505092959194509250565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61457a82614531565b810181811067ffffffffffffffff8211171561459957614598614542565b5b80604052505050565b60006145ac613f82565b90506145b88282614571565b919050565b600067ffffffffffffffff8211156145d8576145d7614542565b5b602082029050602081019050919050565b60006145fc6145f7846145bd565b6145a2565b9050808382526020820190506020840283018581111561461f5761461e6141c6565b5b835b8181101561464857806146348882613fad565b845260208401935050602081019050614621565b5050509392505050565b600082601f830112614667576146666141bc565b5b81356146778482602086016145e9565b91505092915050565b60008060008060008060c0878903121561469d5761469c613f8c565b5b60006146ab89828a01613fad565b96505060206146bc89828a01613fad565b955050604087013567ffffffffffffffff8111156146dd576146dc613f91565b5b6146e989828a01614652565b94505060606146fa89828a01613fad565b935050608061470b89828a01613fad565b92505060a061471c89828a01613fad565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600061476183836143d8565b60208301905092915050565b6000602082019050919050565b600061478582614729565b61478f8185614734565b935061479a83614745565b8060005b838110156147cb5781516147b28882614755565b97506147bd8361476d565b92505060018101905061479e565b5085935050505092915050565b600060208201905081810360008301526147f2818461477a565b905092915050565b6000806000806000806080878903121561481757614816613f8c565b5b600087013567ffffffffffffffff81111561483557614834613f91565b5b61484189828a016141cb565b9650965050602087013567ffffffffffffffff81111561486457614863613f91565b5b61487089828a016141cb565b9450945050604061488389828a0161407a565b925050606061489489828a0161407a565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b60005b838110156148db5780820151818401526020810190506148c0565b60008484015250505050565b60006148f2826148a1565b6148fc81856148ac565b935061490c8185602086016148bd565b61491581614531565b840191505092915050565b60006040820190506149356000830185613efc565b818103602083015261494781846148e7565b90509392505050565b6000806020838503121561496757614966613f8c565b5b600083013567ffffffffffffffff81111561498557614984613f91565b5b614991858286016141cb565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006149d583836143e7565b60208301905092915050565b6000602082019050919050565b60006149f98261499d565b614a0381856149a8565b9350614a0e836149b9565b8060005b83811015614a3f578151614a2688826149c9565b9750614a31836149e1565b925050600181019050614a12565b5085935050505092915050565b600060a0820190508181036000830152614a66818861477a565b90508181036020830152614a7a818761477a565b90508181036040830152614a8e818661477a565b90508181036060830152614aa281856149ee565b90508181036080830152614ab681846149ee565b90509695505050505050565b60006060820190508181036000830152614adc818661477a565b90508181036020830152614af0818561477a565b9050614aff6040830184613f58565b949350505050565b600081519050614b1681613f96565b92915050565b6000614b2f614b2a846145bd565b6145a2565b90508083825260208201905060208402830185811115614b5257614b516141c6565b5b835b81811015614b7b5780614b678882614b07565b845260208401935050602081019050614b54565b5050509392505050565b600082601f830112614b9a57614b996141bc565b5b8151614baa848260208601614b1c565b91505092915050565b600060208284031215614bc957614bc8613f8c565b5b600082015167ffffffffffffffff811115614be757614be6613f91565b5b614bf384828501614b85565b91505092915050565b6000606082019050614c116000830186613f58565b614c1e6020830185613f58565b614c2b604083018461400c565b949350505050565b600081519050614c4281614127565b92915050565b600081519050614c5781614063565b92915050565b600080600060608486031215614c7657614c75613f8c565b5b6000614c8486828701614c33565b9350506020614c9586828701614c33565b9250506040614ca686828701614c48565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614cea82614002565b9150614cf583614002565b9250828201905080821115614d0d57614d0c614cb0565b5b92915050565b6000604082019050614d286000830185613f58565b614d356020830184613f58565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000604082019050614d80600083018561400c565b614d8d602083018461400c565b9392505050565b6000614d9f82614002565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614dd157614dd0614cb0565b5b600182019050919050565b6000819050919050565b6000614df56020840184613fad565b905092915050565b6000602082019050919050565b6000614e168385614734565b9350614e2182614ddc565b8060005b85811015614e5a57614e378284614de6565b614e418882614755565b9750614e4c83614dfd565b925050600181019050614e25565b5085925050509392505050565b60006060820190508181036000830152614e81818761477a565b90508181036020830152614e96818587614e0a565b9050614ea56040830184613f58565b95945050505050565b600082825260208201905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614f1b602683614eae565b9150614f2682614ebf565b604082019050919050565b60006020820190508181036000830152614f4a81614f0e565b9050919050565b6000604082019050614f666000830185613f58565b8181036020830152614f78818461477a565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000614fb7602083614eae565b9150614fc282614f81565b602082019050919050565b60006020820190508181036000830152614fe681614faa565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000615023601f83614eae565b915061502e82614fed565b602082019050919050565b6000602082019050818103600083015261505281615016565b9050919050565b600080fd5b600067ffffffffffffffff82111561507957615078614542565b5b61508282614531565b9050602081019050919050565b60006150a261509d8461505e565b6145a2565b9050828152602081018484840111156150be576150bd615059565b5b6150c98482856148bd565b509392505050565b600082601f8301126150e6576150e56141bc565b5b81516150f684826020860161508f565b91505092915050565b600067ffffffffffffffff82111561511a57615119614542565b5b602082029050602081019050919050565b600061513e615139846150ff565b6145a2565b90508083825260208201905060208402830185811115615161576151606141c6565b5b835b8181101561518a57806151768882614c33565b845260208401935050602081019050615163565b5050509392505050565b600082601f8301126151a9576151a86141bc565b5b81516151b984826020860161512b565b91505092915050565b600080600080608085870312156151dc576151db613f8c565b5b60006151ea87828801614b07565b945050602085015167ffffffffffffffff81111561520b5761520a613f91565b5b615217878288016150d1565b935050604085015167ffffffffffffffff81111561523857615237613f91565b5b61524487828801614b85565b925050606085015167ffffffffffffffff81111561526557615264613f91565b5b61527187828801615194565b91505092959194509250565b60006060820190506152926000830186613f58565b61529f602083018561400c565b6152ac6040830184613f58565b949350505050565b600081905092915050565b60006152ca826148a1565b6152d481856152b4565b93506152e48185602086016148bd565b80840191505092915050565b60006152fc82846152bf565b915081905092915050565b6000602082019050818103600083015261532181846148e7565b905092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61535e81615329565b82525050565b60006020820190506153796000830184615355565b92915050565b60006020828403121561539557615394613f8c565b5b60006153a384828501614c48565b91505092915050565b600060408201905081810360008301526153c6818561477a565b90506153d56020830184613f58565b9392505050565b600080604083850312156153f3576153f2613f8c565b5b600061540185828601614b07565b925050602083015167ffffffffffffffff81111561542257615421613f91565b5b61542e858286016150d1565b9150509250929050565b60006020828403121561544e5761544d613f8c565b5b600061545c84828501614c33565b91505092915050565b600061547082614002565b915061547b83614002565b925082820390508181111561549357615492614cb0565b5b92915050565b60006060820190506154ae6000830186613f58565b6154bb6020830185613f58565b6154c86040830184613efc565b949350505050565b600080600080608085870312156154ea576154e9613f8c565b5b60006154f887828801614c33565b945050602061550987828801614c33565b935050604061551a87828801614c33565b925050606061552b87828801614c48565b91505092959194509250565b600061554282614002565b915061554d83614002565b925082820261555b81614002565b9150828204841483151761557257615571614cb0565b5b5092915050565b6000819050919050565b6000819050919050565b60006155a86155a361559e84615579565b615583565b614002565b9050919050565b6155b88161558d565b82525050565b60006040820190506155d36000830185613f58565b6155e060208301846155af565b9392505050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b6000615643602a83614eae565b915061564e826155e7565b604082019050919050565b6000602082019050818103600083015261567281615636565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006156e282614002565b91506156ed83614002565b9250826156fd576156fc6156a8565b5b828204905092915050565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b6000615764602683614eae565b915061576f82615708565b604082019050919050565b6000602082019050818103600083015261579381615757565b9050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006157d0601d83614eae565b91506157db8261579a565b602082019050919050565b600060208201905081810360008301526157ff816157c3565b9050919050565b600081519050919050565b600061581c82615806565b6158268185614eae565b93506158368185602086016148bd565b61583f81614531565b840191505092915050565b600060208201905081810360008301526158648184615811565b90509291505056fea26469706673582212205531256c66d86d972e5022d29244e2d61f595958b87899ccdd6a2585d8435e3964736f6c63430008130033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c80638a6b4cc911610104578063bfc12c05116100a2578063cf5303cf11610071578063cf5303cf146104e6578063eb14528214610505578063f2fde38b14610539578063fa72968514610555576101cf565b8063bfc12c051461045e578063c21fcdd81461047c578063c45a0155146104ac578063c5e0164a146104ca576101cf565b806395147783116100de57806395147783146103e8578063a8c62e7614610404578063a9d376ef14610422578063b82e16e314610440576101cf565b80638a6b4cc9146103925780638da5cb5b146103ae5780638f7b91c5146103cc576101cf565b806359383489116101715780637ca20fc01161014b5780637ca20fc01461030e5780637cf2510c1461032a5780637ecb7bed146103465780638980f11f14610376576101cf565b806359383489146102c9578063715018a6146102d35780637be7e96e146102dd576101cf565b80631f655b69116101ad5780631f655b691461022e578063274833631461025e5780632aeace391461027a57806333a100ca146102ad576101cf565b80630fd78152146101d4578063112666b7146101f2578063158ef93e14610210575b600080fd5b6101dc610571565b6040516101e99190613f0b565b60405180910390f35b6101fa610677565b6040516102079190613f67565b60405180910390f35b61021861069d565b6040516102259190613f0b565b60405180910390f35b61024860048036038101906102439190613fc2565b6106b0565b604051610255919061401b565b60405180910390f35b61027860048036038101906102739190614036565b6106d5565b005b610294600480360381019061028f919061408f565b6107e9565b6040516102a494939291906140e2565b60405180910390f35b6102c760048036038101906102c29190614036565b6109c5565b005b6102d1610ad2565b005b6102db610e15565b005b6102f760048036038101906102f29190614153565b610e29565b604051610305929190614193565b60405180910390f35b61032860048036038101906103239190614277565b610e8a565b005b610344600480360381019061033f919061432b565b610fb8565b005b610360600480360381019061035b9190614036565b611140565b60405161036d91906144a8565b60405180910390f35b610390600480360381019061038b9190614153565b61123e565b005b6103ac60048036038101906103a791906144ca565b61127c565b005b6103b66112a9565b6040516103c39190613f67565b60405180910390f35b6103e660048036038101906103e19190614680565b6112d2565b005b61040260048036038101906103fd9190614036565b6116ea565b005b61040c611797565b6040516104199190613f67565b60405180910390f35b61042a6117bd565b6040516104379190613f67565b60405180910390f35b6104486117e3565b60405161045591906147d8565b60405180910390f35b6104666117f4565b604051610473919061401b565b60405180910390f35b61049660048036038101906104919190613fc2565b6117fa565b6040516104a3919061401b565b60405180910390f35b6104b461181f565b6040516104c19190613f67565b60405180910390f35b6104e460048036038101906104df91906147fa565b611845565b005b6104ee611923565b6040516104fc929190614920565b60405180910390f35b61051f600480360381019061051a9190614950565b6119a2565b604051610530959493929190614a4c565b60405180910390f35b610553600480360381019061054e9190614036565b611af1565b005b61056f600480360381019061056a9190614036565b611b74565b005b60008061057c611c81565b9050600061058a6005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf8385600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff1660e01b815260040161060f93929190614ac2565b600060405180830381865afa15801561062c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906106559190614bb3565b905060006106638483611d55565b955050505050506000811194505050505090565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600b60149054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6106dd6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610743576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61075781600561216190919063ffffffff16565b1561078e576040517f757d2ccf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107a281600561219190919063ffffffff16565b508073ffffffffffffffffffffffffffffffffffffffff167fbc4a525f9c0127e6139bae298a07809d85a2d907b95942ebf8f11ae80c19162d60405160405180910390a250565b600080600080600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205493506000840361088a576000806000600193509350935093506109bc565b60008060008973ffffffffffffffffffffffffffffffffffffffff16638bba2e378a308a6040518463ffffffff1660e01b81526004016108cc93929190614bfc565b606060405180830381865afa1580156108e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090d9190614c5d565b92509250925060008861092057826109a9565b600360008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054836109a89190614cdf565b5b9050878482849750975097509750505050505b93509350935093565b6109cd6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610a33576040517f4e236e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f254c88e7a2ea123aeeb89b7cc413fb949188fefcdb7584c4f3d493294daf65c5600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682604051610a86929190614d13565b60405180910390a180600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610ada6121c1565b6000610ae4611c81565b90506000610af26005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf8385600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff1660e01b8152600401610b7793929190614ac2565b600060405180830381865afa158015610b94573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610bbd9190614bb3565b9050600080600080600080610bd28988611d55565b95509550955095509550955060008103610bf457505050505050505050610e0b565b60005b81811015610e00576000878281518110610c1457610c13614d3c565b5b602002602001015190506000878381518110610c3357610c32614d3c565b5b602002602001015190506000878481518110610c5257610c51614d3c565b5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610cac57610c9a8284612210565b610cab8284600060016000306123bd565b5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015610d0357506000868581518110610cf957610cf8614d3c565b5b6020026020010151115b15610d3857610d2d8382888781518110610d2057610d1f614d3c565b5b6020026020010151612616565b610d378382612837565b5b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fd58635ff12f09ca41b1f8ea85ef83e3452b032687149d2be108b17130addd7ee8a8881518110610db157610db0614d3c565b5b60200260200101518a8981518110610dcc57610dcb614d3c565b5b6020026020010151604051610de2929190614d6b565b60405180910390a45050508080610df890614d94565b915050610bf7565b505050505050505050505b610e13612921565b565b610e1d6120e3565b610e27600061292a565b565b60046020528160005260406000208181548110610e4557600080fd5b9060005260206000209060020201600091509150508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010154905082565b610e926120e3565b610e9a6121c1565b610ea484846129ee565b610eda576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fa8868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050848480806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612a69565b610fb0612921565b505050505050565b610fc06120e3565b610fc86121c1565b6000610fd46005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf838888600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518563ffffffff1660e01b815260040161105b9493929190614e67565b600060405180830381865afa158015611078573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906110a19190614bb3565b9050611130868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505082868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612a69565b505061113a612921565b50505050565b6060600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b8282101561123357838290600052602060002090600202016040518060400160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015481525050815260200190600101906111a1565b505050509050919050565b6112466120e3565b6112786112516112a9565b828473ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b5050565b6112846120e3565b61128c6121c1565b61129b848484846001336123bd565b6112a3612921565b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600b60149054906101000a900460ff16156112ec57600080fd5b6001600b60146101000a81548160ff021916908315150217905550600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff160361136d576040517f49e27cff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113768661292a565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036113dc576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603611442576040517f2b7d09bc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036114a8576040517f4e236e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361150e576040517f2e7c914400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000845103611549576040517f332b53ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055504260088190555082600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060005b84518110156116e157600073ffffffffffffffffffffffffffffffffffffffff1685828151811061164a57611649614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160361169f576040517ffbf66df100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cd8582815181106116b5576116b4614d3c565b5b6020026020010151600561219190919063ffffffff16565b5080806116d990614d94565b915050611616565b50505050505050565b6116f26120e3565b61170681600561216190919063ffffffff16565b61173c576040517ff7b1bf8e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611750816005612dad90919063ffffffff16565b508073ffffffffffffffffffffffffffffffffffffffff167f6044b319b7468595a28d1a8c817e22028afb652faec55c709426f75584fc8a4060405160405180910390a250565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60606117ef6005611d34565b905090565b60085481565b6003602052816000526040600020602052806000526040600020600091509150505481565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61184d6120e3565b6118556121c1565b838390508686905014611894576040517fa0b1d72d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b86869050811015611912576118ff8787838181106118b8576118b7614d3c565b5b90506020020160208101906118cd9190614036565b8686848181106118e0576118df614d3c565b5b90506020020160208101906118f59190614036565b85856001336123bd565b808061190a90614d94565b915050611897565b5061191b612921565b505050505050565b6000606061192f610571565b9150635938348960e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090509091565b606080606080606060006119b66005611d34565b90506000600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c846d0bf838b8b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518563ffffffff1660e01b8152600401611a3d9493929190614e67565b600060405180830381865afa158015611a5a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190611a839190614bb3565b9050611ad0898980806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505082611d55565b50809750819850829950839a50849b50505050505050509295509295909350565b611af96120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611b68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b5f90614f31565b60405180910390fd5b611b718161292a565b50565b611b7c6120e3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611be2576040517f2e7c914400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fe6e02d5ab813935e6bcfd549ca115804cb5e7812d961d826c8bebe05a0b9a68a600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682604051611c35929190614d13565b60405180910390a180600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eff0fca930611ccc6005611d34565b6040518363ffffffff1660e01b8152600401611ce9929190614f51565b600060405180830381865afa158015611d06573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190611d2f9190614bb3565b905090565b60606000611d4483600001612ddd565b905060608190508092505050919050565b60608060608060606000875167ffffffffffffffff811115611d7a57611d79614542565b5b604051908082528060200260200182016040528015611da85781602001602082028036833780820191505090505b509550875167ffffffffffffffff811115611dc657611dc5614542565b5b604051908082528060200260200182016040528015611df45781602001602082028036833780820191505090505b509450875167ffffffffffffffff811115611e1257611e11614542565b5b604051908082528060200260200182016040528015611e405781602001602082028036833780820191505090505b509350875167ffffffffffffffff811115611e5e57611e5d614542565b5b604051908082528060200260200182016040528015611e8c5781602001602082028036833780820191505090505b509250875167ffffffffffffffff811115611eaa57611ea9614542565b5b604051908082528060200260200182016040528015611ed85781602001602082028036833780820191505090505b5091506000905060005b88518110156120d8576000898281518110611f0057611eff614d3c565b5b602002602001015190506000898381518110611f1f57611f1e614d3c565b5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611f645750506120c5565b600080611f7084612e39565b915091508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146120c057838b8781518110611fbb57611fba614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050818a878151811061200957612008614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508289878151811061205757612056614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050808887815181106120a5576120a4614d3c565b5b60200260200101818152505085806120bc90614d94565b9650505b505050505b80806120d090614d94565b915050611ee2565b509295509295509295565b6120eb612f32565b73ffffffffffffffffffffffffffffffffffffffff166121096112a9565b73ffffffffffffffffffffffffffffffffffffffff161461215f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161215690614fcd565b60405180910390fd5b565b6000612189836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612f3a565b905092915050565b60006121b9836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612f5d565b905092915050565b600260015403612206576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121fd90615039565b60405180910390fd5b6002600181905550565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506000810361229f57506123b9565b60008373ffffffffffffffffffffffffffffffffffffffff16638bba2e378430856040518463ffffffff1660e01b81526004016122de93929190614bfc565b606060405180830381865afa1580156122fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231f9190614c5d565b5091505080600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546123af9190614cdf565b9250508190555050505b5050565b6000806000808973ffffffffffffffffffffffffffffffffffffffff1663bd4e53318a306040518363ffffffff1660e01b81526004016123fe929190614d13565b600060405180830381865afa15801561241b573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061244491906151c2565b935093509350935060005b82518110156124ca576124b78b83838151811061246f5761246e614d3c565b5b602002602001015185848151811061248a57612489614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b80806124c290614d94565b91505061244f565b506124d58484612fcd565b600260008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000905585156125dc57600360008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090555b86156125ee576125ed8a8a87613084565b5b871561260a576125fe898b613516565b506126098933613722565b5b50505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16148061267d5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b806126885750600081145b6128325760008390506126be3330848473ffffffffffffffffffffffffffffffffffffffff16613858909392919063ffffffff16565b6126c98184846138e1565b8273ffffffffffffffffffffffffffffffffffffffff16638b2a4df58584306040518463ffffffff1660e01b81526004016127069392919061527d565b600060405180830381600087803b15801561272057600080fd5b505af1158015612734573d6000803e3d6000fd5b5050505081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546127c49190614cdf565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac884604051612828919061401b565b60405180910390a3505b505050565b600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180604001604052808373ffffffffffffffffffffffffffffffffffffffff16815260200142815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015550505050565b60018081905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080600090505b83839050811015612a5d57612a3c848483818110612a1757612a16614d3c565b5b9050602002016020810190612a2c9190614036565b600561216190919063ffffffff16565b612a4a576000915050612a63565b8080612a5590614d94565b9150506129f6565b50600190505b92915050565b81518351141580612a7c57508051835114155b15612ab3576040517fa0b1d72d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8351811015612d2157600073ffffffffffffffffffffffffffffffffffffffff16848281518110612aea57612ae9614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff160315612d0e576000848281518110612b2357612b22614d3c565b5b602002602001015190506000838381518110612b4257612b41614d3c565b5b602002602001015190506000858481518110612b6157612b60614d3c565b5b60200260200101519050612b983330848673ffffffffffffffffffffffffffffffffffffffff16613858909392919063ffffffff16565b612ba38382846138e1565b8073ffffffffffffffffffffffffffffffffffffffff16638b2a4df58484306040518463ffffffff1660e01b8152600401612be09392919061527d565b600060405180830381600087803b158015612bfa57600080fd5b505af1158015612c0e573d6000803e3d6000fd5b5050505081600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612c9e9190614cdf565b925050819055508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac884604051612d02919061401b565b60405180910390a35050505b8080612d1990614d94565b915050612ab6565b50505050565b612da88363a9059cbb60e01b8484604051602401612d46929190614193565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613a69565b505050565b6000612dd5836000018373ffffffffffffffffffffffffffffffffffffffff1660001b613b30565b905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015612e2d57602002820191906000526020600020905b815481526020019060010190808311612e19575b50505050509050919050565b6000806000612e486005611d34565b905060005b8151811015612f23576000828281518110612e6b57612e6a614d3c565b5b602002602001015190506000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506000811115612f0e5781819550955050505050612f2d565b50508080612f1b90614d94565b915050612e4d565b5060008092509250505b915091565b600033905090565b600080836001016000848152602001908152602001600020541415905092915050565b6000612f698383612f3a565b612fc2578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050612fc7565b600090505b92915050565b6000808373ffffffffffffffffffffffffffffffffffffffff1683604051612ff591906152f0565b6000604051808303816000865af19150503d8060008114613032576040519150601f19603f3d011682016040523d82523d6000602084013e613037565b606091505b50915091508161307e57806040517f59afd6c60000000000000000000000000000000000000000000000000000000081526004016130759190615307565b60405180910390fd5b50505050565b600061308f83613c44565b90508373ffffffffffffffffffffffffffffffffffffffff166301ffc9a77e5f985e000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016130e99190615364565b602060405180830381865afa158015613106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312a919061537f565b1561350f576000808573ffffffffffffffffffffffffffffffffffffffff1663c01a654084306040518363ffffffff1660e01b815260040161316d9291906153ac565b600060405180830381865afa15801561318a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906131b391906153dc565b91509150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614806131f3575060008151145b1561320057505050613511565b60008673ffffffffffffffffffffffffffffffffffffffff16634c9f7c37876040518263ffffffff1660e01b815260040161323b9190613f67565b600060405180830381865afa158015613258573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906132819190614bb3565b90506000815167ffffffffffffffff8111156132a05761329f614542565b5b6040519080825280602002602001820160405280156132ce5781602001602082028036833780820191505090505b50905060005b82518110156133a3578281815181106132f0576132ef614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016133309190613f67565b602060405180830381865afa15801561334d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133719190615438565b82828151811061338457613383614d3c565b5b602002602001018181525050808061339b90614d94565b9150506132d4565b506133ae8484612fcd565b60005b82518110156135095760008382815181106133cf576133ce614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161340f9190613f67565b602060405180830381865afa15801561342c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134509190615438565b9050600083838151811061346757613466614d3c565b5b6020026020010151821161347c5760006134a3565b83838151811061348f5761348e614d3c565b5b6020026020010151826134a29190615465565b5b905060008111156134f4576134f389828786815181106134c6576134c5614d3c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b5b5050808061350190614d94565b9150506133b1565b50505050505b505b505050565b6000803073ffffffffffffffffffffffffffffffffffffffff16632aeace39848660016040518463ffffffff1660e01b815260040161355793929190615499565b608060405180830381865afa158015613574573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359891906154d0565b5092505050600081036135af57600091505061371c565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388c7fff36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136429190615438565b905061365b81836136539190615537565b612710613ce6565b92506000831115613719576136b3600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16848773ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f9e1eaea49c3a4fb24beb4c9b2df59d3fbf568710fe1f5c6ed2d0d1d0dd4dac3e85604051613710919061401b565b60405180910390a35b50505b92915050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161375d9190613f67565b602060405180830381865afa15801561377a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379e9190615438565b9050600081036137da576040517ffba5154e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61380582828573ffffffffffffffffffffffffffffffffffffffff16612d279092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff167fa2bd9fcfcdba69f52bcd9a520846ad4bd685b187483f53efc42d035b2ddebff08260405161384b919061401b565b60405180910390a2505050565b6138db846323b872dd60e01b85858560405160240161387993929190614bfc565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613a69565b50505050565b808373ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30856040518363ffffffff1660e01b815260040161391d929190614d13565b602060405180830381865afa15801561393a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061395e9190615438565b1015613a64578273ffffffffffffffffffffffffffffffffffffffff1663095ea7b38360006040518363ffffffff1660e01b81526004016139a09291906155be565b6020604051808303816000875af11580156139bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e3919061537f565b508273ffffffffffffffffffffffffffffffffffffffff1663095ea7b383836040518363ffffffff1660e01b8152600401613a1f929190614193565b6020604051808303816000875af1158015613a3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a62919061537f565b505b505050565b6000613acb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613d239092919063ffffffff16565b9050600081511115613b2b5780806020019051810190613aeb919061537f565b613b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613b2190615659565b60405180910390fd5b5b505050565b60008083600101600084815260200190815260200160002054905060008114613c38576000600182613b629190615465565b9050600060018660000180549050613b7a9190615465565b9050818114613be9576000866000018281548110613b9b57613b9a614d3c565b5b9060005260206000200154905080876000018481548110613bbf57613bbe614d3c565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b85600001805480613bfd57613bfc615679565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050613c3e565b60009150505b92915050565b6060600167ffffffffffffffff811115613c6157613c60614542565b5b604051908082528060200260200182016040528015613c8f5781602001602082028036833780820191505090505b5090508181600081518110613ca757613ca6614d3c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050919050565b6000808314613d1857600182600185613cff9190615465565b613d0991906156d7565b613d139190614cdf565b613d1b565b60005b905092915050565b6060613d328484600085613d3b565b90509392505050565b606082471015613d80576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613d779061577a565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613da991906152f0565b60006040518083038185875af1925050503d8060008114613de6576040519150601f19603f3d011682016040523d82523d6000602084013e613deb565b606091505b5091509150613dfc87838387613e08565b92505050949350505050565b60608315613e6a576000835103613e6257613e2285613e7d565b613e61576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613e58906157e6565b60405180910390fd5b5b829050613e75565b613e748383613ea0565b5b949350505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600082511115613eb35781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613ee7919061584a565b60405180910390fd5b60008115159050919050565b613f0581613ef0565b82525050565b6000602082019050613f206000830184613efc565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613f5182613f26565b9050919050565b613f6181613f46565b82525050565b6000602082019050613f7c6000830184613f58565b92915050565b6000604051905090565b600080fd5b600080fd5b613f9f81613f46565b8114613faa57600080fd5b50565b600081359050613fbc81613f96565b92915050565b60008060408385031215613fd957613fd8613f8c565b5b6000613fe785828601613fad565b9250506020613ff885828601613fad565b9150509250929050565b6000819050919050565b61401581614002565b82525050565b6000602082019050614030600083018461400c565b92915050565b60006020828403121561404c5761404b613f8c565b5b600061405a84828501613fad565b91505092915050565b61406c81613ef0565b811461407757600080fd5b50565b60008135905061408981614063565b92915050565b6000806000606084860312156140a8576140a7613f8c565b5b60006140b686828701613fad565b93505060206140c786828701613fad565b92505060406140d88682870161407a565b9150509250925092565b60006080820190506140f7600083018761400c565b614104602083018661400c565b614111604083018561400c565b61411e6060830184613efc565b95945050505050565b61413081614002565b811461413b57600080fd5b50565b60008135905061414d81614127565b92915050565b6000806040838503121561416a57614169613f8c565b5b600061417885828601613fad565b92505060206141898582860161413e565b9150509250929050565b60006040820190506141a86000830185613f58565b6141b5602083018461400c565b9392505050565b600080fd5b600080fd5b600080fd5b60008083601f8401126141e1576141e06141bc565b5b8235905067ffffffffffffffff8111156141fe576141fd6141c1565b5b60208301915083602082028301111561421a576142196141c6565b5b9250929050565b60008083601f840112614237576142366141bc565b5b8235905067ffffffffffffffff811115614254576142536141c1565b5b6020830191508360208202830111156142705761426f6141c6565b5b9250929050565b6000806000806000806060878903121561429457614293613f8c565b5b600087013567ffffffffffffffff8111156142b2576142b1613f91565b5b6142be89828a016141cb565b9650965050602087013567ffffffffffffffff8111156142e1576142e0613f91565b5b6142ed89828a016141cb565b9450945050604087013567ffffffffffffffff8111156143105761430f613f91565b5b61431c89828a01614221565b92509250509295509295509295565b6000806000806040858703121561434557614344613f8c565b5b600085013567ffffffffffffffff81111561436357614362613f91565b5b61436f878288016141cb565b9450945050602085013567ffffffffffffffff81111561439257614391613f91565b5b61439e87828801614221565b925092505092959194509250565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6143e181613f46565b82525050565b6143f081614002565b82525050565b60408201600082015161440c60008501826143d8565b50602082015161441f60208501826143e7565b50505050565b600061443183836143f6565b60408301905092915050565b6000602082019050919050565b6000614455826143ac565b61445f81856143b7565b935061446a836143c8565b8060005b8381101561449b5781516144828882614425565b975061448d8361443d565b92505060018101905061446e565b5085935050505092915050565b600060208201905081810360008301526144c2818461444a565b905092915050565b600080600080608085870312156144e4576144e3613f8c565b5b60006144f287828801613fad565b945050602061450387828801613fad565b93505060406145148782880161407a565b92505060606145258782880161407a565b91505092959194509250565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61457a82614531565b810181811067ffffffffffffffff8211171561459957614598614542565b5b80604052505050565b60006145ac613f82565b90506145b88282614571565b919050565b600067ffffffffffffffff8211156145d8576145d7614542565b5b602082029050602081019050919050565b60006145fc6145f7846145bd565b6145a2565b9050808382526020820190506020840283018581111561461f5761461e6141c6565b5b835b8181101561464857806146348882613fad565b845260208401935050602081019050614621565b5050509392505050565b600082601f830112614667576146666141bc565b5b81356146778482602086016145e9565b91505092915050565b60008060008060008060c0878903121561469d5761469c613f8c565b5b60006146ab89828a01613fad565b96505060206146bc89828a01613fad565b955050604087013567ffffffffffffffff8111156146dd576146dc613f91565b5b6146e989828a01614652565b94505060606146fa89828a01613fad565b935050608061470b89828a01613fad565b92505060a061471c89828a01613fad565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600061476183836143d8565b60208301905092915050565b6000602082019050919050565b600061478582614729565b61478f8185614734565b935061479a83614745565b8060005b838110156147cb5781516147b28882614755565b97506147bd8361476d565b92505060018101905061479e565b5085935050505092915050565b600060208201905081810360008301526147f2818461477a565b905092915050565b6000806000806000806080878903121561481757614816613f8c565b5b600087013567ffffffffffffffff81111561483557614834613f91565b5b61484189828a016141cb565b9650965050602087013567ffffffffffffffff81111561486457614863613f91565b5b61487089828a016141cb565b9450945050604061488389828a0161407a565b925050606061489489828a0161407a565b9150509295509295509295565b600081519050919050565b600082825260208201905092915050565b60005b838110156148db5780820151818401526020810190506148c0565b60008484015250505050565b60006148f2826148a1565b6148fc81856148ac565b935061490c8185602086016148bd565b61491581614531565b840191505092915050565b60006040820190506149356000830185613efc565b818103602083015261494781846148e7565b90509392505050565b6000806020838503121561496757614966613f8c565b5b600083013567ffffffffffffffff81111561498557614984613f91565b5b614991858286016141cb565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006149d583836143e7565b60208301905092915050565b6000602082019050919050565b60006149f98261499d565b614a0381856149a8565b9350614a0e836149b9565b8060005b83811015614a3f578151614a2688826149c9565b9750614a31836149e1565b925050600181019050614a12565b5085935050505092915050565b600060a0820190508181036000830152614a66818861477a565b90508181036020830152614a7a818761477a565b90508181036040830152614a8e818661477a565b90508181036060830152614aa281856149ee565b90508181036080830152614ab681846149ee565b90509695505050505050565b60006060820190508181036000830152614adc818661477a565b90508181036020830152614af0818561477a565b9050614aff6040830184613f58565b949350505050565b600081519050614b1681613f96565b92915050565b6000614b2f614b2a846145bd565b6145a2565b90508083825260208201905060208402830185811115614b5257614b516141c6565b5b835b81811015614b7b5780614b678882614b07565b845260208401935050602081019050614b54565b5050509392505050565b600082601f830112614b9a57614b996141bc565b5b8151614baa848260208601614b1c565b91505092915050565b600060208284031215614bc957614bc8613f8c565b5b600082015167ffffffffffffffff811115614be757614be6613f91565b5b614bf384828501614b85565b91505092915050565b6000606082019050614c116000830186613f58565b614c1e6020830185613f58565b614c2b604083018461400c565b949350505050565b600081519050614c4281614127565b92915050565b600081519050614c5781614063565b92915050565b600080600060608486031215614c7657614c75613f8c565b5b6000614c8486828701614c33565b9350506020614c9586828701614c33565b9250506040614ca686828701614c48565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614cea82614002565b9150614cf583614002565b9250828201905080821115614d0d57614d0c614cb0565b5b92915050565b6000604082019050614d286000830185613f58565b614d356020830184613f58565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000604082019050614d80600083018561400c565b614d8d602083018461400c565b9392505050565b6000614d9f82614002565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614dd157614dd0614cb0565b5b600182019050919050565b6000819050919050565b6000614df56020840184613fad565b905092915050565b6000602082019050919050565b6000614e168385614734565b9350614e2182614ddc565b8060005b85811015614e5a57614e378284614de6565b614e418882614755565b9750614e4c83614dfd565b925050600181019050614e25565b5085925050509392505050565b60006060820190508181036000830152614e81818761477a565b90508181036020830152614e96818587614e0a565b9050614ea56040830184613f58565b95945050505050565b600082825260208201905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614f1b602683614eae565b9150614f2682614ebf565b604082019050919050565b60006020820190508181036000830152614f4a81614f0e565b9050919050565b6000604082019050614f666000830185613f58565b8181036020830152614f78818461477a565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000614fb7602083614eae565b9150614fc282614f81565b602082019050919050565b60006020820190508181036000830152614fe681614faa565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000615023601f83614eae565b915061502e82614fed565b602082019050919050565b6000602082019050818103600083015261505281615016565b9050919050565b600080fd5b600067ffffffffffffffff82111561507957615078614542565b5b61508282614531565b9050602081019050919050565b60006150a261509d8461505e565b6145a2565b9050828152602081018484840111156150be576150bd615059565b5b6150c98482856148bd565b509392505050565b600082601f8301126150e6576150e56141bc565b5b81516150f684826020860161508f565b91505092915050565b600067ffffffffffffffff82111561511a57615119614542565b5b602082029050602081019050919050565b600061513e615139846150ff565b6145a2565b90508083825260208201905060208402830185811115615161576151606141c6565b5b835b8181101561518a57806151768882614c33565b845260208401935050602081019050615163565b5050509392505050565b600082601f8301126151a9576151a86141bc565b5b81516151b984826020860161512b565b91505092915050565b600080600080608085870312156151dc576151db613f8c565b5b60006151ea87828801614b07565b945050602085015167ffffffffffffffff81111561520b5761520a613f91565b5b615217878288016150d1565b935050604085015167ffffffffffffffff81111561523857615237613f91565b5b61524487828801614b85565b925050606085015167ffffffffffffffff81111561526557615264613f91565b5b61527187828801615194565b91505092959194509250565b60006060820190506152926000830186613f58565b61529f602083018561400c565b6152ac6040830184613f58565b949350505050565b600081905092915050565b60006152ca826148a1565b6152d481856152b4565b93506152e48185602086016148bd565b80840191505092915050565b60006152fc82846152bf565b915081905092915050565b6000602082019050818103600083015261532181846148e7565b905092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61535e81615329565b82525050565b60006020820190506153796000830184615355565b92915050565b60006020828403121561539557615394613f8c565b5b60006153a384828501614c48565b91505092915050565b600060408201905081810360008301526153c6818561477a565b90506153d56020830184613f58565b9392505050565b600080604083850312156153f3576153f2613f8c565b5b600061540185828601614b07565b925050602083015167ffffffffffffffff81111561542257615421613f91565b5b61542e858286016150d1565b9150509250929050565b60006020828403121561544e5761544d613f8c565b5b600061545c84828501614c33565b91505092915050565b600061547082614002565b915061547b83614002565b925082820390508181111561549357615492614cb0565b5b92915050565b60006060820190506154ae6000830186613f58565b6154bb6020830185613f58565b6154c86040830184613efc565b949350505050565b600080600080608085870312156154ea576154e9613f8c565b5b60006154f887828801614c33565b945050602061550987828801614c33565b935050604061551a87828801614c33565b925050606061552b87828801614c48565b91505092959194509250565b600061554282614002565b915061554d83614002565b925082820261555b81614002565b9150828204841483151761557257615571614cb0565b5b5092915050565b6000819050919050565b6000819050919050565b60006155a86155a361559e84615579565b615583565b614002565b9050919050565b6155b88161558d565b82525050565b60006040820190506155d36000830185613f58565b6155e060208301846155af565b9392505050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b6000615643602a83614eae565b915061564e826155e7565b604082019050919050565b6000602082019050818103600083015261567281615636565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006156e282614002565b91506156ed83614002565b9250826156fd576156fc6156a8565b5b828204905092915050565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b6000615764602683614eae565b915061576f82615708565b604082019050919050565b6000602082019050818103600083015261579381615757565b9050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006157d0601d83614eae565b91506157db8261579a565b602082019050919050565b600060208201905081810360008301526157ff816157c3565b9050919050565b600081519050919050565b600061581c82615806565b6158268185614eae565b93506158368185602086016148bd565b61583f81614531565b840191505092915050565b600060208201905081810360008301526158648184615811565b90509291505056fea26469706673582212205531256c66d86d972e5022d29244e2d61f595958b87899ccdd6a2585d8435e3964736f6c63430008130033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.