Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
RookSwap
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./reentrancyGuard.sol"; import "./whitelist.sol"; import "./owner.sol"; import "./assetManagement.sol"; import "./orderUtils.sol"; import "./utils.sol"; /// @title RookSwap - A token swapping protocol that enables users to receive MEV generated from their orders. /// A keeper executes the order on behalf of the user and extracts the value created by the order to distribute back to the user. /// Orders are signed and submitted to an off-chain orderbook, where keepers can bid for the right to execute. /// Users don't have to pay gas to swap tokens, except for token allowance approvals. /// @author Joey Zacherl - <[email protected]> /// Note: Some critical public/external functions in this contract are appended with underscores and extra characters as a gas optimization /// Example: function() may become function_xyz() or function__abc() // Exception codes // RS:E0 - ETH transfer failed // RS:E1 - Address(0) is not allowed // RS:E2 - Cannot overfill order // RS:E3 - Order not partially fillable, must fill order exactly full // RS:E4 - Order already filled // RS:E5 - Swap tokens must differ // RS:E6 - Order not fillable // RS:E7 - Order signature invalid // RS:E8 - Malformed ecdsa signature // RS:E9 - Invalid ecdsa signature // RS:E10 - Malformed pre-signature // RS:E11 - toUint256_outOfBounds // RS:E12 - Array lengths must match, orders and makerAmountsToSpend // RS:E13 - Do not use takerTokenDistribution_custom for one single order, use takerTokenDistribution_even // RS:E14 - Array lengths must match, orders and takerTokenDistributions // RS:E15 - Orders must not involve the same maker & same tokens // RS:E16 - Presigner must be valid // RS:E17 - Must be owner // RS:E18 - ReentrancyGuard: reentrant call // RS:E19 - Can not approve allowance for 0x0 // RS:E20 - Not permitted to cancel order // RS:E21 - Must be whitelisted Keeper // RS:E22 - Must be whitelisted DexAggKeeper // RS:E23 - maker not satisfied, partiallyFillable = true // RS:E24 - maker not satisfied, partiallyFillable = false // RS:E25 - RookSwap contract surplusToken balances must not decrease, including threshold // RS:E26 - RookSwap contract otherToken balances must not decrease // RS:E27 - Begin and expiry must be valid // RS:E28 - surplusToken must be in all orders // RS:E29 - otherToken must be in all orders // RS:E30 - Must be whitelisted DexAggRouter // RS:E31 - surplusToken and otherToken must differ // RS:E32 - approveToken must be either surplusToken or otherToken /// @dev Keeper interface for the callback function to execute swaps. abstract contract Keeper { function rookSwapExecution_s3gN( address rookSwapsMsgSender, uint256[] calldata makerAmountsSentByRook, bytes calldata data ) external virtual returns (bytes memory keeperReturn); } contract RookSwap is ReentrancyGuard, Owner, AssetManagement, Whitelist, OrderUtils { using Address for address; using SafeERC20 for IERC20; using LibBytes for bytes; /// @dev Facilitate swaps using Keeper's calldata through Keeper's custom trading contract. /// Must be a whitelisted Keeper. /// @param orders The orders to fill /// @param makerAmountsToSpend makerAmounts which to fill, correspond with orders /// @param keeperTaker Keeper's taker address which will execute the swap, typically a trading contract. Must implement rookSwapExecution_s3gN. /// @param data Keeper's calldata to pass into their rookSwapExecution_s3gN implementation. /// This calldata is responsible for facilitating trade and paying the maker back in /// takerTokens or else the transaction will revert. /// @return keeperReturn Return the keeper's keeperCallData return value from rookSwapExecution_s3gN. /// They'll likey want this for simulations, it's up to the keeper to decide what to return. /// to help with tx simulations. function swapKeeper__oASr( Order[] calldata orders, uint256[] calldata makerAmountsToSpend, address keeperTaker, bytes calldata data ) external nonReentrant returns (bytes memory keeperReturn) { // Only allow swap execution whitelisted Keepers require( getKeeperWhitelistPosition__2u3w(keeperTaker) != 0, "RS:E21" ); LibData.MakerData[] memory makerData = _prepareSwapExecution(orders, makerAmountsToSpend, keeperTaker); // Call the keeper's rookSwapExecution_s3gN function to execute the swap // Keeper must satisfy the user based on the signed order within this callback execution // We are passing in msg.sender to the keeper's rookSwapExecution_s3gN function. // We have ensured that keeperTaker is a whitelisted Rook keeper // but we have not ensured that msg.sender is keeperTaker's EOA // Keeper is responsible for ensuring that the msg.sender we pass them is their EOA and in their personal whitelist // Keeper is also responsible for ensuring that only a valid RookSwap contract can call their rookSwapExecution_s3gN function // This RookSwap contract is NOT upgradeable, so you can trust that the msg.sender we're passing along to Keeper is safe and correct keeperReturn = Keeper(keeperTaker).rookSwapExecution_s3gN(msg.sender, makerAmountsToSpend, data); _finalizeSwapExecution(orders, makerAmountsToSpend, makerData, keeperTaker); } /// @dev Facilitate swaps using DexAgg Keeper's calldata through this contract. /// Must be a whitelisted DexAgg Keeper. /// @param orders The orders to fill. /// @param makerAmountsToSpend makerAmounts which to fill, correspond with orders. /// @param makerWeights Mathematical weight for distributing tokens to makers. /// moved this math off chain to save gas and simplify on chain computation. /// corresponds with orders. /// @param swap Execution calldata for facilitating a swap via DEX Aggregators right here on this contract. /// If this function fails to pay back the maker in takerTokens, the transaction will revert. /// @param takerTokenDistributions Quantities for distributing tokens to makers. /// moved this math off chain to save gas and simplify on chain computation. /// corresponds with orders. /// @param metaData Supplementary data for swap execution and how to handle surplusTokens. /// @return surplusAmount Amount of surplusTokens acquired during swap execution. function swapDexAggKeeper_8B77( Order[] calldata orders, uint256[] calldata makerAmountsToSpend, uint256[] calldata makerWeights, LibSwap.DexAggSwap calldata swap, uint256[] calldata takerTokenDistributions, LibSwap.MetaData calldata metaData ) external nonReentrant returns (uint256 surplusAmount) { // Only allow swap execution whitelisted DexAggKeepers require( getDexAggKeeperWhitelistPosition_IkFc(msg.sender) != 0, "RS:E22" ); // Only allow swap execution on whitelisted DexAggs require( getDexAggRouterWhitelistPosition_ZgLC(swap.router) != 0, "RS:E30" ); // surplusToken and otherToken must differ require( metaData.surplusToken != metaData.otherToken, "RS:E31" ); // surplusToken and otherToken must be in every order, meaning there can only be 2 unique tokens in the swap. // otherwise this is an unsupported type of batching, or there is a logic bug with the dexAggKeeper offchain logic. // This check may not be necessary, but it's good to reject types of batching that are not supported today. // Reverting here will help prevent bugs and undesired behaviors. for (uint256 i; i < orders.length;) { require( metaData.surplusToken == orders[i].makerToken || metaData.surplusToken == orders[i].takerToken, "RS:E28" ); require( metaData.otherToken == orders[i].takerToken || metaData.otherToken == orders[i].makerToken, "RS:E29" ); // Gas optimization unchecked { ++i; } } LibData.ContractData memory contractData = LibData.ContractData( IERC20(metaData.surplusToken).balanceOf(address(this)), 0, IERC20(metaData.otherToken).balanceOf(address(this)) ); LibData.MakerData[] memory makerData = _prepareSwapExecution(orders, makerAmountsToSpend, address(this)); // Begin the swap execution by performing swaps on DexAggs uint256 takerTokenAmountToDistribute = _beginDexAggSwapExecution( swap, metaData ); // Complete the swap execution by distributing takerTokens properly if (metaData.takerTokenDistributionType == LibSwap.TakerTokenDistributionType.Even) { _completeDexAggSwapExecution_takerTokenDistribution_even( orders, makerWeights, takerTokenAmountToDistribute ); } else // elif (metaData.takerTokenDistributionType == LibSwap.TakerTokenDistributionType.Custom) { _completeDexAggSwapExecution_takerTokenDistribution_custom( orders, takerTokenDistributions ); } _finalizeSwapExecution(orders, makerAmountsToSpend, makerData, msg.sender); // Return the amount of surplus retained surplusAmount = _finalizeSwapExecution_dexAggKeeper(contractData, metaData); } /// @dev Prepare for swap execution by doing math, validating orders, and /// transferring makerTokens from the maker to the Keeper who will be facilitating swaps. function _prepareSwapExecution( Order[] calldata orders, uint256[] calldata makerAmountsToSpend, address makerTokenRecipient ) private returns (LibData.MakerData[] memory makerData) { uint256 ordersLength = orders.length; require( ordersLength == makerAmountsToSpend.length, "RS:E12" ); makerData = new LibData.MakerData[](ordersLength); for (uint256 i; i < ordersLength;) { // RookSwap does not currently support batching together swaps where 2 or more of the orders have the same maker & same tokens // This could be supported, however the gas efficiency would be horrible // because we'd have to use mappings and storage which costs a lot of gas // If you want to batch together orders in this way, it's still supported if you // by simply calling the RookSwap's swap function separately. // Example: // call (RookSwap).swapKeeper([order1, order2, order3]) // then immediately after call (RookSwap).swapKeeper([order4, order5, order6]) // In this example let's assume that order1 and order4 include the same maker & tokens, so they had to be in separate function calls // examples of order1 and order4 // Order1: Maker0x1234 swapping 900 DAI -> 0.6 WETH // Order4: Maker0x1234 swapping 900 DAI -> 0.6 WETH // The reason for this is that _finalizeSwapExecution would be exposed to an exploit // if it attempted to process them in one single function call // And if we do process it securely in one signle function call, gas efficiency suffers beyond recovery. // examples of order1 and order4 // Order1: Maker0x1234 swapping 0.6 WETH -> 900 DAI // Order4: Maker0x1234 swapping 900 DAI -> 0.6 WETH // examples of order1 and order5 // Order1: Maker0x1234 swapping 0.6 WETH -> 900 DAI // Order5: Maker0x1234 swapping 900 DAI -> 900 USDC for (uint256 j; j < ordersLength;) { if (i != j && orders[i].maker == orders[j].maker && (orders[i].takerToken == orders[j].takerToken || orders[i].makerToken == orders[j].takerToken)) { revert("RS:E15"); } // Gas optimization unchecked { ++j; } } bytes32 orderHash = getOrderHash(orders[i]); // makerData[i] = orders[i].data._decodeData(orderHash); makerData[i] = _decodeData(orders[i].data, orderHash); // Set the balance in the makerData makerData[i].takerTokenBalance_before = IERC20(orders[i].takerToken).balanceOf(orders[i].maker); // We are calling this with doGetActualFillableMakerAmount = false as a gas optimization // and with doRevertOnFailure = true because we expect it to revert if the order is not fillable // We don't care about making that extra gas consuming calls // The only reason we're calling this function, is to validate the order _validateAndGetOrderRelevantStatus(orders[i], orderHash, makerData[i], true, false); // Transfer makers makerToken to the keeper to begin trade execution IERC20(orders[i].makerToken).safeTransferFrom(orders[i].maker, makerTokenRecipient, makerAmountsToSpend[i]); // Update makerAmountFilled now that the makerTokens have been spent // The tx will revert if the maker isn't paid back takerTokens based on the order they signed _updateMakerAmountFilled( orders[i].makerAmount, orderHash, makerAmountsToSpend[i], makerData[i].partiallyFillable ); // Gas optimization unchecked { ++i; } } } /// @dev Begin executing swap for DexAgg Keeper by executing the swap's calldata on the DexAgg. /// Also calculate the amount of tokens we need to distribute. function _beginDexAggSwapExecution( LibSwap.DexAggSwap calldata swap, LibSwap.MetaData calldata metaData ) private returns (uint256 takerTokenAmountToDistribute) { // Begin swap execution by executing the swap on the DexAgg takerTokenAmountToDistribute = _dexAggKeeperSwap(swap, metaData); if (metaData.surplusTokenIsSwapTakerToken) { // With regards to a custom takerToken distribution // SurplusAmount could be extracted both from the makerToken and takerToken of a batched swap // Example: from the makerToken of User1's swap and the takerToken of User2's swap. // In this case User1 and User2 are sharing the tx gas fee. // So in this case, surplusAmountWithheld is only a fraction of the entire tx gas fee // And that's okay because this logic doesn't care about the other fraction of the tx gas fee // that was extracted at the beginning of the tx // Deduct the txs gas fee from the takerTokenAmountToDistribute because it's the surplusToken takerTokenAmountToDistribute = takerTokenAmountToDistribute - metaData.surplusAmountWithheld; } } /// @dev Complete DexAgg Keeper swap execution by distributing the takerTokens evenly among all makers in the batch. /// This function supports on chain positive slippage by utilizing the makerWeights and some simple math. function _completeDexAggSwapExecution_takerTokenDistribution_even( Order[] calldata orders, uint256[] calldata makerWeights, uint256 takerTokenAmountToDistribute ) private { uint256 ordersLength = orders.length; // Transfer takerToken to maker to complete trade // If statement here because we can save gas by not doing math if there's only 1 order in the batch // Otherwise we have to spend some gas on calculating the positive slippage for each user if (ordersLength == 1) { IERC20(orders[0].takerToken).safeTransfer(orders[0].maker, takerTokenAmountToDistribute); } else { // Determine how much to transfer to each maker in the batch for (uint256 i; i < ordersLength;) { IERC20(orders[i].takerToken).safeTransfer(orders[i].maker, takerTokenAmountToDistribute * makerWeights[i] / 1000000000000000000); // Gas optimization unchecked { ++i; } } } } /// @dev Complete DexAgg Keeper swap execution by distributing the takerTokens customly among all makers in the batch. /// This function does NOT support on chain positive slippage as the math is determined off chain ahead of time. function _completeDexAggSwapExecution_takerTokenDistribution_custom( Order[] calldata orders, uint256[] calldata takerTokenDistributions ) private { // For all of our takerTokenDistributions, // Transfer takerToken to maker to complete trade // This function should only be called with 2 or more orders // If only 1 order is being processed, use evenTakerTokenDistribution instead uint256 ordersLength = orders.length; require( ordersLength > 1, "RS:E13" ); // for every order, we must have an takerTokenDistribution require( ordersLength == takerTokenDistributions.length, "RS:E14" ); for (uint256 i; i < ordersLength;) { IERC20(orders[i].takerToken).safeTransfer(orders[i].maker, takerTokenDistributions[i]); // Gas optimization unchecked { ++i; } } } /// @dev Finalize swap execution by doing some math, verifying that each maker got paid, and emitting events. function _finalizeSwapExecution( Order[] calldata orders, uint256[] calldata makerAmountsToSpend, LibData.MakerData[] memory makerData, address taker ) private { // Require that all of the maker's swaps have been satisfied based on the order they signed for (uint256 i; i < orders.length;) { // Measure maker's post-trade balance makerData[i].takerTokenBalance_after = IERC20(orders[i].takerToken).balanceOf(orders[i].maker); // Validate order requirements uint256 takerAmountFilled = makerData[i].takerTokenBalance_after - makerData[i].takerTokenBalance_before; // Ensure the fill meets the maker's signed requirement // Gas optimization // if takerAmountDecayRate is zero, we can save gas by not calling _calculateCurrentTakerAmountMin // otherwise we must perform some extra calculations to determine currentTakerAmountMin uint256 currentTakerAmountMin = orders[i].takerAmountDecayRate == 0 ? orders[i].takerAmountMin : _calculateCurrentTakerAmountMin( orders[i].takerAmountMin, orders[i].takerAmountDecayRate, makerData[i] ); if (makerData[i].partiallyFillable) { // If the order is partiallyFillable, we have to slightly alter our math to support checking this properly // We must factor in the ratio of the makerAmount we're actually spending against the order's full makerAmount // This is because the _calculateCurrentTakerAmountMin is always in terms of the order's full amount // OPTIMIZATION: I could store this in a variable to make the code cleaner, but that costs more gas // So I'm in-lining all this math to save on gas require( takerAmountFilled * orders[i].makerAmount >= currentTakerAmountMin * makerAmountsToSpend[i], "RS:E23" ); } else { require( takerAmountFilled >= currentTakerAmountMin, "RS:E24" ); } // Log the fill event emit Fill( orders[i].maker, taker, orders[i].makerToken, orders[i].takerToken, makerAmountsToSpend[i], takerAmountFilled, makerData[i].orderHash ); // Gas optimization unchecked { ++i; } } } /// @dev Finalize swap execution for the DexAgg Keeper by ensuring that this contract didn't lose value /// and that all thresholds were satisfied. function _finalizeSwapExecution_dexAggKeeper( LibData.ContractData memory contractData, LibSwap.MetaData calldata metaData ) private view returns (uint256 surplusAmount) { // Measure post-trade balances contractData.surplusTokenBalance_after = IERC20(metaData.surplusToken).balanceOf(address(this)); // Gas optimization // not setting a variable here since we only use it once // contractData.otherTokenBalance_after = IERC20(metaData.otherToken).balanceOf(address(this)); // Require that the DexAggKeeper has been satisfied // Revert if the DexAggKeeper has lost value in surplusTokens or otherTokens // We expect to gain surplus in surplusTokens by metaData.surplusProtectionThreshold to cover the cost of gas and other fees // But we do not expect otherToken to increase // This protection is required so that we don't need to trust the DexAggs's calldata nearly as much // surplusToken must increase based on metaData.surplusProtectionThreshold, and should never decrease require( contractData.surplusTokenBalance_after >= (contractData.surplusTokenBalance_before + metaData.surplusProtectionThreshold), "RS:E25" ); // otherToken must at least break even // Typically this balance will not increase, break even is normal require( IERC20(metaData.otherToken).balanceOf(address(this)) >= contractData.otherTokenBalance_before, "RS:E26" ); surplusAmount = contractData.surplusTokenBalance_after - contractData.surplusTokenBalance_before; } /// @dev Calculate the order's current takerAmountMin at this point in time. /// The takerAmountDecayRate behaves like a dutch auction, as the takerAmount decays over time down to the takerAmountMin. /// Setting the takerAmountDecayRate to zero disables this decay feature and the swapping price remains static. /// Ideally, if takerAmountDecayRate is zero you don't even have to call this function because it just returns takerAmountMin. function _calculateCurrentTakerAmountMin( uint256 takerAmountMin, uint256 takerAmountDecayRate, LibData.MakerData memory makerData ) private view returns (uint256 currentTakerAmountMin) { // Saving gas by not creating variables for these // Leaving commented out variables here to help with readability // uint256 elapsedTime = block.timestamp - makerData.begin; // uint256 totalTime = makerData.expiry - makerData.begin; // uint256 timestamp = block.timestamp >= makerData.begin ? block.timestamp : makerData.begin; // uint256 multiplier = block.timestamp < makerData.expiry ? makerData.expiry - timestamp: 0; // currentTakerAmountMin = takerAmountMin + (takerAmountDecayRate * multiplier); // Gas optimization // Saving gas by not creating variables for any of this. // The code is increidbly hard to read, but it saves a lot of gas // The more readable version of the code is commented out above currentTakerAmountMin = takerAmountMin + (takerAmountDecayRate * ( block.timestamp < makerData.expiry ? makerData.expiry - ( block.timestamp >= makerData.begin ? block.timestamp : makerData.begin ) : 0 ) ); } /// @dev Execute the swap calldatas on the DexAggs. Also manage allowances to the DexAggs. function _dexAggKeeperSwap( LibSwap.DexAggSwap memory swap, LibSwap.MetaData calldata metaData ) private returns (uint256 swapOutput) { // Execute all requried allowance approvals before swapping // We will assume that the function caller knows which tokens need approved and which do not // We should be approving the token we're spending inside the swap.callData, if we don't this tx will likely revert // So it's up to the function caller to set this properly or else reverts can happen due to no allowance require( swap.approveToken != address(0), "RS:E19" ); // approveToken must be either surplusToken or otherToken, to prevent arbitrary token allowance approvals require( swap.approveToken == metaData.surplusToken || swap.approveToken == metaData.otherToken, "RS:E32" ); // Approve exactly how much we intend to swap on the DexAgg IERC20(swap.approveToken).approve(swap.router, swap.approvalAmount); // Execute the calldata (bool success, bytes memory returnData) = swap.router.call{ value: 0 }(swap.callData); _verifyCallResult(success, returnData, "callData execution failed"); swapOutput = returnData.toUint256(0); // Revoke the DexAgg's allowance now that the swap has finished IERC20(swap.approveToken).approve(swap.router, 0); } /// @dev Verify the result of a call /// This function reverts and bubbles up a revert reason if there's a problem /// The return value doesn't matter, the function caller should already have the call's result function _verifyCallResult( bool success, bytes memory returnData, string memory errorMessage ) private pure { if (success) { // Nothing needs done, just return return; } else { // 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 assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // Vendored from OpenZeppelin contracts with minor modifications: // - Modified Solidity version // - Formatted code // <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/ReentrancyGuard.sol> pragma solidity 0.8.16; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require( _status != _ENTERED, "RS:E18" ); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; contract Owner { /// @dev Current owner of this contract. address owner; /// @dev Pending owner of this contract. Set when an ownership transfer is initiated. address pendingOwner; /// @dev Event emitted when an ownership transfer is initiated. event OwnershipTransferInitiated( address indexed previousOwner, address indexed newOwner ); /// @dev Event emmitted when ownership transfer has completed. event OwnershipTransferCompleted( address indexed previousOwner, address indexed newOwner ); constructor() { owner = msg.sender; emit OwnershipTransferCompleted(address(0), owner); } modifier onlyOwner() { require( owner == msg.sender, "RS:E17" ); _; } /// @dev Initiates ownership transfer by setting pendingOwner. function transferOwnership( address newOwner ) external onlyOwner { require( newOwner != address(0), "RS:E1" ); pendingOwner = newOwner; emit OwnershipTransferInitiated(owner, newOwner); } /// @dev Allows pendingOwner to claim ownership. function acceptOwnership( ) external { require( pendingOwner == msg.sender, "RS:E17" ); _transferOwnership(msg.sender); } /// @dev Completes ownership transfer. function _transferOwnership( address newOwner ) internal { address oldOwner = owner; owner = newOwner; emit OwnershipTransferCompleted(oldOwner, newOwner); delete pendingOwner; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; import "./owner.sol"; /// @dev Type of whitelist enum WhitelistType { Keeper, DexAggKeeper, DexAggRouter } /// @dev Data specific to a particular whitelist struct WhitelistData { // Array of whitelisted addresses address [] whitelistedAddressArray; // Keyed by whitelisted address, valued by position in whitelistedAddressArray mapping(address => uint256) whitelistedAddress; } /// @dev Whitelist library containing logic resuable for all whitelists library LibWhitelist { /// @dev Event emitting when a new address has been whitelisted event WhitelistEvent( address keeper, bool whitelisted, WhitelistType indexed whitelistType ); /// @dev add addresses to the whitelist function _addToWhitelist( WhitelistData storage whitelist, address[] memory addresses, WhitelistType whitelistType ) internal { uint256 size = addresses.length; for (uint256 i = 0; i < size; i++) { address keeper = addresses[i]; // Get the position of the address in the whitelist uint256 keeperPosition = whitelist.whitelistedAddress[keeper]; // If it's currently whitelisted if (keeperPosition != 0) { // Skip it and emit the event again for reassurance emit WhitelistEvent(keeper, true, whitelistType); continue; } // Otherwise, it's not currently whitelisted // Get the position of the last whitelisted address uint256 position = whitelist.whitelistedAddressArray.length; // Store the new whitelisted address and position + 1 in the mapping and array whitelist.whitelistedAddress[keeper] = position + 1; whitelist.whitelistedAddressArray.push(keeper); emit WhitelistEvent(keeper, true, whitelistType); } } /// @dev remove addresses from the whitelist function _removeFromWhitelist( WhitelistData storage whitelist, address[] memory addresses, WhitelistType whitelistType ) internal { uint256 size = addresses.length; for (uint256 i = 0; i < size; i++) { address keeper = addresses[i]; // Get the position of the address in the whitelist uint256 keeperPosition = whitelist.whitelistedAddress[keeper]; // If it's not currently whitelisted if (keeperPosition == 0) { // Skip it and emit the event again for reassurance emit WhitelistEvent(keeper, false, whitelistType); continue; } // Otherwise, it's currently whitelisted // We need to remove the keeper from the array // We know that keeper is in the position keeperPosition // Get the length of the array uint256 lastKeeperPosition = whitelist.whitelistedAddressArray.length - 1; // Get the address stored in lastKeeperPosition address lastKeeper = whitelist.whitelistedAddressArray[lastKeeperPosition]; // Set the new lastKeeperPosition // Remember that we store position increased by 1 in the mapping whitelist.whitelistedAddressArray[keeperPosition - 1] = whitelist.whitelistedAddressArray[lastKeeperPosition]; // Update the mapping with the new position of the lastKeeper whitelist.whitelistedAddress[lastKeeper] = keeperPosition; // Update the mapping with zero as the new position of the removed keeper whitelist.whitelistedAddress[keeper] = 0; // Pop the last element of the array whitelist.whitelistedAddressArray.pop(); emit WhitelistEvent(keeper, false, whitelistType); } } } /// @dev Manages all whitelists on this contract contract Whitelist is Owner { using LibWhitelist for WhitelistData; /// @dev Whitelist for Keepers WhitelistData keeperWhitelist; /// @dev Whitelist for DexAgg Keepers WhitelistData dexAggKeeperWhitelist; /// @dev Whitelist for DexAgg Routers WhitelistData dexAggRouterWhitelist; /// @dev The address of the next whitelist in the link. address public nextLinkedWhitelist; constructor() { // initialize as the null address, meaning this is the newest address in the chain nextLinkedWhitelist = address(0); } /// @dev Set the next whitelist in the link function setNextLinkedWhitelist( address newNextLinkedWhitelist ) external onlyOwner { nextLinkedWhitelist = newNextLinkedWhitelist; } /// @dev Get current keeper whitelist function getKeeperWhitelist() public view returns (address[] memory) { return keeperWhitelist.whitelistedAddressArray; } /// @dev Get current dex agg keeper whitelist function getDexAggKeeperWhitelist() public view returns (address[] memory) { return dexAggKeeperWhitelist.whitelistedAddressArray; } /// @dev Get current dex agg router whitelist function getDexAggRouterWhitelist() public view returns (address[] memory) { return dexAggRouterWhitelist.whitelistedAddressArray; } /// @dev Get the position of this address in the whitelist. /// If this address is not whitelisted, it will return a zero. function getKeeperWhitelistPosition__2u3w( address keeper ) public view returns (uint256 keeperWhitelistPosition) { keeperWhitelistPosition = keeperWhitelist.whitelistedAddress[keeper]; } /// @dev Get the position of this address in the whitelist. /// If this address is not whitelisted, it will return a zero. function getDexAggKeeperWhitelistPosition_IkFc( address keeper ) public view returns (uint256 dexAggKeeperWhitelistPosition) { dexAggKeeperWhitelistPosition = dexAggKeeperWhitelist.whitelistedAddress[keeper]; } /// @dev Get the position of this address in the whitelist. /// If this address is not whitelisted, it will return a zero. function getDexAggRouterWhitelistPosition_ZgLC( address router ) public view returns (uint256 dexAggRouterWhitelistPosition) { dexAggRouterWhitelistPosition = dexAggRouterWhitelist.whitelistedAddress[router]; } /// @dev Update the whitelist status of these addresses function whitelistKeepers( address[] memory addresses, bool value ) external onlyOwner { if (value) { keeperWhitelist._addToWhitelist(addresses, WhitelistType.Keeper); } else { keeperWhitelist._removeFromWhitelist(addresses, WhitelistType.Keeper); } } /// @dev Update the whitelist status of these addresses function whitelistDexAggKeepers( address[] memory addresses, bool value ) external onlyOwner { if (value) { dexAggKeeperWhitelist._addToWhitelist(addresses, WhitelistType.DexAggKeeper); } else { dexAggKeeperWhitelist._removeFromWhitelist(addresses, WhitelistType.DexAggKeeper); } } /// @dev Update the whitelist status of these addresses function whitelistDexAggRouters( address[] memory addresses, bool value ) external onlyOwner { if (value) { dexAggRouterWhitelist._addToWhitelist(addresses, WhitelistType.DexAggRouter); } else { dexAggRouterWhitelist._removeFromWhitelist(addresses, WhitelistType.DexAggRouter); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./owner.sol"; contract AssetManagement is Owner { using Address for address; using SafeERC20 for IERC20; /// @dev Event emmitted when assets are withdrawn. event Withdraw( address asset, uint256 amount ); /// @dev Withdraw Ether from this contract. function withdrawEther_wEuX( uint256 amount ) external onlyOwner { (bool success, ) = msg.sender.call{value: amount}(""); require( success, "RS:E0" ); emit Withdraw(address(0), amount); } /// @dev Withdraw tokens from this contract. function withdrawToken_14u2( address token, uint256 amount ) external onlyOwner { require( token != address(0), "RS:E1" ); IERC20(token).safeTransfer(msg.sender, amount); emit Withdraw(token, amount); } /// @dev Manually set allowance for a given token & spender pair. /// Allowances for this contract are managed via DexAggSwaps, /// but any manual intervention is performed with this function. function manuallySetAllowances( address spender, IERC20[] memory tokens, uint256[] memory values ) external onlyOwner { require( spender != address(0), "RS:E1" ); for (uint256 i; i < tokens.length;) { require( address(tokens[i]) != address(0), "RS:E1" ); tokens[i].approve(spender, values[i]); // Gas optimization unchecked { ++i; } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./signing.sol"; import "./utils.sol"; contract OrderUtils is Signing { /// @dev Highest bit of a uint256, used to flag cancelled orders. uint256 private constant HIGH_BIT = 1 << 255; /// @dev Keyed by orderHash, valued by order's filled amount in makerTokens paid by the maker. mapping(bytes32 => uint256) public makerAmountFilled; /// @dev Order data containing a signed commitment a user made to swap tokens. struct Order { // items contained within TYPEHASH_ORDER address maker; address makerToken; address takerToken; uint256 makerAmount; uint256 takerAmountMin; uint256 takerAmountDecayRate; uint256 data; // items NOT contained within TYPEHASH_ORDER bytes signature; } /// @dev Status of an order depending on various events that play out. enum OrderStatus { Invalid, Fillable, Filled, Canceled, Expired } /// @dev Info about an order's status and general fillability. struct OrderInfo { bytes32 orderHash; OrderStatus status; uint256 makerFilledAmount; } /// @dev Event emitted when an order is filled. event Fill( address maker, address taker, address makerToken, address takerToken, uint256 makerAmountFilled, uint256 takerAmountFilled, bytes32 orderHash ); /// @dev Event emitted when an order is canceled. event OrderCancelled( bytes32 orderHash, address maker ); constructor() { } /// @dev Update the makerAmountFilled for the order being processed in a swap. function _updateMakerAmountFilled( uint256 makerAmount, bytes32 orderHash, uint256 makerAmountToSpend, bool partiallyFillable ) internal { // Update the fillAmount to prevent replay attacks // differentiate between partial fills and not allowing partial fills if (partiallyFillable) { uint256 newMakerAmountFilled = makerAmountFilled[orderHash] + makerAmountToSpend; // newMakerAmountFilled must be valid require( newMakerAmountFilled <= makerAmount, "RS:E2" ); makerAmountFilled[orderHash] = newMakerAmountFilled; } else { // makerAmount must be valid require( makerAmountToSpend == makerAmount, "RS:E3" ); // order must not already be filled require( makerAmountFilled[orderHash] == 0, "RS:E4" ); // Since partial fills are not allowed, we must set this to the order's full amount makerAmountFilled[orderHash] = makerAmount; } } /// @dev Get relevant order information to determine fillability of many orders. function getOrderRelevantStatuses( Order[] calldata orders ) external view returns ( OrderInfo[] memory orderInfos, uint256[] memory makerAmountsFillable, bool[] memory isSignatureValids ) { uint256 ordersLength = orders.length; orderInfos = new OrderInfo[](ordersLength); makerAmountsFillable = new uint256[](ordersLength); isSignatureValids = new bool[](ordersLength); for (uint256 i; i < ordersLength;) { // try/catches can only be used for external funciton calls try this.getOrderRelevantStatus(orders[i]) returns ( OrderInfo memory orderInfo, uint256 makerAmountFillable, bool isSignatureValid ) { orderInfos[i] = orderInfo; makerAmountsFillable[i] = makerAmountFillable; isSignatureValids[i] = isSignatureValid; } catch {} // Gas optimization unchecked { ++i; } } } /// @dev Get relevant order information to determine fillability of an order. // This function must be public because it's being called in a try catch above. function getOrderRelevantStatus( Order calldata order ) external view returns ( OrderInfo memory orderInfo, uint256 makerAmountFillable, bool isSignatureValid ) { bytes32 orderHash = getOrderHash(order); LibData.MakerData memory makerData = _decodeData(order.data, orderHash); return _validateAndGetOrderRelevantStatus(order, orderHash, makerData, false, true); } /// @dev Validate an order's signature and get relevant order information to determine fillability of an order. /// Depending on what's calling this function, we may want to revert on a failure. /// For example, if we are swapping and something bad happens /// we absolutely want to revert /// But, if we are simply making an off chain call to check the order's status and we see a bad status /// we do NOT want to revert because this is critical information we want to return to the off chain function caller /// Or we may want to provide additional data (or not to save on gas cost). function _validateAndGetOrderRelevantStatus( Order calldata order, bytes32 orderHash, LibData.MakerData memory makerData, bool doRevertOnFailure, bool doGetActualFillableMakerAmount ) internal view returns ( OrderInfo memory orderInfo, uint256 makerAmountFillable, bool isSignatureValid ) { // Tokens must be different require( order.makerToken != order.takerToken, "RS:E5" ); // Set the various parts of orderInfo orderInfo.orderHash = orderHash; orderInfo.makerFilledAmount = makerAmountFilled[orderInfo.orderHash]; // Determine orderInfo.status // The high bit will be set if the order was cancelled if (orderInfo.makerFilledAmount & HIGH_BIT != 0) { orderInfo.status = OrderStatus.Canceled; } // If the order has already been filled to or over the max else if (orderInfo.makerFilledAmount >= order.makerAmount) { orderInfo.status = OrderStatus.Filled; } // Check for expiration else if (makerData.expiry <= block.timestamp) { orderInfo.status = OrderStatus.Expired; } else { // If we've made it this far, the order is fillable orderInfo.status = OrderStatus.Fillable; } // Validate order status // So I have this here that will revert if it's not fillable, but i don't think i'm verifying that it's filled properly. // For example, right now you can doulbe fill an order. I dont think there's anything stopping that require( !doRevertOnFailure || orderInfo.status == OrderStatus.Fillable, "RS:E6" ); // Do not calculate makerAmountFillable internally when swapping, // only calculate it when making external calls checking the status of orders // This is critical because external parties care about this information // but when swapping tokens we do not, and not calling this saves a lot of gas // If when swapping tokens, the transaction were to fail because someone doesn't have an allowance, // we just let it fail and bubble up an exception elsewhere, this is a great gas optimization if (doGetActualFillableMakerAmount) { makerAmountFillable = _getMakerAmountFillable(order, orderInfo); } // Validate order signature against the signer address signer = _recoverOrderSignerFromOrderHash(orderInfo.orderHash, makerData.signingScheme, order.signature); // Order signer must be either the order's maker or the maker's valid signer // Gas optimization // We fist compare the order.maker and signer, before considering calling isValidOrderSigner() // isValidOrderSigner will read from storage which incurs a large gas cost isSignatureValid = signer != address(0) && ( (order.maker == signer) || isValidOrderSigner(order.maker, signer) ); require( !doRevertOnFailure || isSignatureValid, "RS:E7" ); } /// @dev Calculate the actual order fillability based on maker allowance, balances, etc function _getMakerAmountFillable( Order calldata order, OrderInfo memory orderInfo ) private view returns (uint256 makerAmountFillable) { if (orderInfo.status != OrderStatus.Fillable) { // Not fillable return 0; } if (order.makerAmount == 0) { // Empty order return 0; } // It is critical to have already returned above if the order is NOT fillable // because certain statuses like the canceled status modifies the makerFilledAmount value // which would mess up the below logic. // So we must not proceed with the below logic if any bits in makerFilledAmount // have been set by order cancels or something similiar // Get the fillable maker amount based on the order quantities and previously filled amount makerAmountFillable = order.makerAmount - orderInfo.makerFilledAmount; // Clamp it to the amount of maker tokens we can spend on behalf of the maker makerAmountFillable = Math.min( makerAmountFillable, _getSpendableERC20BalanceOf(IERC20(order.makerToken), order.maker) ); } /// @dev Get spendable balance considering allowance. function _getSpendableERC20BalanceOf( IERC20 token, address owner ) internal view returns (uint256 spendableERC20BalanceOf) { spendableERC20BalanceOf = Math.min( token.allowance(owner, address(this)), token.balanceOf(owner) ); } /// @dev Decode order data into its individual components. function _decodeData( uint256 data, bytes32 orderHash ) internal pure returns (LibData.MakerData memory makerData) { // Bits // 0 -> 63 = begin // 64 -> 127 = expiry // 128 = partiallyFillable // 129 -> 130 = signingScheme // 131 -> ... = reserved, must be zero uint256 begin = uint256(uint64(data)); uint256 expiry = uint256(uint64(data >> 64)); bool partiallyFillable = data & 0x100000000000000000000000000000000 != 0; // NOTE: Take advantage of the fact that Solidity will revert if the // following expression does not produce a valid enum value. This means // we check here that the leading reserved bits must be 0. LibSignatures.Scheme signingScheme = LibSignatures.Scheme(data >> 129); // Do not allow orders where begin comes after expiry // This doesn't make sense on a UI/UX level and leads to exceptions with our logic require( expiry >= begin, "RS:E27" ); // Measure maker's pre-trade balance makerData = LibData.MakerData( orderHash, 0, 0, begin, expiry, partiallyFillable, signingScheme ); } /// @dev Cancel multiple orders. The caller must be the maker or a valid order signer. /// Silently succeeds if the order has already been cancelled. function cancelOrders__tYNw( Order[] calldata orders ) external { for (uint256 i; i < orders.length;) { // Must be either the order's maker or the maker's valid signer if (orders[i].maker != msg.sender && !isValidOrderSigner(orders[i].maker, msg.sender)) { revert("RS:E20"); } bytes32 orderHash = getOrderHash(orders[i]); // Set the high bit on the makerAmountFilled to indicate a cancel. // It's okay to cancel twice. makerAmountFilled[orderHash] |= HIGH_BIT; emit OrderCancelled(orderHash, orders[i].maker); // Gas optimization unchecked { ++i; } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.16; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; library LibSignatures { /// @dev Signing scheme used for recovering signers from signatures enum Scheme { Eip712, EthSign, Eip1271, PreSign } } library LibData { /// @dev Data specific to this contract. struct ContractData { uint256 surplusTokenBalance_before; uint256 surplusTokenBalance_after; uint256 otherTokenBalance_before; } /// @dev Data specific to a maker. struct MakerData { // Params we calculate bytes32 orderHash; uint256 takerTokenBalance_before; uint256 takerTokenBalance_after; // Params extracted from order.data uint256 begin; uint256 expiry; bool partiallyFillable; LibSignatures.Scheme signingScheme; } } library LibSwap { /// @dev DexAgg swap calldata. struct DexAggSwap { address router; bytes callData; address approveToken; uint256 approvalAmount; } /// @dev Metadata regarding the swap and how to handle surplus struct MetaData { address surplusToken; uint256 surplusAmountWithheld; address otherToken; bool surplusTokenIsSwapTakerToken; TakerTokenDistributionType takerTokenDistributionType; uint256 surplusProtectionThreshold; } /// @dev How to handle swap takerToken distribution enum TakerTokenDistributionType { Even, Custom } } library LibBytes { /// @dev Convert bytes to uint256 function toUint256( bytes memory bytesToConvert, uint256 start ) internal pure returns (uint256 convertedInt) { require( bytesToConvert.length >= start + 32, "RS:E11" ); assembly { convertedInt := mload(add(add(bytesToConvert, 0x20), start)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "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"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(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) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(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) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason 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 { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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); }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity 0.8.16; import "./orderUtils.sol"; import "./eip1271.sol"; import "./utils.sol"; abstract contract Signing { /// @dev Name of contract. string private constant CONTRACT_NAME = "Rook Swap"; /// @dev Version of contract. string private constant CONTRACT_VERSION = "0.1.0"; /// @dev The EIP-712 typehash for the contract's domain. bytes32 private constant TYPEHASH_DOMAIN = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /// @dev The EIP-712 typehash for the Order struct. bytes32 private constant TYPEHASH_ORDER = keccak256("Order(address maker,address makerToken,address takerToken,uint256 makerAmount,uint256 takerAmountMin,uint256 takerAmountDecayRate,uint256 data)"); /// @dev Storage indicating whether or not an orderHash has been pre signed mapping(bytes32 => bool) public preSign; /// @dev Event that is emitted when an account either pre-signs an order or revokes an existing pre-signature. event PreSign( bytes32 orderHash, bool signed ); /// @dev The length of any signature from an externally owned account. uint256 private constant ECDSA_SIGNATURE_LENGTH = 65; /// @dev Domain separator. bytes32 immutable domainSeparator; /// @dev Contains a mapping of EOA signers who are registered to sign on behalf of a maker address. // Maker can be a contract, this enables any contract to be a maker even if they don't have a private key. // Keyed by maker and valued by mapping which is keyed by signer and valued by bool representing registration status. mapping(address => mapping(address => bool)) orderSignerRegistry; /// @dev Event emitted when a new registration is made to orderSignerRegistry. event OrderSignerRegistered( address maker, address signer, bool allowed ); constructor() { domainSeparator = keccak256( abi.encode( TYPEHASH_DOMAIN, keccak256(bytes(CONTRACT_NAME)), keccak256(bytes(CONTRACT_VERSION)), _getChainId(), address(this) ) ); } /// @dev Gets the chainId. function _getChainId( ) private view returns (uint256 chainId) { assembly { chainId := chainid() } } /// @dev Calculates an order's hash. function getOrderHash( OrderUtils.Order calldata order ) public view returns (bytes32 orderHash) { orderHash = keccak256( abi.encodePacked( "\x19\x01", domainSeparator, keccak256(abi.encode( TYPEHASH_ORDER, order.maker, order.makerToken, order.takerToken, order.makerAmount, order.takerAmountMin, order.takerAmountDecayRate, order.data) ) ) ); } /// @dev Recovers an order's signer from the specified order and signature. /// @param orderHash The computed order hash for recovering a signature. /// @param signingScheme The signing scheme. /// @param encodedSignature The signature bytes. /// @return signer The recovered address from the specified signature, or address(0) if signature is invalid (for 1271 and presign only). /// We are not reverting if signer == address(0) in this function, that responsibility is on the function caller function _recoverOrderSignerFromOrderHash( bytes32 orderHash, LibSignatures.Scheme signingScheme, bytes calldata encodedSignature ) internal view returns (address signer) { if (signingScheme == LibSignatures.Scheme.Eip712) { signer = _ecdsaRecover(orderHash, encodedSignature); } else if (signingScheme == LibSignatures.Scheme.EthSign) { // The signed message is encoded as: // `"\x19Ethereum Signed Message:\n" || length || data`, where // the length is a constant (32 bytes) and the data is defined as: // `orderHash`. signer = _ecdsaRecover( keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", orderHash ) ), encodedSignature); } else if (signingScheme == LibSignatures.Scheme.Eip1271) { // Use assembly to read the verifier address from the encoded // signature bytes. // solhint-disable-next-line no-inline-assembly assembly { // signer = address(encodedSignature[0:20]) signer := shr(96, calldataload(encodedSignature.offset)) } bytes calldata _signature = encodedSignature[20:]; // Set signer to address(0) instead of reverting if isValidSignature fails. // We have to use a try/catch here in case the verifier's implementation of isValidSignature reverts when false // But we cannot rely only on that, because it may return a with a non 1271 magic number instead of reverting. try EIP1271Verifier(signer).isValidSignature(orderHash, _signature) returns (bytes4 magicValue) { // Check if isValidSignature return matches the 1271 magic value spec bool isValid = (magicValue == LibERC1271.MAGICVALUE); // If not, set signer to address(0) assembly { let mask := mul(isValid, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) signer := and(signer, mask) } } catch { signer = address(0); } } else // signingScheme == Scheme.PreSign { assembly { // signer = address(encodedSignature[0:20]) signer := shr(96, calldataload(encodedSignature.offset)) } bool isValid = preSign[orderHash]; assembly { let mask := mul(isValid, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) signer := and(signer, mask) } } return signer; } /// @dev Perform an ECDSA recover for the specified message and calldata /// signature. /// /// The signature is encoded by tighyly packing the following struct: /// ``` /// struct EncodedSignature { /// bytes32 r; /// bytes32 s; /// uint8 v; /// } /// ``` /// @param message The signed message. /// @param encodedSignature The encoded signature. /// @return signer The recovered address from the specified signature. function _ecdsaRecover( bytes32 message, bytes calldata encodedSignature ) internal pure returns (address signer) { require( encodedSignature.length == ECDSA_SIGNATURE_LENGTH, "RS:E8" ); bytes32 r; bytes32 s; uint8 v; // NOTE: Use assembly to efficiently decode signature data. // solhint-disable-next-line no-inline-assembly assembly { // r = uint256(encodedSignature[0:32]) r := calldataload(encodedSignature.offset) // s = uint256(encodedSignature[32:64]) s := calldataload(add(encodedSignature.offset, 32)) // v = uint8(encodedSignature[64]) v := shr(248, calldataload(add(encodedSignature.offset, 64))) } signer = ecrecover(message, v, r, s); } /// @dev Sets presign signatures for a batch of specified orders. /// /// @param orders The order data of the orders to pre-sign. /// @param signed Boolean indicating whether to pre-sign or cancel pre-signature. function setPreSigns_weQh( OrderUtils.Order[] calldata orders, bool signed ) external { for (uint256 i; i < orders.length;) { // Must be either the order's maker or the maker's valid signer require( (orders[i].maker == msg.sender) || isValidOrderSigner(orders[i].maker, msg.sender), "RS:E16" ); bytes32 orderHash = getOrderHash(orders[i]); preSign[orderHash] = signed; emit PreSign(orderHash, signed); // Gas optimization unchecked { ++i; } } } /// @dev Checks if a given address is registered to sign on behalf of a maker address. /// @param maker The maker address encoded in an order (can be a contract). /// @param signer The address that is providing a signature. function isValidOrderSigner( address maker, address signer ) public view returns (bool isValid) { isValid = orderSignerRegistry[maker][signer]; } /// @dev Register a signer who can sign on behalf of msg.sender (msg.sender can be a contract). /// @param signer The address from which you plan to generate signatures. /// @param allowed True to register, false to unregister. function registerAllowedOrderSigner( address signer, bool allowed ) external { require( signer != address(0), "RS:E1" ); orderSignerRegistry[msg.sender][signer] = allowed; emit OrderSignerRegistered(msg.sender, signer, allowed); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { 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; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity 0.8.16; library LibERC1271 { /// @dev Value returned by a call to `isValidSignature` if the signature /// was verified successfully. The value is defined in EIP-1271 as: /// bytes4(keccak256("isValidSignature(bytes32,bytes)")) bytes4 internal constant MAGICVALUE = 0x1626ba7e; } /// @title EIP1271 Interface /// @dev Standardized interface for an implementation of smart contract /// signatures as described in EIP-1271. The code that follows is identical to /// the code in the standard with the exception of formatting and syntax /// changes to adapt the code to our Solidity version. interface EIP1271Verifier { /// @dev Should return whether the signature provided is valid for the /// provided data /// @param _hash Hash of the data to be signed /// @param _signature Signature byte array associated with _data /// /// MUST return the bytes4 magic value 0x1626ba7e when function passes. /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for /// solc > 0.5) /// MUST allow external calls /// function isValidSignature( bytes32 _hash, bytes memory _signature ) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"address","name":"makerToken","type":"address"},{"indexed":false,"internalType":"address","name":"takerToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"makerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"Fill","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"maker","type":"address"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"OrderSignerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"signed","type":"bool"}],"name":"PreSign","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"}],"name":"cancelOrders__tYNw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDexAggKeeperWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"}],"name":"getDexAggKeeperWhitelistPosition_IkFc","outputs":[{"internalType":"uint256","name":"dexAggKeeperWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDexAggRouterWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"getDexAggRouterWhitelistPosition_ZgLC","outputs":[{"internalType":"uint256","name":"dexAggRouterWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKeeperWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"}],"name":"getKeeperWhitelistPosition__2u3w","outputs":[{"internalType":"uint256","name":"keeperWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order","name":"order","type":"tuple"}],"name":"getOrderHash","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order","name":"order","type":"tuple"}],"name":"getOrderRelevantStatus","outputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"enum OrderUtils.OrderStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"makerFilledAmount","type":"uint256"}],"internalType":"struct OrderUtils.OrderInfo","name":"orderInfo","type":"tuple"},{"internalType":"uint256","name":"makerAmountFillable","type":"uint256"},{"internalType":"bool","name":"isSignatureValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"}],"name":"getOrderRelevantStatuses","outputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"enum OrderUtils.OrderStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"makerFilledAmount","type":"uint256"}],"internalType":"struct OrderUtils.OrderInfo[]","name":"orderInfos","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsFillable","type":"uint256[]"},{"internalType":"bool[]","name":"isSignatureValids","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"}],"name":"isValidOrderSigner","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"makerAmountFilled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"manuallySetAllowances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextLinkedWhitelist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"preSign","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"registerAllowedOrderSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newNextLinkedWhitelist","type":"address"}],"name":"setNextLinkedWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"bool","name":"signed","type":"bool"}],"name":"setPreSigns_weQh","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsToSpend","type":"uint256[]"},{"internalType":"uint256[]","name":"makerWeights","type":"uint256[]"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"address","name":"approveToken","type":"address"},{"internalType":"uint256","name":"approvalAmount","type":"uint256"}],"internalType":"struct LibSwap.DexAggSwap","name":"swap","type":"tuple"},{"internalType":"uint256[]","name":"takerTokenDistributions","type":"uint256[]"},{"components":[{"internalType":"address","name":"surplusToken","type":"address"},{"internalType":"uint256","name":"surplusAmountWithheld","type":"uint256"},{"internalType":"address","name":"otherToken","type":"address"},{"internalType":"bool","name":"surplusTokenIsSwapTakerToken","type":"bool"},{"internalType":"enum LibSwap.TakerTokenDistributionType","name":"takerTokenDistributionType","type":"uint8"},{"internalType":"uint256","name":"surplusProtectionThreshold","type":"uint256"}],"internalType":"struct LibSwap.MetaData","name":"metaData","type":"tuple"}],"name":"swapDexAggKeeper_8B77","outputs":[{"internalType":"uint256","name":"surplusAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsToSpend","type":"uint256[]"},{"internalType":"address","name":"keeperTaker","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapKeeper__oASr","outputs":[{"internalType":"bytes","name":"keeperReturn","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistDexAggKeepers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistDexAggRouters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistKeepers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawEther_wEuX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken_14u2","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b506001600081815581546001600160a01b031916339081179092556040517fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d6908290a3600980546001600160a01b0319168155604080518082018252918252680526f6f6b20537761760bc1b602092830152805180820190915260058152640302e312e360dc1b9101527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f76d2af50352d880308e542db11640dafde4ddfa14a4f853259c40706d845b9227faa7cdbe2cce2ec7b606b0e199ddd9b264a6e645e767fb8479a7917dcd1b8693f46604080516020810195909552840192909252606083015260808201523060a082015260c00160408051601f198184030181529190528051602090910120608052608051615b606200015a6000396000611d880152615b606000f3fe608060405234801561001057600080fd5b50600436106101985760003560e01c806361502535116100f9578063982b012311610097578063d0a46b9b11610071578063d0a46b9b14610462578063e567b86914610484578063ea7faa6114610497578063f2fde38b146104aa57600080fd5b8063982b0123146103ea5780639bb6cbd4146103fd578063aaf4f89d1461044257600080fd5b80636e4e952d116100d35780636e4e952d146103bf57806379ba5097146103d257806380c45f1e146103da578063873d0203146103e257600080fd5b8063615025351461035057806361e47ccf146103635780636b52a4a81461037657600080fd5b8060781161015a578063381e360c1161013d578063381e360c146102d357806346c02d7a146102f55780634c93f4ec146103285780635a73dfe31461033b57600080fd5b80607814610254578060c81461028a578060fa1461029d57600080fd5b80604a1161017f5780604a146101eb578060601461020b578060751461024157600080fd5b80600b1461019d5780601c146101b257806036146101c5575b600080fd5b6101b06101ab366004614b4c565b6104bd565b005b6101b06101c0366004614ba3565b61069c565b6101d86101d3366004614be6565b61080e565b6040519081526020015b60405180910390f35b6101fe6101f9366004614d0e565b610ec5565b6040516101e29190614e58565b6101d8610219366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b6101b061024f366004614e88565b6110b2565b6101d8610262366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b6101b0610298366004614eca565b611269565b6101d86102ab366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526006602052604090205490565b6102e66102e1366004614e88565b6113d4565b6040516101e293929190614fb2565b610318610303366004614ba3565b600a6020526000908152604090205460ff1681565b60405190151581526020016101e2565b6101b0610336366004615115565b61160a565b6103436116af565b6040516101e291906151c6565b6101b061035e366004615286565b611721565b6101b0610371366004614e6b565b6119b4565b61031861038436600461535c565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600b6020908152604080832093909416825291909152205460ff1690565b6101b06103cd366004615115565b611a7c565b6101b0611b1d565b610343611ba9565b610343611c19565b6101b06103f8366004615115565b611c89565b60095461041d9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e2565b6101d8610450366004614ba3565b600c6020526000908152604090205481565b610475610470366004615395565b611d2a565b6040516101e2939291906153d1565b6101d8610492366004615395565b611d84565b6101b06104a53660046153f5565b611ee2565b6101b06104b8366004614e6b565b611ff9565b60005b8281101561069657338484838181106104db576104db615423565b90506020028101906104ed9190615452565b6104fb906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610583575061058384848381811061052d5761052d615423565b905060200281019061053f9190615452565b61054d906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600b6020908152604080832033845290915290205460ff1690565b6105ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453136000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600061061785858481811061060557610605615423565b90506020028101906104929190615452565b6000818152600a602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715159081179091558251848152918201529192507fe5132260ede7f3ca4aee317ef8dad1d6ddbd4169e74ea367b7e6964883916587910160405180910390a1506001016104c0565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b604051600090339083908381818185875af1925050503d806000811461075f576040519150601f19603f3d011682016040523d82523d6000602084013e610764565b606091505b50509050806107cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453000000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040805160008152602081018490527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436491015b60405180910390a15050565b600060026000540361087c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b60026000908155338152600660205260409020546000036108f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453232000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109096102626020870187614e6b565b600003610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453330000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109826060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff166109a56020840184614e6b565b73ffffffffffffffffffffffffffffffffffffffff1603610a22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453331000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8a811015610cd1578b8b82818110610a3f57610a3f615423565b9050602002810190610a519190615452565b610a62906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610a856020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610b1257508b8b82818110610ab457610ab4615423565b9050602002810190610ac69190615452565b610ad7906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610afa6020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610b78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453238000000000000000000000000000000000000000000000000000060448201526064016105e5565b8b8b82818110610b8a57610b8a615423565b9050602002810190610b9c9190615452565b610bad906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610bd36060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610c6357508b8b82818110610c0257610c02615423565b9050602002810190610c149190615452565b610c25906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610c4b6060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610cc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453239000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101610a25565b50604080516060810190915260009080610cee6020860186614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190615490565b815260200160008152602001846040016020810190610d9d9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190615490565b905290506000610e408d8d8d8d3061216e565b90506000610e4e88866127c7565b90506000610e6260a08701608088016154a9565b6001811115610e7357610e73614ef6565b03610e8a57610e858e8e8c8c85612809565b610e96565b610e968e8e898961293d565b610ea48e8e8e8e8633612a84565b610eae8386612f81565b60016000559e9d5050505050505050505050505050565b6060600260005403610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b6002600090815573ffffffffffffffffffffffffffffffffffffffff8516815260046020526040902054600003610fc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453231000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000610fd5898989898961216e565b6040517c5700000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff86169060579061102c9033908b908b908a908a90600401615513565b6000604051808303816000875af115801561104b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261109191908101906155e1565b91506110a189898989858a612a84565b506001600055979650505050505050565b60005b8181101561126457338383838181106110d0576110d0615423565b90506020028101906110e29190615452565b6110f0906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff1614158015611126575061112483838381811061052d5761052d615423565b155b1561118d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453230000000000000000000000000000000000000000000000000000060448201526064016105e5565b60006111a484848481811061060557610605615423565b6000818152600c6020526040902080547f800000000000000000000000000000000000000000000000000000000000000017905590507fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527538185858581811061120e5761120e615423565b90506020028101906112209190615452565b61122e906020810190614e6b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a1506001016110b5565b505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146112ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff8216611367576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b61138873ffffffffffffffffffffffffffffffffffffffff831633836131c4565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649101610802565b60608080838067ffffffffffffffff8111156113f2576113f261504a565b60405190808252806020026020018201604052801561145b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816114105790505b5093508067ffffffffffffffff8111156114775761147761504a565b6040519080825280602002602001820160405280156114a0578160200160208202803683370190505b5092508067ffffffffffffffff8111156114bc576114bc61504a565b6040519080825280602002602001820160405280156114e5578160200160208202803683370190505b50915060005b81811015611601573063d0a46b9b88888481811061150b5761150b615423565b905060200281019061151d9190615452565b6040518263ffffffff1660e01b815260040161153991906156bc565b60a060405180830381865afa925050508015611590575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261158d91810190615787565b60015b156115f957828885815181106115a8576115a8615423565b6020026020010181905250818785815181106115c6576115c6615423565b602002602001018181525050808685815181106115e5576115e5615423565b911515602092830291909101909101525050505b6001016114eb565b50509250925092565b60015473ffffffffffffffffffffffffffffffffffffffff16331461168b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b80156116a25761169e6007836002613298565b5050565b61169e6007836002613451565b6060600760000180548060200260200160405190810160405280929190818152602001828054801561171757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575b5050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff1633146117a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff831661181f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b825181101561069657600073ffffffffffffffffffffffffffffffffffffffff1683828151811061185557611855615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036118da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b8281815181106118ec576118ec615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b38584848151811061192257611922615423565b60200260200101516040518363ffffffff1660e01b815260040161196892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6020604051808303816000875af1158015611987573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ab919061580f565b50600101611822565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611b105761169e6005836001613298565b61169e6005836001613451565b60025473ffffffffffffffffffffffffffffffffffffffff163314611b9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b611ba73361371e565b565b606060056000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b606060036000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611d1d5761169e6003836000613298565b61169e6003836000613451565b60408051606081018252600080825260208201819052918101829052908080611d5285611d84565b90506000611d648660c00135836137bd565b9050611d75868383600060016138f9565b94509450945050509193909250565b60007f00000000000000000000000000000000000000000000000000000000000000007f4319db3766093257e119019721ad33761927ac79912abb48d42c37a7fe85fdfd611dd56020850185614e6b565b611de56040860160208701614e6b565b611df56060870160408801614e6b565b866060013587608001358860a001358960c00135604051602001611e6d98979695949392919097885273ffffffffffffffffffffffffffffffffffffffff96871660208901529486166040880152929094166060860152608085015260a084019290925260c083019190915260e08201526101000190565b60405160208183030381529060405280519060200120604051602001611ec59291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b336000818152600b6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016861515908117909155815194855291840192909252908201527f6ea9dbe8b2cc119348716a9220a0742ad62b7884ecb0ff4b32cd508121fd937990606001610802565b60015473ffffffffffffffffffffffffffffffffffffffff16331461207a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff81166120f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600154604051919216907fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a90600090a350565b6060848381146121da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453132000000000000000000000000000000000000000000000000000060448201526064016105e5565b8067ffffffffffffffff8111156121f3576121f361504a565b60405190808252806020026020018201604052801561226157816020015b61224e6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b8152602001906001900390816122115790505b50915060005b818110156127bc5760005b828110156124c257808214158015612317575088888281811061229757612297615423565b90506020028101906122a99190615452565b6122b7906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff168989848181106122df576122df615423565b90506020028101906122f19190615452565b6122ff906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b8015612453575088888281811061233057612330615423565b90506020028101906123429190615452565b612353906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061237b5761237b615423565b905060200281019061238d9190615452565b61239e906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16148061245357508888828181106123cd576123cd615423565b90506020028101906123df9190615452565b6123f0906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061241857612418615423565b905060200281019061242a9190615452565b61243b906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b156124ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453135000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101612272565b5060006124da89898481811061060557610605615423565b905061250d8989848181106124f1576124f1615423565b90506020028101906125039190615452565b60c00135826137bd565b84838151811061251f5761251f615423565b602002602001018190525088888381811061253c5761253c615423565b905060200281019061254e9190615452565b61255f906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a082318a8a8581811061258c5761258c615423565b905060200281019061259e9190615452565b6125ac906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126399190615490565b84838151811061264b5761264b615423565b602002602001015160200181815250506126a689898481811061267057612670615423565b90506020028101906126829190615452565b8286858151811061269557612695615423565b6020026020010151600160006138f9565b50505061274b8989848181106126be576126be615423565b90506020028101906126d09190615452565b6126de906020810190614e6b565b868989868181106126f1576126f1615423565b905060200201358c8c8781811061270a5761270a615423565b905060200281019061271c9190615452565b61272d906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16929190613c4c565b6127b389898481811061276057612760615423565b90506020028101906127729190615452565b606001358289898681811061278957612789615423565b905060200201358786815181106127a2576127a2615423565b602002602001015160a00151613caa565b50600101612267565b505095945050505050565b60006127db6127d58461582c565b83613e3c565b90506127ed60808301606084016158dc565b1561280357612800602083013582615928565b90505b92915050565b8360018190036128a25761289d8686600081811061282957612829615423565b905060200281019061283b9190615452565b612849906020810190614e6b565b838888600081811061285d5761285d615423565b905060200281019061286f9190615452565b612880906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1691906131c4565b612935565b60005b818110156129335761292b8787838181106128c2576128c2615423565b90506020028101906128d49190615452565b6128e2906020810190614e6b565b670de0b6b3a76400008787858181106128fd576128fd615423565b905060200201358661290f919061593b565b6129199190615978565b89898581811061285d5761285d615423565b6001016128a5565b505b505050505050565b82600181116129a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453133000000000000000000000000000000000000000000000000000060448201526064016105e5565b808214612a11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453134000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8181101561293557612a7c868683818110612a3157612a31615423565b9050602002810190612a439190615452565b612a51906020810190614e6b565b858584818110612a6357612a63615423565b9050602002013588888581811061285d5761285d615423565b600101612a14565b60005b8581101561293357868682818110612aa157612aa1615423565b9050602002810190612ab39190615452565b612ac4906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a08231888884818110612af157612af1615423565b9050602002810190612b039190615452565b612b11906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e9190615490565b838281518110612bb057612bb0615423565b602002602001015160400181815250506000838281518110612bd457612bd4615423565b602002602001015160200151848381518110612bf257612bf2615423565b602002602001015160400151612c089190615928565b90506000888884818110612c1e57612c1e615423565b9050602002810190612c309190615452565b60a0013515612cb057612cab898985818110612c4e57612c4e615423565b9050602002810190612c609190615452565b608001358a8a86818110612c7657612c76615423565b9050602002810190612c889190615452565b60a00135878681518110612c9e57612c9e615423565b60200260200101516141c3565b612cd9565b888884818110612cc257612cc2615423565b9050602002810190612cd49190615452565b608001355b9050848381518110612ced57612ced615423565b602002602001015160a0015115612dc157868684818110612d1057612d10615423565b9050602002013581612d22919061593b565b898985818110612d3457612d34615423565b9050602002810190612d469190615452565b612d5490606001358461593b565b1015612dbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453233000000000000000000000000000000000000000000000000000060448201526064016105e5565b612e2b565b80821015612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453234000000000000000000000000000000000000000000000000000060448201526064016105e5565b7fcabd156033bc5efebccd321136638073b3e452c01a38d36cbfc9bdec2ffd0f9d898985818110612e5e57612e5e615423565b9050602002810190612e709190615452565b612e7e906020810190614e6b565b858b8b87818110612e9157612e91615423565b9050602002810190612ea39190615452565b612eb4906040810190602001614e6b565b8c8c88818110612ec657612ec6615423565b9050602002810190612ed89190615452565b612ee9906060810190604001614e6b565b8b8b89818110612efb57612efb615423565b90506020020135878b8a81518110612f1557612f15615423565b602090810291909101810151516040805173ffffffffffffffffffffffffffffffffffffffff998a1681529789169288019290925294871686820152929095166060850152608084015260a083019390935260c082015290519081900360e00190a15050600101612a87565b6000612f906020830183614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015612ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130209190615490565b602084015282516130369060a0840135906159b3565b836020015110156130a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453235000000000000000000000000000000000000000000000000000060448201526064016105e5565b82604001518260400160208101906130bb9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190615490565b10156131b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453236000000000000000000000000000000000000000000000000000060448201526064016105e5565b825160208401516128009190615928565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112649084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261421a565b815160005b8181101561344a5760008482815181106132b9576132b9615423565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260018901909252604090912054909150801561335f5784600281111561330957613309614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050613438565b865461336c8160016159b3565b73ffffffffffffffffffffffffffffffffffffffff841660008181526001808c01602090815260408320949094558b549081018c558b825292902090910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790558560028111156133e5576133e5614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050505b80613442816159c6565b91505061329d565b5050505050565b815160005b8181101561344a57600084828151811061347257613472615423565b6020026020010151905060008660010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003613532578460028111156134dc576134dc614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505061370c565b865460009061354390600190615928565b9050600088600001828154811061355c5761355c615423565b600091825260209091200154895473ffffffffffffffffffffffffffffffffffffffff909116915089908390811061359657613596615423565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16896135c4600186615928565b815481106135d4576135d4615423565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff948516179055838316825260018c0190526040808220869055918616815290812055885489908061364e5761364e6159fe565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558660028111156136b8576136b8614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff87168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505050505b80613716816159c6565b915050613456565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d690600090a35050600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6137fa6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b67ffffffffffffffff80841690604085901c16700100000000000000000000000000000000851615156000608187901c600381111561383b5761383b614ef6565b9050838310156138a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453237000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040518060e00160405280878152602001600081526020016000815260200185815260200184815260200183151581526020018260038111156138ec576138ec614ef6565b9052979650505050505050565b60408051606081018252600080825260208201819052918101919091526000806139296060890160408a01614e6b565b73ffffffffffffffffffffffffffffffffffffffff1661394f60408a0160208b01614e6b565b73ffffffffffffffffffffffffffffffffffffffff16036139cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453500000000000000000000000000000000000000000000000000000060448201526064016105e5565b8683526000878152600c602052604090819020549084018190527f80000000000000000000000000000000000000000000000000000000000000001615613a2f576020830160035b90816004811115613a2757613a27614ef6565b905250613a67565b8760600135836040015110613a4957602083016002613a14565b42866080015111613a5f57602083016004613a14565b600160208401525b841580613a895750600183602001516004811115613a8757613a87614ef6565b145b613aef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453600000000000000000000000000000000000000000000000000000060448201526064016105e5565b8315613b0257613aff8884614326565b91505b825160c0870151600091613b2291613b1d60e08d018d615a2d565b6143a4565b905073ffffffffffffffffffffffffffffffffffffffff811615801590613bce575073ffffffffffffffffffffffffffffffffffffffff8116613b6860208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480613bce5750613bce613b9560208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600b602090815260408083209386168352929052205460ff1690565b9150851580613bda5750815b613c40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453700000000000000000000000000000000000000000000000000000060448201526064016105e5565b50955095509592505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526106969085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613216565b8015613d4a576000838152600c6020526040812054613cca9084906159b3565b905084811115613d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453200000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000848152600c6020526040902055610696565b838214613db3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453300000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000838152600c602052604090205415613e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453400000000000000000000000000000000000000000000000000000060448201526064016105e5565b50506000908152600c6020526040902055565b604082015160009073ffffffffffffffffffffffffffffffffffffffff16613ec0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453139000000000000000000000000000000000000000000000000000060448201526064016105e5565b613ecd6020830183614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff161480613f485750613f156060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff16145b613fae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453332000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040838101518451606086015192517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810193909352169063095ea7b3906044016020604051808303816000875af115801561402f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614053919061580f565b50600080846000015173ffffffffffffffffffffffffffffffffffffffff16600086602001516040516140869190615a92565b60006040518083038185875af1925050503d80600081146140c3576040519150601f19603f3d011682016040523d82523d6000602084013e6140c8565b606091505b509150915061410d82826040518060400160405280601981526020017f63616c6c4461746120657865637574696f6e206661696c6564000000000000008152506145ce565b61411881600061461d565b604086810151875191517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015260006024820152929550169063095ea7b3906044016020604051808303816000875af1158015614196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ba919061580f565b50505092915050565b6000816080015142106141d75760006141fe565b81606001514210156141ed5781606001516141ef565b425b82608001516141fe9190615928565b614208908461593b565b61421290856159b3565b949350505050565b600061427c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661469d9092919063ffffffff16565b805190915015611264578080602001905181019061429a919061580f565b611264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105e5565b600060018260200151600481111561434057614340614ef6565b1461434d57506000612803565b826060013560000361436157506000612803565b6040820151614374906060850135615928565b90506128008161439f61438d6040870160208801614e6b565b61439a6020880188614e6b565b6146b3565b6147e0565b6000808460038111156143b9576143b9614ef6565b036143d0576143c98584846147f6565b9050614212565b60018460038111156143e4576143e4614ef6565b0361443d576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018690526143c990605c016040516020818303038152906040528051906020012084846147f6565b600284600381111561445157614451614ef6565b036145955750813560601c36600061446c8460148188615aa4565b6040517f1626ba7e000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff841690631626ba7e906144c7908a9086908690600401615ace565b602060405180830381865afa92505050801561451e575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261451b91810190615ae8565b60015b61452b576000925061458e565b73ffffffffffffffffffffffffffffffffffffffff7fffffffff00000000000000000000000000000000000000000000000000000000919091167f1626ba7e00000000000000000000000000000000000000000000000000000000140292909216915b5050614212565b506000848152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff60ff90911602823560601c16949350505050565b82156145d957505050565b8151156145e95781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b600061462a8260206159b3565b83511015614694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453131000000000000000000000000000000000000000000000000000060448201526064016105e5565b50016020015190565b606061421284846000856148f6565b9392505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523060248301526000916128009185169063dd62ed3e90604401602060405180830381865afa15801561472c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147509190615490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156147bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061439f9190615490565b60008183106147ef5781612800565b5090919050565b600060418214614862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453800000000000000000000000000000000000000000000000000000060448201526064016105e5565b604080516000815260208082018084528790528583013560f81c92820183905285356060830181905290860135608083018190529092909160019060a0016020604051602081039080840390855afa1580156148c2573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015198975050505050505050565b606082471015614988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105e5565b73ffffffffffffffffffffffffffffffffffffffff85163b614a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e5565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614a2f9190615a92565b60006040518083038185875af1925050503d8060008114614a6c576040519150601f19603f3d011682016040523d82523d6000602084013e614a71565b606091505b5091509150614a81828286614a8c565b979650505050505050565b60608315614a9b5750816146ac565b825115614aab5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b60008083601f840112614af157600080fd5b50813567ffffffffffffffff811115614b0957600080fd5b6020830191508360208260051b8501011115614b2457600080fd5b9250929050565b8015158114614b3957600080fd5b50565b8035614b4781614b2b565b919050565b600080600060408486031215614b6157600080fd5b833567ffffffffffffffff811115614b7857600080fd5b614b8486828701614adf565b9094509250506020840135614b9881614b2b565b809150509250925092565b600060208284031215614bb557600080fd5b5035919050565b600060808284031215614bce57600080fd5b50919050565b600060c08284031215614bce57600080fd5b6000806000806000806000806000806101608b8d031215614c0657600080fd5b8a3567ffffffffffffffff80821115614c1e57600080fd5b614c2a8e838f01614adf565b909c509a5060208d0135915080821115614c4357600080fd5b614c4f8e838f01614adf565b909a50985060408d0135915080821115614c6857600080fd5b614c748e838f01614adf565b909850965060608d0135915080821115614c8d57600080fd5b614c998e838f01614bbc565b955060808d0135915080821115614caf57600080fd5b50614cbc8d828e01614adf565b9094509250614cd090508c60a08d01614bd4565b90509295989b9194979a5092959850565b73ffffffffffffffffffffffffffffffffffffffff81168114614b3957600080fd5b8035614b4781614ce1565b60008060008060008060006080888a031215614d2957600080fd5b873567ffffffffffffffff80821115614d4157600080fd5b614d4d8b838c01614adf565b909950975060208a0135915080821115614d6657600080fd5b614d728b838c01614adf565b909750955060408a01359150614d8782614ce1565b90935060608901359080821115614d9d57600080fd5b818a0191508a601f830112614db157600080fd5b813581811115614dc057600080fd5b8b6020828501011115614dd257600080fd5b60208301945080935050505092959891949750929550565b60005b83811015614e05578181015183820152602001614ded565b50506000910152565b60008151808452614e26816020860160208601614dea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006128006020830184614e0e565b600060208284031215614e7d57600080fd5b81356146ac81614ce1565b60008060208385031215614e9b57600080fd5b823567ffffffffffffffff811115614eb257600080fd5b614ebe85828601614adf565b90969095509350505050565b60008060408385031215614edd57600080fd5b8235614ee881614ce1565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b80518252602081015160058110614f65577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020830152604090810151910152565b600081518084526020808501945080840160005b83811015614fa7578151151587529582019590820190600101614f89565b509495945050505050565b60608082528451828201819052600091906020906080850190828901855b82811015614ff357614fe3848351614f25565b9285019290840190600101614fd0565b50505084810382860152865180825290820192508682019060005b8181101561502a5782518552938301939183019160010161500e565b5050505082810360408401526150408185614f75565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561509c5761509c61504a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156150e9576150e961504a565b604052919050565b600067ffffffffffffffff82111561510b5761510b61504a565b5060051b60200190565b6000806040838503121561512857600080fd5b823567ffffffffffffffff81111561513f57600080fd5b8301601f8101851361515057600080fd5b80356020615165615160836150f1565b6150a2565b82815260059290921b8301810191818101908884111561518457600080fd5b938201935b838510156151ab57843561519c81614ce1565b82529382019390820190615189565b95506151ba9050868201614b3c565b93505050509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561521457835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016151e2565b50909695505050505050565b600082601f83011261523157600080fd5b81356020615241615160836150f1565b82815260059290921b8401810191818101908684111561526057600080fd5b8286015b8481101561527b5780358352918301918301615264565b509695505050505050565b60008060006060848603121561529b57600080fd5b83356152a681614ce1565b925060208481013567ffffffffffffffff808211156152c457600080fd5b818701915087601f8301126152d857600080fd5b81356152e6615160826150f1565b81815260059190911b8301840190848101908a83111561530557600080fd5b938501935b8285101561532c57843561531d81614ce1565b8252938501939085019061530a565b96505050604087013592508083111561534457600080fd5b505061535286828701615220565b9150509250925092565b6000806040838503121561536f57600080fd5b823561537a81614ce1565b9150602083013561538a81614ce1565b809150509250929050565b6000602082840312156153a757600080fd5b813567ffffffffffffffff8111156153be57600080fd5b820161010081850312156146ac57600080fd5b60a081016153df8286614f25565b8360608301528215156080830152949350505050565b6000806040838503121561540857600080fd5b823561541381614ce1565b9150602083013561538a81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183360301811261548657600080fd5b9190910192915050565b6000602082840312156154a257600080fd5b5051919050565b6000602082840312156154bb57600080fd5b8135600281106146ac57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff861681526060602082015283606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85111561556857600080fd5b8460051b8087608085013782018281036080908101604085015261558f90820185876154ca565b98975050505050505050565b600067ffffffffffffffff8211156155b5576155b561504a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000602082840312156155f357600080fd5b815167ffffffffffffffff81111561560a57600080fd5b8201601f8101841361561b57600080fd5b80516156296151608261559b565b81815285602083850101111561563e57600080fd5b61564f826020830160208601614dea565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261568d57600080fd5b830160208101925035905067ffffffffffffffff8111156156ad57600080fd5b803603821315614b2457600080fd5b60208152600082356156cd81614ce1565b73ffffffffffffffffffffffffffffffffffffffff81166020840152506156f660208401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660408401525061571f60408401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e083015261577160e0840184615658565b6101008481015261564f610120850182846154ca565b600080600083850360a081121561579d57600080fd5b60608112156157ab57600080fd5b506040516060810181811067ffffffffffffffff821117156157cf576157cf61504a565b604052845181526020850151600581106157e857600080fd5b602082015260408581015190820152606085015160808601519194509250614b9881614b2b565b60006020828403121561582157600080fd5b81516146ac81614b2b565b60006080823603121561583e57600080fd5b615846615079565b823561585181614ce1565b815260208381013567ffffffffffffffff81111561586e57600080fd5b840136601f82011261587f57600080fd5b803561588d6151608261559b565b81815236848385010111156158a157600080fd5b818484018583013760008483830101528084860152505050506158c660408401614d03565b6040820152606092830135928101929092525090565b6000602082840312156158ee57600080fd5b81356146ac81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612803576128036158f9565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615973576159736158f9565b500290565b6000826159ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115612803576128036158f9565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036159f7576159f76158f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6257600080fd5b83018035915067ffffffffffffffff821115615a7d57600080fd5b602001915036819003821315614b2457600080fd5b60008251615486818460208701614dea565b60008085851115615ab457600080fd5b83861115615ac157600080fd5b5050820193919092039150565b83815260406020820152600061564f6040830184866154ca565b600060208284031215615afa57600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146146ac57600080fdfea2646970667358221220cedcf2d3206d2775a915263f0bbcc407cafccdf4dea9439be2437f1bcb0e330064736f6c63430008100033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101985760003560e01c806361502535116100f9578063982b012311610097578063d0a46b9b11610071578063d0a46b9b14610462578063e567b86914610484578063ea7faa6114610497578063f2fde38b146104aa57600080fd5b8063982b0123146103ea5780639bb6cbd4146103fd578063aaf4f89d1461044257600080fd5b80636e4e952d116100d35780636e4e952d146103bf57806379ba5097146103d257806380c45f1e146103da578063873d0203146103e257600080fd5b8063615025351461035057806361e47ccf146103635780636b52a4a81461037657600080fd5b8060781161015a578063381e360c1161013d578063381e360c146102d357806346c02d7a146102f55780634c93f4ec146103285780635a73dfe31461033b57600080fd5b80607814610254578060c81461028a578060fa1461029d57600080fd5b80604a1161017f5780604a146101eb578060601461020b578060751461024157600080fd5b80600b1461019d5780601c146101b257806036146101c5575b600080fd5b6101b06101ab366004614b4c565b6104bd565b005b6101b06101c0366004614ba3565b61069c565b6101d86101d3366004614be6565b61080e565b6040519081526020015b60405180910390f35b6101fe6101f9366004614d0e565b610ec5565b6040516101e29190614e58565b6101d8610219366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b6101b061024f366004614e88565b6110b2565b6101d8610262366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b6101b0610298366004614eca565b611269565b6101d86102ab366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526006602052604090205490565b6102e66102e1366004614e88565b6113d4565b6040516101e293929190614fb2565b610318610303366004614ba3565b600a6020526000908152604090205460ff1681565b60405190151581526020016101e2565b6101b0610336366004615115565b61160a565b6103436116af565b6040516101e291906151c6565b6101b061035e366004615286565b611721565b6101b0610371366004614e6b565b6119b4565b61031861038436600461535c565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600b6020908152604080832093909416825291909152205460ff1690565b6101b06103cd366004615115565b611a7c565b6101b0611b1d565b610343611ba9565b610343611c19565b6101b06103f8366004615115565b611c89565b60095461041d9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e2565b6101d8610450366004614ba3565b600c6020526000908152604090205481565b610475610470366004615395565b611d2a565b6040516101e2939291906153d1565b6101d8610492366004615395565b611d84565b6101b06104a53660046153f5565b611ee2565b6101b06104b8366004614e6b565b611ff9565b60005b8281101561069657338484838181106104db576104db615423565b90506020028101906104ed9190615452565b6104fb906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610583575061058384848381811061052d5761052d615423565b905060200281019061053f9190615452565b61054d906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600b6020908152604080832033845290915290205460ff1690565b6105ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453136000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600061061785858481811061060557610605615423565b90506020028101906104929190615452565b6000818152600a602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715159081179091558251848152918201529192507fe5132260ede7f3ca4aee317ef8dad1d6ddbd4169e74ea367b7e6964883916587910160405180910390a1506001016104c0565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b604051600090339083908381818185875af1925050503d806000811461075f576040519150601f19603f3d011682016040523d82523d6000602084013e610764565b606091505b50509050806107cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453000000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040805160008152602081018490527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436491015b60405180910390a15050565b600060026000540361087c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b60026000908155338152600660205260409020546000036108f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453232000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109096102626020870187614e6b565b600003610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453330000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109826060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff166109a56020840184614e6b565b73ffffffffffffffffffffffffffffffffffffffff1603610a22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453331000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8a811015610cd1578b8b82818110610a3f57610a3f615423565b9050602002810190610a519190615452565b610a62906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610a856020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610b1257508b8b82818110610ab457610ab4615423565b9050602002810190610ac69190615452565b610ad7906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610afa6020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610b78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453238000000000000000000000000000000000000000000000000000060448201526064016105e5565b8b8b82818110610b8a57610b8a615423565b9050602002810190610b9c9190615452565b610bad906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610bd36060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610c6357508b8b82818110610c0257610c02615423565b9050602002810190610c149190615452565b610c25906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610c4b6060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610cc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453239000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101610a25565b50604080516060810190915260009080610cee6020860186614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190615490565b815260200160008152602001846040016020810190610d9d9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190615490565b905290506000610e408d8d8d8d3061216e565b90506000610e4e88866127c7565b90506000610e6260a08701608088016154a9565b6001811115610e7357610e73614ef6565b03610e8a57610e858e8e8c8c85612809565b610e96565b610e968e8e898961293d565b610ea48e8e8e8e8633612a84565b610eae8386612f81565b60016000559e9d5050505050505050505050505050565b6060600260005403610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b6002600090815573ffffffffffffffffffffffffffffffffffffffff8516815260046020526040902054600003610fc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453231000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000610fd5898989898961216e565b6040517c5700000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff86169060579061102c9033908b908b908a908a90600401615513565b6000604051808303816000875af115801561104b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261109191908101906155e1565b91506110a189898989858a612a84565b506001600055979650505050505050565b60005b8181101561126457338383838181106110d0576110d0615423565b90506020028101906110e29190615452565b6110f0906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff1614158015611126575061112483838381811061052d5761052d615423565b155b1561118d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453230000000000000000000000000000000000000000000000000000060448201526064016105e5565b60006111a484848481811061060557610605615423565b6000818152600c6020526040902080547f800000000000000000000000000000000000000000000000000000000000000017905590507fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527538185858581811061120e5761120e615423565b90506020028101906112209190615452565b61122e906020810190614e6b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a1506001016110b5565b505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146112ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff8216611367576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b61138873ffffffffffffffffffffffffffffffffffffffff831633836131c4565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649101610802565b60608080838067ffffffffffffffff8111156113f2576113f261504a565b60405190808252806020026020018201604052801561145b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816114105790505b5093508067ffffffffffffffff8111156114775761147761504a565b6040519080825280602002602001820160405280156114a0578160200160208202803683370190505b5092508067ffffffffffffffff8111156114bc576114bc61504a565b6040519080825280602002602001820160405280156114e5578160200160208202803683370190505b50915060005b81811015611601573063d0a46b9b88888481811061150b5761150b615423565b905060200281019061151d9190615452565b6040518263ffffffff1660e01b815260040161153991906156bc565b60a060405180830381865afa925050508015611590575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261158d91810190615787565b60015b156115f957828885815181106115a8576115a8615423565b6020026020010181905250818785815181106115c6576115c6615423565b602002602001018181525050808685815181106115e5576115e5615423565b911515602092830291909101909101525050505b6001016114eb565b50509250925092565b60015473ffffffffffffffffffffffffffffffffffffffff16331461168b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b80156116a25761169e6007836002613298565b5050565b61169e6007836002613451565b6060600760000180548060200260200160405190810160405280929190818152602001828054801561171757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575b5050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff1633146117a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff831661181f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b825181101561069657600073ffffffffffffffffffffffffffffffffffffffff1683828151811061185557611855615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036118da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b8281815181106118ec576118ec615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b38584848151811061192257611922615423565b60200260200101516040518363ffffffff1660e01b815260040161196892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6020604051808303816000875af1158015611987573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ab919061580f565b50600101611822565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611b105761169e6005836001613298565b61169e6005836001613451565b60025473ffffffffffffffffffffffffffffffffffffffff163314611b9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b611ba73361371e565b565b606060056000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b606060036000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611d1d5761169e6003836000613298565b61169e6003836000613451565b60408051606081018252600080825260208201819052918101829052908080611d5285611d84565b90506000611d648660c00135836137bd565b9050611d75868383600060016138f9565b94509450945050509193909250565b60007f235bf3c2a237ed301bc29f4edaf2b675dcd12f0dea94469169802cafaaee3f307f4319db3766093257e119019721ad33761927ac79912abb48d42c37a7fe85fdfd611dd56020850185614e6b565b611de56040860160208701614e6b565b611df56060870160408801614e6b565b866060013587608001358860a001358960c00135604051602001611e6d98979695949392919097885273ffffffffffffffffffffffffffffffffffffffff96871660208901529486166040880152929094166060860152608085015260a084019290925260c083019190915260e08201526101000190565b60405160208183030381529060405280519060200120604051602001611ec59291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b336000818152600b6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016861515908117909155815194855291840192909252908201527f6ea9dbe8b2cc119348716a9220a0742ad62b7884ecb0ff4b32cd508121fd937990606001610802565b60015473ffffffffffffffffffffffffffffffffffffffff16331461207a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff81166120f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600154604051919216907fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a90600090a350565b6060848381146121da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453132000000000000000000000000000000000000000000000000000060448201526064016105e5565b8067ffffffffffffffff8111156121f3576121f361504a565b60405190808252806020026020018201604052801561226157816020015b61224e6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b8152602001906001900390816122115790505b50915060005b818110156127bc5760005b828110156124c257808214158015612317575088888281811061229757612297615423565b90506020028101906122a99190615452565b6122b7906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff168989848181106122df576122df615423565b90506020028101906122f19190615452565b6122ff906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b8015612453575088888281811061233057612330615423565b90506020028101906123429190615452565b612353906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061237b5761237b615423565b905060200281019061238d9190615452565b61239e906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16148061245357508888828181106123cd576123cd615423565b90506020028101906123df9190615452565b6123f0906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061241857612418615423565b905060200281019061242a9190615452565b61243b906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b156124ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453135000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101612272565b5060006124da89898481811061060557610605615423565b905061250d8989848181106124f1576124f1615423565b90506020028101906125039190615452565b60c00135826137bd565b84838151811061251f5761251f615423565b602002602001018190525088888381811061253c5761253c615423565b905060200281019061254e9190615452565b61255f906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a082318a8a8581811061258c5761258c615423565b905060200281019061259e9190615452565b6125ac906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126399190615490565b84838151811061264b5761264b615423565b602002602001015160200181815250506126a689898481811061267057612670615423565b90506020028101906126829190615452565b8286858151811061269557612695615423565b6020026020010151600160006138f9565b50505061274b8989848181106126be576126be615423565b90506020028101906126d09190615452565b6126de906020810190614e6b565b868989868181106126f1576126f1615423565b905060200201358c8c8781811061270a5761270a615423565b905060200281019061271c9190615452565b61272d906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16929190613c4c565b6127b389898481811061276057612760615423565b90506020028101906127729190615452565b606001358289898681811061278957612789615423565b905060200201358786815181106127a2576127a2615423565b602002602001015160a00151613caa565b50600101612267565b505095945050505050565b60006127db6127d58461582c565b83613e3c565b90506127ed60808301606084016158dc565b1561280357612800602083013582615928565b90505b92915050565b8360018190036128a25761289d8686600081811061282957612829615423565b905060200281019061283b9190615452565b612849906020810190614e6b565b838888600081811061285d5761285d615423565b905060200281019061286f9190615452565b612880906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1691906131c4565b612935565b60005b818110156129335761292b8787838181106128c2576128c2615423565b90506020028101906128d49190615452565b6128e2906020810190614e6b565b670de0b6b3a76400008787858181106128fd576128fd615423565b905060200201358661290f919061593b565b6129199190615978565b89898581811061285d5761285d615423565b6001016128a5565b505b505050505050565b82600181116129a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453133000000000000000000000000000000000000000000000000000060448201526064016105e5565b808214612a11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453134000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8181101561293557612a7c868683818110612a3157612a31615423565b9050602002810190612a439190615452565b612a51906020810190614e6b565b858584818110612a6357612a63615423565b9050602002013588888581811061285d5761285d615423565b600101612a14565b60005b8581101561293357868682818110612aa157612aa1615423565b9050602002810190612ab39190615452565b612ac4906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a08231888884818110612af157612af1615423565b9050602002810190612b039190615452565b612b11906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e9190615490565b838281518110612bb057612bb0615423565b602002602001015160400181815250506000838281518110612bd457612bd4615423565b602002602001015160200151848381518110612bf257612bf2615423565b602002602001015160400151612c089190615928565b90506000888884818110612c1e57612c1e615423565b9050602002810190612c309190615452565b60a0013515612cb057612cab898985818110612c4e57612c4e615423565b9050602002810190612c609190615452565b608001358a8a86818110612c7657612c76615423565b9050602002810190612c889190615452565b60a00135878681518110612c9e57612c9e615423565b60200260200101516141c3565b612cd9565b888884818110612cc257612cc2615423565b9050602002810190612cd49190615452565b608001355b9050848381518110612ced57612ced615423565b602002602001015160a0015115612dc157868684818110612d1057612d10615423565b9050602002013581612d22919061593b565b898985818110612d3457612d34615423565b9050602002810190612d469190615452565b612d5490606001358461593b565b1015612dbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453233000000000000000000000000000000000000000000000000000060448201526064016105e5565b612e2b565b80821015612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453234000000000000000000000000000000000000000000000000000060448201526064016105e5565b7fcabd156033bc5efebccd321136638073b3e452c01a38d36cbfc9bdec2ffd0f9d898985818110612e5e57612e5e615423565b9050602002810190612e709190615452565b612e7e906020810190614e6b565b858b8b87818110612e9157612e91615423565b9050602002810190612ea39190615452565b612eb4906040810190602001614e6b565b8c8c88818110612ec657612ec6615423565b9050602002810190612ed89190615452565b612ee9906060810190604001614e6b565b8b8b89818110612efb57612efb615423565b90506020020135878b8a81518110612f1557612f15615423565b602090810291909101810151516040805173ffffffffffffffffffffffffffffffffffffffff998a1681529789169288019290925294871686820152929095166060850152608084015260a083019390935260c082015290519081900360e00190a15050600101612a87565b6000612f906020830183614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015612ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130209190615490565b602084015282516130369060a0840135906159b3565b836020015110156130a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453235000000000000000000000000000000000000000000000000000060448201526064016105e5565b82604001518260400160208101906130bb9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190615490565b10156131b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453236000000000000000000000000000000000000000000000000000060448201526064016105e5565b825160208401516128009190615928565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112649084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261421a565b815160005b8181101561344a5760008482815181106132b9576132b9615423565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260018901909252604090912054909150801561335f5784600281111561330957613309614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050613438565b865461336c8160016159b3565b73ffffffffffffffffffffffffffffffffffffffff841660008181526001808c01602090815260408320949094558b549081018c558b825292902090910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790558560028111156133e5576133e5614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050505b80613442816159c6565b91505061329d565b5050505050565b815160005b8181101561344a57600084828151811061347257613472615423565b6020026020010151905060008660010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003613532578460028111156134dc576134dc614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505061370c565b865460009061354390600190615928565b9050600088600001828154811061355c5761355c615423565b600091825260209091200154895473ffffffffffffffffffffffffffffffffffffffff909116915089908390811061359657613596615423565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16896135c4600186615928565b815481106135d4576135d4615423565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff948516179055838316825260018c0190526040808220869055918616815290812055885489908061364e5761364e6159fe565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558660028111156136b8576136b8614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff87168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505050505b80613716816159c6565b915050613456565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d690600090a35050600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6137fa6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b67ffffffffffffffff80841690604085901c16700100000000000000000000000000000000851615156000608187901c600381111561383b5761383b614ef6565b9050838310156138a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453237000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040518060e00160405280878152602001600081526020016000815260200185815260200184815260200183151581526020018260038111156138ec576138ec614ef6565b9052979650505050505050565b60408051606081018252600080825260208201819052918101919091526000806139296060890160408a01614e6b565b73ffffffffffffffffffffffffffffffffffffffff1661394f60408a0160208b01614e6b565b73ffffffffffffffffffffffffffffffffffffffff16036139cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453500000000000000000000000000000000000000000000000000000060448201526064016105e5565b8683526000878152600c602052604090819020549084018190527f80000000000000000000000000000000000000000000000000000000000000001615613a2f576020830160035b90816004811115613a2757613a27614ef6565b905250613a67565b8760600135836040015110613a4957602083016002613a14565b42866080015111613a5f57602083016004613a14565b600160208401525b841580613a895750600183602001516004811115613a8757613a87614ef6565b145b613aef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453600000000000000000000000000000000000000000000000000000060448201526064016105e5565b8315613b0257613aff8884614326565b91505b825160c0870151600091613b2291613b1d60e08d018d615a2d565b6143a4565b905073ffffffffffffffffffffffffffffffffffffffff811615801590613bce575073ffffffffffffffffffffffffffffffffffffffff8116613b6860208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480613bce5750613bce613b9560208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600b602090815260408083209386168352929052205460ff1690565b9150851580613bda5750815b613c40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453700000000000000000000000000000000000000000000000000000060448201526064016105e5565b50955095509592505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526106969085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613216565b8015613d4a576000838152600c6020526040812054613cca9084906159b3565b905084811115613d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453200000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000848152600c6020526040902055610696565b838214613db3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453300000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000838152600c602052604090205415613e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453400000000000000000000000000000000000000000000000000000060448201526064016105e5565b50506000908152600c6020526040902055565b604082015160009073ffffffffffffffffffffffffffffffffffffffff16613ec0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453139000000000000000000000000000000000000000000000000000060448201526064016105e5565b613ecd6020830183614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff161480613f485750613f156060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff16145b613fae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453332000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040838101518451606086015192517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810193909352169063095ea7b3906044016020604051808303816000875af115801561402f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614053919061580f565b50600080846000015173ffffffffffffffffffffffffffffffffffffffff16600086602001516040516140869190615a92565b60006040518083038185875af1925050503d80600081146140c3576040519150601f19603f3d011682016040523d82523d6000602084013e6140c8565b606091505b509150915061410d82826040518060400160405280601981526020017f63616c6c4461746120657865637574696f6e206661696c6564000000000000008152506145ce565b61411881600061461d565b604086810151875191517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015260006024820152929550169063095ea7b3906044016020604051808303816000875af1158015614196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ba919061580f565b50505092915050565b6000816080015142106141d75760006141fe565b81606001514210156141ed5781606001516141ef565b425b82608001516141fe9190615928565b614208908461593b565b61421290856159b3565b949350505050565b600061427c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661469d9092919063ffffffff16565b805190915015611264578080602001905181019061429a919061580f565b611264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105e5565b600060018260200151600481111561434057614340614ef6565b1461434d57506000612803565b826060013560000361436157506000612803565b6040820151614374906060850135615928565b90506128008161439f61438d6040870160208801614e6b565b61439a6020880188614e6b565b6146b3565b6147e0565b6000808460038111156143b9576143b9614ef6565b036143d0576143c98584846147f6565b9050614212565b60018460038111156143e4576143e4614ef6565b0361443d576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018690526143c990605c016040516020818303038152906040528051906020012084846147f6565b600284600381111561445157614451614ef6565b036145955750813560601c36600061446c8460148188615aa4565b6040517f1626ba7e000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff841690631626ba7e906144c7908a9086908690600401615ace565b602060405180830381865afa92505050801561451e575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261451b91810190615ae8565b60015b61452b576000925061458e565b73ffffffffffffffffffffffffffffffffffffffff7fffffffff00000000000000000000000000000000000000000000000000000000919091167f1626ba7e00000000000000000000000000000000000000000000000000000000140292909216915b5050614212565b506000848152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff60ff90911602823560601c16949350505050565b82156145d957505050565b8151156145e95781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b600061462a8260206159b3565b83511015614694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453131000000000000000000000000000000000000000000000000000060448201526064016105e5565b50016020015190565b606061421284846000856148f6565b9392505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523060248301526000916128009185169063dd62ed3e90604401602060405180830381865afa15801561472c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147509190615490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156147bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061439f9190615490565b60008183106147ef5781612800565b5090919050565b600060418214614862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453800000000000000000000000000000000000000000000000000000060448201526064016105e5565b604080516000815260208082018084528790528583013560f81c92820183905285356060830181905290860135608083018190529092909160019060a0016020604051602081039080840390855afa1580156148c2573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015198975050505050505050565b606082471015614988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105e5565b73ffffffffffffffffffffffffffffffffffffffff85163b614a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e5565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614a2f9190615a92565b60006040518083038185875af1925050503d8060008114614a6c576040519150601f19603f3d011682016040523d82523d6000602084013e614a71565b606091505b5091509150614a81828286614a8c565b979650505050505050565b60608315614a9b5750816146ac565b825115614aab5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b60008083601f840112614af157600080fd5b50813567ffffffffffffffff811115614b0957600080fd5b6020830191508360208260051b8501011115614b2457600080fd5b9250929050565b8015158114614b3957600080fd5b50565b8035614b4781614b2b565b919050565b600080600060408486031215614b6157600080fd5b833567ffffffffffffffff811115614b7857600080fd5b614b8486828701614adf565b9094509250506020840135614b9881614b2b565b809150509250925092565b600060208284031215614bb557600080fd5b5035919050565b600060808284031215614bce57600080fd5b50919050565b600060c08284031215614bce57600080fd5b6000806000806000806000806000806101608b8d031215614c0657600080fd5b8a3567ffffffffffffffff80821115614c1e57600080fd5b614c2a8e838f01614adf565b909c509a5060208d0135915080821115614c4357600080fd5b614c4f8e838f01614adf565b909a50985060408d0135915080821115614c6857600080fd5b614c748e838f01614adf565b909850965060608d0135915080821115614c8d57600080fd5b614c998e838f01614bbc565b955060808d0135915080821115614caf57600080fd5b50614cbc8d828e01614adf565b9094509250614cd090508c60a08d01614bd4565b90509295989b9194979a5092959850565b73ffffffffffffffffffffffffffffffffffffffff81168114614b3957600080fd5b8035614b4781614ce1565b60008060008060008060006080888a031215614d2957600080fd5b873567ffffffffffffffff80821115614d4157600080fd5b614d4d8b838c01614adf565b909950975060208a0135915080821115614d6657600080fd5b614d728b838c01614adf565b909750955060408a01359150614d8782614ce1565b90935060608901359080821115614d9d57600080fd5b818a0191508a601f830112614db157600080fd5b813581811115614dc057600080fd5b8b6020828501011115614dd257600080fd5b60208301945080935050505092959891949750929550565b60005b83811015614e05578181015183820152602001614ded565b50506000910152565b60008151808452614e26816020860160208601614dea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006128006020830184614e0e565b600060208284031215614e7d57600080fd5b81356146ac81614ce1565b60008060208385031215614e9b57600080fd5b823567ffffffffffffffff811115614eb257600080fd5b614ebe85828601614adf565b90969095509350505050565b60008060408385031215614edd57600080fd5b8235614ee881614ce1565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b80518252602081015160058110614f65577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020830152604090810151910152565b600081518084526020808501945080840160005b83811015614fa7578151151587529582019590820190600101614f89565b509495945050505050565b60608082528451828201819052600091906020906080850190828901855b82811015614ff357614fe3848351614f25565b9285019290840190600101614fd0565b50505084810382860152865180825290820192508682019060005b8181101561502a5782518552938301939183019160010161500e565b5050505082810360408401526150408185614f75565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561509c5761509c61504a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156150e9576150e961504a565b604052919050565b600067ffffffffffffffff82111561510b5761510b61504a565b5060051b60200190565b6000806040838503121561512857600080fd5b823567ffffffffffffffff81111561513f57600080fd5b8301601f8101851361515057600080fd5b80356020615165615160836150f1565b6150a2565b82815260059290921b8301810191818101908884111561518457600080fd5b938201935b838510156151ab57843561519c81614ce1565b82529382019390820190615189565b95506151ba9050868201614b3c565b93505050509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561521457835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016151e2565b50909695505050505050565b600082601f83011261523157600080fd5b81356020615241615160836150f1565b82815260059290921b8401810191818101908684111561526057600080fd5b8286015b8481101561527b5780358352918301918301615264565b509695505050505050565b60008060006060848603121561529b57600080fd5b83356152a681614ce1565b925060208481013567ffffffffffffffff808211156152c457600080fd5b818701915087601f8301126152d857600080fd5b81356152e6615160826150f1565b81815260059190911b8301840190848101908a83111561530557600080fd5b938501935b8285101561532c57843561531d81614ce1565b8252938501939085019061530a565b96505050604087013592508083111561534457600080fd5b505061535286828701615220565b9150509250925092565b6000806040838503121561536f57600080fd5b823561537a81614ce1565b9150602083013561538a81614ce1565b809150509250929050565b6000602082840312156153a757600080fd5b813567ffffffffffffffff8111156153be57600080fd5b820161010081850312156146ac57600080fd5b60a081016153df8286614f25565b8360608301528215156080830152949350505050565b6000806040838503121561540857600080fd5b823561541381614ce1565b9150602083013561538a81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183360301811261548657600080fd5b9190910192915050565b6000602082840312156154a257600080fd5b5051919050565b6000602082840312156154bb57600080fd5b8135600281106146ac57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff861681526060602082015283606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85111561556857600080fd5b8460051b8087608085013782018281036080908101604085015261558f90820185876154ca565b98975050505050505050565b600067ffffffffffffffff8211156155b5576155b561504a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000602082840312156155f357600080fd5b815167ffffffffffffffff81111561560a57600080fd5b8201601f8101841361561b57600080fd5b80516156296151608261559b565b81815285602083850101111561563e57600080fd5b61564f826020830160208601614dea565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261568d57600080fd5b830160208101925035905067ffffffffffffffff8111156156ad57600080fd5b803603821315614b2457600080fd5b60208152600082356156cd81614ce1565b73ffffffffffffffffffffffffffffffffffffffff81166020840152506156f660208401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660408401525061571f60408401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e083015261577160e0840184615658565b6101008481015261564f610120850182846154ca565b600080600083850360a081121561579d57600080fd5b60608112156157ab57600080fd5b506040516060810181811067ffffffffffffffff821117156157cf576157cf61504a565b604052845181526020850151600581106157e857600080fd5b602082015260408581015190820152606085015160808601519194509250614b9881614b2b565b60006020828403121561582157600080fd5b81516146ac81614b2b565b60006080823603121561583e57600080fd5b615846615079565b823561585181614ce1565b815260208381013567ffffffffffffffff81111561586e57600080fd5b840136601f82011261587f57600080fd5b803561588d6151608261559b565b81815236848385010111156158a157600080fd5b818484018583013760008483830101528084860152505050506158c660408401614d03565b6040820152606092830135928101929092525090565b6000602082840312156158ee57600080fd5b81356146ac81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612803576128036158f9565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615973576159736158f9565b500290565b6000826159ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115612803576128036158f9565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036159f7576159f76158f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6257600080fd5b83018035915067ffffffffffffffff821115615a7d57600080fd5b602001915036819003821315614b2457600080fd5b60008251615486818460208701614dea565b60008085851115615ab457600080fd5b83861115615ac157600080fd5b5050820193919092039150565b83815260406020820152600061564f6040830184866154ca565b600060208284031215615afa57600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146146ac57600080fdfea2646970667358221220cedcf2d3206d2775a915263f0bbcc407cafccdf4dea9439be2437f1bcb0e330064736f6c63430008100033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.