Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 736 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Update Protocol ... | 19586306 | 239 days ago | IN | 0 ETH | 0.00078427 | ||||
Update Protocol ... | 19583650 | 239 days ago | IN | 0 ETH | 0.00234565 | ||||
Process Requests | 19582097 | 239 days ago | IN | 0 ETH | 0.00522606 | ||||
Request Withdraw | 19582096 | 239 days ago | IN | 0.00824001 ETH | 0.00472806 | ||||
Process Requests | 19581665 | 239 days ago | IN | 0 ETH | 0.00487829 | ||||
Request Withdraw | 19581664 | 239 days ago | IN | 0.00824001 ETH | 0.00395593 | ||||
Process Requests | 19581403 | 240 days ago | IN | 0 ETH | 0.00496228 | ||||
Request Deposit | 19581402 | 240 days ago | IN | 0.00803232 ETH | 0.00474041 | ||||
Process Requests | 19581284 | 240 days ago | IN | 0 ETH | 0.00512778 | ||||
Request Withdraw | 19581283 | 240 days ago | IN | 0.00803232 ETH | 0.00414973 | ||||
Process Requests | 19578547 | 240 days ago | IN | 0 ETH | 0.00600352 | ||||
Request Withdraw | 19578546 | 240 days ago | IN | 0.01441905 ETH | 0.00520773 | ||||
Update Protocol ... | 19572705 | 241 days ago | IN | 0 ETH | 0.00113903 | ||||
Update Protocol ... | 19571728 | 241 days ago | IN | 0 ETH | 0.001554 | ||||
Process Requests | 19570265 | 241 days ago | IN | 0 ETH | 0.01165202 | ||||
Request Deposit | 19570264 | 241 days ago | IN | 0.02786143 ETH | 0.01240409 | ||||
Update Protocol ... | 19567800 | 241 days ago | IN | 0 ETH | 0.00139203 | ||||
Update Protocol ... | 19565201 | 242 days ago | IN | 0 ETH | 0.00162565 | ||||
Update Protocol ... | 19562033 | 242 days ago | IN | 0 ETH | 0.00228588 | ||||
Update Protocol ... | 19559816 | 243 days ago | IN | 0 ETH | 0.00089322 | ||||
Process Requests | 19558103 | 243 days ago | IN | 0 ETH | 0.00438079 | ||||
Request Withdraw | 19558102 | 243 days ago | IN | 0.00894394 ETH | 0.00384597 | ||||
Process Requests | 19552950 | 244 days ago | IN | 0 ETH | 0.00491794 | ||||
Request Withdraw | 19552949 | 244 days ago | IN | 0.00815549 ETH | 0.00432686 | ||||
Process Requests | 19552839 | 244 days ago | IN | 0 ETH | 0.00475365 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
19588039 | 239 days ago | 1.04614019 ETH | ||||
19375114 | 269 days ago | 0.86784168 ETH | ||||
19318934 | 276 days ago | 0.09906877 ETH | ||||
19318897 | 276 days ago | 0.09906877 ETH | ||||
19318878 | 276 days ago | 0.09906877 ETH | ||||
19268512 | 283 days ago | 0.79792783 ETH | ||||
19184299 | 295 days ago | 0.63053251 ETH | ||||
19120234 | 304 days ago | 1.21780993 ETH | ||||
18984237 | 323 days ago | 0.34132299 ETH | ||||
18946942 | 329 days ago | 0.02975896 ETH | ||||
18946942 | 329 days ago | 0.02975896 ETH | ||||
18946927 | 329 days ago | 0.02975896 ETH | ||||
18946913 | 329 days ago | 0.02975896 ETH | ||||
18946913 | 329 days ago | 0.02975896 ETH | ||||
18946892 | 329 days ago | 0.02975896 ETH | ||||
18946892 | 329 days ago | 0.02975896 ETH | ||||
18946873 | 329 days ago | 0.03033674 ETH | ||||
18946873 | 329 days ago | 0.03033674 ETH | ||||
18946844 | 329 days ago | 0.03033674 ETH | ||||
18735050 | 358 days ago | 0.36342664 ETH | ||||
18732949 | 359 days ago | 0.06864685 ETH | ||||
18732949 | 359 days ago | 0.06864685 ETH | ||||
18732881 | 359 days ago | 0.07204431 ETH | ||||
18732876 | 359 days ago | 0.07204431 ETH | ||||
18732861 | 359 days ago | 0.07204431 ETH |
Loading...
Loading
Contract Name:
Relayer
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; // Core import {Fyde} from "./Fyde.sol"; import {QuarantineList} from "./core/QuarantineList.sol"; import {RequestQueue} from "./core/RequestQueue.sol"; // Structs import {UserRequest, RequestData} from "./core/Structs.sol"; // Utils import {BaseChecker} from "./utils/BaseChecker.sol"; import {Ownable} from "./utils/Ownable.sol"; import {PercentageMath} from "./utils/PercentageMath.sol"; // Interfaces import {IFyde} from "./interfaces/IFyde.sol"; import {IGovernanceModule} from "./interfaces/IGovernanceModule.sol"; import {IOracle} from "./interfaces/IOracle.sol"; ///@title Relayer ///@notice The relayer is the entry point contract for users to interact with the protocol. /// User call request functions to make a deposit or a withdrawal, that request will be /// queued and be processed by a keeper. /// The relayer is monitored by a Gelato bot that will process the requests. /// The relayer is also monitored by a Gelato bot that will update the protocol AUM. contract Relayer is RequestQueue, QuarantineList, BaseChecker { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Fyde contract IFyde public fyde; ///@notice OracleModule contract IOracle public oracleModule; //@notice GovernanceModule contract IGovernanceModule public immutable GOVERNANCE_MODULE; //@notice Max number assets accepted in one request uint8 public constant MAX_ASSET_TO_REQUEST = 5; ///@notice Max requests to be processed in one batch uint8 public constant MAX_BATCH_SIZE = 3; ///@dev Only used for tracking events offchain uint32 public nonce; ///@notice Threshold of deviation for updating AUM uint16 public deviationThreshold; ///@notice State of the protocol bool public paused; //@notice Swap state bool public swapPaused; ///@notice Map the relayer action to the gas usage ///@dev We hash a string for simplicity : keccak256("Deposit_Standard_2") => Deposit 2 assets in /// standard pool mapping(bytes32 => uint256) public actionToGasUsage; /*////////////////////////////////////////////////////////////// ERROR //////////////////////////////////////////////////////////////*/ error NoRequest(); error ValueOutOfBounds(); error ActionPaused(); error DuplicatesAssets(); error IncorrectNumOfAsset(); error SlippageExceed(uint256 amountOut, uint256 minAmountOut); error NotEnoughGas(uint256 received, uint256 required); error AssetNotSupported(address asset); error SwapDisabled(address asset); error AssetPriceNotAvailable(); error AssetNotAllowedInGovernancePool(address asset); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Pause(uint256 timestamp); event Unpause(uint256 timestamp); event DepositRequested(uint32 requestId, RequestData request); event WithdrawRequested(uint32 requestId, RequestData request); event ProcessRequestSuccess(uint32 requestId); event ProcessRequestFailed(uint32 requestId, bytes data); event SwapRequested(uint32 requestId, RequestData request); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ ///@dev _relayerActions are a hash of a string, like keccak256("Deposit_Standard_1") -> Deposit 1 /// asset in standard pool constructor( address _oracleModule, address _govModule, uint8 _deviationThreshold, bytes32[] memory _relayerActions, uint256[] memory _gasUsages ) Ownable(msg.sender) { oracleModule = IOracle(_oracleModule); GOVERNANCE_MODULE = IGovernanceModule(_govModule); updateDeviationThreshold(_deviationThreshold); for (uint256 i; i < _relayerActions.length; i++) { actionToGasUsage[_relayerActions[i]] = _gasUsages[i]; } } /*////////////////////////////////////////////////////////////// GUARD //////////////////////////////////////////////////////////////*/ ///@notice Pause the protocol function pauseProtocol() external onlyGuard { paused = true; emit Pause(block.timestamp); } ///@notice Unpause the protocol function unpauseProtocol() external onlyGuard { paused = false; emit Unpause(block.timestamp); } ///@notice Pause the swaps function pauseSwap() external onlyGuard { swapPaused = true; emit Pause(block.timestamp); } ///@notice Unpause the swaps function unpauseSwap() external onlyGuard { swapPaused = false; emit Unpause(block.timestamp); } ///@notice Delete a request from the queue function deleteRequest(uint64 _numRequests) external onlyGuard { _deleteRequests(_numRequests); } /*////////////////////////////////////////////////////////////// OWNER //////////////////////////////////////////////////////////////*/ ///@notice sets the addres of fyde contract ///@param _fyde address of fyde function setFyde(address _fyde) external onlyOwner { fyde = IFyde(_fyde); } ///@notice Set the oracle module function setOracleModule(address _oracleModule) external onlyOwner { oracleModule = IOracle(_oracleModule); } ///@notice Change the deviation threshold ///@dev 50 = 0.5 % of deviation function updateDeviationThreshold(uint16 _threshold) public onlyOwner { // We bound the threshold between 0.1 % to 10% if (_threshold < 10 || _threshold > 1000) revert ValueOutOfBounds(); deviationThreshold = _threshold; } ///@notice Update the gas usage for a specific relayer action ///@param _relayerAction hash of the relayer action ///@param _gasUsage gas usage function updateGasUsageToAction(bytes32 _relayerAction, uint256 _gasUsage) public onlyOwner { actionToGasUsage[_relayerAction] = _gasUsage; } ///@notice Collect ETH from the contract, this for paying keeper services function collectEth(address payable _recipient) external onlyOwner { uint256 ethBalance = address(this).balance; (bool sent,) = _recipient.call{value: ethBalance}(""); require(sent); } /*////////////////////////////////////////////////////////////// EXT USER ENTRY POINT //////////////////////////////////////////////////////////////*/ ///@notice Entry function for requesting a deposit, can be a standard deposit or a governance /// deposit ///@param _userRequest struct containing data ///@param _keepGovRights If true make a governance ///@param _minTRSYExpected Slippage parameter ensuring minimum amout of TRSY to be received ///@dev User have to forward some eth paying for the keeper /// Once the function is called the request is stack on the queue function requestDeposit( UserRequest[] calldata _userRequest, bool _keepGovRights, uint256 _minTRSYExpected ) external payable whenNotPaused onlyUser { bytes32 actionHash = _keepGovRights ? keccak256(abi.encodePacked("Deposit_Governance_", _uint2str(_userRequest.length))) : keccak256(abi.encodePacked("Deposit_Standard_", _uint2str(_userRequest.length))); uint256 gasToForward = actionToGasUsage[actionHash]; if (_keepGovRights) { bool userHasProxy = GOVERNANCE_MODULE.userToProxy(msg.sender) == address(0x0) ? false : true; gasToForward = userHasProxy ? gasToForward : actionToGasUsage[actionHash] + actionToGasUsage[keccak256(abi.encodePacked("Proxy_Creation"))]; } gasToForward = gasToForward * oracleModule.getGweiPrice(); if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward); _checkNumberOfAsset(_userRequest.length); address[] memory assetIn = new address[](_userRequest.length); uint256[] memory amountIn = new uint256[](_userRequest.length); for (uint256 i; i < _userRequest.length; i++) { // Checks zero inputs _checkZeroValue(_userRequest[i].amount); _checkZeroAddress(_userRequest[i].asset); // Unpack data assetIn[i] = _userRequest[i].asset; amountIn[i] = _userRequest[i].amount; } // Length assetIn == amountIn _checkForConsistentLength(assetIn, amountIn); // Check not double assets _checkForDuplicates(assetIn); // Checks if asset is supported and not quarantined _checkIsSupported(assetIn); _checkIsNotQuarantined(assetIn); if (_keepGovRights) _checkIsAllowedInGov(assetIn); // Put request in queue, for deposits, assetOut and amountOut are set to empty array RequestData memory request = RequestData({ // Id is only used for events purpose id: nonce, requestor: msg.sender, assetIn: assetIn, amountIn: amountIn, assetOut: new address[](0), amountOut: new uint256[](0), keepGovRights: _keepGovRights, slippageChecker: _minTRSYExpected }); _enqueueRequest(request); emit DepositRequested(nonce, request); nonce++; } ///@notice Entry function for requesting a standard ///@param _userRequest struct containing data ///@param _maxTRSYToPay Slippage parameter ensure maximum amout of TRSY willing to pay ///@dev User have to forward some eth paying for the keeper /// Once the function is called the request is stack on the queue function requestWithdraw(UserRequest[] calldata _userRequest, uint256 _maxTRSYToPay) external payable whenNotPaused onlyUser { bytes32 actionHash = keccak256(abi.encodePacked("Withdraw_Standard_", _uint2str(_userRequest.length))); uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice(); if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward); _checkNumberOfAsset(_userRequest.length); address[] memory assetOut = new address[](_userRequest.length); uint256[] memory amountOut = new uint256[](_userRequest.length); for (uint256 i; i < _userRequest.length; i++) { // Checks zero inputs _checkZeroValue(_userRequest[i].amount); _checkZeroAddress(_userRequest[i].asset); assetOut[i] = _userRequest[i].asset; amountOut[i] = _userRequest[i].amount; } // Check not double assets _checkForDuplicates(assetOut); // Length assetIn == amountIn _checkForConsistentLength(assetOut, amountOut); // Checks asset is supported in Fyde _checkIsNotQuarantined(assetOut); _checkIsSupported(assetOut); // put request in queue, for withdraw, assetIn and amountIn are set to empty array RequestData memory request = RequestData({ id: nonce, requestor: msg.sender, assetIn: new address[](0), amountIn: new uint256[](0), assetOut: assetOut, amountOut: amountOut, keepGovRights: false, slippageChecker: _maxTRSYToPay }); _enqueueRequest(request); emit WithdrawRequested(nonce, request); nonce++; } ///@notice Function used by user to make a (single-token) withdrawal from their governance proxy ///@param _userRequest struct containing data ///@param _user address of user who makes the withdraw ///@param _maxTRSYToPay maximum amout of stTRSY willing to pay, otherwise withdraw reverts ///@dev This function create a request that will be process by the keeper ///@dev owner of fyde can force withdraw for other users function requestGovernanceWithdraw( UserRequest memory _userRequest, address _user, uint256 _maxTRSYToPay ) external payable whenNotPaused onlyUser { bytes32 actionHash = keccak256(abi.encodePacked("Withdraw_Governance_1")); uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice(); if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward); if (msg.sender != _user && msg.sender != owner) revert Unauthorized(); address[] memory assetOut = new address[](1); uint256[] memory amountOut = new uint256[](1); // Checks zero inputs _checkZeroValue(_userRequest.amount); _checkZeroAddress(_userRequest.asset); assetOut[0] = _userRequest.asset; amountOut[0] = _userRequest.amount; // Checks asset is supported in Fyde _checkIsNotQuarantined(assetOut); _checkIsSupported(assetOut); // put request in queue, for withdraw, assetIn and amountIn are set to empty array RequestData memory request = RequestData({ id: nonce, requestor: _user, assetIn: new address[](0), amountIn: new uint256[](0), assetOut: assetOut, amountOut: amountOut, keepGovRights: true, slippageChecker: _maxTRSYToPay }); _enqueueRequest(request); emit WithdrawRequested(nonce, request); nonce++; } function getGasToForward(string memory action, bool isGovDeposit) external view returns (uint256) { bytes32 actionHash = keccak256(abi.encodePacked(action)); uint256 gasToForward = actionToGasUsage[actionHash]; if (isGovDeposit) { bool userHasProxy = GOVERNANCE_MODULE.userToProxy(msg.sender) == address(0x0) ? false : true; gasToForward = userHasProxy ? gasToForward : actionToGasUsage[actionHash] + actionToGasUsage[keccak256(abi.encodePacked("Proxy_Creation"))]; } gasToForward = gasToForward * oracleModule.getGweiPrice(); return gasToForward; } /*////////////////////////////////////////////////////////////// SWAP //////////////////////////////////////////////////////////////*/ function requestSwap( address _assetIn, uint256 _amountIn, address _assetOut, uint256 _minAmountOut ) external payable whenSwapNotPaused onlySwapper { // Gas comsumption bytes32 actionHash = keccak256(abi.encodePacked("Swap")); uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice(); if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward); // Checks zero inputs _checkZeroValue(_amountIn); _checkZeroAddress(_assetIn); _checkZeroAddress(_assetOut); if (_assetIn == _assetOut) revert DuplicatesAssets(); // Checks if asset is supported and not quarantined address[] memory assets = new address[](2); assets[0] = _assetIn; assets[1] = _assetOut; _checkIsSupported(assets); _checkIsNotQuarantined(assets); _checkIfSwapAllowed(assets); address[] memory assetIn = new address[](1); uint256[] memory amountIn = new uint256[](1); address[] memory assetOut = new address[](1); uint256[] memory amountOut = new uint256[](1); assetIn[0] = _assetIn; amountIn[0] = _amountIn; assetOut[0] = _assetOut; // put request in queue RequestData memory request = RequestData({ id: nonce, requestor: msg.sender, assetIn: assetIn, amountIn: amountIn, assetOut: assetOut, amountOut: amountOut, keepGovRights: false, slippageChecker: _minAmountOut }); _enqueueRequest(request); emit SwapRequested(nonce, request); nonce++; } /*////////////////////////////////////////////////////////////// GELATO FUNCTIONS //////////////////////////////////////////////////////////////*/ ///@notice Check if there is pending request function checker_processRequest() external view returns (bool, bytes memory) { if (getNumPendingRequest() == 0) return (false, bytes("No pending request")); uint256 protocolAUM = fyde.computeProtocolAUM(); return (true, abi.encodeCall(this.processRequests, (protocolAUM))); } ///@notice Batch process requests in queue ///@param _protocolAUM Computed by the keeper off chain ///@dev This function is called by the keeper parsing the AUM and will execute both deposits and /// withdraws function processRequests(uint256 _protocolAUM) external onlyKeeper whenNotPaused { uint256 nReq = getNumPendingRequest(); if (nReq == 0) revert NoRequest(); nReq = MAX_BATCH_SIZE >= nReq ? nReq : MAX_BATCH_SIZE; //collect management fee fyde.collectManagementFee(); uint256 currentAUM = _protocolAUM; // Loop over requests and execute for (uint256 i; i < nReq; i++) { if (getRequest(0).assetIn.length > 0 && getRequest(0).assetOut.length == 0) { RequestData memory request = _dequeueRequest(); // Deposit try fyde.processDeposit(currentAUM, request) returns (uint256 usdDeposit) { currentAUM += usdDeposit; emit ProcessRequestSuccess(request.id); } catch (bytes memory reason) { emit ProcessRequestFailed(request.id, reason); } } else if (getRequest(0).assetOut.length > 0 && getRequest(0).assetIn.length == 0) { RequestData memory request = _dequeueRequest(); // Withdraw try fyde.processWithdraw(currentAUM, request) returns (uint256 usdWithdraw) { currentAUM -= usdWithdraw; emit ProcessRequestSuccess(request.id); } catch (bytes memory reason) { emit ProcessRequestFailed(request.id, reason); } } else if (getRequest(0).assetIn.length == 1 && getRequest(0).assetOut.length == 1) { RequestData memory request = _dequeueRequest(); // Swap try fyde.processSwap(_protocolAUM, request) returns (int256 deltaAUM) { if (deltaAUM > 0) currentAUM += uint256(deltaAUM); else if (deltaAUM < 0) currentAUM -= uint256(-deltaAUM); else currentAUM = currentAUM; emit ProcessRequestSuccess(request.id); } catch (bytes memory reason) { emit ProcessRequestFailed(request.id, reason); } } } } ///@notice Offchain checker for gelato bot function checker_updateProtocolAUM() external view returns (bool, bytes memory) { uint256 aum = fyde.getProtocolAUM(); uint256 nAum = fyde.computeProtocolAUM(); if (aum == 0 && nAum != 0) return (true, abi.encodeCall(this.updateProtocolAUM, (nAum))); // Update if deviation threshold % (0.5 by default) diff between on-chain and offchain if (PercentageMath._isInRange(aum, nAum, deviationThreshold)) { return (false, bytes("AUM is in range")); } else { return (true, abi.encodeCall(this.updateProtocolAUM, (nAum))); } } ///@notice Update the protocol AUM, called by Gelato Bot function updateProtocolAUM(uint256 nAum) external onlyKeeper { fyde.updateProtocolAUM(nAum); } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ function _checkNumberOfAsset(uint256 userRequestLength) internal pure { if (userRequestLength > MAX_ASSET_TO_REQUEST || userRequestLength == 0) { revert IncorrectNumOfAsset(); } } function _checkIsSupported(address[] memory _assets) internal view { address notSupportedAsset = fyde.isAnyNotSupported(_assets); if (notSupportedAsset != address(0x0)) revert AssetNotSupported(notSupportedAsset); } function _checkIsNotQuarantined(address[] memory _assets) internal view { address quarantinedAsset = isAnyQuarantined(_assets); if (quarantinedAsset != address(0x0)) revert AssetIsQuarantined(quarantinedAsset); } function _checkIsAllowedInGov(address[] memory _assets) internal view { address notAllowedInGovAsset = GOVERNANCE_MODULE.isAnyNotOnGovWhitelist(_assets); if (notAllowedInGovAsset != address(0x0)) { revert AssetNotAllowedInGovernancePool(notAllowedInGovAsset); } } function _checkForDuplicates(address[] memory _assetList) internal pure { for (uint256 idx; idx < _assetList.length - 1; idx++) { for (uint256 idx2 = idx + 1; idx2 < _assetList.length; idx2++) { if (_assetList[idx] == _assetList[idx2]) revert DuplicatesAssets(); } } } function _checkIfSwapAllowed(address[] memory _assets) internal view { address notAllowedAsset = fyde.isSwapAllowed(_assets); if (notAllowedAsset != address(0x0)) revert SwapDisabled(notAllowedAsset); } function _uint2str(uint256 _i) internal pure returns (string memory) { if (_i == 0) return "0"; uint256 j = _i; uint256 len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint256 k = len; while (_i != 0) { k = k - 1; uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } modifier whenNotPaused() { if (paused) revert ActionPaused(); _; } modifier whenSwapNotPaused() { if (swapPaused) revert ActionPaused(); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; /// Import from Core ///// import {TRSY} from "./core/TRSY.sol"; import {AssetRegistry} from "./core/AssetRegistry.sol"; import {AddressRegistry} from "./core/AddressRegistry.sol"; import {ProtocolState} from "./core/ProtocolState.sol"; import {Tax} from "./core/Tax.sol"; import {GovernanceAccess} from "./core/GovernanceAccess.sol"; /// Structs ///// import {RequestData, ProcessParam, AssetInfo} from "./core/Structs.sol"; /// Utils ///// import {Ownable} from "./utils/Ownable.sol"; import {PercentageMath} from "./utils/PercentageMath.sol"; import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; //Interfaces import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; import {IRelayer} from "src/interfaces/IRelayer.sol"; ///@title Fyde contract ///@notice Fyde is the main contract of the protocol, it handles logic of deposit and withdraw in /// the protocol /// Deposit and withdraw occurs a mint or a burn of TRSY (ERC20 that represent shares of the /// procotol in USD value) /// Users can both deposit/withdraw in standard or governance pool contract Fyde is TRSY, AddressRegistry, ProtocolState, AssetRegistry, GovernanceAccess, Tax { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event FeesCollected(address indexed recipient, uint256 trsyFeesCollected); event Deposit(uint32 requestId, uint256 trsyPrice, uint256 usdDepositValue, uint256 trsyMinted); event Withdraw(uint32 requestId, uint256 trsyPrice, uint256 usdWithdrawValue, uint256 trsyBurned); event Swap(uint32 requestId, address assetOut, uint256 amountOut); event ManagementFeeCollected(uint256 feeToMint); /*////////////////////////////////////////////////////////////// ERROR //////////////////////////////////////////////////////////////*/ error AumNotInRange(); error OnlyOneUpdatePerBlock(); error SlippageExceed(); error FydeBalanceInsufficient(); error InsufficientTRSYBalance(); error AssetPriceNotAvailable(); error SwapAmountNotAvailable(); error AssetNotSupported(address asset); error SwapDisabled(address asset); error AssetIsQuarantined(address asset); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( address _relayer, address _oracleModule, address _governanceModule, uint16 _maxAumDeviationAllowed, uint72 _taxFactor, uint72 _managementFee ) Ownable(msg.sender) AddressRegistry(_governanceModule, _relayer) { oracleModule = IOracle(_oracleModule); updateMaxAumDeviationAllowed(_maxAumDeviationAllowed); updateTaxFactor(_taxFactor); updateManagementFee(_managementFee); _updateLastFeeCollectionTime(); } /*////////////////////////////////////////////////////////////// AUTH //////////////////////////////////////////////////////////////*/ ///@notice Collect and send TRSY fees (from tax fees) to an external address ///@param _recipient Address to send TRSY fees to ///@param _amount Amount of TRSY to send function collectFees(address _recipient, uint256 _amount) external onlyOwner { _checkZeroAddress(_recipient); _checkZeroValue(_amount); balanceOf[address(this)] -= _amount; balanceOf[_recipient] += _amount; emit FeesCollected(_recipient, _amount); } ///@notice Collect management fee by inflating TRSY and minting to Fyde /// is called by the relayer when processingRequests function collectManagementFee() external { uint256 feePerSecond = uint256(protocolData.managementFee / 31_557_600); uint256 timePeriod = block.timestamp - protocolData.lastFeeCollectionTime; if (timePeriod == 0) return; uint256 feeToMint = feePerSecond * timePeriod * totalSupply / 1e18; _updateLastFeeCollectionTime(); _mint(address(this), feeToMint); emit ManagementFeeCollected(feeToMint); } /*////////////////////////////////////////////////////////////// RELAYER & KEEPER FUNCTIONS //////////////////////////////////////////////////////////////*/ ///@notice Update protocol AUM, called by keeper ///@param _aum New AUM ///@dev Can at most be updated by maxDeviationThreshold and only once per block function updateProtocolAUM(uint256 _aum) external { if (msg.sender != RELAYER && msg.sender != owner) revert Unauthorized(); if (block.number == protocolData.lastAUMUpdateBlock) revert CoolDownPeriodActive(); protocolData.lastAUMUpdateBlock = uint48(block.number); (, uint256 limitedAum) = _AumIsInRange(_aum); _updateProtocolAUM(limitedAum); } /*////////////////////////////////////////////////////////////// PROCESSING DEPOSIT ACTIONS //////////////////////////////////////////////////////////////*/ ///@notice Process deposit action, called by relayer ///@param _protocolAUM AUM given by keeper ///@param _req RequestData struct ///@return totalUsdDeposit USD value of the deposit function processDeposit(uint256 _protocolAUM, RequestData calldata _req) external onlyRelayer returns (uint256) { // Check if asset is supported _checkIsSupported(_req.assetIn); _checkIsNotQuarantined(_req.assetIn); // is keeper AUM in range (bool isInRange,) = _AumIsInRange(_protocolAUM); if (!isInRange) revert AumNotInRange(); ( ProcessParam[] memory processParam, uint256 sharesToMint, uint256 taxInTRSY, uint256 totalUsdDeposit ) = getProcessParamDeposit(_req, _protocolAUM); // Slippage checker if (_req.slippageChecker > sharesToMint) revert SlippageExceed(); // Transfer assets to Fyde for (uint256 i; i < _req.assetIn.length; i++) { IERC20(_req.assetIn[i]).safeTransferFrom(_req.requestor, address(this), _req.amountIn[i]); } if (_req.keepGovRights) _govDeposit(_req, processParam); else _standardDeposit(_req, sharesToMint); // Mint tax and keep in contract if (taxInTRSY > 0) _mint(address(this), taxInTRSY); _updateProtocolAUM(_protocolAUM + totalUsdDeposit); uint256 trsyPrice = (1e18 * (_protocolAUM + totalUsdDeposit)) / totalSupply; emit Deposit(_req.id, trsyPrice, totalUsdDeposit, sharesToMint); return totalUsdDeposit; } function _standardDeposit(RequestData calldata _req, uint256 _sharesToMint) internal { // Accounting _increaseAssetTotalAmount(_req.assetIn, _req.amountIn); // Minting shares _mint(_req.requestor, _sharesToMint); } function _govDeposit(RequestData calldata _req, ProcessParam[] memory _processParam) internal { uint256[] memory sharesAfterTax = new uint256[](_req.assetIn.length); uint256[] memory amountInAfterTax = new uint256[](_req.assetIn.length); // Same average tax rate is applied to each asset uint256 taxMultiplicator; uint256 totalTrsy; for (uint256 i; i < _req.assetIn.length; i++) { taxMultiplicator = 1e18 * _processParam[i].sharesAfterTax / (_processParam[i].sharesBeforeTax); amountInAfterTax[i] = _req.amountIn[i] * taxMultiplicator / 1e18; sharesAfterTax[i] = _processParam[i].sharesAfterTax; totalTrsy += sharesAfterTax[i]; } // Mint stTRSY and transfer token into proxy address proxy = GOVERNANCE_MODULE.govDeposit( _req.requestor, _req.assetIn, amountInAfterTax, sharesAfterTax, totalTrsy ); for (uint256 i; i < _req.assetIn.length; i++) { IERC20(_req.assetIn[i]).safeTransfer(proxy, amountInAfterTax[i]); } // Accounting _increaseAssetTotalAmount(_req.assetIn, _req.amountIn); _increaseAssetProxyAmount(_req.assetIn, amountInAfterTax); // Mint _mint(address(GOVERNANCE_MODULE), totalTrsy); } /*////////////////////////////////////////////////////////////// PROCESSING WITHDRAW ACTIONS //////////////////////////////////////////////////////////////*/ ///@notice Process withdraw action, called by relayer ///@param _protocolAUM AUM given by keeper ///@param _req RequestData struct ///@return totalUsdWithdraw USD value of the withdraw function processWithdraw(uint256 _protocolAUM, RequestData calldata _req) external onlyRelayer returns (uint256) { // Check if asset is supported _checkIsSupported(_req.assetOut); _checkIsNotQuarantined(_req.assetOut); // is keeper AUM in range (bool isInRange,) = _AumIsInRange(_protocolAUM); if (!isInRange) revert AumNotInRange(); uint256 totalUsdWithdraw; uint256 totalSharesToBurn; (totalUsdWithdraw, totalSharesToBurn) = _req.keepGovRights ? _govWithdraw(_protocolAUM, _req) : _standardWithdraw(_protocolAUM, _req); // Accounting _decreaseAssetTotalAmount(_req.assetOut, _req.amountOut); _updateProtocolAUM(_protocolAUM - totalUsdWithdraw); // Calculate for offchain purpose uint256 trsyPrice = totalSupply != 0 ? (1e18 * (_protocolAUM - totalUsdWithdraw)) / totalSupply : 0; emit Withdraw(_req.id, trsyPrice, totalUsdWithdraw, totalSharesToBurn); return totalUsdWithdraw; } function _govWithdraw(uint256 _protocolAUM, RequestData calldata _req) internal returns (uint256, uint256) { uint256 usdVal = getQuote(_req.assetOut[0], _req.amountOut[0]); if (usdVal == 0) revert AssetPriceNotAvailable(); uint256 trsyToBurn = _convertToShares(usdVal, _protocolAUM); if (_req.slippageChecker < trsyToBurn) revert SlippageExceed(); _burn(address(GOVERNANCE_MODULE), trsyToBurn); _decreaseAssetProxyAmount(_req.assetOut, _req.amountOut); GOVERNANCE_MODULE.govWithdraw(_req.requestor, _req.assetOut[0], _req.amountOut[0], trsyToBurn); IERC20(_req.assetOut[0]).safeTransfer(_req.requestor, _req.amountOut[0]); return (usdVal, trsyToBurn); } function _standardWithdraw(uint256 _protocolAUM, RequestData calldata _req) internal returns (uint256, uint256) { // check if requested token are available for (uint256 i = 0; i < _req.assetOut.length; i++) { if (standardAssetAccounting(_req.assetOut[i]) < _req.amountOut[i]) { revert FydeBalanceInsufficient(); } } (, uint256 totalSharesToBurn,, uint256 taxInTRSY, uint256 totalUsdWithdraw) = getProcessParamWithdraw(_req, _protocolAUM); if (totalSharesToBurn > _req.slippageChecker) revert SlippageExceed(); if (balanceOf[_req.requestor] < totalSharesToBurn) revert InsufficientTRSYBalance(); _burn(_req.requestor, totalSharesToBurn); // Give tax to this contract if (taxInTRSY > 0) _mint(address(this), taxInTRSY); for (uint256 i = 0; i < _req.assetOut.length; i++) { // Send asset to recipient IERC20(_req.assetOut[i]).safeTransfer(_req.requestor, _req.amountOut[i]); } return (totalUsdWithdraw, totalSharesToBurn); } /*////////////////////////////////////////////////////////////// SWAP //////////////////////////////////////////////////////////////*/ function processSwap(uint256 _protocolAUM, RequestData calldata _req) external onlyRelayer returns (int256) { // Check if asset is supported _checkIsSupported(_req.assetIn); _checkIsSupported(_req.assetOut); _checkIsNotQuarantined(_req.assetIn); _checkIsNotQuarantined(_req.assetOut); _checkIfSwapAllowed(_req.assetIn); _checkIfSwapAllowed(_req.assetOut); // is keeper AUM in range (bool isInRange,) = _AumIsInRange(_protocolAUM); if (!isInRange) revert AumNotInRange(); (uint256 amountOut, int256 deltaAUM) = getSwapAmountOut(_req.assetIn[0], _req.amountIn[0], _req.assetOut[0], _protocolAUM); if (amountOut == 0) revert SwapAmountNotAvailable(); if (amountOut < _req.slippageChecker) revert SlippageExceed(); // Check enough asset in protocol if (standardAssetAccounting(_req.assetOut[0]) < amountOut) revert FydeBalanceInsufficient(); // Update AUM uint256 aum; // If the swapper pays net tax, we mint the corresponding TRSY to fyde. This way TRSY price // stays constant if (deltaAUM > 0) { aum = _protocolAUM + uint256(deltaAUM); _mint(address(this), _convertToShares(uint256(deltaAUM), _protocolAUM)); // If incentives are higher tan taxes, we burn TRSY from fyde, to keep TRSY price constant // as backup if not enough TRSY in Fyde, we don't burn, i.e. TRSY price goes down and // incentives are // paid by pool // this way by frequently cashing out TRSY from fyde we can manually decide how much tax to // keep for ourselves // or leave in Fyde for incentives } else if (deltaAUM < 0) { aum = _protocolAUM - uint256(-deltaAUM); uint256 trsyToBurn = _convertToShares(uint256(-deltaAUM), _protocolAUM); trsyToBurn = balanceOf[address(this)] >= trsyToBurn ? trsyToBurn : balanceOf[address(this)]; if (trsyToBurn != 0) _burn(address(this), trsyToBurn); } else { aum = _protocolAUM; } _updateProtocolAUM(aum); // Log accounting _increaseAssetTotalAmount(_req.assetIn[0], _req.amountIn[0]); _decreaseAssetTotalAmount(_req.assetOut[0], amountOut); // Transfer asset IERC20(_req.assetIn[0]).safeTransferFrom(_req.requestor, address(this), _req.amountIn[0]); IERC20(_req.assetOut[0]).safeTransfer(_req.requestor, amountOut); emit Swap(_req.id, _req.assetOut[0], amountOut); return deltaAUM; } /*/////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ ///@notice Give a quote for a speficic asset deposit ///@param _asset asset address to quote ///@param _amount amount of asset to deposit ///@return USD value of the specified deposit (return 18 decimals, 1USD = 1e18) ///@dev If price is inconsistent or not available, returns 0 from oracle module -> needs proper /// handling function getQuote(address _asset, uint256 _amount) public view override returns (uint256) { AssetInfo memory _assetInfo = assetInfo[_asset]; uint256 price = oracleModule.getPriceInUSD(_asset, _assetInfo); return (_amount * price) / (10 ** _assetInfo.assetDecimals); } ///@notice Get the USD value of an asset in the protocol ///@param _asset asset address ///@return USD value of the asset ///@dev If price is inconsistent or not available, returns 0 -> needs proper handling function getAssetAUM(address _asset) public view returns (uint256) { return getQuote(_asset, totalAssetAccounting[_asset]); } ///@notice Compute the USD AUM for the protocol ///@dev Should NOT be call within a contract (GAS EXPENSIVE), called off-chain by keeper function computeProtocolAUM() public view returns (uint256) { address asset; uint256 aum; uint256 assetAUM; address[] memory nAsset = assetsList; uint256 length = nAsset.length; for (uint256 i = 0; i < length; ++i) { asset = nAsset[i]; if (totalAssetAccounting[asset] == 0) continue; assetAUM = getAssetAUM(asset); if (assetAUM == 0) return protocolData.aum; aum += assetAUM; } return aum; } /*////////////////////////////////////////////////////////////// PROCESS PARAM //////////////////////////////////////////////////////////////*/ ///@notice Return the process param for a deposit ///@param _req RequestData struct ///@param _protocolAUM AUM given by keeper ///@return processParam array of ProcessParam struct ///@return sharesToMint amount of shares to mint ///@return taxInTRSY amount of tax in TRSY ///@return totalUsdDeposit USD value of the depositn function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM) public view returns ( ProcessParam[] memory processParam, uint256 sharesToMint, uint256 taxInTRSY, uint256 totalUsdDeposit ) { processParam = new ProcessParam[](_req.assetIn.length); // Build data struct and compute value of deposit for (uint256 i; i < _req.assetIn.length; i++) { uint256 usdVal = getQuote(_req.assetIn[i], _req.amountIn[i]); if (usdVal == 0) revert AssetPriceNotAvailable(); processParam[i] = ProcessParam({ targetConc: assetInfo[_req.assetIn[i]].targetConcentration, currentConc: _getAssetConcentration(_req.assetIn[i], _protocolAUM), usdValue: usdVal, sharesBeforeTax: _convertToShares(usdVal, _protocolAUM), taxableAmount: 0, taxInUSD: 0, sharesAfterTax: 0 }); totalUsdDeposit += usdVal; } for (uint256 i; i < processParam.length; i++) { // Get the TaxInUSD processParam[i] = _getDepositTax(processParam[i], _protocolAUM, totalUsdDeposit, protocolData.taxFactor); // Apply tax to the deposit processParam[i].sharesAfterTax = _convertToShares(processParam[i].usdValue - processParam[i].taxInUSD, _protocolAUM); sharesToMint += processParam[i].sharesAfterTax; taxInTRSY += processParam[i].sharesBeforeTax - processParam[i].sharesAfterTax; } return (processParam, sharesToMint, taxInTRSY, totalUsdDeposit); } ///@notice Return the process param for a withdraw ///@param _req RequestData struct ///@param _protocolAUM AUM given by keeper ///@return processParam array of ProcessParam struct ///@return totalSharesToBurn amount of shares to burn ///@return sharesToBurnBeforeTax amount of shares to burn before tax ///@return taxInTRSY amount of tax in TRSY ///@return totalUsdWithdraw USD value of the withdraw function getProcessParamWithdraw(RequestData calldata _req, uint256 _protocolAUM) public view returns ( ProcessParam[] memory processParam, uint256 totalSharesToBurn, uint256 sharesToBurnBeforeTax, uint256 taxInTRSY, uint256 totalUsdWithdraw ) { processParam = new ProcessParam[](_req.assetOut.length); // Build data struct and compute value of deposit for (uint256 i; i < _req.assetOut.length; i++) { uint256 usdVal = getQuote(_req.assetOut[i], _req.amountOut[i]); if (usdVal == 0) revert AssetPriceNotAvailable(); processParam[i] = ProcessParam({ targetConc: assetInfo[_req.assetOut[i]].targetConcentration, currentConc: _getAssetConcentration(_req.assetOut[i], _protocolAUM), usdValue: usdVal, sharesBeforeTax: 0, taxableAmount: 0, taxInUSD: 0, sharesAfterTax: 0 }); totalUsdWithdraw += usdVal; } for (uint256 i; i < processParam.length; i++) { // Get the TaxInUSD processParam[i] = _getWithdrawTax(processParam[i], _protocolAUM, totalUsdWithdraw, protocolData.taxFactor); taxInTRSY += _convertToShares(processParam[i].taxInUSD, _protocolAUM); } sharesToBurnBeforeTax = _convertToShares(totalUsdWithdraw, _protocolAUM); totalSharesToBurn = sharesToBurnBeforeTax + taxInTRSY; } ///@notice Return the amountOut for a swap accounting for tax and incentive ///@param _assetIn asset address to swap ///@param _amountIn amount of asset to swap ///@param _assetOut asset address to receive ///@param _protocolAUM AUM given by keeper function getSwapAmountOut( address _assetIn, uint256 _amountIn, address _assetOut, uint256 _protocolAUM ) public view returns (uint256, int256) { // Scope to avoid stack too deep { uint256 usdValIn = getQuote(_assetIn, _amountIn); uint256 assetOutPrice = getQuote(_assetOut, 10 ** assetInfo[_assetOut].assetDecimals); if (usdValIn == 0 || assetOutPrice == 0) return (0, int256(0)); } ProcessParam memory processParamIn = ProcessParam({ targetConc: assetInfo[_assetIn].targetConcentration, currentConc: _getAssetConcentration(_assetIn, _protocolAUM), usdValue: getQuote(_assetIn, _amountIn), sharesBeforeTax: 0, taxableAmount: 0, taxInUSD: 0, sharesAfterTax: 0 }); ProcessParam memory processParamOut = ProcessParam({ targetConc: assetInfo[_assetOut].targetConcentration, currentConc: _getAssetConcentration(_assetOut, _protocolAUM), usdValue: 0, sharesBeforeTax: 0, taxableAmount: 0, taxInUSD: 0, sharesAfterTax: 0 }); uint256 usdAmountOut = _getSwapRate( processParamIn, processParamOut, _protocolAUM, protocolData.taxFactor, assetInfo[_assetIn].incentiveFactor, assetInfo[_assetOut].incentiveFactor ); return ( 1e18 * usdAmountOut / getQuote(_assetOut, 1e18), int256(processParamIn.usdValue) - int256(usdAmountOut) ); } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ ///@notice Return asset concentration with keeper AUM ///@param _asset asset address ///@param _protocolAUM AUM given by keeper ///@return current concentration for an asset ///@dev If price is inconsistent or not available, returns 0 -> needs proper handling function _getAssetConcentration(address _asset, uint256 _protocolAUM) internal view returns (uint256) { // To avoid division by 0 if (_protocolAUM == 0 && protocolData.aum == 0) return 0; return (1e20 * getAssetAUM(_asset)) / _protocolAUM; } ///@notice Perform the comparison between AUM registry and one given by Keeper, return limited AUM /// if out of bounds function _AumIsInRange(uint256 _keeperAUM) internal view returns (bool, uint256) { uint16 maxAumDeviationAllowed = protocolData.maxAumDeviationAllowed; uint256 currAum = protocolData.aum; uint256 lowerBound = PercentageMath.percentSub(currAum, maxAumDeviationAllowed); uint256 upperBound = PercentageMath.percentAdd(currAum, maxAumDeviationAllowed); if (_keeperAUM < lowerBound) return (false, lowerBound); if (_keeperAUM > upperBound) return (false, upperBound); return (true, _keeperAUM); } function _checkIsSupported(address[] memory _assets) internal view { address notSupportedAsset = isAnyNotSupported(_assets); if (notSupportedAsset != address(0x0)) revert AssetNotSupported(notSupportedAsset); } function _checkIsNotQuarantined(address[] memory _assets) internal view { address quarantinedAsset = IRelayer(RELAYER).isAnyQuarantined(_assets); if (quarantinedAsset != address(0x0)) revert AssetIsQuarantined(quarantinedAsset); } function _checkIfSwapAllowed(address[] memory _assets) internal view { address notAllowedAsset = isSwapAllowed(_assets); if (notAllowedAsset != address(0x0)) revert SwapDisabled(notAllowedAsset); } /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ modifier onlyRelayer() { if (msg.sender != RELAYER) revert Unauthorized(); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {AccessControl} from "./AccessControl.sol"; ///@title QuarantineList contract ///@notice Handle the logic for the quarantine list abstract contract QuarantineList is AccessControl { /// ----------------------------- /// Storage /// ----------------------------- uint256 public min_quarantine_duration = 1 days; mapping(address => uint128) public quarantineList; /// ----------------------------- /// Events /// ----------------------------- event AddedToQuarantine(address asset, uint128 expirationTime); event RemovedFromQuarantine(address asset); /// ----------------------------- /// Errors /// ----------------------------- error AssetIsQuarantined(address asset); error AssetIsNotQuarantined(address asset); error ShortenedExpiration(uint128 currentExpiration, uint128 expiration); error ShortQurantineDuration(uint128 duration); /// ----------------------------- /// Admin external /// ----------------------------- ///@notice Set the minimum quarantine duration ///@param _min_quarantine_duration the new minimum quarantine duration function set_min_quarantine_duration(uint256 _min_quarantine_duration) external onlyOwner { min_quarantine_duration = _min_quarantine_duration; } ///@notice Add an asset to the quarantine list ///@param _asset the address of the assset to be added to the quarantine list ///@param _duration the time (in seconds) that the asset should stay in quarantine function addToQuarantine(address _asset, uint128 _duration) external onlyGuard { if (_duration < min_quarantine_duration) revert ShortQurantineDuration(_duration); // gas savings uint128 expiration = uint128(block.timestamp) + _duration; uint128 currentExpiration = quarantineList[_asset]; // the new expiration cannot be before the curent one i.e. expiration cannot be reduced, just // extended if (expiration <= currentExpiration) revert ShortenedExpiration(currentExpiration, expiration); quarantineList[_asset] = expiration; emit AddedToQuarantine(_asset, expiration); } ///@notice Remove an asset from the quarantine list ///@param _asset the address of the assset to be removed from the quarantine list function removeFromQuarantine(address _asset) external onlyGuard { // If the asset has nto been quarantined or the duarion period has expired then revert if (quarantineList[_asset] < uint128(block.timestamp)) revert AssetIsNotQuarantined(_asset); // just set the duration to zero to remove from quarantine quarantineList[_asset] = 0; emit RemovedFromQuarantine(_asset); } /// ----------------------------- /// External view functions /// ----------------------------- ///@notice Check if an asset is quarantined ///@param _asset the address of the assset to be checked function isQuarantined(address _asset) public view returns (bool) { return quarantineList[_asset] >= uint128(block.timestamp); } ///@notice Check if any asset from a given list is quarantined ///@param _assets an array of asset addresses that need to be checked ///@return address of first quarantined asset or address(0x0) if none quarantined function isAnyQuarantined(address[] memory _assets) public view returns (address) { for (uint256 i = 0; i < _assets.length;) { if (isQuarantined(_assets[i])) return _assets[i]; unchecked { ++i; } } return address(0x0); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {RequestQ, RequestData} from "./Structs.sol"; ///@title RequestQueue contract ///@notice Handle the logic for the request queue abstract contract RequestQueue { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Request queue RequestQ internal requestQueue; /*////////////////////////////////////////////////////////////// ERROR //////////////////////////////////////////////////////////////*/ error EmptyQueue(); error NotEnoughRequests(); /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ ///@notice Return the request, to get the first request pass 0 as index ///@dev return arbitrary requests in queue by providing the desired index e.g. 0 => first element /// in queue function getRequest(uint64 idx) public view returns (RequestData memory) { return requestQueue.requestData[requestQueue.start + idx]; } ///@notice Return the number of pending request function getNumPendingRequest() public view returns (uint256) { return requestQueue.end - requestQueue.start; } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ ///@dev Add request to the end of the queue ///@param _newRequest: RequestData - data to be added to the queue function _enqueueRequest(RequestData memory _newRequest) internal { requestQueue.requestData[requestQueue.end++] = _newRequest; } ///@dev removes and returns request from the beginning of the queue function _dequeueRequest() internal returns (RequestData memory) { uint64 start = requestQueue.start++; if (start == requestQueue.end) revert EmptyQueue(); RequestData memory firstRequest = requestQueue.requestData[start]; delete requestQueue.requestData[start]; return firstRequest; } ///@dev removes and returns request from the beginning of the queue function _deleteRequests(uint64 _numRequests) internal { if (_numRequests > getNumPendingRequest()) revert NotEnoughRequests(); uint64 start = requestQueue.start; uint64 end = start + _numRequests; while (start < end) { delete requestQueue.requestData[start]; start++; } requestQueue.start = start; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; struct AssetInfo { uint72 targetConcentration; address uniswapPool; int72 incentiveFactor; uint8 assetDecimals; uint8 quoteTokenDecimals; address uniswapQuoteToken; bool isSupported; } struct ProtocolData { ///@notice Protocol AUM in USD uint256 aum; ///@notice multiplicator for the tax equation, 100% = 100e18 uint72 taxFactor; ///@notice Max deviation allowed between AUM from keeper and registry uint16 maxAumDeviationAllowed; // Default val 200 == 2 % ///@notice block number where AUM was last updated uint48 lastAUMUpdateBlock; ///@notice annual fee on AUM, in % per year 100% = 100e18 uint72 managementFee; ///@notice last block.timestamp when fee was collected uint48 lastFeeCollectionTime; } struct UserRequest { address asset; uint256 amount; } struct RequestData { uint32 id; address requestor; address[] assetIn; uint256[] amountIn; address[] assetOut; uint256[] amountOut; bool keepGovRights; uint256 slippageChecker; } struct RequestQ { uint64 start; uint64 end; mapping(uint64 => RequestData) requestData; } struct ProcessParam { uint256 targetConc; uint256 currentConc; uint256 usdValue; uint256 taxableAmount; uint256 taxInUSD; uint256 sharesBeforeTax; uint256 sharesAfterTax; } struct RebalanceParam { address asset; uint256 assetTotalAmount; uint256 assetProxyAmount; uint256 assetPrice; uint256 sTrsyTotalSupply; uint256 trsyPrice; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; abstract contract BaseChecker { error ZeroParameter(); error InconsistentLengths(); function _checkZeroValue(uint256 val) internal pure { if (val == 0) revert ZeroParameter(); } function _checkZeroAddress(address addr) internal pure { if (addr == address(0x0)) revert ZeroParameter(); } function _checkForConsistentLength(address[] memory arr1, uint256[] memory arr2) internal pure { if (arr1.length != arr2.length) revert InconsistentLengths(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; ///@title Ownable contract /// @notice Simple 2step owner authorization combining solmate and OZ implementation abstract contract Ownable { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Address of the owner address public owner; ///@notice Address of the pending owner address public pendingOwner; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnershipTransferred(address indexed user, address indexed newOner); event OwnershipTransferStarted(address indexed user, address indexed newOwner); event OwnershipTransferCanceled(address indexed pendingOwner); /*////////////////////////////////////////////////////////////// ERROR //////////////////////////////////////////////////////////////*/ error Unauthorized(); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _owner) { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ ///@notice Transfer ownership to a new address ///@param newOwner address of the new owner ///@dev newOwner have to acceptOwnership function transferOwnership(address newOwner) external onlyOwner { pendingOwner = newOwner; emit OwnershipTransferStarted(msg.sender, pendingOwner); } ///@notice NewOwner accept the ownership, it transfer the ownership to newOwner function acceptOwnership() external { if (msg.sender != pendingOwner) revert Unauthorized(); address oldOwner = owner; owner = pendingOwner; delete pendingOwner; emit OwnershipTransferred(oldOwner, owner); } ///@notice Cancel the ownership transfer function cancelTransferOwnership() external onlyOwner { emit OwnershipTransferCanceled(pendingOwner); delete pendingOwner; } modifier onlyOwner() { if (msg.sender != owner) revert Unauthorized(); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; library PercentageMath { /// CONSTANTS /// uint256 internal constant PERCENTAGE_FACTOR = 1e4; // 100.00% uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4; // 50.00% uint256 internal constant MAX_UINT256 = 2 ** 256 - 1; uint256 internal constant MAX_UINT256_MINUS_HALF_PERCENTAGE = 2 ** 256 - 1 - 0.5e4; /// INTERNAL /// ///@notice Check if value are within the range function _isInRange(uint256 valA, uint256 valB, uint256 deviationThreshold) internal pure returns (bool) { uint256 lowerBound = percentSub(valA, deviationThreshold); uint256 upperBound = percentAdd(valA, deviationThreshold); if (valB < lowerBound || valB > upperBound) return false; else return true; } /// @notice Executes a percentage addition (x * (1 + p)), rounded up. /// @param x The value to which to add the percentage. /// @param percentage The percentage of the value to add. /// @return y The result of the addition. function percentAdd(uint256 x, uint256 percentage) internal pure returns (uint256 y) { // Must revert if // PERCENTAGE_FACTOR + percentage > type(uint256).max // or x * (PERCENTAGE_FACTOR + percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max // <=> percentage > type(uint256).max - PERCENTAGE_FACTOR // or x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR + percentage) // Note: PERCENTAGE_FACTOR + percentage >= PERCENTAGE_FACTOR > 0 assembly { y := add(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas. if or( gt(percentage, sub(MAX_UINT256, PERCENTAGE_FACTOR)), gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y)) ) { revert(0, 0) } y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR) } } /// @notice Executes a percentage subtraction (x * (1 - p)), rounded up. /// @param x The value to which to subtract the percentage. /// @param percentage The percentage of the value to subtract. /// @return y The result of the subtraction. function percentSub(uint256 x, uint256 percentage) internal pure returns (uint256 y) { // Must revert if // percentage > PERCENTAGE_FACTOR // or x * (PERCENTAGE_FACTOR - percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max // <=> percentage > PERCENTAGE_FACTOR // or ((PERCENTAGE_FACTOR - percentage) > 0 and x > (type(uint256).max - // HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR - percentage)) assembly { y := sub(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas. if or( gt(percentage, PERCENTAGE_FACTOR), mul(y, gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y))) ) { revert(0, 0) } y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR) } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; import {RequestData, RebalanceParam, ProcessParam} from "src/core/Structs.sol"; interface IFyde { function protocolData() external view returns (uint256, uint72, uint16, uint48, uint72, uint48); function isAnyNotSupported(address[] calldata _assets) external view returns (address); function isSwapAllowed(address[] calldata _assets) external view returns (address); function computeProtocolAUM() external view returns (uint256); function getProtocolAUM() external view returns (uint256); function updateProtocolAUM(uint256) external; function processDeposit(uint256, RequestData calldata) external returns (uint256); function processWithdraw(uint256, RequestData calldata) external returns (uint256); function totalAssetAccounting(address) external view returns (uint256); function proxyAssetAccounting(address) external view returns (uint256); function standardAssetAccounting(address) external view returns (uint256); function getQuote(address, uint256) external view returns (uint256); function getAssetDecimals(address) external view returns (uint8); function collectManagementFee() external; function processSwap(uint256, RequestData calldata) external returns (int256); function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM) external view returns ( ProcessParam[] memory processParam, uint256 sharesToMint, uint256 taxInTRSY, uint256 totalUsdDeposit ); // GOVERNANCE ACCESS FUNCTIONS function transferAsset(address _asset, address _recipient, uint256 _amount) external; function getRebalanceParams(address _asset) external view returns (RebalanceParam memory); function updateAssetProxyAmount(address _asset, uint256 _amount) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; interface IGovernanceModule { function fyde() external view returns (address); function proxyImplementation() external view returns (address); function proxyBalance(address proxy, address asset) external view returns (uint256); function strsyBalance(address _user, address _govToken) external view returns (uint256 balance); function assetToStrsy(address _asset) external view returns (address); function userToProxy(address _user) external view returns (address); function proxyToUser(address _proxy) external view returns (address); function isOnGovernanceWhitelist(address _asset) external view returns (bool); function getAllGovUsers() external view returns (address[] memory); function isAnyNotOnGovWhitelist(address[] calldata _assets) external view returns (address); function getUserGTAllowance(uint256 _TRSYAmount, address _token) external view returns (uint256); function govDeposit( address _depositor, address[] calldata _govToken, uint256[] calldata _amount, uint256[] calldata _amountTRSY, uint256 _totalTRSY ) external returns (address proxy); function govWithdraw( address _user, address _asset, uint256 _amountToWithdraw, uint256 _trsyToBurn ) external; function onStrsyTransfer(address sender, address _recipient) external; function unstakeGov(uint256 _amount, address _asset) external; function rebalanceProxy(address _proxy, address _asset, address[] memory _usersToRebalance) external; }
//SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; import {AssetInfo} from "../core/Structs.sol"; interface IOracle { function getPriceInUSD(address, AssetInfo calldata) external view returns (uint256); function getGweiPrice() external view returns (uint256); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; ///@title TRSY contract ///@notice Handle the logic for minting and burning TRSY shares abstract contract TRSY is ERC20 { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor() ERC20("TRSY", "TRSY", 18) {} /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ ///@notice Convert the value of deposit into share of the protocol ///@param _usdValue usd value of the deposit ///@param _usdAUM AUM of the protocol in USD (given by keeper) ///@return TSRY share for an USD deposit function _convertToShares(uint256 _usdValue, uint256 _usdAUM) internal view returns (uint256) { uint256 supply = totalSupply; return supply == 0 ? _usdValue : (_usdValue * supply) / _usdAUM; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {AssetInfo} from "./Structs.sol"; import {AddressRegistry} from "./AddressRegistry.sol"; import {ProtocolState} from "./ProtocolState.sol"; import {Ownable} from "../utils/Ownable.sol"; import {BaseChecker} from "../utils/BaseChecker.sol"; import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IUniswapV3PoolImmutables} from "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol"; import {IRelayer} from "src/interfaces/IRelayer.sol"; ///@title AssetRegistry contract ///@notice Handle logic and state for logging informations regarding the assets abstract contract AssetRegistry is Ownable, BaseChecker, AddressRegistry, ProtocolState { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Asset list; address[] public assetsList; ///@notice last block incentiveFactor was updated, safety measure uint128 public lastIncentiveUpdateBlock; int128 public incentiveCap; ///@notice Map asset address to struct containing info mapping(address => AssetInfo) public assetInfo; /*////////////////////////////////////////////////////////////// ERROR //////////////////////////////////////////////////////////////*/ error PoolNotValid(); error AssetSupported(address asset); error NotNormalized(); error NotZero(); error CoolDownPeriodActive(); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event AssetAdded(address _asset); event IncentiveFactorUpdated(address indexed asset, int72 incentiveFactor); event TargetConcentrationsUpdated(); event UniswapPoolUpdated(address indexed asset, address uniswapPool); event AssetRemoved(address indexed asset); /*////////////////////////////////////////////////////////////// ADD ASSETS //////////////////////////////////////////////////////////////*/ ///@notice Add assets in batch ///@param _assets Array of assets to add ///@param _uniswapPools Array of address of uniswap Pool ///@dev only uniswap pool and incentive factor are relevant in assetsInfo, the rest is retrieved /// on-chain function addAssets(address[] calldata _assets, address[] calldata _uniswapPools) external onlyOwner { if (_assets.length != _uniswapPools.length) revert InconsistentLengths(); for (uint256 i; i < _assets.length; ++i) { _addAsset(_assets[i], _uniswapPools[i]); } } function _addAsset(address _asset, address _uniswapPool) private { if (assetInfo[_asset].isSupported) revert AssetSupported(_asset); assetInfo[_asset].isSupported = true; assetInfo[_asset].assetDecimals = IERC20Metadata(_asset).decimals(); setUniswapPool(_asset, _uniswapPool); assetsList.push(_asset); emit AssetAdded(_asset); } ///@notice Removes asset from the protocol whitelist ///@param _assetIdx index of the asset in the assets array ///@dev only possible if there are no tokens of the asset held by the protocol anymore function removeAsset(uint256 _assetIdx) external onlyOwner { address asset = assetsList[_assetIdx]; if (totalAssetAccounting[asset] != 0) revert NotZero(); if (assetInfo[asset].targetConcentration != 0) revert NotZero(); delete assetInfo[asset]; assetsList[_assetIdx] = assetsList[assetsList.length - 1]; assetsList.pop(); emit AssetRemoved(asset); } /*////////////////////////////////////////////////////////////// SETTER //////////////////////////////////////////////////////////////*/ ///@notice Set target concentration for all asset ///@param _targetConcentrations Target concentration (1e18 -> 1%) ///@dev 1e18 = 1% ///@dev targetConcentrations must have same length as assetsList -> can only update all conc at // once to enforce normalization function setTargetConcentrations(uint72[] calldata _targetConcentrations) external onlyOwner { if (_targetConcentrations.length != assetsList.length) revert InconsistentLengths(); uint72 sum; for (uint256 i; i < _targetConcentrations.length; i++) { sum += _targetConcentrations[i]; } if (sum > (1e20 + 1e10) || sum < (1e20 - 1e10)) revert NotNormalized(); for (uint256 i; i < _targetConcentrations.length; i++) { assetInfo[assetsList[i]].targetConcentration = _targetConcentrations[i]; } emit TargetConcentrationsUpdated(); } ///@notice Set target concentration for an asset ///@param _asset Asset address ///@param _incentiveFactor IncentiveFactor (1e18 -> 1%) ///@dev 1e18 = 1%, max incentiveCap ///@dev Can only be called every 5 blocks, safety measure in case of compromised IncentiveManager function setIncentiveFactor(address _asset, int72 _incentiveFactor) external onlyIncentiveManager { if (int128(_incentiveFactor) > incentiveCap) revert ValueOutOfBounds(); if (block.number < uint256(lastIncentiveUpdateBlock) + 5) revert CoolDownPeriodActive(); lastIncentiveUpdateBlock = uint128(block.number); assetInfo[_asset].incentiveFactor = _incentiveFactor; emit IncentiveFactorUpdated(_asset, _incentiveFactor); } ///@notice Set maximum incentive factor ///@param _incentiveCap maximum incentive factor ///@dev Capped at 1e20 == 100% function setIncentiveCap(int128 _incentiveCap) external onlyOwner { if (_incentiveCap > 1e20) revert ValueOutOfBounds(); incentiveCap = _incentiveCap; } ///@notice Set uniswap pool for an asset ///@param _asset Asset address ///@param _uniswapPool Uniswap pool address function setUniswapPool(address _asset, address _uniswapPool) public onlyOwner { if (_uniswapPool == address(0x0)) { assetInfo[_asset].uniswapPool = _uniswapPool; } else { address token0 = IUniswapV3PoolImmutables(_uniswapPool).token0(); address token1 = IUniswapV3PoolImmutables(_uniswapPool).token1(); address quoteToken; if (token0 == _asset) quoteToken = token1; else if (token1 == _asset) quoteToken = token0; else revert PoolNotValid(); assetInfo[_asset].uniswapPool = _uniswapPool; assetInfo[_asset].uniswapQuoteToken = quoteToken; assetInfo[_asset].quoteTokenDecimals = IERC20Metadata(quoteToken).decimals(); } emit UniswapPoolUpdated(_asset, _uniswapPool); } /*////////////////////////////////////////////////////////////// GETTER //////////////////////////////////////////////////////////////*/ ///@notice Get isSupported for an asset ///@param _assets asset addresses ///@return address of first not supported asset or address(0x0) if all supported function isAnyNotSupported(address[] memory _assets) public view returns (address) { for (uint256 i; i < _assets.length; i++) { if (!assetInfo[_assets[i]].isSupported) return _assets[i]; } return address(0x0); } ///@notice Get isSwapAllowed for an asset array ///@param _assets asset addresses ///@return address of first not supported asset or address(0x0) if all supported function isSwapAllowed(address[] memory _assets) public view returns (address) { for (uint256 i; i < _assets.length; i++) { if (assetInfo[_assets[i]].incentiveFactor == -100e18) return _assets[i]; } return address(0x0); } ///@notice Get number of asset decimals ///@param _asset Asset address ///@return number of decimals function getAssetDecimals(address _asset) external view returns (uint8) { return assetInfo[_asset].assetDecimals; } ///@notice Get number of assets in protocol function getAssetsListLength() public view returns (uint256) { return assetsList.length; } ///@dev caller has to be whitelisted manager on relayer modifier onlyIncentiveManager() { if (!IRelayer(RELAYER).isIncentiveManager(msg.sender)) revert Unauthorized(); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; // Utils import {Ownable} from "../utils/Ownable.sol"; // Interfaces import {IOracle} from "../interfaces/IOracle.sol"; import {IGovernanceModule} from "../interfaces/IGovernanceModule.sol"; ///@title AddressRegistry contract ///@notice Handle state and logic for external authorized call (mainly keeper) and the oracle module abstract contract AddressRegistry is Ownable { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Address of the oracle module IOracle public oracleModule; ///@notice Governance Registry contract address interface IGovernanceModule public immutable GOVERNANCE_MODULE; address public RELAYER; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OracleModuleUpdated(address indexed oracleModule); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _govModule, address _relayer) { GOVERNANCE_MODULE = IGovernanceModule(_govModule); RELAYER = _relayer; } /*////////////////////////////////////////////////////////////// ACCESS //////////////////////////////////////////////////////////////*/ function setRelayer(address _relayer) external onlyOwner { RELAYER = _relayer; } ///@notice Set the oracle address function setOracleModule(address _oracle) public onlyOwner { oracleModule = IOracle(_oracle); emit OracleModuleUpdated(_oracle); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {ProtocolData} from "./Structs.sol"; import {Ownable} from "../utils/Ownable.sol"; ///@title ProtocolState contract ///@notice Protocol data storage abstract contract ProtocolState is Ownable { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice Protocol data ProtocolData public protocolData; ///@notice Number of token in the protocol mapping(address => uint256) public totalAssetAccounting; ///@notice Number of token in the proxy mapping(address => uint256) public proxyAssetAccounting; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ error ValueOutOfBounds(); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event ProtocolAumUpdated(uint256); event MaxAumDeviationAllowedUpdated(uint16); event TaxFactorUpdated(uint72); event ManagementFeeUpdated(uint72); /*////////////////////////////////////////////////////////////// GETTER //////////////////////////////////////////////////////////////*/ ///@notice Get number of token in the standard pool ///@param _asset asset address ///@return number of token in standard pool function standardAssetAccounting(address _asset) public view returns (uint256) { return totalAssetAccounting[_asset] - proxyAssetAccounting[_asset]; } ///@notice Get protocolAUM in USD ///@return protocol AUM function getProtocolAUM() external view returns (uint256) { return protocolData.aum; } /*////////////////////////////////////////////////////////////// SETTER //////////////////////////////////////////////////////////////*/ ///@notice Change the AUM's comparison deviation threshold ///@param threshold new threshold ///@dev 200 = 2 % of deviation function updateMaxAumDeviationAllowed(uint16 threshold) public onlyOwner { // We bound the threshold to 0.1 % to 5% if (threshold < 10 || threshold > 500) revert ValueOutOfBounds(); protocolData.maxAumDeviationAllowed = threshold; emit MaxAumDeviationAllowedUpdated(threshold); } ///@notice set the tax factor ///@param _taxFactor new tax factor ///@dev 100% = 100e18 function updateTaxFactor(uint72 _taxFactor) public onlyOwner { if (_taxFactor > 100e18) revert ValueOutOfBounds(); protocolData.taxFactor = _taxFactor; emit TaxFactorUpdated(_taxFactor); } ///@notice change annual management fee ///@param _annualFee new annual fee ///@dev 100% = 1e18 function updateManagementFee(uint72 _annualFee) public onlyOwner { // We bound the fee to 0 % to 5% if (_annualFee > 5e16) revert ValueOutOfBounds(); protocolData.managementFee = _annualFee; emit ManagementFeeUpdated(_annualFee); } ///@notice Update last fee collection time to current timestamp function _updateLastFeeCollectionTime() internal { protocolData.lastFeeCollectionTime = uint48(block.timestamp); } ///@notice Update the protocol AUM function _updateProtocolAUM(uint256 _aum) internal { protocolData.aum = _aum; emit ProtocolAumUpdated(_aum); } function _increaseAssetTotalAmount(address[] memory _assets, uint256[] memory _amounts) internal { for (uint256 i; i < _assets.length; i++) { _increaseAssetTotalAmount(_assets[i], _amounts[i]); } } function _increaseAssetTotalAmount(address _asset, uint256 _amount) internal { totalAssetAccounting[_asset] += _amount; } function _increaseAssetProxyAmount(address[] memory _assets, uint256[] memory _amounts) internal { for (uint256 i; i < _assets.length; i++) { proxyAssetAccounting[_assets[i]] += _amounts[i]; } } function _decreaseAssetTotalAmount(address[] memory _assets, uint256[] memory _amounts) internal { for (uint256 i; i < _assets.length; i++) { _decreaseAssetTotalAmount(_assets[i], _amounts[i]); } } function _decreaseAssetTotalAmount(address _asset, uint256 _amount) internal { totalAssetAccounting[_asset] -= _amount; } function _decreaseAssetProxyAmount(address[] memory _assets, uint256[] memory _amounts) internal { for (uint256 i; i < _assets.length; i++) { proxyAssetAccounting[_assets[i]] -= _amounts[i]; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {ProcessParam} from "./Structs.sol"; import {MathUtil} from "../utils/MathUtil.sol"; import "synthetix-v3/utils/core-contracts/contracts/utils/SafeCast.sol"; ///@title Tax contract ///@notice Handle tax logic, for either a deposit or withdraw compute if the action is unabalacing /// the protocol /// if this is the case, the protocol will compute a tax applied on deposit or withdraw by /// reducing the number of shares to mint /// or by increasing the number of shares to burn. This tax is then minted to fyde contract. /// The main logic is that for each action, we compute a taxable amount which is the amount that /// unbalance the protocol for a given deposit or withdraw, /// then we apply the tax on this taxable amount. /// For the swap the logic is the same, we compute the tax and incentive for assetIn and that give /// the value of assetOut, /// for this value we compute the tax and incentive for assetOut. /// SwapRate can be greater for assetOut in case there is no tax and some incentives, the same if no /// tax no incentive, or lower if there is tax and no incentive. abstract contract Tax { using SafeCastU256 for uint256; using SafeCastI256 for int256; /*////////////////////////////////////////////////////////////// MAIN //////////////////////////////////////////////////////////////*/ function _getDepositTax( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdDeposit, uint256 taxFactor ) internal pure returns (ProcessParam memory) { if (processParam.targetConc == 0) { processParam.taxInUSD = processParam.usdValue; return processParam; } if (taxFactor == 0) return processParam; processParam = _computeDepositTaxableAmount(processParam, protocolAUM, totalUsdDeposit); if (processParam.taxableAmount == 0) return processParam; processParam = _computeDepositTaxInUSD(processParam, protocolAUM, totalUsdDeposit, taxFactor); return processParam; } function _getWithdrawTax( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdWithdraw, uint256 taxFactor ) internal pure returns (ProcessParam memory) { if (taxFactor == 0) return processParam; processParam = _computeWithdrawTaxableAmount(processParam, protocolAUM, totalUsdWithdraw); if (processParam.taxableAmount == 0) return processParam; processParam = _computeWithdrawTaxInUSD(processParam, protocolAUM, totalUsdWithdraw, taxFactor); return processParam; } function _getSwapRate( ProcessParam memory processParamIn, ProcessParam memory processParamOut, uint256 protocolAUM, uint256 taxFactor, int256 incentiveFactorIn, int256 incentiveFactorOut ) internal pure returns (uint256) { // Compute tax on deposit processParamIn = _getDepositTax(processParamIn, protocolAUM, 0, taxFactor); int256 valIn = incentiveFactorIn * int256(processParamIn.usdValue - processParamIn.taxableAmount) / int256(1e20); // usdValue adjusted with potential tax and incentive uint256 withdrawValOut = valIn >= 0 ? processParamIn.usdValue - processParamIn.taxInUSD + valIn.toUint() : processParamIn.usdValue - processParamIn.taxInUSD - (-1 * valIn).toUint(); processParamOut.usdValue = withdrawValOut; processParamOut = _getWithdrawTax(processParamOut, protocolAUM, 0, taxFactor); // usdValueOut adjusted with potential tax and incentive int256 valOut = incentiveFactorOut * int256(withdrawValOut - processParamOut.taxableAmount) / 1e20; uint256 usdValOut = valOut >= 0 ? withdrawValOut - processParamOut.taxInUSD + valOut.toUint() : withdrawValOut - processParamOut.taxInUSD - (-1 * valOut).toUint(); return usdValOut; } /*////////////////////////////////////////////////////////////// DEPOSIT //////////////////////////////////////////////////////////////*/ function _computeDepositTaxableAmount( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdDeposit ) internal pure returns (ProcessParam memory) { int256 deltaConc = protocolAUM.toInt() * (processParam.currentConc.toInt() - processParam.targetConc.toInt()) / 1e20; int256 targetDeposit = totalUsdDeposit != 0 ? processParam.targetConc.toInt() * totalUsdDeposit.toInt() / 1e20 : int256(0); int256 tax = processParam.usdValue.toInt() + deltaConc - targetDeposit; processParam.taxableAmount = MathUtil.min(processParam.usdValue.toInt(), MathUtil.max(tax, int256(0))).toUint(); return processParam; } function _computeDepositTaxInUSD( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdDeposit, uint256 taxFactor ) internal pure returns (ProcessParam memory) { uint256 numerator = (protocolAUM * processParam.currentConc / 1e20) + processParam.usdValue; uint256 denominator = (protocolAUM + totalUsdDeposit) * processParam.targetConc / 1e20; uint256 eq = (1e18 * numerator / denominator) - 1e18; uint256 tmpRes = MathUtil.min(eq, 1e18); uint256 taxPerc = taxFactor * tmpRes / 1e20; // 1e20 for applying expressing tax as a percentage processParam.taxInUSD = processParam.taxableAmount * taxPerc / 1e18; return processParam; } /*////////////////////////////////////////////////////////////// WITHDRAW //////////////////////////////////////////////////////////////*/ function _computeWithdrawTaxableAmount( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdWithdraw ) internal pure returns (ProcessParam memory) { int256 deltaConc = protocolAUM.toInt() * (processParam.currentConc.toInt() - processParam.targetConc.toInt()) / 1e20; int256 targetDeposit = processParam.targetConc.toInt() * totalUsdWithdraw.toInt() / 1e20; int256 tax = processParam.usdValue.toInt() - deltaConc - targetDeposit; processParam.taxableAmount = MathUtil.min(processParam.usdValue.toInt(), MathUtil.max(tax, int256(0))).toUint(); return processParam; } function _computeWithdrawTaxInUSD( ProcessParam memory processParam, uint256 protocolAUM, uint256 totalUsdWithdraw, uint256 taxFactor ) internal pure returns (ProcessParam memory) { int256 numerator = protocolAUM.toInt() * processParam.currentConc.toInt() / 1e20 - processParam.usdValue.toInt(); int256 denominator = processParam.targetConc.toInt() * (protocolAUM.toInt() - totalUsdWithdraw.toInt()) / 1e20; int256 tmpRes = 1e18 - (1e18 * numerator / denominator); uint256 tmpRes2 = MathUtil.min(tmpRes.toUint(), 1e18); uint256 taxPerc = taxFactor * tmpRes2 / 1e20; // 1e20 for applying expressing tax as a // percentage processParam.taxInUSD = processParam.taxableAmount * taxPerc / 1e18; return processParam; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; /// Import from Core ///// import {AddressRegistry} from "./AddressRegistry.sol"; import {AssetRegistry} from "./AssetRegistry.sol"; import {ProtocolState} from "./ProtocolState.sol"; import {TRSY} from "./TRSY.sol"; /// Structs ///// import {RebalanceParam} from "./Structs.sol"; // Interface import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; ///@title GovernanceAccess contract ///@notice Exposes functions for the governance module abstract contract GovernanceAccess is TRSY, AddressRegistry, ProtocolState, AssetRegistry { using SafeERC20 for IERC20; ///@notice Called by the governance module in order to transfer asset when proxy is funded ///@param _asset asset address ///@param _recipient user address ///@param _amount number of token function transferAsset(address _asset, address _recipient, uint256 _amount) external onlyGovernance { IERC20(_asset).safeTransfer(_recipient, _amount); } ///@notice Called by the governance module in order to update proxy Accounting after rebalancing ///@param _asset asset address ///@param _amount number of token function updateAssetProxyAmount(address _asset, uint256 _amount) external onlyGovernance { proxyAssetAccounting[_asset] = _amount; } ///@notice Called by the governance module in order get all variables for rebalancing in one call /// (save gas) ///@param _asset asset address ///@return RebalanceParam struct with rebalance parameters function getRebalanceParams(address _asset) external view returns (RebalanceParam memory) { return RebalanceParam( _asset, totalAssetAccounting[_asset], proxyAssetAccounting[_asset], getQuote(_asset, 1e18), 0, 1e18 * protocolData.aum / totalSupply ); } function getQuote(address _asset, uint256 _amount) public view virtual returns (uint256); modifier onlyGovernance() { if (msg.sender != address(GOVERNANCE_MODULE)) revert Unauthorized(); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (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: UNLICENSED pragma solidity 0.8.19; import {RequestData, UserRequest} from "../core/Structs.sol"; interface IRelayer { function getNumPendingRequest() external view returns (uint256); function getRequest(uint64 idx) external view returns (RequestData memory); function requestGovernanceWithdraw( UserRequest memory _userRequest, address _user, uint256 _maxTRSYToPay ) external payable; function requestWithdraw(UserRequest[] memory _userRequest, uint256 _maxTRSYToPay) external payable; function requestDeposit( UserRequest[] memory _userRequest, bool _keepGovRights, uint256 _minTRSYExpected ) external payable; function requestSwap( address _assetIn, uint256 _amountIn, address _assetOut, uint256 _minAmountOut ) external payable; function processRequests(uint256 _protocolAUM) external; function isQuarantined(address _asset) external view returns (bool); function isIncentiveManager(address _incentiveManager) external view returns (bool); function MAX_ASSET_TO_REQUEST() external view returns (uint8); function actionToGasUsage(bytes32 _actionHash) external view returns (uint256); function isUser(address _asset) external view returns (bool); function isAnyQuarantined(address[] memory _assets) external view returns (address); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {Ownable} from "src/utils/Ownable.sol"; ///@title AccessControl /// @notice Handles the different access authorizations for the relayer abstract contract AccessControl is Ownable { /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ ///@notice exclusive user - when != address(0x0), other users are removed from whitelist /// intended for short term use during incidence response, escrow, migration address public exclusiveUser; ///@notice guard authorization mapping(address => bool) public isGuard; ///@notice keeper authorization mapping(address => bool) public isKeeper; ///@notice user authorization, can use deposit, withdraw mapping(address => bool) public isUser; ///@notice swapper authorization, can use swap mapping(address => bool) public isSwapper; ///@notice incentive manager authorization, can set incentives for swaps on Fyde mapping(address => bool) public isIncentiveManager; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event ExclusiveUserSet(address indexed); event GuardAdded(address indexed); event GuardRemoved(address indexed); event KeeperAdded(address indexed); event KeeperRemoved(address indexed); event UserAdded(address indexed); event UserRemoved(address indexed); event IncentiveManagerAdded(address indexed); event IncentiveManagerRemoved(address indexed); event SwapperAdded(address indexed); event SwapperRemoved(address indexed); /*////////////////////////////////////////////////////////////// SETTER //////////////////////////////////////////////////////////////*/ function setExclusiveUser(address _exclusiveUser) external onlyOwner { exclusiveUser = _exclusiveUser; emit ExclusiveUserSet(_exclusiveUser); } function addGuard(address _guard) external onlyOwner { isGuard[_guard] = true; emit GuardAdded(_guard); } function removeGuard(address _guard) external onlyOwner { isGuard[_guard] = false; emit GuardRemoved(_guard); } function addKeeper(address _keeper) external onlyOwner { isKeeper[_keeper] = true; emit KeeperAdded(_keeper); } function removeKeeper(address _keeper) external onlyOwner { isKeeper[_keeper] = false; emit KeeperRemoved(_keeper); } function addUser(address[] calldata _user) external onlyOwner { for (uint256 i; i < _user.length; ++i) { isUser[_user[i]] = true; emit UserAdded(_user[i]); } } function removeUser(address[] calldata _user) external onlyOwner { for (uint256 i; i < _user.length; ++i) { isUser[_user[i]] = false; emit UserRemoved(_user[i]); } } function addIncentiveManager(address _incentiveManager) external onlyOwner { isIncentiveManager[_incentiveManager] = true; emit IncentiveManagerAdded(_incentiveManager); } function removeIncentiveManager(address _incentiveManager) external onlyOwner { isIncentiveManager[_incentiveManager] = false; emit IncentiveManagerRemoved(_incentiveManager); } function addSwapper(address _swapper) external onlyOwner { isSwapper[_swapper] = true; emit SwapperAdded(_swapper); } function removeSwapper(address _swapper) external onlyOwner { isSwapper[_swapper] = false; emit SwapperRemoved(_swapper); } /*////////////////////////////////////////////////////////////// MODIFIER //////////////////////////////////////////////////////////////*/ ///@notice only a registered keeper can access modifier onlyKeeper() { if (!isKeeper[msg.sender]) revert Unauthorized(); _; } ///@notice only a registered guard can access modifier onlyGuard() { if (!isGuard[msg.sender]) revert Unauthorized(); _; } ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access ///@dev setting exclusiveUser to != address(0x0) blocks everyone else /// - intended for escrow, incidence response and migration modifier onlyUser() { // if whitelist is not disabeld and user not whitelisted -> no access if (!isUser[address(0x0)] && !isUser[msg.sender]) revert Unauthorized(); // if exclusive user exists and is not user -> no accesss if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized(); _; } ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access ///@dev setting exclusiveUser to != address(0x0) blocks everyone else /// - intended for escrow, incidence response and migration modifier onlySwapper() { if (!isSwapper[address(0x0)] && !isSwapper[msg.sender]) revert Unauthorized(); if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized(); _; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that never changes /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values interface IUniswapV3PoolImmutables { /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface /// @return The contract address function factory() external view returns (address); /// @notice The first of the two tokens of the pool, sorted by address /// @return The token contract address function token0() external view returns (address); /// @notice The second of the two tokens of the pool, sorted by address /// @return The token contract address function token1() external view returns (address); /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 /// @return The fee function fee() external view returns (uint24); /// @notice The pool tick spacing /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... /// This value is an int24 to avoid casting even though it is always positive. /// @return The tick spacing function tickSpacing() external view returns (int24); /// @notice The maximum amount of position liquidity that can use any tick in the range /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool /// @return The max amount of liquidity per tick function maxLiquidityPerTick() external view returns (uint128); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol) pragma solidity 0.8.19; /** * @dev Standard math utilities missing in the Solidity language. */ library MathUtil { /** * @dev Returns the largest of two numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * Utilities that convert numeric types avoiding silent overflows. */ import "./SafeCast/SafeCastU32.sol"; import "./SafeCast/SafeCastI32.sol"; import "./SafeCast/SafeCastI24.sol"; import "./SafeCast/SafeCastU56.sol"; import "./SafeCast/SafeCastI56.sol"; import "./SafeCast/SafeCastU64.sol"; import "./SafeCast/SafeCastI128.sol"; import "./SafeCast/SafeCastI256.sol"; import "./SafeCast/SafeCastU128.sol"; import "./SafeCast/SafeCastU160.sol"; import "./SafeCast/SafeCastU256.sol"; import "./SafeCast/SafeCastAddress.sol"; import "./SafeCast/SafeCastBytes32.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU32 { error OverflowUint32ToInt32(); function toInt(uint32 x) internal pure returns (int32) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint32(type(int32).max)) { revert OverflowUint32ToInt32(); } return int32(x); } function to256(uint32 x) internal pure returns (uint256) { return uint256(x); } function to56(uint32 x) internal pure returns (uint56) { return uint56(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI32 { error OverflowInt32ToUint32(); function toUint(int32 x) internal pure returns (uint32) { // ----------------------<========o========>---------------------- // ----------------------xxxxxxxxxo=========>---------------------- if (x < 0) { revert OverflowInt32ToUint32(); } return uint32(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI24 { function to256(int24 x) internal pure returns (int256) { return int256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU56 { error OverflowUint56ToInt56(); function toInt(uint56 x) internal pure returns (int56) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint56(type(int56).max)) { revert OverflowUint56ToInt56(); } return int56(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI56 { error OverflowInt56ToInt24(); function to24(int56 x) internal pure returns (int24) { // ----------------------<========o========>----------------------- // ----------------------xxx<=====o=====>xxx----------------------- if (x < int(type(int24).min) || x > int(type(int24).max)) { revert OverflowInt56ToInt24(); } return int24(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU64 { error OverflowUint64ToInt64(); function toInt(uint64 x) internal pure returns (int64) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint64(type(int64).max)) { revert OverflowUint64ToInt64(); } return int64(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI128 { error OverflowInt128ToUint128(); error OverflowInt128ToInt32(); function toUint(int128 x) internal pure returns (uint128) { // ----------------<==============o==============>----------------- // ----------------xxxxxxxxxxxxxxxo===============>---------------- if (x < 0) { revert OverflowInt128ToUint128(); } return uint128(x); } function to256(int128 x) internal pure returns (int256) { return int256(x); } function to32(int128 x) internal pure returns (int32) { // ----------------<==============o==============>----------------- // ----------------xxxxxxxxxxxx<==o==>xxxxxxxxxxxx----------------- if (x < int(type(int32).min) || x > int(type(int32).max)) { revert OverflowInt128ToInt32(); } return int32(x); } function zero() internal pure returns (int128) { return int128(0); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI256 { error OverflowInt256ToUint256(); error OverflowInt256ToInt128(); error OverflowInt256ToInt24(); function to128(int256 x) internal pure returns (int128) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxx<==============o==============>xxxxxxxxxxxxx---- if (x < int256(type(int128).min) || x > int256(type(int128).max)) { revert OverflowInt256ToInt128(); } return int128(x); } function to24(int256 x) internal pure returns (int24) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxxxxxxxxxx<======o=======>xxxxxxxxxxxxxxxxxxxx---- if (x < int256(type(int24).min) || x > int256(type(int24).max)) { revert OverflowInt256ToInt24(); } return int24(x); } function toUint(int256 x) internal pure returns (uint256) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxxxxxxxxxxxxxxxxxo===============================> if (x < 0) { revert OverflowInt256ToUint256(); } return uint256(x); } function zero() internal pure returns (int256) { return int256(0); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU128 { error OverflowUint128ToInt128(); function to256(uint128 x) internal pure returns (uint256) { return uint256(x); } function toInt(uint128 x) internal pure returns (int128) { // -------------------------------o===============>---------------- // ----------------<==============o==============>x---------------- if (x > uint128(type(int128).max)) { revert OverflowUint128ToInt128(); } return int128(x); } function toBytes32(uint128 x) internal pure returns (bytes32) { return bytes32(uint256(x)); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU160 { function to256(uint160 x) internal pure returns (uint256) { return uint256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU256 { error OverflowUint256ToUint128(); error OverflowUint256ToInt256(); error OverflowUint256ToUint64(); error OverflowUint256ToUint32(); error OverflowUint256ToUint160(); function to128(uint256 x) internal pure returns (uint128) { // -------------------------------o===============================> // -------------------------------o===============>xxxxxxxxxxxxxxxx if (x > type(uint128).max) { revert OverflowUint256ToUint128(); } return uint128(x); } function to64(uint256 x) internal pure returns (uint64) { // -------------------------------o===============================> // -------------------------------o======>xxxxxxxxxxxxxxxxxxxxxxxxx if (x > type(uint64).max) { revert OverflowUint256ToUint64(); } return uint64(x); } function to32(uint256 x) internal pure returns (uint32) { // -------------------------------o===============================> // -------------------------------o===>xxxxxxxxxxxxxxxxxxxxxxxxxxxx if (x > type(uint32).max) { revert OverflowUint256ToUint32(); } return uint32(x); } function to160(uint256 x) internal pure returns (uint160) { // -------------------------------o===============================> // -------------------------------o==================>xxxxxxxxxxxxx if (x > type(uint160).max) { revert OverflowUint256ToUint160(); } return uint160(x); } function toBytes32(uint256 x) internal pure returns (bytes32) { return bytes32(x); } function toInt(uint256 x) internal pure returns (int256) { // -------------------------------o===============================> // ----<==========================o===========================>xxxx if (x > uint256(type(int256).max)) { revert OverflowUint256ToInt256(); } return int256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastAddress { function toBytes32(address x) internal pure returns (bytes32) { return bytes32(uint256(uint160(x))); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastBytes32 { function toAddress(bytes32 x) internal pure returns (address) { return address(uint160(uint256(x))); } function toUint(bytes32 x) internal pure returns (uint) { return uint(x); } }
{ "remappings": [ "@uniswap/v3-core/=lib/v3-core/", "@uniswap/v3-periphery/=lib/v3-periphery/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "solmate/=lib/solmate/src/", "synthetix-v3/=lib/synthetix-v3/", "v3-core/=lib/v3-core/contracts/", "v3-periphery/=lib/v3-periphery/contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_oracleModule","type":"address"},{"internalType":"address","name":"_govModule","type":"address"},{"internalType":"uint8","name":"_deviationThreshold","type":"uint8"},{"internalType":"bytes32[]","name":"_relayerActions","type":"bytes32[]"},{"internalType":"uint256[]","name":"_gasUsages","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ActionPaused","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetIsNotQuarantined","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetIsQuarantined","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetNotAllowedInGovernancePool","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetNotSupported","type":"error"},{"inputs":[],"name":"AssetPriceNotAvailable","type":"error"},{"inputs":[],"name":"DuplicatesAssets","type":"error"},{"inputs":[],"name":"EmptyQueue","type":"error"},{"inputs":[],"name":"InconsistentLengths","type":"error"},{"inputs":[],"name":"IncorrectNumOfAsset","type":"error"},{"inputs":[],"name":"NoRequest","type":"error"},{"inputs":[{"internalType":"uint256","name":"received","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"NotEnoughGas","type":"error"},{"inputs":[],"name":"NotEnoughRequests","type":"error"},{"inputs":[{"internalType":"uint128","name":"duration","type":"uint128"}],"name":"ShortQurantineDuration","type":"error"},{"inputs":[{"internalType":"uint128","name":"currentExpiration","type":"uint128"},{"internalType":"uint128","name":"expiration","type":"uint128"}],"name":"ShortenedExpiration","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"SlippageExceed","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"SwapDisabled","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ValueOutOfBounds","type":"error"},{"inputs":[],"name":"ZeroParameter","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"expirationTime","type":"uint128"}],"name":"AddedToQuarantine","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"DepositRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"ExclusiveUserSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"GuardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"GuardRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"IncentiveManagerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"IncentiveManagerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"KeeperAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"KeeperRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ProcessRequestFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"}],"name":"ProcessRequestSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"RemovedFromQuarantine","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"SwapRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"SwapperAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"SwapperRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"UserAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"UserRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"WithdrawRequested","type":"event"},{"inputs":[],"name":"GOVERNANCE_MODULE","outputs":[{"internalType":"contract IGovernanceModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ASSET_TO_REQUEST","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BATCH_SIZE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"actionToGasUsage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guard","type":"address"}],"name":"addGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_incentiveManager","type":"address"}],"name":"addIncentiveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"addKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"addSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint128","name":"_duration","type":"uint128"}],"name":"addToQuarantine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_user","type":"address[]"}],"name":"addUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelTransferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checker_processRequest","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checker_updateProtocolAUM","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_recipient","type":"address"}],"name":"collectEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_numRequests","type":"uint64"}],"name":"deleteRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deviationThreshold","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exclusiveUser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fyde","outputs":[{"internalType":"contract IFyde","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"action","type":"string"},{"internalType":"bool","name":"isGovDeposit","type":"bool"}],"name":"getGasToForward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumPendingRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"idx","type":"uint64"}],"name":"getRequest","outputs":[{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"internalType":"struct RequestData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"}],"name":"isAnyQuarantined","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGuard","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isIncentiveManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isKeeper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"isQuarantined","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isSwapper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isUser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"min_quarantine_duration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleModule","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_protocolAUM","type":"uint256"}],"name":"processRequests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"quarantineList","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"removeFromQuarantine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_guard","type":"address"}],"name":"removeGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_incentiveManager","type":"address"}],"name":"removeIncentiveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"removeKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"removeSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_user","type":"address[]"}],"name":"removeUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest[]","name":"_userRequest","type":"tuple[]"},{"internalType":"bool","name":"_keepGovRights","type":"bool"},{"internalType":"uint256","name":"_minTRSYExpected","type":"uint256"}],"name":"requestDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest","name":"_userRequest","type":"tuple"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_maxTRSYToPay","type":"uint256"}],"name":"requestGovernanceWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_assetIn","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_assetOut","type":"address"},{"internalType":"uint256","name":"_minAmountOut","type":"uint256"}],"name":"requestSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest[]","name":"_userRequest","type":"tuple[]"},{"internalType":"uint256","name":"_maxTRSYToPay","type":"uint256"}],"name":"requestWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_exclusiveUser","type":"address"}],"name":"setExclusiveUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fyde","type":"address"}],"name":"setFyde","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracleModule","type":"address"}],"name":"setOracleModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min_quarantine_duration","type":"uint256"}],"name":"set_min_quarantine_duration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_threshold","type":"uint16"}],"name":"updateDeviationThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_relayerAction","type":"bytes32"},{"internalType":"uint256","name":"_gasUsage","type":"uint256"}],"name":"updateGasUsageToAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nAum","type":"uint256"}],"name":"updateProtocolAUM","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405262015180600a553480156200001857600080fd5b50604051620052f2380380620052f28339810160408190526200003b91620002b4565b600280546001600160a01b0319163390811790915560405181906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350600d80546001600160a01b0319166001600160a01b03878116919091179091558416608052620000b160ff841662000130565b60005b82518110156200012457818181518110620000d357620000d3620003bb565b6020026020010151600e6000858481518110620000f457620000f4620003bb565b602002602001015181526020019081526020016000208190555080806200011b90620003d1565b915050620000b4565b505050505050620003f9565b6002546001600160a01b031633146200015b576040516282b42960e81b815260040160405180910390fd5b600a8161ffff1610806200017457506103e88161ffff16115b156200019357604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b80516001600160a01b0381168114620001cd57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620002135762000213620001d2565b604052919050565b60006001600160401b03821115620002375762000237620001d2565b5060051b60200190565b600082601f8301126200025357600080fd5b815160206200026c62000266836200021b565b620001e8565b82815260059290921b840181019181810190868411156200028c57600080fd5b8286015b84811015620002a9578051835291830191830162000290565b509695505050505050565b600080600080600060a08688031215620002cd57600080fd5b620002d886620001b5565b94506020620002e9818801620001b5565b9450604087015160ff811681146200030057600080fd5b60608801519094506001600160401b03808211156200031e57600080fd5b818901915089601f8301126200033357600080fd5b81516200034462000266826200021b565b81815260059190911b8301840190848101908c8311156200036457600080fd5b938501935b82851015620003845784518252938501939085019062000369565b60808c015190975094505050808311156200039e57600080fd5b5050620003ae8882890162000241565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600060018201620003f257634e487b7160e01b600052601160045260246000fd5b5060010190565b608051614ec86200042a6000396000818161043001528181612bd6015281816133b901526142f20152614ec86000f3fe6080604052600436106103975760003560e01c806399385006116101dc578063cdfca7f211610102578063e30c3978116100a0578063f2fde38b1161006f578063f2fde38b14610b4e578063f6164a3714610b6e578063f80c6d9c14610b8e578063f93b6be514610bae57600080fd5b8063e30c397814610ad9578063e787caa414610af9578063ec2a099214610b19578063ed6b5ad514610b3957600080fd5b8063d94ad837116100dc578063d94ad83714610a4f578063d9fd6fb814610a84578063dbf6248914610aa4578063ddb015c814610ab957600080fd5b8063cdfca7f214610a04578063cfdbf25414610a24578063d307983d14610a3957600080fd5b8063b6ea98011161017a578063c87eed4611610149578063c87eed4614610984578063c9d6293c146109a4578063cbbf67ec146109b7578063cc88688b146109e457600080fd5b8063b6ea980114610911578063b813a8df14610924578063bae2be5614610944578063c073fd741461096457600080fd5b8063b19e52dc116101b6578063b19e52dc1461087e578063b588c10b14610891578063b6235016146108c1578063b64230ba146108e157600080fd5b80639938500614610810578063a01605c314610830578063affed0e01461084557600080fd5b8063526631fb116102c1578063769315a41161025f57806392fede001161022e57806392fede001461079457806393263b8a146107a9578063979edeba146107c957806398fbfbea146107f057600080fd5b8063769315a41461071f57806379ba50971461073f5780637cb1d3df146107545780638da5cb5b1461077457600080fd5b80636913a63c1161029b5780636913a63c1461069a5780636ba42aaa146106ba5780636d9b83ec146106ea578063708968c11461070a57600080fd5b8063526631fb146106435780635c975abb14610666578063608530051461068757600080fd5b80633ddc66ba11610339578063485d6ef811610308578063485d6ef81461058e578063489c1202146105d357806349bcb7b2146106035780634fd8cb941461062357600080fd5b80633ddc66ba146104fe5780633f2d41311461051e5780634032b72b1461053e5780634209fff11461055e57600080fd5b80631a366bb7116103755780631a366bb71461041e57806320157d0a1461046a578063245a4e721461047f57806339663ef2146104b057600080fd5b806305e2f3801461039c57806314ae9f2e146103dc57806315db9304146103fe575b600080fd5b3480156103a857600080fd5b506103c96103b7366004614516565b600e6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156103e857600080fd5b506103fc6103f7366004614544565b610bc3565b005b34801561040a57600080fd5b506103fc610419366004614561565b610c36565b34801561042a57600080fd5b506104527f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d3565b34801561047657600080fd5b506103fc610c72565b34801561048b57600080fd5b50600d546104a090600160d81b900460ff1681565b60405190151581526020016103d3565b3480156104bc57600080fd5b506104e66104cb366004614544565b600b602052600090815260409020546001600160801b031681565b6040516001600160801b0390911681526020016103d3565b34801561050a57600080fd5b506103fc610519366004614516565b610ce4565b34801561052a57600080fd5b506103fc610539366004614544565b6111eb565b34801561054a57600080fd5b506103fc610559366004614544565b61125e565b34801561056a57600080fd5b506104a0610579366004614544565b60076020526000908152604090205460ff1681565b34801561059a57600080fd5b506104a06105a9366004614544565b6001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b3480156105df57600080fd5b506104a06105ee366004614544565b60056020526000908152604090205460ff1681565b34801561060f57600080fd5b506103fc61061e366004614583565b6112d4565b34801561062f57600080fd5b50600c54610452906001600160a01b031681565b34801561064f57600080fd5b5061065861130f565b6040516103d39291906145fc565b34801561067257600080fd5b50600d546104a090600160d01b900460ff1681565b6103fc61069536600461466a565b61141c565b3480156106a657600080fd5b506103fc6106b5366004614544565b6118d6565b3480156106c657600080fd5b506104a06106d5366004614544565b60066020526000908152604090205460ff1681565b3480156106f657600080fd5b506103fc610705366004614516565b61194c565b34801561071657600080fd5b506103c961197b565b34801561072b57600080fd5b506103fc61073a3660046146b5565b6119aa565b34801561074b57600080fd5b506103fc611aa5565b34801561076057600080fd5b506103fc61076f366004614729565b611b2c565b34801561078057600080fd5b50600254610452906001600160a01b031681565b3480156107a057600080fd5b506103fc611bae565b3480156107b557600080fd5b50600454610452906001600160a01b031681565b3480156107d557600080fd5b506107de600581565b60405160ff90911681526020016103d3565b3480156107fc57600080fd5b506103fc61080b366004614544565b611c22565b34801561081c57600080fd5b506103fc61082b366004614544565b611c98565b34801561083c57600080fd5b50610658611d0e565b34801561085157600080fd5b50600d5461086990600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016103d3565b6103fc61088c366004614793565b611eed565b34801561089d57600080fd5b506104a06108ac366004614544565b60096020526000908152604090205460ff1681565b3480156108cd57600080fd5b506103fc6108dc366004614544565b612273565b3480156108ed57600080fd5b506104a06108fc366004614544565b60086020526000908152604090205460ff1681565b6103fc61091f366004614815565b6122e6565b34801561093057600080fd5b506103fc61093f36600461485d565b612781565b34801561095057600080fd5b506103fc61095f366004614544565b6128c1565b34801561097057600080fd5b506103fc61097f366004614544565b61299d565b34801561099057600080fd5b506103fc61099f366004614544565b6129e9565b6103fc6109b23660046148b7565b612a5c565b3480156109c357600080fd5b506109d76109d2366004614583565b613067565b6040516103d39190614a40565b3480156109f057600080fd5b50600d54610452906001600160a01b031681565b348015610a1057600080fd5b506103fc610a1f3660046146b5565b61325f565b348015610a3057600080fd5b506107de600381565b348015610a4557600080fd5b506103c9600a5481565b348015610a5b57600080fd5b50600d54610a7190600160c01b900461ffff1681565b60405161ffff90911681526020016103d3565b348015610a9057600080fd5b506103c9610a9f366004614a53565b613355565b348015610ab057600080fd5b506103fc613545565b348015610ac557600080fd5b506103fc610ad4366004614544565b6135b9565b348015610ae557600080fd5b50600354610452906001600160a01b031681565b348015610b0557600080fd5b506103fc610b14366004614516565b61362d565b348015610b2557600080fd5b506103fc610b34366004614544565b6136bd565b348015610b4557600080fd5b506103fc613709565b348015610b5a57600080fd5b506103fc610b69366004614544565b61377d565b348015610b7a57600080fd5b506103fc610b89366004614544565b6137f3565b348015610b9a57600080fd5b50610452610ba9366004614af8565b61387d565b348015610bba57600080fd5b506103fc613909565b6002546001600160a01b03163314610bed576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19169055517fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf809190a250565b6002546001600160a01b03163314610c60576040516282b42960e81b815260040160405180910390fd5b6000918252600e602052604090912055565b3360009081526005602052604090205460ff16610ca1576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab7906020015b60405180910390a1565b3360009081526006602052604090205460ff16610d13576040516282b42960e81b815260040160405180910390fd5b600d54600160d01b900460ff1615610d3e576040516318c0a57b60e31b815260040160405180910390fd5b6000610d4861197b565b905080600003610d6b576040516333ff64eb60e21b815260040160405180910390fd5b6003811115610d7b576003610d7d565b805b9050600c60009054906101000a90046001600160a01b03166001600160a01b0316639c016ffd6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b5084925060009150505b828110156111e5576000610e016000613067565b6040015151118015610e1f5750610e186000613067565b6080015151155b15610f61576000610e2e613975565b600c54604051632d58fd7560e01b81529192506001600160a01b031690632d58fd7590610e619086908590600401614ba9565b6020604051808303816000875af1925050508015610e9c575060408051601f3d908101601f19168201909252610e9991810190614bc2565b60015b610f10573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e91610f02918490614bdb565b60405180910390a150610f5b565b610f1a8185614c10565b825160405163ffffffff90911681529094507f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b506111d3565b6000610f6d6000613067565b6080015151118015610f8b5750610f846000613067565b6040015151155b15611040576000610f9a613975565b600c54604051633ea1a23f60e11b81529192506001600160a01b031690637d43447e90610fcd9086908590600401614ba9565b6020604051808303816000875af1925050508015611008575060408051601f3d908101601f1916820190925261100591810190614bc2565b60015b611036573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b610f1a8185614c23565b61104a6000613067565b6040015151600114801561106c57506110636000613067565b60800151516001145b156111d357600061107b613975565b600c54604051633d655c3760e11b81529192506001600160a01b031690637acab86e906110ae9088908590600401614ba9565b6020604051808303816000875af19250505080156110e9575060408051601f3d908101601f191682019092526110e691810190614bc2565b60015b61115d573d808015611117576040519150601f19603f3d011682016040523d82523d6000602084013e61111c565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e9161114f918490614bdb565b60405180910390a1506111d1565b6000811315611177576111708185614c10565b9350611193565b60008112156111935761118981614c36565b6111709085614c23565b815160405163ffffffff90911681527f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b505b806111dd81614c52565b915050610ded565b50505050565b6002546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19169055517f1bdacea439f3d52c1495fc881858088a33f2ad0cc4a6a1a08780ca96eb6b702d9190a250565b6002546001600160a01b03163314611288576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19166001179055517f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9190a250565b3360009081526005602052604090205460ff16611303576040516282b42960e81b815260040160405180910390fd5b61130c81613c4e565b50565b6000606061131b61197b565b600003611352575050604080518082019091526012815271139bc81c195b991a5b99c81c995c5d595cdd60721b6020820152600091565b600c54604080516383307f4360e01b815290516000926001600160a01b0316916383307f439160048083019260209291908290030181865afa15801561139c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c09190614bc2565b90506001306001600160a01b0316633ddc66ba836040516024016113e691815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505092509250509091565b600d54600160d01b900460ff1615611447576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff1615801561149257503360009081526007602052604090205460ff16155b156114af576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b0316158015906114d457506004546001600160a01b03163314155b156114f1576040516282b42960e81b815260040160405180910390fd5b60006114fc83613d60565b60405160200161150c9190614c6b565b60408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115909190614bc2565b6000838152600e60205260409020546115a99190614ca5565b9050803410156115da57604051634fd394c160e11b8152346004820152602481018290526044015b60405180910390fd5b6115e384613e8b565b6000846001600160401b038111156115fd576115fd61474d565b604051908082528060200260200182016040528015611626578160200160208202803683370190505b5090506000856001600160401b038111156116435761164361474d565b60405190808252806020026020018201604052801561166c578160200160208202803683370190505b50905060005b868110156117735761169e88888381811061168f5761168f614cbc565b90506040020160200135613eb6565b6116ce8888838181106116b3576116b3614cbc565b6116c99260206040909202019081019150614544565b613ed7565b8787828181106116e0576116e0614cbc565b6116f69260206040909202019081019150614544565b83828151811061170857611708614cbc565b60200260200101906001600160a01b031690816001600160a01b03168152505087878281811061173a5761173a614cbc565b9050604002016020013582828151811061175657611756614cbc565b60209081029190910101528061176b81614c52565b915050611672565b5061177d82613efe565b6117878282613fbc565b61179082613fde565b6117998261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681523360208201526000918101826040519080825280602002602001820160405280156117eb578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561181c578160200160208202803683370190505b5081526020810185905260408101849052600060608201526080018790529050611845816140c5565b7fa75ba6b66423ec9aec6231c21821a3ecfda0db0813ccf69d66808e73b8c860e8600d60149054906101000a900463ffffffff1682604051611888929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff169060146118ad83614cf1565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b6002546001600160a01b03163314611900576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19166001179055517f93405f05cd04f0d1bd875f2de00f1f3890484ffd0589248953bdfd29ba7f2f599190a250565b6002546001600160a01b03163314611976576040516282b42960e81b815260040160405180910390fd5b600a55565b6000805461199c906001600160401b0380821691600160401b900416614d14565b6001600160401b0316905090565b6002546001600160a01b031633146119d4576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576000600760008585858181106119f7576119f7614cbc565b9050602002016020810190611a0c9190614544565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055828282818110611a4657611a46614cbc565b9050602002016020810190611a5b9190614544565b6001600160a01b03167fe9dce8c992623ce791725b21e857e33248d1f190a25b5168313420eebdaae99d60405160405180910390a2611a9981614c52565b90506119d7565b505050565b6003546001600160a01b03163314611acf576040516282b42960e81b815260040160405180910390fd5b60028054600380546001600160a01b03198084166001600160a01b038381169182179096559116909155604051929091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b03163314611b56576040516282b42960e81b815260040160405180910390fd5b600a8161ffff161080611b6e57506103e88161ffff16115b15611b8c57604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b6002546001600160a01b03163314611bd8576040516282b42960e81b815260040160405180910390fd5b6003546040516001600160a01b03909116907f6ecd4842251bedd053b09547c0fabaab9ec98506ebf24469e8dd5560412ed37f90600090a2600380546001600160a01b0319169055565b6002546001600160a01b03163314611c4c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517ff7de8a78655219727a7c0d0db42a97c024f91cb131c5713063c555ce7d7c56529190a250565b6002546001600160a01b03163314611cc2576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19166001179055517f459edbd4f641bfd6cb8eba5ac1b9e7a4629853efd00cc505d055fd3b051867e09190a250565b600060606000600c60009054906101000a90046001600160a01b03166001600160a01b0316638be859ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190614bc2565b90506000600c60009054906101000a90046001600160a01b03166001600160a01b03166383307f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e069190614bc2565b905081158015611e1557508015155b15611e75576001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509350935050509091565b600d54611e909083908390600160c01b900461ffff166141e9565b15611ec95760006040518060400160405280600f81526020016e41554d20697320696e2072616e676560881b8152509350935050509091565b6001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b600d54600160d01b900460ff1615611f18576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015611f6357503360009081526007602052604090205460ff16155b15611f80576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590611fa557506004546001600160a01b03163314155b15611fc2576040516282b42960e81b815260040160405180910390fd5b6040517457697468647261775f476f7665726e616e63655f3160581b602082015260009060350160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206d9190614bc2565b6000838152600e60205260409020546120869190614ca5565b9050803410156120b257604051634fd394c160e11b8152346004820152602481018290526044016115d1565b336001600160a01b038516148015906120d657506002546001600160a01b03163314155b156120f3576040516282b42960e81b815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090506121458760200151613eb6565b865161215090613ed7565b86600001518260008151811061216857612168614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250508660200151816000815181106121a0576121a0614cbc565b6020026020010181815250506121b582613fde565b6121be8261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681526001600160a01b0388166020820152600091810182604051908082528060200260200182016040528015612219578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561224a578160200160208202803683370190505b5081526020810185905260408101849052600160608201526080018790529050611845816140c5565b6002546001600160a01b0316331461229d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19169055517f59926e0a78d12238b668b31c8e3f6ece235a59a00ede111d883e255b68c4d0489190a250565b600d54600160d81b900460ff1615612311576040516318c0a57b60e31b815260040160405180910390fd5b6000805260086020527f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c75460ff1615801561235c57503360009081526008602052604090205460ff16155b15612379576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03161580159061239e57506004546001600160a01b03163314155b156123bb576040516282b42960e81b815260040160405180910390fd5b604051630537761760e41b602082015260009060240160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124559190614bc2565b6000838152600e602052604090205461246e9190614ca5565b90508034101561249a57604051634fd394c160e11b8152346004820152602481018290526044016115d1565b6124a385613eb6565b6124ac86613ed7565b6124b584613ed7565b836001600160a01b0316866001600160a01b0316036124e757604051630df7483960e41b815260040160405180910390fd5b604080516002808252606082018352600092602083019080368337019050509050868160008151811061251c5761251c614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061255057612550614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250506125798161401e565b61258281613fde565b61258b81614231565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508a8460008151811061262a5761262a614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050898360008151811061265e5761265e614cbc565b602002602001018181525050888260008151811061267e5761267e614cbc565b6001600160a01b039092166020928302919091018201526040805161010081018252600d5463ffffffff600160a01b90910416815233928101929092528101859052606081018490526080810183905260a08101829052600060c082015260e081018990526126ec816140c5565b7f806ee47a6104a6e7ede1e15a9c9f384c0706b79fcea8a9edb136fe3b51b496b6600d60149054906101000a900463ffffffff168260405161272f929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461275483614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050505050565b3360009081526005602052604090205460ff166127b0576040516282b42960e81b815260040160405180910390fd5b600a54816001600160801b031610156127e757604051632797027760e01b81526001600160801b03821660048201526024016115d1565b60006127f38242614d3b565b6001600160a01b0384166000908152600b60205260409020549091506001600160801b03908116908216811061284f57604051630a4dc5d360e31b81526001600160801b038083166004830152831660248201526044016115d1565b6001600160a01b0384166000818152600b602090815260409182902080546001600160801b0319166001600160801b0387169081179091558251938452908301527f6a8b3a105d0f4bca0c6d25f6f59282c2cac3e2acdd3f3466dabe9725f3299703910160405180910390a150505050565b3360009081526005602052604090205460ff166128f0576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020546001600160801b034281169116101561294057604051637407be0b60e11b81526001600160a01b03821660048201526024016115d1565b6001600160a01b0381166000818152600b602090815260409182902080546001600160801b031916905590519182527f975fea2c466fdf1d3e8dd9969713fbfde0822e5b3921c18c0003e033f216bf5a910160405180910390a150565b6002546001600160a01b031633146129c7576040516282b42960e81b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b03163314612a13576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517f4d6789b83afc897fab8472d33d3f7cc1fba254215a1cd007ce51f3c5bf6f57729190a250565b600d54600160d01b900460ff1615612a87576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015612ad257503360009081526007602052604090205460ff16155b15612aef576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590612b1457506004546001600160a01b03163314155b15612b31576040516282b42960e81b815260040160405180910390fd5b600082612b6c57612b4184613d60565b604051602001612b519190614d5b565b60405160208183030381529060405280519060200120612b9c565b612b7584613d60565b604051602001612b859190614d94565b604051602081830303815290604052805190602001205b6000818152600e60205260409020549091508315612cd657604051630311ead160e61b815233600482015260009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c47ab44090602401602060405180830381865afa158015612c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c419190614dcf565b6001600160a01b031614612c56576001612c59565b60005b905080612cd057600e6000604051602001612c88906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e600085815260200190815260200160002054612ccb9190614c10565b612cd2565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4d9190614bc2565b612d579082614ca5565b905080341015612d8357604051634fd394c160e11b8152346004820152602481018290526044016115d1565b612d8c85613e8b565b6000856001600160401b03811115612da657612da661474d565b604051908082528060200260200182016040528015612dcf578160200160208202803683370190505b5090506000866001600160401b03811115612dec57612dec61474d565b604051908082528060200260200182016040528015612e15578160200160208202803683370190505b50905060005b87811015612ef257612e3889898381811061168f5761168f614cbc565b612e4d8989838181106116b3576116b3614cbc565b888882818110612e5f57612e5f614cbc565b612e759260206040909202019081019150614544565b838281518110612e8757612e87614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050888882818110612eb957612eb9614cbc565b90506040020160200135828281518110612ed557612ed5614cbc565b602090810291909101015280612eea81614c52565b915050612e1b565b50612efd8282613fbc565b612f0682613efe565b612f0f8261401e565b612f1882613fde565b8515612f2757612f27826142d8565b6040805161010081018252600d54600160a01b900463ffffffff168152336020820152908101839052606081018290526000906080810182604051908082528060200260200182016040528015612f88578160200160208202803683370190505b5081526020016000604051908082528060200260200182016040528015612fb9578160200160208202803683370190505b50815288151560208201526040018790529050612fd5816140c5565b7f8f1d75282d5f6e805e4525ffa180022a0be9390ae16b9391e8bae3b0f85e39bf600d60149054906101000a900463ffffffff1682604051613018929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461303d83614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b61306f6143ed565b600080546001919061308b9085906001600160401b0316614dec565b6001600160401b031681526020808201929092526040908101600020815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015260018201805484518187028101870186528181529295939486019383018282801561312457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613106575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561317c57602002820191906000526020600020905b815481526020019060010190808311613168575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156131de57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116131c0575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561323657602002820191906000526020600020905b815481526020019060010190808311613222575b5050509183525050600582015460ff161515602082015260069091015460409091015292915050565b6002546001600160a01b03163314613289576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576001600760008585858181106132ac576132ac614cbc565b90506020020160208101906132c19190614544565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106132fb576132fb614cbc565b90506020020160208101906133109190614544565b6001600160a01b03167f19ef9a4877199f89440a26acb26895ec02ed86f2df1aeaa90dc18041b892f71f60405160405180910390a261334e81614c52565b905061328c565b600080836040516020016133699190614e0c565b60408051601f1981840301815291815281516020928301206000818152600e90935291205490915083156134b957604051630311ead160e61b815233600482015260009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c47ab44090602401602060405180830381865afa158015613400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134249190614dcf565b6001600160a01b03161461343957600161343c565b60005b9050806134b357600e600060405160200161346b906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e6000858152602001908152602001600020546134ae9190614c10565b6134b5565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135309190614bc2565b61353a9082614ca5565b925050505b92915050565b3360009081526005602052604090205460ff16613574576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b1916600160d01b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146135e3576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0383169081179091556040517fc720a84179457777bd6ab883473f5ae8b0d37c9d100fd66f070ddcbd35af020f90600090a250565b3360009081526006602052604090205460ff1661365c576040516282b42960e81b815260040160405180910390fd5b600c546040516339e1f2a960e21b8152600481018390526001600160a01b039091169063e787caa490602401600060405180830381600087803b1580156136a257600080fd5b505af11580156136b6573d6000803e3d6000fd5b5050505050565b6002546001600160a01b031633146136e7576040516282b42960e81b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b3360009081526005602052604090205460ff16613738576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b1916600160d81b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146137a7576040516282b42960e81b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350565b6002546001600160a01b0316331461381d576040516282b42960e81b815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461386a576040519150601f19603f3d011682016040523d82523d6000602084013e61386f565b606091505b5050905080611aa057600080fd5b6000805b8251811015613900576138d183828151811061389f5761389f614cbc565b60200260200101516001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b156138f8578281815181106138e8576138e8614cbc565b6020026020010151915050919050565b600101613881565b50600092915050565b3360009081526005602052604090205460ff16613938576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab790602001610cda565b61397d6143ed565b600080546001600160401b0316818061399583614e28565b91906101000a8154816001600160401b0302191690836001600160401b0316021790555090506000800160089054906101000a90046001600160401b03166001600160401b0316816001600160401b031603613a04576040516331dcf2b560e01b815260040160405180910390fd5b6001600160401b0381166000908152600160208181526040808420815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015293810180548351818602810186018552818152929493860193830182828015613a9a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a7c575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015613af257602002820191906000526020600020905b815481526020019060010190808311613ade575b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015613b5457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b36575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020018280548015613bac57602002820191906000526020600020905b815481526020019060010190808311613b98575b5050509183525050600582015460ff1615156020808301919091526006909201546040918201526001600160401b038516600090815260019283905290812080546001600160c01b031916815592935090613c0990830182614443565b613c17600283016000614443565b613c25600383016000614443565b613c33600483016000614443565b5060058101805460ff19169055600060069091015592915050565b613c5661197b565b816001600160401b03161115613c7f5760405163ad2ce56160e01b815260040160405180910390fd5b600080546001600160401b031690613c978383614dec565b90505b806001600160401b0316826001600160401b03161015613d3b576001600160401b0382166000908152600160208190526040822080546001600160c01b03191681559190613cea90830182614443565b613cf8600283016000614443565b613d06600383016000614443565b613d14600483016000614443565b5060058101805460ff19169055600060069091015581613d3381614e28565b925050613c9a565b506000805467ffffffffffffffff19166001600160401b039290921691909117905550565b606081600003613d875750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613db15780613d9b81614c52565b9150613daa9050600a83614e44565b9150613d8b565b6000816001600160401b03811115613dcb57613dcb61474d565b6040519080825280601f01601f191660200182016040528015613df5576020820181803683370190505b509050815b8515613e8257613e0b600182614c23565b90506000613e1a600a88614e44565b613e2590600a614ca5565b613e2f9088614c23565b613e3a906030614e66565b905060008160f81b905080848481518110613e5757613e57614cbc565b60200101906001600160f81b031916908160001a905350613e79600a89614e44565b97505050613dfa565b50949350505050565b6005811180613e98575080155b1561130c57604051631d7317ad60e31b815260040160405180910390fd5b8060000361130c5760405163339d6ef160e21b815260040160405180910390fd5b6001600160a01b03811661130c5760405163339d6ef160e21b815260040160405180910390fd5b60005b60018251613f0f9190614c23565b811015613fb8576000613f23826001614c10565b90505b8251811015613fa557828181518110613f4157613f41614cbc565b60200260200101516001600160a01b0316838381518110613f6457613f64614cbc565b60200260200101516001600160a01b031603613f9357604051630df7483960e41b815260040160405180910390fd5b80613f9d81614c52565b915050613f26565b5080613fb081614c52565b915050613f01565b5050565b8051825114613fb85760405163b1f40f7760e01b815260040160405180910390fd5b6000613fe98261387d565b90506001600160a01b03811615613fb85760405163fc31f2eb60e01b81526001600160a01b03821660048201526024016115d1565b600c5460405163db10e60f60e01b81526000916001600160a01b03169063db10e60f9061404f908590600401614e7f565b602060405180830381865afa15801561406c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140909190614dcf565b90506001600160a01b03811615613fb857604051632777a68f60e11b81526001600160a01b03821660048201526024016115d1565b600080548291600191600160401b90046001600160401b03168160086140ea83614e28565b82546101009290920a6001600160401b038181021990931691831602179091551681526020808201929092526040908101600020835181548585015163ffffffff9092166001600160c01b0319909116176401000000006001600160a01b0390921691909102178155908301518051919261416d92600185019290910190614461565b50606082015180516141899160028401916020909101906144c6565b50608082015180516141a5916003840191602090910190614461565b5060a082015180516141c19160048401916020909101906144c6565b5060c082015160058201805460ff191691151591909117905560e09091015160069091015550565b6000806141f6858461439d565b9050600061420486856143ce565b90508185108061421357508085115b156142235760009250505061422a565b6001925050505b9392505050565b600c5460405163aeabb17f60e01b81526000916001600160a01b03169063aeabb17f90614262908590600401614e7f565b602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a39190614dcf565b90506001600160a01b03811615613fb8576040516303e4acb760e01b81526001600160a01b03821660048201526024016115d1565b60405163eb2fea2d60e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063eb2fea2d90614327908590600401614e7f565b602060405180830381865afa158015614344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143689190614dcf565b90506001600160a01b03811615613fb85760405163b3ef117360e01b81526001600160a01b03821660048201526024016115d1565b612710818103908211611388198290048411820217156143bc57600080fd5b61271092026113880191909104919050565b612710810161271019821161138819829004841117156143bc57600080fd5b604051806101000160405280600063ffffffff16815260200160006001600160a01b0316815260200160608152602001606081526020016060815260200160608152602001600015158152602001600081525090565b508054600082559060005260206000209081019061130c9190614501565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614481565b506144c2929150614501565b5090565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b65782518255916020019190600101906144e6565b5b808211156144c25760008155600101614502565b60006020828403121561452857600080fd5b5035919050565b6001600160a01b038116811461130c57600080fd5b60006020828403121561455657600080fd5b813561422a8161452f565b6000806040838503121561457457600080fd5b50508035926020909101359150565b60006020828403121561459557600080fd5b81356001600160401b038116811461422a57600080fd5b60005b838110156145c75781810151838201526020016145af565b50506000910152565b600081518084526145e88160208601602086016145ac565b601f01601f19169290920160200192915050565b821515815260406020820152600061461760408301846145d0565b949350505050565b60008083601f84011261463157600080fd5b5081356001600160401b0381111561464857600080fd5b6020830191508360208260061b850101111561466357600080fd5b9250929050565b60008060006040848603121561467f57600080fd5b83356001600160401b0381111561469557600080fd5b6146a18682870161461f565b909790965060209590950135949350505050565b600080602083850312156146c857600080fd5b82356001600160401b03808211156146df57600080fd5b818501915085601f8301126146f357600080fd5b81358181111561470257600080fd5b8660208260051b850101111561471757600080fd5b60209290920196919550909350505050565b60006020828403121561473b57600080fd5b813561ffff8116811461422a57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561478b5761478b61474d565b604052919050565b600080600083850360808112156147a957600080fd5b60408112156147b757600080fd5b50604051604081018181106001600160401b03821117156147da576147da61474d565b60405284356147e88161452f565b815260208581013590820152925060408401356148048161452f565b929592945050506060919091013590565b6000806000806080858703121561482b57600080fd5b84356148368161452f565b935060208501359250604085013561484d8161452f565b9396929550929360600135925050565b6000806040838503121561487057600080fd5b823561487b8161452f565b915060208301356001600160801b038116811461489757600080fd5b809150509250929050565b803580151581146148b257600080fd5b919050565b600080600080606085870312156148cd57600080fd5b84356001600160401b038111156148e357600080fd5b6148ef8782880161461f565b90955093506149029050602086016148a2565b9396929550929360400135925050565b600081518084526020808501945080840160005b8381101561494b5781516001600160a01b031687529582019590820190600101614926565b509495945050505050565b600081518084526020808501945080840160005b8381101561494b5781518752958201959082019060010161496a565b805163ffffffff168252600061010060208301516149af60208601826001600160a01b03169052565b5060408301518160408601526149c782860182614912565b915050606083015184820360608601526149e18282614956565b915050608083015184820360808601526149fb8282614912565b91505060a083015184820360a0860152614a158282614956565b91505060c0830151614a2b60c086018215159052565b5060e083015160e08501528091505092915050565b60208152600061422a6020830184614986565b60008060408385031215614a6657600080fd5b82356001600160401b0380821115614a7d57600080fd5b818501915085601f830112614a9157600080fd5b8135602082821115614aa557614aa561474d565b614ab7601f8301601f19168201614763565b92508183528781838601011115614acd57600080fd5b81818501828501376000818385010152829550614aeb8188016148a2565b9450505050509250929050565b60006020808385031215614b0b57600080fd5b82356001600160401b0380821115614b2257600080fd5b818501915085601f830112614b3657600080fd5b813581811115614b4857614b4861474d565b8060051b9150614b59848301614763565b8181529183018401918481019088841115614b7357600080fd5b938501935b83851015614b9d5784359250614b8d8361452f565b8282529385019390850190614b78565b98975050505050505050565b8281526040602082015260006146176040830184614986565b600060208284031215614bd457600080fd5b5051919050565b63ffffffff8316815260406020820152600061461760408301846145d0565b634e487b7160e01b600052601160045260246000fd5b8082018082111561353f5761353f614bfa565b8181038181111561353f5761353f614bfa565b6000600160ff1b8201614c4b57614c4b614bfa565b5060000390565b600060018201614c6457614c64614bfa565b5060010190565b7157697468647261775f5374616e646172645f60701b815260008251614c988160128501602087016145ac565b9190910160120192915050565b808202811582820484141761353f5761353f614bfa565b634e487b7160e01b600052603260045260246000fd5b63ffffffff831681526040602082015260006146176040830184614986565b600063ffffffff808316818103614d0a57614d0a614bfa565b6001019392505050565b6001600160401b03828116828216039080821115614d3457614d34614bfa565b5092915050565b6001600160801b03818116838216019080821115614d3457614d34614bfa565b704465706f7369745f5374616e646172645f60781b815260008251614d878160118501602087016145ac565b9190910160110192915050565b724465706f7369745f476f7665726e616e63655f60681b815260008251614dc28160138501602087016145ac565b9190910160130192915050565b600060208284031215614de157600080fd5b815161422a8161452f565b6001600160401b03818116838216019080821115614d3457614d34614bfa565b60008251614e1e8184602087016145ac565b9190910192915050565b60006001600160401b03808316818103614d0a57614d0a614bfa565b600082614e6157634e487b7160e01b600052601260045260246000fd5b500490565b60ff818116838216019081111561353f5761353f614bfa565b60208152600061422a602083018461491256fea2646970667358221220d0814db886cb03bb1e8f13667a8fb195ba1e886c5131e80b719c8d76e36df0b764736f6c634300081300330000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000012b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd3693f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a64975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd5430f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a66284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34adccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed1721330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879bbfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1bb04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b00373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef56109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6dbce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000061a80
Deployed Bytecode
0x6080604052600436106103975760003560e01c806399385006116101dc578063cdfca7f211610102578063e30c3978116100a0578063f2fde38b1161006f578063f2fde38b14610b4e578063f6164a3714610b6e578063f80c6d9c14610b8e578063f93b6be514610bae57600080fd5b8063e30c397814610ad9578063e787caa414610af9578063ec2a099214610b19578063ed6b5ad514610b3957600080fd5b8063d94ad837116100dc578063d94ad83714610a4f578063d9fd6fb814610a84578063dbf6248914610aa4578063ddb015c814610ab957600080fd5b8063cdfca7f214610a04578063cfdbf25414610a24578063d307983d14610a3957600080fd5b8063b6ea98011161017a578063c87eed4611610149578063c87eed4614610984578063c9d6293c146109a4578063cbbf67ec146109b7578063cc88688b146109e457600080fd5b8063b6ea980114610911578063b813a8df14610924578063bae2be5614610944578063c073fd741461096457600080fd5b8063b19e52dc116101b6578063b19e52dc1461087e578063b588c10b14610891578063b6235016146108c1578063b64230ba146108e157600080fd5b80639938500614610810578063a01605c314610830578063affed0e01461084557600080fd5b8063526631fb116102c1578063769315a41161025f57806392fede001161022e57806392fede001461079457806393263b8a146107a9578063979edeba146107c957806398fbfbea146107f057600080fd5b8063769315a41461071f57806379ba50971461073f5780637cb1d3df146107545780638da5cb5b1461077457600080fd5b80636913a63c1161029b5780636913a63c1461069a5780636ba42aaa146106ba5780636d9b83ec146106ea578063708968c11461070a57600080fd5b8063526631fb146106435780635c975abb14610666578063608530051461068757600080fd5b80633ddc66ba11610339578063485d6ef811610308578063485d6ef81461058e578063489c1202146105d357806349bcb7b2146106035780634fd8cb941461062357600080fd5b80633ddc66ba146104fe5780633f2d41311461051e5780634032b72b1461053e5780634209fff11461055e57600080fd5b80631a366bb7116103755780631a366bb71461041e57806320157d0a1461046a578063245a4e721461047f57806339663ef2146104b057600080fd5b806305e2f3801461039c57806314ae9f2e146103dc57806315db9304146103fe575b600080fd5b3480156103a857600080fd5b506103c96103b7366004614516565b600e6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156103e857600080fd5b506103fc6103f7366004614544565b610bc3565b005b34801561040a57600080fd5b506103fc610419366004614561565b610c36565b34801561042a57600080fd5b506104527f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e181565b6040516001600160a01b0390911681526020016103d3565b34801561047657600080fd5b506103fc610c72565b34801561048b57600080fd5b50600d546104a090600160d81b900460ff1681565b60405190151581526020016103d3565b3480156104bc57600080fd5b506104e66104cb366004614544565b600b602052600090815260409020546001600160801b031681565b6040516001600160801b0390911681526020016103d3565b34801561050a57600080fd5b506103fc610519366004614516565b610ce4565b34801561052a57600080fd5b506103fc610539366004614544565b6111eb565b34801561054a57600080fd5b506103fc610559366004614544565b61125e565b34801561056a57600080fd5b506104a0610579366004614544565b60076020526000908152604090205460ff1681565b34801561059a57600080fd5b506104a06105a9366004614544565b6001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b3480156105df57600080fd5b506104a06105ee366004614544565b60056020526000908152604090205460ff1681565b34801561060f57600080fd5b506103fc61061e366004614583565b6112d4565b34801561062f57600080fd5b50600c54610452906001600160a01b031681565b34801561064f57600080fd5b5061065861130f565b6040516103d39291906145fc565b34801561067257600080fd5b50600d546104a090600160d01b900460ff1681565b6103fc61069536600461466a565b61141c565b3480156106a657600080fd5b506103fc6106b5366004614544565b6118d6565b3480156106c657600080fd5b506104a06106d5366004614544565b60066020526000908152604090205460ff1681565b3480156106f657600080fd5b506103fc610705366004614516565b61194c565b34801561071657600080fd5b506103c961197b565b34801561072b57600080fd5b506103fc61073a3660046146b5565b6119aa565b34801561074b57600080fd5b506103fc611aa5565b34801561076057600080fd5b506103fc61076f366004614729565b611b2c565b34801561078057600080fd5b50600254610452906001600160a01b031681565b3480156107a057600080fd5b506103fc611bae565b3480156107b557600080fd5b50600454610452906001600160a01b031681565b3480156107d557600080fd5b506107de600581565b60405160ff90911681526020016103d3565b3480156107fc57600080fd5b506103fc61080b366004614544565b611c22565b34801561081c57600080fd5b506103fc61082b366004614544565b611c98565b34801561083c57600080fd5b50610658611d0e565b34801561085157600080fd5b50600d5461086990600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016103d3565b6103fc61088c366004614793565b611eed565b34801561089d57600080fd5b506104a06108ac366004614544565b60096020526000908152604090205460ff1681565b3480156108cd57600080fd5b506103fc6108dc366004614544565b612273565b3480156108ed57600080fd5b506104a06108fc366004614544565b60086020526000908152604090205460ff1681565b6103fc61091f366004614815565b6122e6565b34801561093057600080fd5b506103fc61093f36600461485d565b612781565b34801561095057600080fd5b506103fc61095f366004614544565b6128c1565b34801561097057600080fd5b506103fc61097f366004614544565b61299d565b34801561099057600080fd5b506103fc61099f366004614544565b6129e9565b6103fc6109b23660046148b7565b612a5c565b3480156109c357600080fd5b506109d76109d2366004614583565b613067565b6040516103d39190614a40565b3480156109f057600080fd5b50600d54610452906001600160a01b031681565b348015610a1057600080fd5b506103fc610a1f3660046146b5565b61325f565b348015610a3057600080fd5b506107de600381565b348015610a4557600080fd5b506103c9600a5481565b348015610a5b57600080fd5b50600d54610a7190600160c01b900461ffff1681565b60405161ffff90911681526020016103d3565b348015610a9057600080fd5b506103c9610a9f366004614a53565b613355565b348015610ab057600080fd5b506103fc613545565b348015610ac557600080fd5b506103fc610ad4366004614544565b6135b9565b348015610ae557600080fd5b50600354610452906001600160a01b031681565b348015610b0557600080fd5b506103fc610b14366004614516565b61362d565b348015610b2557600080fd5b506103fc610b34366004614544565b6136bd565b348015610b4557600080fd5b506103fc613709565b348015610b5a57600080fd5b506103fc610b69366004614544565b61377d565b348015610b7a57600080fd5b506103fc610b89366004614544565b6137f3565b348015610b9a57600080fd5b50610452610ba9366004614af8565b61387d565b348015610bba57600080fd5b506103fc613909565b6002546001600160a01b03163314610bed576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19169055517fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf809190a250565b6002546001600160a01b03163314610c60576040516282b42960e81b815260040160405180910390fd5b6000918252600e602052604090912055565b3360009081526005602052604090205460ff16610ca1576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab7906020015b60405180910390a1565b3360009081526006602052604090205460ff16610d13576040516282b42960e81b815260040160405180910390fd5b600d54600160d01b900460ff1615610d3e576040516318c0a57b60e31b815260040160405180910390fd5b6000610d4861197b565b905080600003610d6b576040516333ff64eb60e21b815260040160405180910390fd5b6003811115610d7b576003610d7d565b805b9050600c60009054906101000a90046001600160a01b03166001600160a01b0316639c016ffd6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b5084925060009150505b828110156111e5576000610e016000613067565b6040015151118015610e1f5750610e186000613067565b6080015151155b15610f61576000610e2e613975565b600c54604051632d58fd7560e01b81529192506001600160a01b031690632d58fd7590610e619086908590600401614ba9565b6020604051808303816000875af1925050508015610e9c575060408051601f3d908101601f19168201909252610e9991810190614bc2565b60015b610f10573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e91610f02918490614bdb565b60405180910390a150610f5b565b610f1a8185614c10565b825160405163ffffffff90911681529094507f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b506111d3565b6000610f6d6000613067565b6080015151118015610f8b5750610f846000613067565b6040015151155b15611040576000610f9a613975565b600c54604051633ea1a23f60e11b81529192506001600160a01b031690637d43447e90610fcd9086908590600401614ba9565b6020604051808303816000875af1925050508015611008575060408051601f3d908101601f1916820190925261100591810190614bc2565b60015b611036573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b610f1a8185614c23565b61104a6000613067565b6040015151600114801561106c57506110636000613067565b60800151516001145b156111d357600061107b613975565b600c54604051633d655c3760e11b81529192506001600160a01b031690637acab86e906110ae9088908590600401614ba9565b6020604051808303816000875af19250505080156110e9575060408051601f3d908101601f191682019092526110e691810190614bc2565b60015b61115d573d808015611117576040519150601f19603f3d011682016040523d82523d6000602084013e61111c565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e9161114f918490614bdb565b60405180910390a1506111d1565b6000811315611177576111708185614c10565b9350611193565b60008112156111935761118981614c36565b6111709085614c23565b815160405163ffffffff90911681527f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b505b806111dd81614c52565b915050610ded565b50505050565b6002546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19169055517f1bdacea439f3d52c1495fc881858088a33f2ad0cc4a6a1a08780ca96eb6b702d9190a250565b6002546001600160a01b03163314611288576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19166001179055517f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9190a250565b3360009081526005602052604090205460ff16611303576040516282b42960e81b815260040160405180910390fd5b61130c81613c4e565b50565b6000606061131b61197b565b600003611352575050604080518082019091526012815271139bc81c195b991a5b99c81c995c5d595cdd60721b6020820152600091565b600c54604080516383307f4360e01b815290516000926001600160a01b0316916383307f439160048083019260209291908290030181865afa15801561139c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c09190614bc2565b90506001306001600160a01b0316633ddc66ba836040516024016113e691815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505092509250509091565b600d54600160d01b900460ff1615611447576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff1615801561149257503360009081526007602052604090205460ff16155b156114af576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b0316158015906114d457506004546001600160a01b03163314155b156114f1576040516282b42960e81b815260040160405180910390fd5b60006114fc83613d60565b60405160200161150c9190614c6b565b60408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115909190614bc2565b6000838152600e60205260409020546115a99190614ca5565b9050803410156115da57604051634fd394c160e11b8152346004820152602481018290526044015b60405180910390fd5b6115e384613e8b565b6000846001600160401b038111156115fd576115fd61474d565b604051908082528060200260200182016040528015611626578160200160208202803683370190505b5090506000856001600160401b038111156116435761164361474d565b60405190808252806020026020018201604052801561166c578160200160208202803683370190505b50905060005b868110156117735761169e88888381811061168f5761168f614cbc565b90506040020160200135613eb6565b6116ce8888838181106116b3576116b3614cbc565b6116c99260206040909202019081019150614544565b613ed7565b8787828181106116e0576116e0614cbc565b6116f69260206040909202019081019150614544565b83828151811061170857611708614cbc565b60200260200101906001600160a01b031690816001600160a01b03168152505087878281811061173a5761173a614cbc565b9050604002016020013582828151811061175657611756614cbc565b60209081029190910101528061176b81614c52565b915050611672565b5061177d82613efe565b6117878282613fbc565b61179082613fde565b6117998261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681523360208201526000918101826040519080825280602002602001820160405280156117eb578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561181c578160200160208202803683370190505b5081526020810185905260408101849052600060608201526080018790529050611845816140c5565b7fa75ba6b66423ec9aec6231c21821a3ecfda0db0813ccf69d66808e73b8c860e8600d60149054906101000a900463ffffffff1682604051611888929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff169060146118ad83614cf1565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b6002546001600160a01b03163314611900576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19166001179055517f93405f05cd04f0d1bd875f2de00f1f3890484ffd0589248953bdfd29ba7f2f599190a250565b6002546001600160a01b03163314611976576040516282b42960e81b815260040160405180910390fd5b600a55565b6000805461199c906001600160401b0380821691600160401b900416614d14565b6001600160401b0316905090565b6002546001600160a01b031633146119d4576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576000600760008585858181106119f7576119f7614cbc565b9050602002016020810190611a0c9190614544565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055828282818110611a4657611a46614cbc565b9050602002016020810190611a5b9190614544565b6001600160a01b03167fe9dce8c992623ce791725b21e857e33248d1f190a25b5168313420eebdaae99d60405160405180910390a2611a9981614c52565b90506119d7565b505050565b6003546001600160a01b03163314611acf576040516282b42960e81b815260040160405180910390fd5b60028054600380546001600160a01b03198084166001600160a01b038381169182179096559116909155604051929091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b03163314611b56576040516282b42960e81b815260040160405180910390fd5b600a8161ffff161080611b6e57506103e88161ffff16115b15611b8c57604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b6002546001600160a01b03163314611bd8576040516282b42960e81b815260040160405180910390fd5b6003546040516001600160a01b03909116907f6ecd4842251bedd053b09547c0fabaab9ec98506ebf24469e8dd5560412ed37f90600090a2600380546001600160a01b0319169055565b6002546001600160a01b03163314611c4c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517ff7de8a78655219727a7c0d0db42a97c024f91cb131c5713063c555ce7d7c56529190a250565b6002546001600160a01b03163314611cc2576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19166001179055517f459edbd4f641bfd6cb8eba5ac1b9e7a4629853efd00cc505d055fd3b051867e09190a250565b600060606000600c60009054906101000a90046001600160a01b03166001600160a01b0316638be859ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190614bc2565b90506000600c60009054906101000a90046001600160a01b03166001600160a01b03166383307f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e069190614bc2565b905081158015611e1557508015155b15611e75576001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509350935050509091565b600d54611e909083908390600160c01b900461ffff166141e9565b15611ec95760006040518060400160405280600f81526020016e41554d20697320696e2072616e676560881b8152509350935050509091565b6001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b600d54600160d01b900460ff1615611f18576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015611f6357503360009081526007602052604090205460ff16155b15611f80576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590611fa557506004546001600160a01b03163314155b15611fc2576040516282b42960e81b815260040160405180910390fd5b6040517457697468647261775f476f7665726e616e63655f3160581b602082015260009060350160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206d9190614bc2565b6000838152600e60205260409020546120869190614ca5565b9050803410156120b257604051634fd394c160e11b8152346004820152602481018290526044016115d1565b336001600160a01b038516148015906120d657506002546001600160a01b03163314155b156120f3576040516282b42960e81b815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090506121458760200151613eb6565b865161215090613ed7565b86600001518260008151811061216857612168614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250508660200151816000815181106121a0576121a0614cbc565b6020026020010181815250506121b582613fde565b6121be8261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681526001600160a01b0388166020820152600091810182604051908082528060200260200182016040528015612219578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561224a578160200160208202803683370190505b5081526020810185905260408101849052600160608201526080018790529050611845816140c5565b6002546001600160a01b0316331461229d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19169055517f59926e0a78d12238b668b31c8e3f6ece235a59a00ede111d883e255b68c4d0489190a250565b600d54600160d81b900460ff1615612311576040516318c0a57b60e31b815260040160405180910390fd5b6000805260086020527f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c75460ff1615801561235c57503360009081526008602052604090205460ff16155b15612379576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03161580159061239e57506004546001600160a01b03163314155b156123bb576040516282b42960e81b815260040160405180910390fd5b604051630537761760e41b602082015260009060240160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124559190614bc2565b6000838152600e602052604090205461246e9190614ca5565b90508034101561249a57604051634fd394c160e11b8152346004820152602481018290526044016115d1565b6124a385613eb6565b6124ac86613ed7565b6124b584613ed7565b836001600160a01b0316866001600160a01b0316036124e757604051630df7483960e41b815260040160405180910390fd5b604080516002808252606082018352600092602083019080368337019050509050868160008151811061251c5761251c614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061255057612550614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250506125798161401e565b61258281613fde565b61258b81614231565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508a8460008151811061262a5761262a614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050898360008151811061265e5761265e614cbc565b602002602001018181525050888260008151811061267e5761267e614cbc565b6001600160a01b039092166020928302919091018201526040805161010081018252600d5463ffffffff600160a01b90910416815233928101929092528101859052606081018490526080810183905260a08101829052600060c082015260e081018990526126ec816140c5565b7f806ee47a6104a6e7ede1e15a9c9f384c0706b79fcea8a9edb136fe3b51b496b6600d60149054906101000a900463ffffffff168260405161272f929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461275483614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050505050565b3360009081526005602052604090205460ff166127b0576040516282b42960e81b815260040160405180910390fd5b600a54816001600160801b031610156127e757604051632797027760e01b81526001600160801b03821660048201526024016115d1565b60006127f38242614d3b565b6001600160a01b0384166000908152600b60205260409020549091506001600160801b03908116908216811061284f57604051630a4dc5d360e31b81526001600160801b038083166004830152831660248201526044016115d1565b6001600160a01b0384166000818152600b602090815260409182902080546001600160801b0319166001600160801b0387169081179091558251938452908301527f6a8b3a105d0f4bca0c6d25f6f59282c2cac3e2acdd3f3466dabe9725f3299703910160405180910390a150505050565b3360009081526005602052604090205460ff166128f0576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020546001600160801b034281169116101561294057604051637407be0b60e11b81526001600160a01b03821660048201526024016115d1565b6001600160a01b0381166000818152600b602090815260409182902080546001600160801b031916905590519182527f975fea2c466fdf1d3e8dd9969713fbfde0822e5b3921c18c0003e033f216bf5a910160405180910390a150565b6002546001600160a01b031633146129c7576040516282b42960e81b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b03163314612a13576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517f4d6789b83afc897fab8472d33d3f7cc1fba254215a1cd007ce51f3c5bf6f57729190a250565b600d54600160d01b900460ff1615612a87576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015612ad257503360009081526007602052604090205460ff16155b15612aef576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590612b1457506004546001600160a01b03163314155b15612b31576040516282b42960e81b815260040160405180910390fd5b600082612b6c57612b4184613d60565b604051602001612b519190614d5b565b60405160208183030381529060405280519060200120612b9c565b612b7584613d60565b604051602001612b859190614d94565b604051602081830303815290604052805190602001205b6000818152600e60205260409020549091508315612cd657604051630311ead160e61b815233600482015260009081906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063c47ab44090602401602060405180830381865afa158015612c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c419190614dcf565b6001600160a01b031614612c56576001612c59565b60005b905080612cd057600e6000604051602001612c88906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e600085815260200190815260200160002054612ccb9190614c10565b612cd2565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4d9190614bc2565b612d579082614ca5565b905080341015612d8357604051634fd394c160e11b8152346004820152602481018290526044016115d1565b612d8c85613e8b565b6000856001600160401b03811115612da657612da661474d565b604051908082528060200260200182016040528015612dcf578160200160208202803683370190505b5090506000866001600160401b03811115612dec57612dec61474d565b604051908082528060200260200182016040528015612e15578160200160208202803683370190505b50905060005b87811015612ef257612e3889898381811061168f5761168f614cbc565b612e4d8989838181106116b3576116b3614cbc565b888882818110612e5f57612e5f614cbc565b612e759260206040909202019081019150614544565b838281518110612e8757612e87614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050888882818110612eb957612eb9614cbc565b90506040020160200135828281518110612ed557612ed5614cbc565b602090810291909101015280612eea81614c52565b915050612e1b565b50612efd8282613fbc565b612f0682613efe565b612f0f8261401e565b612f1882613fde565b8515612f2757612f27826142d8565b6040805161010081018252600d54600160a01b900463ffffffff168152336020820152908101839052606081018290526000906080810182604051908082528060200260200182016040528015612f88578160200160208202803683370190505b5081526020016000604051908082528060200260200182016040528015612fb9578160200160208202803683370190505b50815288151560208201526040018790529050612fd5816140c5565b7f8f1d75282d5f6e805e4525ffa180022a0be9390ae16b9391e8bae3b0f85e39bf600d60149054906101000a900463ffffffff1682604051613018929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461303d83614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b61306f6143ed565b600080546001919061308b9085906001600160401b0316614dec565b6001600160401b031681526020808201929092526040908101600020815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015260018201805484518187028101870186528181529295939486019383018282801561312457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613106575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561317c57602002820191906000526020600020905b815481526020019060010190808311613168575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156131de57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116131c0575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561323657602002820191906000526020600020905b815481526020019060010190808311613222575b5050509183525050600582015460ff161515602082015260069091015460409091015292915050565b6002546001600160a01b03163314613289576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576001600760008585858181106132ac576132ac614cbc565b90506020020160208101906132c19190614544565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106132fb576132fb614cbc565b90506020020160208101906133109190614544565b6001600160a01b03167f19ef9a4877199f89440a26acb26895ec02ed86f2df1aeaa90dc18041b892f71f60405160405180910390a261334e81614c52565b905061328c565b600080836040516020016133699190614e0c565b60408051601f1981840301815291815281516020928301206000818152600e90935291205490915083156134b957604051630311ead160e61b815233600482015260009081906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063c47ab44090602401602060405180830381865afa158015613400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134249190614dcf565b6001600160a01b03161461343957600161343c565b60005b9050806134b357600e600060405160200161346b906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e6000858152602001908152602001600020546134ae9190614c10565b6134b5565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135309190614bc2565b61353a9082614ca5565b925050505b92915050565b3360009081526005602052604090205460ff16613574576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b1916600160d01b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146135e3576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0383169081179091556040517fc720a84179457777bd6ab883473f5ae8b0d37c9d100fd66f070ddcbd35af020f90600090a250565b3360009081526006602052604090205460ff1661365c576040516282b42960e81b815260040160405180910390fd5b600c546040516339e1f2a960e21b8152600481018390526001600160a01b039091169063e787caa490602401600060405180830381600087803b1580156136a257600080fd5b505af11580156136b6573d6000803e3d6000fd5b5050505050565b6002546001600160a01b031633146136e7576040516282b42960e81b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b3360009081526005602052604090205460ff16613738576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b1916600160d81b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146137a7576040516282b42960e81b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350565b6002546001600160a01b0316331461381d576040516282b42960e81b815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461386a576040519150601f19603f3d011682016040523d82523d6000602084013e61386f565b606091505b5050905080611aa057600080fd5b6000805b8251811015613900576138d183828151811061389f5761389f614cbc565b60200260200101516001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b156138f8578281815181106138e8576138e8614cbc565b6020026020010151915050919050565b600101613881565b50600092915050565b3360009081526005602052604090205460ff16613938576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab790602001610cda565b61397d6143ed565b600080546001600160401b0316818061399583614e28565b91906101000a8154816001600160401b0302191690836001600160401b0316021790555090506000800160089054906101000a90046001600160401b03166001600160401b0316816001600160401b031603613a04576040516331dcf2b560e01b815260040160405180910390fd5b6001600160401b0381166000908152600160208181526040808420815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015293810180548351818602810186018552818152929493860193830182828015613a9a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a7c575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015613af257602002820191906000526020600020905b815481526020019060010190808311613ade575b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015613b5457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b36575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020018280548015613bac57602002820191906000526020600020905b815481526020019060010190808311613b98575b5050509183525050600582015460ff1615156020808301919091526006909201546040918201526001600160401b038516600090815260019283905290812080546001600160c01b031916815592935090613c0990830182614443565b613c17600283016000614443565b613c25600383016000614443565b613c33600483016000614443565b5060058101805460ff19169055600060069091015592915050565b613c5661197b565b816001600160401b03161115613c7f5760405163ad2ce56160e01b815260040160405180910390fd5b600080546001600160401b031690613c978383614dec565b90505b806001600160401b0316826001600160401b03161015613d3b576001600160401b0382166000908152600160208190526040822080546001600160c01b03191681559190613cea90830182614443565b613cf8600283016000614443565b613d06600383016000614443565b613d14600483016000614443565b5060058101805460ff19169055600060069091015581613d3381614e28565b925050613c9a565b506000805467ffffffffffffffff19166001600160401b039290921691909117905550565b606081600003613d875750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613db15780613d9b81614c52565b9150613daa9050600a83614e44565b9150613d8b565b6000816001600160401b03811115613dcb57613dcb61474d565b6040519080825280601f01601f191660200182016040528015613df5576020820181803683370190505b509050815b8515613e8257613e0b600182614c23565b90506000613e1a600a88614e44565b613e2590600a614ca5565b613e2f9088614c23565b613e3a906030614e66565b905060008160f81b905080848481518110613e5757613e57614cbc565b60200101906001600160f81b031916908160001a905350613e79600a89614e44565b97505050613dfa565b50949350505050565b6005811180613e98575080155b1561130c57604051631d7317ad60e31b815260040160405180910390fd5b8060000361130c5760405163339d6ef160e21b815260040160405180910390fd5b6001600160a01b03811661130c5760405163339d6ef160e21b815260040160405180910390fd5b60005b60018251613f0f9190614c23565b811015613fb8576000613f23826001614c10565b90505b8251811015613fa557828181518110613f4157613f41614cbc565b60200260200101516001600160a01b0316838381518110613f6457613f64614cbc565b60200260200101516001600160a01b031603613f9357604051630df7483960e41b815260040160405180910390fd5b80613f9d81614c52565b915050613f26565b5080613fb081614c52565b915050613f01565b5050565b8051825114613fb85760405163b1f40f7760e01b815260040160405180910390fd5b6000613fe98261387d565b90506001600160a01b03811615613fb85760405163fc31f2eb60e01b81526001600160a01b03821660048201526024016115d1565b600c5460405163db10e60f60e01b81526000916001600160a01b03169063db10e60f9061404f908590600401614e7f565b602060405180830381865afa15801561406c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140909190614dcf565b90506001600160a01b03811615613fb857604051632777a68f60e11b81526001600160a01b03821660048201526024016115d1565b600080548291600191600160401b90046001600160401b03168160086140ea83614e28565b82546101009290920a6001600160401b038181021990931691831602179091551681526020808201929092526040908101600020835181548585015163ffffffff9092166001600160c01b0319909116176401000000006001600160a01b0390921691909102178155908301518051919261416d92600185019290910190614461565b50606082015180516141899160028401916020909101906144c6565b50608082015180516141a5916003840191602090910190614461565b5060a082015180516141c19160048401916020909101906144c6565b5060c082015160058201805460ff191691151591909117905560e09091015160069091015550565b6000806141f6858461439d565b9050600061420486856143ce565b90508185108061421357508085115b156142235760009250505061422a565b6001925050505b9392505050565b600c5460405163aeabb17f60e01b81526000916001600160a01b03169063aeabb17f90614262908590600401614e7f565b602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a39190614dcf565b90506001600160a01b03811615613fb8576040516303e4acb760e01b81526001600160a01b03821660048201526024016115d1565b60405163eb2fea2d60e01b81526000906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063eb2fea2d90614327908590600401614e7f565b602060405180830381865afa158015614344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143689190614dcf565b90506001600160a01b03811615613fb85760405163b3ef117360e01b81526001600160a01b03821660048201526024016115d1565b612710818103908211611388198290048411820217156143bc57600080fd5b61271092026113880191909104919050565b612710810161271019821161138819829004841117156143bc57600080fd5b604051806101000160405280600063ffffffff16815260200160006001600160a01b0316815260200160608152602001606081526020016060815260200160608152602001600015158152602001600081525090565b508054600082559060005260206000209081019061130c9190614501565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614481565b506144c2929150614501565b5090565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b65782518255916020019190600101906144e6565b5b808211156144c25760008155600101614502565b60006020828403121561452857600080fd5b5035919050565b6001600160a01b038116811461130c57600080fd5b60006020828403121561455657600080fd5b813561422a8161452f565b6000806040838503121561457457600080fd5b50508035926020909101359150565b60006020828403121561459557600080fd5b81356001600160401b038116811461422a57600080fd5b60005b838110156145c75781810151838201526020016145af565b50506000910152565b600081518084526145e88160208601602086016145ac565b601f01601f19169290920160200192915050565b821515815260406020820152600061461760408301846145d0565b949350505050565b60008083601f84011261463157600080fd5b5081356001600160401b0381111561464857600080fd5b6020830191508360208260061b850101111561466357600080fd5b9250929050565b60008060006040848603121561467f57600080fd5b83356001600160401b0381111561469557600080fd5b6146a18682870161461f565b909790965060209590950135949350505050565b600080602083850312156146c857600080fd5b82356001600160401b03808211156146df57600080fd5b818501915085601f8301126146f357600080fd5b81358181111561470257600080fd5b8660208260051b850101111561471757600080fd5b60209290920196919550909350505050565b60006020828403121561473b57600080fd5b813561ffff8116811461422a57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561478b5761478b61474d565b604052919050565b600080600083850360808112156147a957600080fd5b60408112156147b757600080fd5b50604051604081018181106001600160401b03821117156147da576147da61474d565b60405284356147e88161452f565b815260208581013590820152925060408401356148048161452f565b929592945050506060919091013590565b6000806000806080858703121561482b57600080fd5b84356148368161452f565b935060208501359250604085013561484d8161452f565b9396929550929360600135925050565b6000806040838503121561487057600080fd5b823561487b8161452f565b915060208301356001600160801b038116811461489757600080fd5b809150509250929050565b803580151581146148b257600080fd5b919050565b600080600080606085870312156148cd57600080fd5b84356001600160401b038111156148e357600080fd5b6148ef8782880161461f565b90955093506149029050602086016148a2565b9396929550929360400135925050565b600081518084526020808501945080840160005b8381101561494b5781516001600160a01b031687529582019590820190600101614926565b509495945050505050565b600081518084526020808501945080840160005b8381101561494b5781518752958201959082019060010161496a565b805163ffffffff168252600061010060208301516149af60208601826001600160a01b03169052565b5060408301518160408601526149c782860182614912565b915050606083015184820360608601526149e18282614956565b915050608083015184820360808601526149fb8282614912565b91505060a083015184820360a0860152614a158282614956565b91505060c0830151614a2b60c086018215159052565b5060e083015160e08501528091505092915050565b60208152600061422a6020830184614986565b60008060408385031215614a6657600080fd5b82356001600160401b0380821115614a7d57600080fd5b818501915085601f830112614a9157600080fd5b8135602082821115614aa557614aa561474d565b614ab7601f8301601f19168201614763565b92508183528781838601011115614acd57600080fd5b81818501828501376000818385010152829550614aeb8188016148a2565b9450505050509250929050565b60006020808385031215614b0b57600080fd5b82356001600160401b0380821115614b2257600080fd5b818501915085601f830112614b3657600080fd5b813581811115614b4857614b4861474d565b8060051b9150614b59848301614763565b8181529183018401918481019088841115614b7357600080fd5b938501935b83851015614b9d5784359250614b8d8361452f565b8282529385019390850190614b78565b98975050505050505050565b8281526040602082015260006146176040830184614986565b600060208284031215614bd457600080fd5b5051919050565b63ffffffff8316815260406020820152600061461760408301846145d0565b634e487b7160e01b600052601160045260246000fd5b8082018082111561353f5761353f614bfa565b8181038181111561353f5761353f614bfa565b6000600160ff1b8201614c4b57614c4b614bfa565b5060000390565b600060018201614c6457614c64614bfa565b5060010190565b7157697468647261775f5374616e646172645f60701b815260008251614c988160128501602087016145ac565b9190910160120192915050565b808202811582820484141761353f5761353f614bfa565b634e487b7160e01b600052603260045260246000fd5b63ffffffff831681526040602082015260006146176040830184614986565b600063ffffffff808316818103614d0a57614d0a614bfa565b6001019392505050565b6001600160401b03828116828216039080821115614d3457614d34614bfa565b5092915050565b6001600160801b03818116838216019080821115614d3457614d34614bfa565b704465706f7369745f5374616e646172645f60781b815260008251614d878160118501602087016145ac565b9190910160110192915050565b724465706f7369745f476f7665726e616e63655f60681b815260008251614dc28160138501602087016145ac565b9190910160130192915050565b600060208284031215614de157600080fd5b815161422a8161452f565b6001600160401b03818116838216019080821115614d3457614d34614bfa565b60008251614e1e8184602087016145ac565b9190910192915050565b60006001600160401b03808316818103614d0a57614d0a614bfa565b600082614e6157634e487b7160e01b600052601260045260246000fd5b500490565b60ff818116838216019081111561353f5761353f614bfa565b60208152600061422a602083018461491256fea2646970667358221220d0814db886cb03bb1e8f13667a8fb195ba1e886c5131e80b719c8d76e36df0b764736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000012b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd3693f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a64975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd5430f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a66284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34adccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed1721330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879bbfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1bb04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b00373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef56109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6dbce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000061a80
-----Decoded View---------------
Arg [0] : _oracleModule (address): 0x9B122361E8708Be33B785e44FcE4d6ca86AB6C5a
Arg [1] : _govModule (address): 0xc6F50903a058f3807111619bD4B24cA64b8239E1
Arg [2] : _deviationThreshold (uint8): 100
Arg [3] : _relayerActions (bytes32[]): System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[]
Arg [4] : _gasUsages (uint256[]): 200000,300000,400000,500000,600000,200000,300000,400000,500000,600000,200000,300000,400000,500000,600000,300000,100000,400000
-----Encoded View---------------
43 Constructor Arguments found :
Arg [0] : 0000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a
Arg [1] : 000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000300
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [6] : b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd36
Arg [7] : 93f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a6
Arg [8] : 4975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd54
Arg [9] : 30f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041
Arg [10] : f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0
Arg [11] : a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e
Arg [12] : 75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a6
Arg [13] : 6284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8
Arg [14] : 295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34ad
Arg [15] : ccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed172
Arg [16] : 1330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a
Arg [17] : 1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879b
Arg [18] : bfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1b
Arg [19] : b04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b0
Arg [20] : 0373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5
Arg [21] : cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef5
Arg [22] : 6109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6d
Arg [23] : bce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [25] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [26] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [27] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [28] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [29] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [30] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [31] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [32] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [33] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [34] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [35] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [36] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [37] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [38] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [39] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [40] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [41] : 00000000000000000000000000000000000000000000000000000000000186a0
Arg [42] : 0000000000000000000000000000000000000000000000000000000000061a80
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ 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.