Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 21 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
20414829 | 24 days ago | 0.00053076 ETH | ||||
20414829 | 24 days ago | 0.00000808 ETH | ||||
20414829 | 24 days ago | 0.00053885 ETH | ||||
20344357 | 34 days ago | 0.00109498 ETH | ||||
20344357 | 34 days ago | 0.00001667 ETH | ||||
20344357 | 34 days ago | 0.00111165 ETH | ||||
20324789 | 37 days ago | 0.00203089 ETH | ||||
20324789 | 37 days ago | 0.00003092 ETH | ||||
20324789 | 37 days ago | 0.00206182 ETH | ||||
20287465 | 42 days ago | 0.00085934 ETH | ||||
20287465 | 42 days ago | 0.00001308 ETH | ||||
20287465 | 42 days ago | 0.00087242 ETH | ||||
20283698 | 42 days ago | 0.00222211 ETH | ||||
20283698 | 42 days ago | 0.00003383 ETH | ||||
20283698 | 42 days ago | 0.00225595 ETH | ||||
20235604 | 49 days ago | 0.00282424 ETH | ||||
20235604 | 49 days ago | 0.000043 ETH | ||||
20235604 | 49 days ago | 0.00286724 ETH | ||||
19582747 | 140 days ago | 0 ETH | ||||
19582747 | 140 days ago | Contract Creation | 0 ETH | |||
19582747 | 140 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Settlement
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { SimpleSettlement } from "./SimpleSettlement.sol"; /** * @title Settlement contract * @notice Contract to execute limit orders settlement on Mainnet, created by Fusion mode. */ contract Settlement is SimpleSettlement { error InvalidPriorityFee(); constructor(address limitOrderProtocol, IERC20 feeToken, address weth, address owner) SimpleSettlement(limitOrderProtocol, feeToken, weth, owner) {} function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual override { if (!_isPriorityFeeValid()) revert InvalidPriorityFee(); super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } /** * @dev Validates priority fee according to the spec * https://snapshot.org/#/1inch.eth/proposal/0xa040c60050147a0f67042ae024673e92e813b5d2c0f748abf70ddfa1ed107cbe * For blocks with baseFee <10.6 gwei – the priorityFee is capped at 70% of the baseFee. * For blocks with baseFee between 10.6 gwei and 104.1 gwei – the priorityFee is capped at 50% of the baseFee. * For blocks with baseFee >104.1 gwei – priorityFee is capped at 65% of the block’s baseFee. */ function _isPriorityFeeValid() internal view returns(bool) { unchecked { uint256 baseFee = block.basefee; uint256 priorityFee = tx.gasprice - baseFee; if (baseFee < 10.6 gwei) { return priorityFee * 100 <= baseFee * 70; } else if (baseFee > 104.1 gwei) { return priorityFee * 100 <= baseFee * 65; } else { return priorityFee * 2 <= baseFee; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IOrderMixin.sol"; interface IAmountGetter { /** * @notice View method that gets called to determine the actual making amount * @param order Order being processed * @param extension Order extension data * @param orderHash Hash of the order being processed * @param taker Taker address * @param takingAmount Actual taking amount * @param remainingMakingAmount Order remaining making amount * @param extraData Extra data */ function getMakingAmount( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external view returns (uint256); /** * @notice View method that gets called to determine the actual making amount * @param order Order being processed * @param extension Order extension data * @param orderHash Hash of the order being processed * @param taker Taker address * @param makingAmount Actual taking amount * @param remainingMakingAmount Order remaining making amount * @param extraData Extra data */ function getTakingAmount( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@1inch/solidity-utils/contracts/libraries/AddressLib.sol"; import "../libraries/MakerTraitsLib.sol"; import "../libraries/TakerTraitsLib.sol"; interface IOrderMixin { struct Order { uint256 salt; Address maker; Address receiver; Address makerAsset; Address takerAsset; uint256 makingAmount; uint256 takingAmount; MakerTraits makerTraits; } error InvalidatedOrder(); error TakingAmountExceeded(); error PrivateOrder(); error BadSignature(); error OrderExpired(); error WrongSeriesNonce(); error SwapWithZeroAmount(); error PartialFillNotAllowed(); error OrderIsNotSuitableForMassInvalidation(); error EpochManagerAndBitInvalidatorsAreIncompatible(); error ReentrancyDetected(); error PredicateIsNotTrue(); error TakingAmountTooHigh(); error MakingAmountTooLow(); error TransferFromMakerToTakerFailed(); error TransferFromTakerToMakerFailed(); error MismatchArraysLengths(); error InvalidPermit2Transfer(); error SimulationResults(bool success, bytes res); /** * @notice Emitted when order gets filled * @param orderHash Hash of the order * @param remainingAmount Amount of the maker asset that remains to be filled */ event OrderFilled( bytes32 orderHash, uint256 remainingAmount ); /** * @notice Emitted when order without `useBitInvalidator` gets cancelled * @param orderHash Hash of the order */ event OrderCancelled( bytes32 orderHash ); /** * @notice Emitted when order with `useBitInvalidator` gets cancelled * @param maker Maker address * @param slotIndex Slot index that was updated * @param slotValue New slot value */ event BitInvalidatorUpdated( address indexed maker, uint256 slotIndex, uint256 slotValue ); /** * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes * @param maker Maker address * @param slot Slot number to return bitmask for * @return result Each bit represents whether corresponding was already invalidated */ function bitInvalidatorForOrder(address maker, uint256 slot) external view returns(uint256 result); /** * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes * @param orderHash Hash of the order * @return remaining Remaining amount of the order */ function remainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remaining); /** * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes * @param orderHash Hash of the order * @return remainingRaw Inverse of the remaining amount of the order if order was filled at least once, otherwise 0 */ function rawRemainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remainingRaw); /** * @notice Cancels order's quote * @param makerTraits Order makerTraits * @param orderHash Hash of the order to cancel */ function cancelOrder(MakerTraits makerTraits, bytes32 orderHash) external; /** * @notice Cancels orders' quotes * @param makerTraits Orders makerTraits * @param orderHashes Hashes of the orders to cancel */ function cancelOrders(MakerTraits[] calldata makerTraits, bytes32[] calldata orderHashes) external; /** * @notice Cancels all quotes of the maker (works for bit-invalidating orders only) * @param makerTraits Order makerTraits * @param additionalMask Additional bitmask to invalidate orders */ function bitsInvalidateForOrder(MakerTraits makerTraits, uint256 additionalMask) external; /** * @notice Returns order hash, hashed with limit order protocol contract EIP712 * @param order Order * @return orderHash Hash of the order */ function hashOrder(IOrderMixin.Order calldata order) external view returns(bytes32 orderHash); /** * @notice Delegates execution to custom implementation. Could be used to validate if `transferFrom` works properly * @dev The function always reverts and returns the simulation results in revert data. * @param target Addresses that will be delegated * @param data Data that will be passed to delegatee */ function simulate(address target, bytes calldata data) external; /** * @notice Fills order's quote, fully or partially (whichever is possible). * @param order Order quote to fill * @param r R component of signature * @param vs VS component of signature * @param amount Taker amount to fill * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit. * @return makingAmount Actual amount transferred from maker to taker * @return takingAmount Actual amount transferred from taker to maker * @return orderHash Hash of the filled order */ function fillOrder( Order calldata order, bytes32 r, bytes32 vs, uint256 amount, TakerTraits takerTraits ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash); /** * @notice Same as `fillOrder` but allows to specify arguments that are used by the taker. * @param order Order quote to fill * @param r R component of signature * @param vs VS component of signature * @param amount Taker amount to fill * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit. * @param args Arguments that are used by the taker (target, extension, interaction, permit) * @return makingAmount Actual amount transferred from maker to taker * @return takingAmount Actual amount transferred from taker to maker * @return orderHash Hash of the filled order */ function fillOrderArgs( IOrderMixin.Order calldata order, bytes32 r, bytes32 vs, uint256 amount, TakerTraits takerTraits, bytes calldata args ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash); /** * @notice Same as `fillOrder` but uses contract-based signatures. * @param order Order quote to fill * @param signature Signature to confirm quote ownership * @param amount Taker amount to fill * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit. * @return makingAmount Actual amount transferred from maker to taker * @return takingAmount Actual amount transferred from taker to maker * @return orderHash Hash of the filled order * @dev See tests for examples */ function fillContractOrder( Order calldata order, bytes calldata signature, uint256 amount, TakerTraits takerTraits ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash); /** * @notice Same as `fillContractOrder` but allows to specify arguments that are used by the taker. * @param order Order quote to fill * @param signature Signature to confirm quote ownership * @param amount Taker amount to fill * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit. * @param args Arguments that are used by the taker (target, extension, interaction, permit) * @return makingAmount Actual amount transferred from maker to taker * @return takingAmount Actual amount transferred from taker to maker * @return orderHash Hash of the filled order * @dev See tests for examples */ function fillContractOrderArgs( Order calldata order, bytes calldata signature, uint256 amount, TakerTraits takerTraits, bytes calldata args ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IOrderMixin.sol"; interface IPostInteraction { /** * @notice Callback method that gets called after all fund transfers * @param order Order being processed * @param extension Order extension data * @param orderHash Hash of the order being processed * @param taker Taker address * @param makingAmount Actual making amount * @param takingAmount Actual taking amount * @param remainingMakingAmount Order remaining making amount * @param extraData Extra data */ function postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IOrderMixin.sol"; interface IPreInteraction { /** * @notice Callback method that gets called before any funds transfers * @param order Order being processed * @param extension Order extension data * @param orderHash Hash of the order being processed * @param taker Taker address * @param makingAmount Actual making amount * @param takingAmount Actual taking amount * @param remainingMakingAmount Order remaining making amount * @param extraData Extra data */ function preInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; type MakerTraits is uint256; /** * @title MakerTraitsLib * @notice A library to manage and check MakerTraits, which are used to encode the maker's preferences for an order in a single uint256. * @dev * The MakerTraits type is a uint256 and different parts of the number are used to encode different traits. * High bits are used for flags * 255 bit `NO_PARTIAL_FILLS_FLAG` - if set, the order does not allow partial fills * 254 bit `ALLOW_MULTIPLE_FILLS_FLAG` - if set, the order permits multiple fills * 253 bit - unused * 252 bit `PRE_INTERACTION_CALL_FLAG` - if set, the order requires pre-interaction call * 251 bit `POST_INTERACTION_CALL_FLAG` - if set, the order requires post-interaction call * 250 bit `NEED_CHECK_EPOCH_MANAGER_FLAG` - if set, the order requires to check the epoch manager * 249 bit `HAS_EXTENSION_FLAG` - if set, the order has extension(s) * 248 bit `USE_PERMIT2_FLAG` - if set, the order uses permit2 * 247 bit `UNWRAP_WETH_FLAG` - if set, the order requires to unwrap WETH * Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series * uint80 last 10 bytes of allowed sender address (0 if any) * uint40 expiration timestamp (0 if none) * uint40 nonce or epoch * uint40 series */ library MakerTraitsLib { // Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series uint256 private constant _ALLOWED_SENDER_MASK = type(uint80).max; uint256 private constant _EXPIRATION_OFFSET = 80; uint256 private constant _EXPIRATION_MASK = type(uint40).max; uint256 private constant _NONCE_OR_EPOCH_OFFSET = 120; uint256 private constant _NONCE_OR_EPOCH_MASK = type(uint40).max; uint256 private constant _SERIES_OFFSET = 160; uint256 private constant _SERIES_MASK = type(uint40).max; uint256 private constant _NO_PARTIAL_FILLS_FLAG = 1 << 255; uint256 private constant _ALLOW_MULTIPLE_FILLS_FLAG = 1 << 254; uint256 private constant _PRE_INTERACTION_CALL_FLAG = 1 << 252; uint256 private constant _POST_INTERACTION_CALL_FLAG = 1 << 251; uint256 private constant _NEED_CHECK_EPOCH_MANAGER_FLAG = 1 << 250; uint256 private constant _HAS_EXTENSION_FLAG = 1 << 249; uint256 private constant _USE_PERMIT2_FLAG = 1 << 248; uint256 private constant _UNWRAP_WETH_FLAG = 1 << 247; /** * @notice Checks if the order has the extension flag set. * @dev If the `HAS_EXTENSION_FLAG` is set in the makerTraits, then the protocol expects that the order has extension(s). * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the flag is set. */ function hasExtension(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _HAS_EXTENSION_FLAG) != 0; } /** * @notice Checks if the maker allows a specific taker to fill the order. * @param makerTraits The traits of the maker. * @param sender The address of the taker to be checked. * @return result A boolean indicating whether the taker is allowed. */ function isAllowedSender(MakerTraits makerTraits, address sender) internal pure returns (bool) { uint160 allowedSender = uint160(MakerTraits.unwrap(makerTraits) & _ALLOWED_SENDER_MASK); return allowedSender == 0 || allowedSender == uint160(sender) & _ALLOWED_SENDER_MASK; } /** * @notice Checks if the order has expired. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the order has expired. */ function isExpired(MakerTraits makerTraits) internal view returns (bool) { uint256 expiration = (MakerTraits.unwrap(makerTraits) >> _EXPIRATION_OFFSET) & _EXPIRATION_MASK; return expiration != 0 && expiration < block.timestamp; // solhint-disable-line not-rely-on-time } /** * @notice Returns the nonce or epoch of the order. * @param makerTraits The traits of the maker. * @return result The nonce or epoch of the order. */ function nonceOrEpoch(MakerTraits makerTraits) internal pure returns (uint256) { return (MakerTraits.unwrap(makerTraits) >> _NONCE_OR_EPOCH_OFFSET) & _NONCE_OR_EPOCH_MASK; } /** * @notice Returns the series of the order. * @param makerTraits The traits of the maker. * @return result The series of the order. */ function series(MakerTraits makerTraits) internal pure returns (uint256) { return (MakerTraits.unwrap(makerTraits) >> _SERIES_OFFSET) & _SERIES_MASK; } /** * @notice Determines if the order allows partial fills. * @dev If the _NO_PARTIAL_FILLS_FLAG is not set in the makerTraits, then the order allows partial fills. * @param makerTraits The traits of the maker, determining their preferences for the order. * @return result A boolean indicating whether the maker allows partial fills. */ function allowPartialFills(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _NO_PARTIAL_FILLS_FLAG) == 0; } /** * @notice Checks if the maker needs pre-interaction call. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the maker needs a pre-interaction call. */ function needPreInteractionCall(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _PRE_INTERACTION_CALL_FLAG) != 0; } /** * @notice Checks if the maker needs post-interaction call. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the maker needs a post-interaction call. */ function needPostInteractionCall(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _POST_INTERACTION_CALL_FLAG) != 0; } /** * @notice Determines if the order allows multiple fills. * @dev If the _ALLOW_MULTIPLE_FILLS_FLAG is set in the makerTraits, then the maker allows multiple fills. * @param makerTraits The traits of the maker, determining their preferences for the order. * @return result A boolean indicating whether the maker allows multiple fills. */ function allowMultipleFills(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _ALLOW_MULTIPLE_FILLS_FLAG) != 0; } /** * @notice Determines if an order should use the bit invalidator or remaining amount validator. * @dev The bit invalidator can be used if the order does not allow partial or multiple fills. * @param makerTraits The traits of the maker, determining their preferences for the order. * @return result A boolean indicating whether the bit invalidator should be used. * True if the order requires the use of the bit invalidator. */ function useBitInvalidator(MakerTraits makerTraits) internal pure returns (bool) { return !allowPartialFills(makerTraits) || !allowMultipleFills(makerTraits); } /** * @notice Checks if the maker needs to check the epoch. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the maker needs to check the epoch manager. */ function needCheckEpochManager(MakerTraits makerTraits) internal pure returns (bool) { return (MakerTraits.unwrap(makerTraits) & _NEED_CHECK_EPOCH_MANAGER_FLAG) != 0; } /** * @notice Checks if the maker uses permit2. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the maker uses permit2. */ function usePermit2(MakerTraits makerTraits) internal pure returns (bool) { return MakerTraits.unwrap(makerTraits) & _USE_PERMIT2_FLAG != 0; } /** * @notice Checks if the maker needs to unwraps WETH. * @param makerTraits The traits of the maker. * @return result A boolean indicating whether the maker needs to unwrap WETH. */ function unwrapWeth(MakerTraits makerTraits) internal pure returns (bool) { return MakerTraits.unwrap(makerTraits) & _UNWRAP_WETH_FLAG != 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; type TakerTraits is uint256; /** * @title TakerTraitsLib * @notice This library to manage and check TakerTraits, which are used to encode the taker's preferences for an order in a single uint256. * @dev The TakerTraits are structured as follows: * High bits are used for flags * 255 bit `_MAKER_AMOUNT_FLAG` - If set, the taking amount is calculated based on making amount, otherwise making amount is calculated based on taking amount. * 254 bit `_UNWRAP_WETH_FLAG` - If set, the WETH will be unwrapped into ETH before sending to taker. * 253 bit `_SKIP_ORDER_PERMIT_FLAG` - If set, the order skips maker's permit execution. * 252 bit `_USE_PERMIT2_FLAG` - If set, the order uses the permit2 function for authorization. * 251 bit `_ARGS_HAS_TARGET` - If set, then first 20 bytes of args are treated as target address for maker’s funds transfer. * 224-247 bits `ARGS_EXTENSION_LENGTH` - The length of the extension calldata in the args. * 200-223 bits `ARGS_INTERACTION_LENGTH` - The length of the interaction calldata in the args. * 0-184 bits - The threshold amount (the maximum amount a taker agrees to give in exchange for a making amount). */ library TakerTraitsLib { uint256 private constant _MAKER_AMOUNT_FLAG = 1 << 255; uint256 private constant _UNWRAP_WETH_FLAG = 1 << 254; uint256 private constant _SKIP_ORDER_PERMIT_FLAG = 1 << 253; uint256 private constant _USE_PERMIT2_FLAG = 1 << 252; uint256 private constant _ARGS_HAS_TARGET = 1 << 251; uint256 private constant _ARGS_EXTENSION_LENGTH_OFFSET = 224; uint256 private constant _ARGS_EXTENSION_LENGTH_MASK = 0xffffff; uint256 private constant _ARGS_INTERACTION_LENGTH_OFFSET = 200; uint256 private constant _ARGS_INTERACTION_LENGTH_MASK = 0xffffff; uint256 private constant _AMOUNT_MASK = 0x000000000000000000ffffffffffffffffffffffffffffffffffffffffffffff; /** * @notice Checks if the args should contain target address. * @param takerTraits The traits of the taker. * @return result A boolean indicating whether the args should contain target address. */ function argsHasTarget(TakerTraits takerTraits) internal pure returns (bool) { return (TakerTraits.unwrap(takerTraits) & _ARGS_HAS_TARGET) != 0; } /** * @notice Retrieves the length of the extension calldata from the takerTraits. * @param takerTraits The traits of the taker. * @return result The length of the extension calldata encoded in the takerTraits. */ function argsExtensionLength(TakerTraits takerTraits) internal pure returns (uint256) { return (TakerTraits.unwrap(takerTraits) >> _ARGS_EXTENSION_LENGTH_OFFSET) & _ARGS_EXTENSION_LENGTH_MASK; } /** * @notice Retrieves the length of the interaction calldata from the takerTraits. * @param takerTraits The traits of the taker. * @return result The length of the interaction calldata encoded in the takerTraits. */ function argsInteractionLength(TakerTraits takerTraits) internal pure returns (uint256) { return (TakerTraits.unwrap(takerTraits) >> _ARGS_INTERACTION_LENGTH_OFFSET) & _ARGS_INTERACTION_LENGTH_MASK; } /** * @notice Checks if the taking amount should be calculated based on making amount. * @param takerTraits The traits of the taker. * @return result A boolean indicating whether the taking amount should be calculated based on making amount. */ function isMakingAmount(TakerTraits takerTraits) internal pure returns (bool) { return (TakerTraits.unwrap(takerTraits) & _MAKER_AMOUNT_FLAG) != 0; } /** * @notice Checks if the order should unwrap WETH and send ETH to taker. * @param takerTraits The traits of the taker. * @return result A boolean indicating whether the order should unwrap WETH. */ function unwrapWeth(TakerTraits takerTraits) internal pure returns (bool) { return (TakerTraits.unwrap(takerTraits) & _UNWRAP_WETH_FLAG) != 0; } /** * @notice Checks if the order should skip maker's permit execution. * @param takerTraits The traits of the taker. * @return result A boolean indicating whether the order don't apply permit. */ function skipMakerPermit(TakerTraits takerTraits) internal pure returns (bool) { return (TakerTraits.unwrap(takerTraits) & _SKIP_ORDER_PERMIT_FLAG) != 0; } /** * @notice Checks if the order uses the permit2 instead of permit. * @param takerTraits The traits of the taker. * @return result A boolean indicating whether the order uses the permit2. */ function usePermit2(TakerTraits takerTraits) internal pure returns (bool) { return (TakerTraits.unwrap(takerTraits) & _USE_PERMIT2_FLAG) != 0; } /** * @notice Retrieves the threshold amount from the takerTraits. * The maximum amount a taker agrees to give in exchange for a making amount. * @param takerTraits The traits of the taker. * @return result The threshold amount encoded in the takerTraits. */ function threshold(TakerTraits takerTraits) internal pure returns (uint256) { return TakerTraits.unwrap(takerTraits) & _AMOUNT_MASK; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IDaiLikePermit { function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IERC20MetadataUppercase { function NAME() external view returns (string memory); // solhint-disable-line func-name-mixedcase function SYMBOL() external view returns (string memory); // solhint-disable-line func-name-mixedcase }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPermit2 { struct PermitDetails { // ERC20 token address address token; // the maximum amount allowed to spend uint160 amount; // timestamp at which a spender's token allowances become invalid uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature uint48 nonce; } /// @notice The permit message signed for a single token allownce struct PermitSingle { // the permit data for a single token alownce PermitDetails details; // address permissioned on the allowed tokens address spender; // deadline on the permit signature uint256 sigDeadline; } /// @notice Packed allowance struct PackedAllowance { // amount allowed uint160 amount; // permission expiry uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature uint48 nonce; } function transferFrom(address user, address spender, uint160 amount, address token) external; function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external; function allowance(address user, address token, address spender) external view returns (PackedAllowance memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWETH is IERC20 { event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); function deposit() external payable; function withdraw(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; type Address is uint256; /** * @dev Library for working with addresses encoded as uint256 values, which can include flags in the highest bits. */ library AddressLib { uint256 private constant _LOW_160_BIT_MASK = (1 << 160) - 1; /** * @notice Returns the address representation of a uint256. * @param a The uint256 value to convert to an address. * @return The address representation of the provided uint256 value. */ function get(Address a) internal pure returns (address) { return address(uint160(Address.unwrap(a) & _LOW_160_BIT_MASK)); } /** * @notice Checks if a given flag is set for the provided address. * @param a The address to check for the flag. * @param flag The flag to check for in the provided address. * @return True if the provided flag is set in the address, false otherwise. */ function getFlag(Address a, uint256 flag) internal pure returns (bool) { return (Address.unwrap(a) & flag) != 0; } /** * @notice Returns a uint32 value stored at a specific bit offset in the provided address. * @param a The address containing the uint32 value. * @param offset The bit offset at which the uint32 value is stored. * @return The uint32 value stored in the address at the specified bit offset. */ function getUint32(Address a, uint256 offset) internal pure returns (uint32) { return uint32(Address.unwrap(a) >> offset); } /** * @notice Returns a uint64 value stored at a specific bit offset in the provided address. * @param a The address containing the uint64 value. * @param offset The bit offset at which the uint64 value is stored. * @return The uint64 value stored in the address at the specified bit offset. */ function getUint64(Address a, uint256 offset) internal pure returns (uint64) { return uint64(Address.unwrap(a) >> offset); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Revert reason forwarder. library RevertReasonForwarder { /// @dev Forwards latest externall call revert. function reRevert() internal pure { // bubble up revert reason from latest external call assembly ("memory-safe") { // solhint-disable-line no-inline-assembly let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } /// @dev Returns latest external call revert reason. function reReason() internal pure returns (bytes memory reason) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly reason := mload(0x40) let length := returndatasize() mstore(reason, length) returndatacopy(add(reason, 0x20), 0, length) mstore(0x40, add(reason, add(0x20, length))) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import "../interfaces/IDaiLikePermit.sol"; import "../interfaces/IPermit2.sol"; import "../interfaces/IWETH.sol"; import "../libraries/RevertReasonForwarder.sol"; /** * @title Implements efficient safe methods for ERC20 interface. * @notice Compared to the standard ERC20, this implementation offers several enhancements: * 1. more gas-efficient, providing significant savings in transaction costs. * 2. support for different permit implementations * 3. forceApprove functionality * 4. support for WETH deposit and withdraw */ library SafeERC20 { error SafeTransferFailed(); error SafeTransferFromFailed(); error ForceApproveFailed(); error SafeIncreaseAllowanceFailed(); error SafeDecreaseAllowanceFailed(); error SafePermitBadLength(); error Permit2TransferAmountTooHigh(); // Uniswap Permit2 address address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector uint256 private constant _RAW_CALL_GAS_LIMIT = 5000; /** * @notice Fetches the balance of a specific ERC20 token held by an account. * Consumes less gas then regular `ERC20.balanceOf`. * @dev Note that the implementation does not perform dirty bits cleaning, so it is the * responsibility of the caller to make sure that the higher 96 bits of the `account` parameter are clean. * @param token The IERC20 token contract for which the balance will be fetched. * @param account The address of the account whose token balance will be fetched. * @return tokenBalance The balance of the specified ERC20 token held by the account. */ function safeBalanceOf( IERC20 token, address account ) internal view returns(uint256 tokenBalance) { bytes4 selector = IERC20.balanceOf.selector; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly mstore(0x00, selector) mstore(0x04, account) let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20) tokenBalance := mload(0) if or(iszero(success), lt(returndatasize(), 0x20)) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } } /** * @notice Attempts to safely transfer tokens from one address to another. * @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom. * Either requires `true` in return data, or requires target to be smart-contract and empty return data. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean. * @param token The IERC20 token contract from which the tokens will be transferred. * @param from The address from which the tokens will be transferred. * @param to The address to which the tokens will be transferred. * @param amount The amount of tokens to transfer. * @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom. */ function safeTransferFromUniversal( IERC20 token, address from, address to, uint256 amount, bool permit2 ) internal { if (permit2) { safeTransferFromPermit2(token, from, to, amount); } else { safeTransferFrom(token, from, to, amount); } } /** * @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard. * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean. * @param token The IERC20 token contract from which the tokens will be transferred. * @param from The address from which the tokens will be transferred. * @param to The address to which the tokens will be transferred. * @param amount The amount of tokens to transfer. */ function safeTransferFrom( IERC20 token, address from, address to, uint256 amount ) internal { bytes4 selector = token.transferFrom.selector; bool success; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly let data := mload(0x40) mstore(data, selector) mstore(add(data, 0x04), from) mstore(add(data, 0x24), to) mstore(add(data, 0x44), amount) success := call(gas(), token, 0, data, 100, 0x0, 0x20) if success { switch returndatasize() case 0 { success := gt(extcodesize(token), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } } if (!success) revert SafeTransferFromFailed(); } /** * @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard. * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean. * @param token The IERC20 token contract from which the tokens will be transferred. * @param from The address from which the tokens will be transferred. * @param to The address to which the tokens will be transferred. * @param amount The amount of tokens to transfer. */ function safeTransferFromPermit2( IERC20 token, address from, address to, uint256 amount ) internal { if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh(); bytes4 selector = IPermit2.transferFrom.selector; bool success; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly let data := mload(0x40) mstore(data, selector) mstore(add(data, 0x04), from) mstore(add(data, 0x24), to) mstore(add(data, 0x44), amount) mstore(add(data, 0x64), token) success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0) if success { success := gt(extcodesize(_PERMIT2), 0) } } if (!success) revert SafeTransferFromFailed(); } /** * @notice Attempts to safely transfer tokens to another address. * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `to` parameter are clean. * @param token The IERC20 token contract from which the tokens will be transferred. * @param to The address to which the tokens will be transferred. * @param value The amount of tokens to transfer. */ function safeTransfer( IERC20 token, address to, uint256 value ) internal { if (!_makeCall(token, token.transfer.selector, to, value)) { revert SafeTransferFailed(); } } /** * @notice Attempts to approve a spender to spend a certain amount of tokens. * @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `spender` parameter are clean. * @param token The IERC20 token contract on which the call will be made. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function forceApprove( IERC20 token, address spender, uint256 value ) internal { if (!_makeCall(token, token.approve.selector, spender, value)) { if ( !_makeCall(token, token.approve.selector, spender, 0) || !_makeCall(token, token.approve.selector, spender, value) ) { revert ForceApproveFailed(); } } } /** * @notice Safely increases the allowance of a spender. * @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction. * Then uses `forceApprove` to increase the allowance. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `spender` parameter are clean. * @param token The IERC20 token contract on which the call will be made. * @param spender The address which will spend the funds. * @param value The amount of tokens to increase the allowance by. */ function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 allowance = token.allowance(address(this), spender); if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed(); forceApprove(token, spender, allowance + value); } /** * @notice Safely decreases the allowance of a spender. * @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction. * Then uses `forceApprove` to increase the allowance. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `spender` parameter are clean. * @param token The IERC20 token contract on which the call will be made. * @param spender The address which will spend the funds. * @param value The amount of tokens to decrease the allowance by. */ function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 allowance = token.allowance(address(this), spender); if (value > allowance) revert SafeDecreaseAllowanceFailed(); forceApprove(token, spender, allowance - value); } /** * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters. * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2). * @dev Wraps `tryPermit` function and forwards revert reason if permit fails. * @param token The IERC20 token to execute the permit function on. * @param permit The permit data to be used in the function call. */ function safePermit(IERC20 token, bytes calldata permit) internal { if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert(); } /** * @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters. * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2). * @dev Wraps `tryPermit` function and forwards revert reason if permit fails. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean. * @param token The IERC20 token to execute the permit function on. * @param owner The owner of the tokens for which the permit is made. * @param spender The spender allowed to spend the tokens by the permit. * @param permit The permit data to be used in the function call. */ function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal { if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert(); } /** * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters. * @dev Invokes `tryPermit` with sender as owner and contract as spender. * @param token The IERC20 token to execute the permit function on. * @param permit The permit data to be used in the function call. * @return success Returns true if the permit function was successfully executed, false otherwise. */ function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) { return tryPermit(token, msg.sender, address(this), permit); } /** * @notice The function attempts to call the permit function on a given ERC20 token. * @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2. * It accommodates both Compact and Full formats of these permit types. * Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter * for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by * gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and * zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one * before invocation for optimized performance. * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean. * @param token The address of the ERC20 token on which to call the permit function. * @param owner The owner of the tokens. This address should have signed the off-chain permit. * @param spender The address which will be approved for transfer of tokens. * @param permit The off-chain permit data, containing different fields depending on the type of permit function. * @return success A boolean indicating whether the permit call was successful. */ function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) { // load function selectors for different permit standards bytes4 permitSelector = IERC20Permit.permit.selector; bytes4 daiPermitSelector = IDaiLikePermit.permit.selector; bytes4 permit2Selector = IPermit2.permit.selector; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly let ptr := mload(0x40) // Switch case for different permit lengths, indicating different permit standards switch permit.length // Compact IERC20Permit case 100 { mstore(ptr, permitSelector) // store selector mstore(add(ptr, 0x04), owner) // store owner mstore(add(ptr, 0x24), spender) // store spender // Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs) { // stack too deep let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23 let vs := calldataload(add(permit.offset, 0x44)) // loads permit.offset 0x44..0x63 calldatacopy(add(ptr, 0x44), permit.offset, 0x20) // store value = copy permit.offset 0x00..0x19 mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline = deadline - 1 mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28) calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r = copy permit.offset 0x24..0x43 mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s = vs without most significant bit } // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) success := call(gas(), token, 0, ptr, 0xe4, 0, 0) } // Compact IDaiLikePermit case 72 { mstore(ptr, daiPermitSelector) // store selector mstore(add(ptr, 0x04), owner) // store owner mstore(add(ptr, 0x24), spender) // store spender // Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs) { // stack too deep let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07 let vs := calldataload(add(permit.offset, 0x28)) // loads permit.offset 0x28..0x47 mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce = copy permit.offset 0x00..0x03 mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry = expiry - 1 mstore(add(ptr, 0x84), true) // store allowed = true mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28) calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20) // store r = copy permit.offset 0x08..0x27 mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s = vs without most significant bit } // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) success := call(gas(), token, 0, ptr, 0x104, 0, 0) } // IERC20Permit case 224 { mstore(ptr, permitSelector) calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) success := call(gas(), token, 0, ptr, 0xe4, 0, 0) } // IDaiLikePermit case 256 { mstore(ptr, daiPermitSelector) calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) success := call(gas(), token, 0, ptr, 0x104, 0, 0) } // Compact IPermit2 case 96 { // Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs) mstore(ptr, permit2Selector) // store selector mstore(add(ptr, 0x04), owner) // store owner mstore(add(ptr, 0x24), token) // store token calldatacopy(add(ptr, 0x50), permit.offset, 0x14) // store amount = copy permit.offset 0x00..0x13 // and(0xffffffffffff, ...) - conversion to uint48 mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff) mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b mstore(add(ptr, 0xa4), spender) // store spender // and(0xffffffffffff, ...) - conversion to uint48 mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff) mstore(add(ptr, 0xe4), 0x100) // store offset = 256 mstore(add(ptr, 0x104), 0x40) // store length = 64 calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r = copy permit.offset 0x20..0x3f calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs = copy permit.offset 0x40..0x5f // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature) success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0) } // IPermit2 case 352 { mstore(ptr, permit2Selector) calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature) success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0) } // Unknown default { mstore(ptr, _PERMIT_LENGTH_ERROR) revert(ptr, 4) } } } /** * @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns. * @param token The IERC20 token contract on which the call will be made. * @param selector The function signature that is to be called on the token contract. * @param to The address to which the token amount will be transferred. * @param amount The token amount to be transferred. * @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure. * In case of success but no returned data, validates that the contract code exists. * In case of returned data, ensures that it's a boolean `true`. */ function _makeCall( IERC20 token, bytes4 selector, address to, uint256 amount ) private returns (bool success) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly let data := mload(0x40) mstore(data, selector) mstore(add(data, 0x04), to) mstore(add(data, 0x24), amount) success := call(gas(), token, 0, data, 0x44, 0x0, 0x20) if success { switch returndatasize() case 0 { success := gt(extcodesize(token), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } } } /** * @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`. * @param weth The IWETH token contract. * @param amount The amount of Ether to deposit into the IWETH contract. */ function safeDeposit(IWETH weth, uint256 amount) internal { if (amount > 0) { bytes4 selector = IWETH.deposit.selector; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly mstore(0, selector) if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } } } /** * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`. * @dev Uses inline assembly to interact with the IWETH contract. * @param weth The IWETH token contract. * @param amount The amount of wrapped Ether to withdraw from the IWETH contract. */ function safeWithdraw(IWETH weth, uint256 amount) internal { bytes4 selector = IWETH.withdraw.selector; assembly ("memory-safe") { // solhint-disable-line no-inline-assembly mstore(0, selector) mstore(4, amount) if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } } /** * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient. * Consumes less gas then regular `IWETH.withdraw`. * @param weth The IWETH token contract. * @param amount The amount of wrapped Ether to withdraw from the IWETH contract. * @param to The recipient of the withdrawn Ether. */ function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal { safeWithdraw(weth, amount); if (to != address(this)) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Library with gas-efficient string operations library StringUtil { function toHex(uint256 value) internal pure returns (string memory) { return toHex(abi.encodePacked(value)); } function toHex(address value) internal pure returns (string memory) { return toHex(abi.encodePacked(value)); } /// @dev this is the assembly adaptation of highly optimized toHex16 code from Mikhail Vladimirov /// https://stackoverflow.com/a/69266989 function toHex(bytes memory data) internal pure returns (string memory result) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly function _toHex16(input) -> output { output := or( and(input, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000), shr(64, and(input, 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000)) ) output := or( and(output, 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000), shr(32, and(output, 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000)) ) output := or( and(output, 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000), shr(16, and(output, 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000)) ) output := or( and(output, 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000), shr(8, and(output, 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000)) ) output := or( shr(4, and(output, 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000)), shr(8, and(output, 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00)) ) output := add( add(0x3030303030303030303030303030303030303030303030303030303030303030, output), mul( and( shr(4, add(output, 0x0606060606060606060606060606060606060606060606060606060606060606)), 0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ), 7 // Change 7 to 39 for lower case output ) ) } result := mload(0x40) let length := mload(data) let resultLength := shl(1, length) let toPtr := add(result, 0x22) // 32 bytes for length + 2 bytes for '0x' mstore(0x40, add(toPtr, resultLength)) // move free memory pointer mstore(add(result, 2), 0x3078) // 0x3078 is right aligned so we write to `result + 2` // to store the last 2 bytes in the beginning of the string mstore(result, add(resultLength, 2)) // extra 2 bytes for '0x' for { let fromPtr := add(data, 0x20) let endPtr := add(fromPtr, length) } lt(fromPtr, endPtr) { fromPtr := add(fromPtr, 0x20) } { let rawData := mload(fromPtr) let hexData := _toHex16(rawData) mstore(toPtr, hexData) toPtr := add(toPtr, 0x20) hexData := _toHex16(shl(128, rawData)) mstore(toPtr, hexData) toPtr := add(toPtr, 0x20) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "../interfaces/IERC20MetadataUppercase.sol"; import "./SafeERC20.sol"; import "./StringUtil.sol"; /// @title Library, which allows usage of ETH as ERC20 and ERC20 itself. Uses SafeERC20 library for ERC20 interface. library UniERC20 { using SafeERC20 for IERC20; error InsufficientBalance(); error ApproveCalledOnETH(); error NotEnoughValue(); error FromIsNotSender(); error ToIsNotThis(); error ETHTransferFailed(); uint256 private constant _RAW_CALL_GAS_LIMIT = 5000; IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); IERC20 private constant _ZERO_ADDRESS = IERC20(address(0)); /// @dev Returns true if `token` is ETH. function isETH(IERC20 token) internal pure returns (bool) { return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS); } /// @dev Returns `account` ERC20 `token` balance. function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) { if (isETH(token)) { return account.balance; } else { return token.balanceOf(account); } } /// @dev `token` transfer `to` `amount`. /// Note that this function does nothing in case of zero amount. function uniTransfer( IERC20 token, address payable to, uint256 amount ) internal { if (amount > 0) { if (isETH(token)) { if (address(this).balance < amount) revert InsufficientBalance(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}(""); if (!success) revert ETHTransferFailed(); } else { token.safeTransfer(to, amount); } } } /// @dev `token` transfer `from` `to` `amount`. /// Note that this function does nothing in case of zero amount. function uniTransferFrom( IERC20 token, address payable from, address to, uint256 amount ) internal { if (amount > 0) { if (isETH(token)) { if (msg.value < amount) revert NotEnoughValue(); if (from != msg.sender) revert FromIsNotSender(); if (to != address(this)) revert ToIsNotThis(); if (msg.value > amount) { // Return remainder if exist unchecked { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = from.call{value: msg.value - amount, gas: _RAW_CALL_GAS_LIMIT}(""); if (!success) revert ETHTransferFailed(); } } } else { token.safeTransferFrom(from, to, amount); } } } /// @dev Returns `token` symbol from ERC20 metadata. function uniSymbol(IERC20 token) internal view returns (string memory) { return _uniDecode(token, IERC20Metadata.symbol.selector, IERC20MetadataUppercase.SYMBOL.selector); } /// @dev Returns `token` name from ERC20 metadata. function uniName(IERC20 token) internal view returns (string memory) { return _uniDecode(token, IERC20Metadata.name.selector, IERC20MetadataUppercase.NAME.selector); } /// @dev Reverts if `token` is ETH, otherwise performs ERC20 forceApprove. function uniApprove( IERC20 token, address to, uint256 amount ) internal { if (isETH(token)) revert ApproveCalledOnETH(); token.forceApprove(to, amount); } /// @dev 20K gas is provided to account for possible implementations of name/symbol /// (token implementation might be behind proxy or store the value in storage) function _uniDecode( IERC20 token, bytes4 lowerCaseSelector, bytes4 upperCaseSelector ) private view returns (string memory result) { if (isETH(token)) { return "ETH"; } (bool success, bytes memory data) = address(token).staticcall{gas: 20000}( abi.encodeWithSelector(lowerCaseSelector) ); if (!success) { (success, data) = address(token).staticcall{gas: 20000}(abi.encodeWithSelector(upperCaseSelector)); } if (success && data.length >= 0x40) { (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256)); /* return data is padded up to 32 bytes with ABI encoder also sometimes there is extra 32 bytes of zeros padded in the end: https://github.com/ethereum/solidity/issues/10170 because of that we can't check for equality and instead check that overall data length is greater or equal than string length + extra 64 bytes */ if (offset == 0x20 && data.length >= 0x40 + len) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly result := add(data, 0x40) } return result; } } if (success && data.length == 32) { uint256 len = 0; while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) { unchecked { len++; } } if (len > 0) { assembly ("memory-safe") { // solhint-disable-line no-inline-assembly mstore(data, len) } return string(data); } } return StringUtil.toHex(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @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 towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { IPostInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPostInteraction.sol"; import { IPreInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPreInteraction.sol"; import { IAmountGetter } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IAmountGetter.sol"; /** * @title Base Extension contract * @notice Contract to define the basic functionality for the limit orders settlement. */ contract BaseExtension is IPreInteraction, IPostInteraction, IAmountGetter { error OnlyLimitOrderProtocol(); uint256 private constant _BASE_POINTS = 10_000_000; // 100% uint256 private constant _GAS_PRICE_BASE = 1_000_000; // 1000 means 1 Gwei address private immutable _LIMIT_ORDER_PROTOCOL; /// @dev Modifier to check if the caller is the limit order protocol contract. modifier onlyLimitOrderProtocol { if (msg.sender != _LIMIT_ORDER_PROTOCOL) revert OnlyLimitOrderProtocol(); _; } /** * @notice Initializes the contract. * @param limitOrderProtocol The limit order protocol contract. */ constructor(address limitOrderProtocol) { _LIMIT_ORDER_PROTOCOL = limitOrderProtocol; } /** * See {IAmountGetter-getMakingAmount} */ function getMakingAmount( IOrderMixin.Order calldata order, bytes calldata /* extension */, bytes32 /* orderHash */, address /* taker */, uint256 takingAmount, uint256 /* remainingMakingAmount */, bytes calldata extraData ) external view returns (uint256) { uint256 rateBump = _getRateBump(extraData); return Math.mulDiv(order.makingAmount, takingAmount * _BASE_POINTS, order.takingAmount * (_BASE_POINTS + rateBump)); } /** * See {IAmountGetter-getTakingAmount} */ function getTakingAmount( IOrderMixin.Order calldata order, bytes calldata /* extension */, bytes32 /* orderHash */, address /* taker */, uint256 makingAmount, uint256 /* remainingMakingAmount */, bytes calldata extraData ) external view returns (uint256) { uint256 rateBump = _getRateBump(extraData); return Math.mulDiv(order.takingAmount, makingAmount * (_BASE_POINTS + rateBump), order.makingAmount * _BASE_POINTS, Math.Rounding.Ceil); } /** * See {IPreInteraction-preInteraction} */ function preInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external onlyLimitOrderProtocol { _preInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } /** * See {IPostInteraction-postInteraction} */ function postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) external onlyLimitOrderProtocol { _postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } function _preInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual {} function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual {} /** * @dev Parses auction rate bump data from the `auctionDetails` field. * `gasBumpEstimate` and `gasPriceEstimate` are used to estimate the transaction costs * which are then offset from the auction rate bump. * @param auctionDetails AuctionDetails is a tightly packed struct of the following format: * ``` * struct AuctionDetails { * bytes3 gasBumpEstimate; * bytes4 gasPriceEstimate; * bytes4 auctionStartTime; * bytes3 auctionDuration; * bytes3 initialRateBump; * (bytes3,bytes2)[N] pointsAndTimeDeltas; * } * ``` * @return rateBump The rate bump. */ function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) { unchecked { uint256 gasBumpEstimate = uint24(bytes3(auctionDetails[0:3])); uint256 gasPriceEstimate = uint32(bytes4(auctionDetails[3:7])); uint256 gasBump = gasBumpEstimate == 0 || gasPriceEstimate == 0 ? 0 : gasBumpEstimate * block.basefee / gasPriceEstimate / _GAS_PRICE_BASE; uint256 auctionStartTime = uint32(bytes4(auctionDetails[7:11])); uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[11:14])); uint256 initialRateBump = uint24(bytes3(auctionDetails[14:17])); uint256 auctionBump = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]); return auctionBump > gasBump ? auctionBump - gasBump : 0; } } /** * @dev Calculates auction price bump. Auction is represented as a piecewise linear function with `N` points. * Each point is represented as a pair of `(rateBump, timeDelta)`, where `rateBump` is the * rate bump in basis points and `timeDelta` is the time delta in seconds. * The rate bump is interpolated linearly between the points. * The last point is assumed to be `(0, auctionDuration)`. * @param auctionStartTime The time when the auction starts. * @param auctionFinishTime The time when the auction finishes. * @param initialRateBump The initial rate bump. * @param pointsAndTimeDeltas The points and time deltas structure. * @return The rate bump at the current time. */ function _getAuctionBump(uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas) private view returns (uint256) { unchecked { if (block.timestamp <= auctionStartTime) { return initialRateBump; } else if (block.timestamp >= auctionFinishTime) { return 0; } uint256 currentPointTime = auctionStartTime; uint256 currentRateBump = initialRateBump; while (pointsAndTimeDeltas.length > 0) { uint256 nextRateBump = uint24(bytes3(pointsAndTimeDeltas[:3])); uint256 nextPointTime = currentPointTime + uint16(bytes2(pointsAndTimeDeltas[3:5])); if (block.timestamp <= nextPointTime) { return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime); } currentRateBump = nextRateBump; currentPointTime = nextPointTime; pointsAndTimeDeltas = pointsAndTimeDeltas[5:]; } return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Extension Library * @notice Library to retrieve data from the bitmap. */ library ExtensionLib { bytes1 private constant _RESOLVER_FEE_FLAG = 0x01; bytes1 private constant _INTEGRATOR_FEE_FLAG = 0x02; bytes1 private constant _CUSTOM_RECEIVER_FLAG = 0x04; uint256 private constant _WHITELIST_SHIFT = 3; /** * @notice Checks if the resolver fee is enabled * @param extraData Data to be processed in the extension * @return True if the resolver fee is enabled */ function resolverFeeEnabled(bytes calldata extraData) internal pure returns (bool) { return extraData[extraData.length - 1] & _RESOLVER_FEE_FLAG == _RESOLVER_FEE_FLAG; } /** * @notice Checks if the integrator fee is enabled * @param extraData Data to be processed in the extension * @return True if the integrator fee is enabled */ function integratorFeeEnabled(bytes calldata extraData) internal pure returns (bool) { return extraData[extraData.length - 1] & _INTEGRATOR_FEE_FLAG == _INTEGRATOR_FEE_FLAG; } /** * @notice Checks if the custom receiver is enabled * @param extraData Data to be processed in the extension * @return True if the custom receiver is specified */ function hasCustomReceiver(bytes calldata extraData) internal pure returns (bool) { return extraData[extraData.length - 1] & _CUSTOM_RECEIVER_FLAG == _CUSTOM_RECEIVER_FLAG; } /** * @notice Gets the number of resolvers in the whitelist * @param extraData Data to be processed in the extension * @return The number of resolvers in the whitelist */ function resolversCount(bytes calldata extraData) internal pure returns (uint256) { return uint8(extraData[extraData.length - 1]) >> _WHITELIST_SHIFT; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { MakerTraits, MakerTraitsLib } from "@1inch/limit-order-protocol-contract/contracts/libraries/MakerTraitsLib.sol"; import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; import { Address, AddressLib } from "@1inch/solidity-utils/contracts/libraries/AddressLib.sol"; import { UniERC20 } from "@1inch/solidity-utils/contracts/libraries/UniERC20.sol"; import { BaseExtension } from "./BaseExtension.sol"; import { ExtensionLib } from "./ExtensionLib.sol"; /** * @title Integrator Fee Extension * @notice Abstract contract designed to integrate fee processing within the post-interaction phase of order execution. */ abstract contract IntegratorFeeExtension is BaseExtension, Ownable { using SafeERC20 for IERC20; using AddressLib for Address; using ExtensionLib for bytes; using MakerTraitsLib for MakerTraits; using UniERC20 for IERC20; /** * @dev Eth transfer failed. The target fallback may have reverted. */ error EthTransferFailed(); /// @dev Allows fees in range [1e-5, 0.65536] uint256 private constant _FEE_BASE = 1e5; address private immutable _WETH; constructor(address weth) { _WETH = weth; } /** * @notice Fallback function to receive ETH. */ receive() external payable {} /** * @param extraData Structured data of length n bytes, segmented as follows: * [0:2] - Fee percentage in basis points. * [2:22] - Integrator address. * [22:42] - Custom receiver address. * [42:n] - ExtraData for other extensions, not utilized by this integration fee extension. * [n] - Bitmap indicating usage flags, where `xxxx xx1x` signifies integration fee usage. Other bits in this bitmap are not used by this extension. */ function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual override { if (extraData.integratorFeeEnabled()) { uint256 fee = takingAmount * uint256(uint16(bytes2(extraData))) / _FEE_BASE; address feeRecipient = address(bytes20(extraData[2:22])); extraData = extraData[22:]; address receiver = order.maker.get(); if (extraData.hasCustomReceiver()) { receiver = address(bytes20(extraData)); extraData = extraData[20:]; } bool isEth = order.takerAsset.get() == address(_WETH) && order.makerTraits.unwrapWeth(); if (isEth) { if (fee > 0) { _sendEth(feeRecipient, fee); } unchecked { _sendEth(receiver, takingAmount - fee); } } else { if (fee > 0) { IERC20(order.takerAsset.get()).safeTransfer(feeRecipient, fee); } unchecked { IERC20(order.takerAsset.get()).safeTransfer(receiver, takingAmount - fee); } } } super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } /** * @notice Retrieves funds accidently sent directly to the contract address * @param token ERC20 token to retrieve * @param amount amount to retrieve */ function rescueFunds(IERC20 token, uint256 amount) external onlyOwner { token.uniTransfer(payable(msg.sender), amount); } function _sendEth(address target, uint256 amount) private { (bool success, ) = target.call{value: amount}(""); if (!success) { revert EthTransferFailed(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { FeeBankCharger } from "../FeeBankCharger.sol"; import { BaseExtension } from "./BaseExtension.sol"; import { ExtensionLib } from "./ExtensionLib.sol"; /** * @title Resolver Fee Extension * @notice Abstract contract used as an extension in settlement contract to charge a fee resolver in the `postInteraction` method. */ abstract contract ResolverFeeExtension is BaseExtension, FeeBankCharger { using ExtensionLib for bytes; uint256 private constant _ORDER_FEE_BASE_POINTS = 1e15; constructor(IERC20 feeToken, address owner) FeeBankCharger(feeToken, owner) {} /** * @dev Calculates the resolver fee. * @param fee Scaled resolver fee. * @param orderMakingAmount Making amount from the order. * @param actualMakingAmount Making amount that was actually filled. * @return resolverFee Calculated resolver fee. */ function _getResolverFee( uint256 fee, uint256 orderMakingAmount, uint256 actualMakingAmount ) internal pure virtual returns(uint256) { return fee * _ORDER_FEE_BASE_POINTS * actualMakingAmount / orderMakingAmount; } /** * @param extraData Structured data of length n bytes, segmented as follows: * [0:4] - Resolver fee information. * [4:n] - ExtraData for other extensions, not utilized by this resolver fee extension. * [n] - Bitmap indicating usage flags, where `xxxx xxx1` signifies resolver fee usage. Other bits in this bitmap are not used by this extension. */ function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual override { if (extraData.resolverFeeEnabled()) { uint256 resolverFee = _getResolverFee(uint256(uint32(bytes4(extraData[:4]))), order.makingAmount, makingAmount); _chargeFee(taker, resolverFee); extraData = extraData[4:]; } super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { BaseExtension } from "./BaseExtension.sol"; import { ExtensionLib } from "./ExtensionLib.sol"; /** * @title Whitelist Extension * @notice Abstract contract designed to check resolvers from orders in whitelist within the post-interaction phase of order execution. * Ensures that only transactions from whitelisted resolvers are processed, enhancing security and compliance. */ abstract contract WhitelistExtension is BaseExtension { using ExtensionLib for bytes; error ResolverIsNotWhitelisted(); /** * @dev Validates whether the resolver is whitelisted. * @param whitelist Whitelist is tightly packed struct of the following format: * ``` * struct WhitelistDetails { * bytes4 auctionStartTime; * (bytes10,bytes2)[N] resolversAddressesAndTimeDeltas; * } * ``` * Resolvers in the list are sorted in ascending order by the time when they are allowed to interact with the order. * Time deltas represent the time in seconds between the adjacent resolvers. * Only 10 lowest bytes of the resolver address are used for comparison. * @param whitelistSize The amount of resolvers in the whitelist. * @param resolver The resolver to check. * @return Whether the resolver is whitelisted. */ function _isWhitelisted(bytes calldata whitelist, uint256 whitelistSize, address resolver) internal view virtual returns (bool) { unchecked { uint256 allowedTime = uint32(bytes4(whitelist[0:4])); // initially set to auction start time whitelist = whitelist[4:]; uint80 maskedResolverAddress = uint80(uint160(resolver)); for (uint256 i = 0; i < whitelistSize; i++) { uint80 whitelistedAddress = uint80(bytes10(whitelist[:10])); allowedTime += uint16(bytes2(whitelist[10:12])); // add next time delta if (maskedResolverAddress == whitelistedAddress) { return allowedTime <= block.timestamp; } else if (allowedTime > block.timestamp) { return false; } whitelist = whitelist[12:]; } return false; } } /** * @param extraData Structured data of length n bytes, segmented as follows: * [0:k] - Data as defined by the `whitelist` parameter for the `_isWhitelisted` method, * where k depends on the amount of resolvers in the whitelist, as indicated by the bitmap in the last byte. * [k:n] - ExtraData for other extensions, not utilized by this whitelist extension. * [n] - Bitmap `VVVV Vxxx` where V bits represent the amount of resolvers in the whitelist. The remaining bits in this bitmap are not used by this extension. */ function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual override { uint256 resolversCount = extraData.resolversCount(); unchecked { uint256 whitelistSize = 4 + resolversCount * 12; if (!_isWhitelisted(extraData[:whitelistSize], resolversCount, taker)) revert ResolverIsNotWhitelisted(); super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData[whitelistSize:]); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol"; import { IFeeBank } from "./interfaces/IFeeBank.sol"; /** * @title FeeBank * @notice FeeBank contract introduces a credit system for paying fees. * A user can deposit tokens to the FeeBank contract, obtain credits and then use them to pay fees. * @dev FeeBank is coupled with FeeBankCharger to actually charge fees. */ contract FeeBank is IFeeBank, Ownable { using SafeERC20 for IERC20; error ZeroAddress(); IERC20 private immutable _FEE_TOKEN; IFeeBankCharger private immutable _CHARGER; mapping(address account => uint256 availableCredit) private _accountDeposits; constructor(IFeeBankCharger charger, IERC20 feeToken, address owner) Ownable(owner) { if (address(feeToken) == address(0)) revert ZeroAddress(); _CHARGER = charger; _FEE_TOKEN = feeToken; } /** * @notice See {IFeeBank-availableCredit}. */ function availableCredit(address account) external view returns (uint256) { return _CHARGER.availableCredit(account); } /** * @notice See {IFeeBank-deposit}. */ function deposit(uint256 amount) external returns (uint256) { return _depositFor(msg.sender, amount); } /** * @notice See {IFeeBank-depositFor}. */ function depositFor(address account, uint256 amount) external returns (uint256) { return _depositFor(account, amount); } /** * @notice See {IFeeBank-depositWithPermit}. */ function depositWithPermit(uint256 amount, bytes calldata permit) external returns (uint256) { return depositForWithPermit(msg.sender, amount, permit); } /** * @notice See {IFeeBank-depositForWithPermit}. */ function depositForWithPermit( address account, uint256 amount, bytes calldata permit ) public returns (uint256) { _FEE_TOKEN.safePermit(permit); return _depositFor(account, amount); } /** * @notice See {IFeeBank-withdraw}. */ function withdraw(uint256 amount) external returns (uint256) { return _withdrawTo(msg.sender, amount); } /** * @notice See {IFeeBank-withdrawTo}. */ function withdrawTo(address account, uint256 amount) external returns (uint256) { return _withdrawTo(account, amount); } /** * @notice Admin method returns commissions spent by users. * @param accounts Accounts whose commissions are being withdrawn. * @return totalAccountFees The total amount of accounts commissions. */ function gatherFees(address[] calldata accounts) external onlyOwner returns (uint256 totalAccountFees) { uint256 accountsLength = accounts.length; unchecked { for (uint256 i = 0; i < accountsLength; ++i) { address account = accounts[i]; uint256 accountDeposit = _accountDeposits[account]; uint256 availableCredit_ = _CHARGER.availableCredit(account); _accountDeposits[account] = availableCredit_; totalAccountFees += accountDeposit - availableCredit_; // overflow is impossible due to checks in FeeBankCharger } } _FEE_TOKEN.safeTransfer(msg.sender, totalAccountFees); } function _depositFor(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) { if (account == address(0)) revert ZeroAddress(); _FEE_TOKEN.safeTransferFrom(msg.sender, address(this), amount); unchecked { _accountDeposits[account] += amount; // overflow is impossible due to limited _FEE_TOKEN supply } totalAvailableCredit = _CHARGER.increaseAvailableCredit(account, amount); } function _withdrawTo(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) { totalAvailableCredit = _CHARGER.decreaseAvailableCredit(msg.sender, amount); unchecked { _accountDeposits[msg.sender] -= amount; // underflow is impossible due to checks in FeeBankCharger } _FEE_TOKEN.safeTransfer(account, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IFeeBank } from "./interfaces/IFeeBank.sol"; import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol"; import { FeeBank } from "./FeeBank.sol"; /** * @title FeeBankCharger * @notice FeeBankCharger contract implements logic to increase or decrease users' credits in FeeBank. */ contract FeeBankCharger is IFeeBankCharger { error OnlyFeeBankAccess(); error NotEnoughCredit(); /** * @notice See {IFeeBankCharger-feeBank}. */ IFeeBank public immutable FEE_BANK; mapping(address => uint256) private _creditAllowance; /** * @dev Modifier to check if the sender is a FEE_BANK contract. */ modifier onlyFeeBank() { if (msg.sender != address(FEE_BANK)) revert OnlyFeeBankAccess(); _; } constructor(IERC20 feeToken, address owner) { FEE_BANK = new FeeBank(this, feeToken, owner); } /** * @notice See {IFeeBankCharger-availableCredit}. */ function availableCredit(address account) external view returns (uint256) { return _creditAllowance[account]; } /** * @notice See {IFeeBankCharger-increaseAvailableCredit}. */ function increaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) { allowance = _creditAllowance[account]; unchecked { allowance += amount; // overflow is impossible due to limited _token supply } _creditAllowance[account] = allowance; } /** * @notice See {IFeeBankCharger-decreaseAvailableCredit}. */ function decreaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) { return _creditAllowance[account] -= amount; // checked math is needed to prevent underflow } /** * @notice Internal function that charges a specified fee from a given account's credit allowance. * @dev Reverts with 'NotEnoughCredit' if the account's credit allowance is insufficient to cover the fee. * @param account The address of the account from which the fee is being charged. * @param fee The amount of fee to be charged from the account. */ function _chargeFee(address account, uint256 fee) internal virtual { if (fee > 0) { uint256 currentAllowance = _creditAllowance[account]; if (currentAllowance < fee) revert NotEnoughCredit(); unchecked { _creditAllowance[account] = currentAllowance - fee; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFeeBank { /** * @notice Returns the available credit for a given account in the FeeBank contract. * @param account The address of the account for which the available credit is being queried. * @return availableCredit The available credit of the queried account. */ function availableCredit(address account) external view returns (uint256 availableCredit); /** * @notice Increases the caller's available credit by the specified amount. * @param amount The amount of credit to be added to the caller's account. * @return totalAvailableCredit The updated available credit of the caller's account. */ function deposit(uint256 amount) external returns (uint256 totalAvailableCredit); /** * @notice Increases the specified account's available credit by the specified amount. * @param account The address of the account for which the available credit is being increased. * @param amount The amount of credit to be added to the account. * @return totalAvailableCredit The updated available credit of the specified account. */ function depositFor(address account, uint256 amount) external returns (uint256 totalAvailableCredit); /** * @notice Increases the caller's available credit by a specified amount with permit. * @param amount The amount of credit to be added to the caller's account. * @param permit The permit data authorizing the transaction. * @return totalAvailableCredit The updated available credit of the caller's account. */ function depositWithPermit(uint256 amount, bytes calldata permit) external returns (uint256 totalAvailableCredit); /** * @notice Increases the specified account's available credit by a specified amount with permit. * @param account The address of the account for which the available credit is being increased. * @param amount The amount of credit to be added to the account. * @param permit The permit data authorizing the transaction. * @return totalAvailableCredit The updated available credit of the specified account. */ function depositForWithPermit(address account, uint256 amount, bytes calldata permit) external returns (uint256 totalAvailableCredit); /** * @notice Withdraws a specified amount of credit from the caller's account. * @param amount The amount of credit to be withdrawn from the caller's account. * @return totalAvailableCredit The updated available credit of the caller's account. */ function withdraw(uint256 amount) external returns (uint256 totalAvailableCredit); /** * @notice Withdraws a specified amount of credit to the specified account. * @param account The address of the account to which the credit is being withdrawn. * @param amount The amount of credit to be withdrawn. * @return totalAvailableCredit The updated available credit of the caller's account. */ function withdrawTo(address account, uint256 amount) external returns (uint256 totalAvailableCredit); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IFeeBank } from "./IFeeBank.sol"; interface IFeeBankCharger { /** * @notice Returns the instance of the FeeBank contract. * @return The instance of the FeeBank contract. */ function FEE_BANK() external view returns (IFeeBank); // solhint-disable-line func-name-mixedcase /** * @notice Returns the available credit for a given account. * @param account The address of the account for which the available credit is being queried. * @return The available credit of the queried account. */ function availableCredit(address account) external view returns (uint256); /** * @notice Increases the available credit of a given account by a specified amount. * @param account The address of the account for which the available credit is being increased. * @param amount The amount by which the available credit will be increased. * @return allowance The updated available credit of the specified account. */ function increaseAvailableCredit(address account, uint256 amount) external returns (uint256 allowance); /** * @notice Decreases the available credit of a given account by a specified amount. * @param account The address of the account for which the available credit is being decreased. * @param amount The amount by which the available credit will be decreased. * @return allowance The updated available credit of the specified account. */ function decreaseAvailableCredit(address account, uint256 amount) external returns (uint256 allowance); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; import { BaseExtension } from "./extensions/BaseExtension.sol"; import { IntegratorFeeExtension } from "./extensions/IntegratorFeeExtension.sol"; import { ResolverFeeExtension } from "./extensions/ResolverFeeExtension.sol"; import { WhitelistExtension } from "./extensions/WhitelistExtension.sol"; /** * @title Simple Settlement contract * @notice Contract to execute limit orders settlement, created by Fusion mode. */ contract SimpleSettlement is WhitelistExtension, ResolverFeeExtension, IntegratorFeeExtension { /** * @notice Initializes the contract. * @param limitOrderProtocol The limit order protocol contract. * @param feeToken The token to charge protocol fees in. * @param weth The WETH address. * @param owner The owner of the contract. */ constructor(address limitOrderProtocol, IERC20 feeToken, address weth, address owner) BaseExtension(limitOrderProtocol) ResolverFeeExtension(feeToken, owner) IntegratorFeeExtension(weth) Ownable(owner) {} function _postInteraction( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData ) internal virtual override(WhitelistExtension, ResolverFeeExtension, IntegratorFeeExtension) { super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "evmVersion": "shanghai", "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"limitOrderProtocol","type":"address"},{"internalType":"contract IERC20","name":"feeToken","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"EthTransferFailed","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPriorityFee","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"NotEnoughCredit","type":"error"},{"inputs":[],"name":"OnlyFeeBankAccess","type":"error"},{"inputs":[],"name":"OnlyLimitOrderProtocol","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ResolverIsNotWhitelisted","type":"error"},{"inputs":[],"name":"SafeTransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"FEE_BANK","outputs":[{"internalType":"contract IFeeBank","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"availableCredit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"decreaseAvailableCredit","outputs":[{"internalType":"uint256","name":"allowance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"receiver","type":"uint256"},{"internalType":"Address","name":"makerAsset","type":"uint256"},{"internalType":"Address","name":"takerAsset","type":"uint256"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"MakerTraits","name":"makerTraits","type":"uint256"}],"internalType":"struct IOrderMixin.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"getMakingAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"receiver","type":"uint256"},{"internalType":"Address","name":"makerAsset","type":"uint256"},{"internalType":"Address","name":"takerAsset","type":"uint256"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"MakerTraits","name":"makerTraits","type":"uint256"}],"internalType":"struct IOrderMixin.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"getTakingAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseAvailableCredit","outputs":[{"internalType":"uint256","name":"allowance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"receiver","type":"uint256"},{"internalType":"Address","name":"makerAsset","type":"uint256"},{"internalType":"Address","name":"takerAsset","type":"uint256"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"MakerTraits","name":"makerTraits","type":"uint256"}],"internalType":"struct IOrderMixin.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"extension","type":"bytes"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"uint256","name":"remainingMakingAmount","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"postInteraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"receiver","type":"uint256"},{"internalType":"Address","name":"makerAsset","type":"uint256"},{"internalType":"Address","name":"takerAsset","type":"uint256"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"MakerTraits","name":"makerTraits","type":"uint256"}],"internalType":"struct IOrderMixin.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"extension","type":"bytes"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"makingAmount","type":"uint256"},{"internalType":"uint256","name":"takingAmount","type":"uint256"},{"internalType":"uint256","name":"remainingMakingAmount","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"preInteraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60e0346200018657601f6200283038819003918201601f1916830192916001600160401b03918285118486101762000172578160809285926040978852833981010312620001865762000052826200018a565b60208301516001600160a01b03918282169182900362000186576200008760606200007f8888016200018a565b96016200018a565b906080528551916110898084019584871090871117620001725760609284928692620017a785393088526020880152169485888201520301905ff080156200016857811660a05281156200015157600180546001600160a01b031981168417909155935193167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360c0526116079081620001a082396080518181816106590152610e02015260a05181818161037901528181610c610152610d16015260c05181610a0e0152f35b8351631e4fbdf760e01b81525f6004820152602490fd5b84513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b0382168203620001865756fe608060409080825260049081361015610022575b5050503615610020575f80fd5b005b5f3560e01c9081630986bdd514610dd1575080631d9671c314610d3a5780632ce26aeb14610ccc5780633ee5ef1f14610c10578063462ebde2146106265780635886216f146105c5578063715018a61461052957806378e3214f1461041957806385eda2de146103295780638da5cb5b146102d7578063d7ff8a80146101985763f2fde38b146100b25780610013565b346101945760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610194576100e9610e7d565b906100f26111fd565b73ffffffffffffffffffffffffffffffffffffffff809216928315610165575050600154827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b905f60249251917f1e4fbdf7000000000000000000000000000000000000000000000000000000008352820152fd5b5f80fd5b509034610194576101ba906101ac36610f4d565b95509350509693505061106a565b9060c08101359362989680928301908184116102ab5760a0916101dc91610ff3565b91013582810292818404149015171561027f576101fa828286611131565b9382156102535709610211575b6020925051908152f35b9060018101809111610227576020925090610207565b6011837f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6012867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011877f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b503461019457817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457610360610e7d565b73ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036103f157165f525f602052815f209182549160243583039283116103c55750816020935551908152f35b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8284517fa4544199000000000000000000000000000000000000000000000000000000008152fd5b50903461019457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457813573ffffffffffffffffffffffffffffffffffffffff81169081810361019457602435916104766111fd565b8261047d57005b801590811561050b575b50156104fc57508047106104d5575f8080809333611388f16104a761124e565b50156104af57005b517fb12d13eb000000000000000000000000000000000000000000000000000000008152fd5b50517ff4d678b8000000000000000000000000000000000000000000000000000000008152fd5b909150610020925033906113e6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9150145f610487565b34610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945761055f6111fd565b5f73ffffffffffffffffffffffffffffffffffffffff6001547fffffffffffffffffffffffff00000000000000000000000000000000000000008116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b82346101945760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945760209073ffffffffffffffffffffffffffffffffffffffff610615610e7d565b165f525f8252805f20549051908152f35b5090346101945761063636610ea0565b9398965093965050925073ffffffffffffffffffffffffffffffffffffffff90817f0000000000000000000000000000000000000000000000000000000000000000163303610be8576106876113a4565b15610bc057809280917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff95868201828111610b94576106eb907f0200000000000000000000000000000000000000000000000000000000000000918291858761147e565b351614610910575b505050829481978585018581116108e457610733907f0100000000000000000000000000000000000000000000000000000000000000918291888761147e565b3516146107c8575b5050505050810181811161079c5761075490828561147e565b3560fb1c90600c820286019081116101945761076f936114f9565b1561077657005b517f4b576069000000000000000000000000000000000000000000000000000000008152fd5b6011867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b91939750919394508689116101945766038d7ea4c68000833560e01c818102939181159185041417156108b8579161080a61080f9260a08c9695013592610ff3565b611033565b9283610848575b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9250019301905f8080808061073b565b851692835f525f602052875f2054818110610890577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc945f525f60205203875f20555f610816565b8389517fa7fd3792000000000000000000000000000000000000000000000000000000008152fd5b60118a7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b60118c7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b9194509150620186a061095785357fffff0000000000000000000000000000000000000000000000000000000000008082169160028710610b7f575b505060f01c83610ff3565b04908260161161019457600285013560601c9160168601917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea850194839580988860208d013516957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe98401838111610b50576109f8907f0400000000000000000000000000000000000000000000000000000000000000918291868561147e565b351614610ac3575b505050508560808a013516867f000000000000000000000000000000000000000000000000000000000000000016811480610a95575b15610a685750610a50938280610a58575b505003906114b7565b5f80806106f3565b610a61916114b7565b5f82610a47565b929190610a7f948280610a84575b505003916113e6565b610a50565b610a8e91866113e6565b5f82610a76565b507e8000000000000000000000000000000000000000000000000000000000000060e08b0135161515610a36565b9297509298509350357fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008082169160148610610b38575b505060601c9260141161019457602a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd691019401955f808080610a00565b908092506016886014030160031b1b16165f80610afa565b50505060118f7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b908092508660020360031b1b16165f8061094c565b60118d7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8888517f8c331638000000000000000000000000000000000000000000000000000000008152fd5b8888517fd25aa106000000000000000000000000000000000000000000000000000000008152fd5b50903461019457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457610c48610e7d565b73ffffffffffffffffffffffffffffffffffffffff90817f0000000000000000000000000000000000000000000000000000000000000000163303610ca45760209350165f525f8252805f209060243582540180925551908152f35b5050517fa4544199000000000000000000000000000000000000000000000000000000008152fd5b8234610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610194576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50903461019457610d5e91610d4e36610f4d565b955093505095949793505061106a565b90629896809182840293808504841490151715610da55782018092116103c5575092610d9e9160a0610d9660209660c0850135610ff3565b920135611131565b9051908152f35b6011827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b90503461019457610de136610ea0565b5050505050505050505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610e2a57005b7fd25aa106000000000000000000000000000000000000000000000000000000008152fd5b9181601f840112156101945782359167ffffffffffffffff8311610194576020838186019501011161019457565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019457565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc81016101e0811261019457610100136101945760049167ffffffffffffffff610104358181116101945783610ef891600401610e4f565b9390939261012435926101443573ffffffffffffffffffffffffffffffffffffffff811681036101945792610164359261018435926101a435926101c43591821161019457610f4991600401610e4f565b9091565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82016101c0811261019457610100136101945760049167ffffffffffffffff90610104358281116101945781610fa791600401610e4f565b9390939261012435926101443573ffffffffffffffffffffffffffffffffffffffff811681036101945792610164359261018435926101a43591821161019457610f4991600401610e4f565b8181029291811591840414171561100657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b811561103d570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b908060031161019457813560e81c8160071161019457600383013560e01c9080158015611129575b156111125750505f915b81600b1161019457600781013560e01c82600e116101945782601111610194577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef6111009301916011810191600b600e83013560e81c92013560e81c8101906112e3565b8181111561110c570390565b50505f90565b620f424091611122914802611033565b049161109c565b508115611092565b9091828202917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848209938380861095039480860395146111ed57848311156111c357829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b60046040517f227bc153000000000000000000000000000000000000000000000000000000008152fd5b5050906111fa9250611033565b90565b73ffffffffffffffffffffffffffffffffffffffff60015416330361121e57565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b3d156112de5767ffffffffffffffff903d8281116112b15760405192601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168401908111848210176112b15760405282523d5f602084013e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b606090565b90919392814211155f146112f8575050505090565b829394919242101561139b57849291925b61131f57506111fa935082039142900302611033565b9060039085821161019457823560e81c906005928784116101945784013560f01c8101948542111561137c575050907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb9193920194019384611309565b9250936111fa9650809550849193500393429003029142030201611033565b50505050505f90565b483a03640277cf2a0048105f146113c2576064604648029102111590565b64183cd7f1004811156113dc576064604148029102111590565b489060011b111590565b9160446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282855af1908161145c575b501561143257565b60046040517ffb7f5079000000000000000000000000000000000000000000000000000000008152fd5b90503d15611476575060015f5114601f3d11165b5f61142a565b3b1515611470565b9082101561148a570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f80809381935af16114c761124e565b50156114cf57565b60046040517f6d963f88000000000000000000000000000000000000000000000000000000008152fd5b9291909283600411610194579069ffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6004843560e01c940195019316925f925b828410611554575050505050505f90565b600a9082821161019457600c918383116101945787013560f01c0195803560b01c86036115875750505050505042101590565b95929394954284116115c65701949360010192917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff49190910190611543565b505050505050505f9056fea26469706673582212205430c44c5fe34972cb4f2595e568f20c54bb9b66c75864ec1431738b3954cd4364736f6c6343000817003360c03461013057601f61108938819003918201601f19168301916001600160401b03831184841017610134578084926060946040528339810103126101305780516001600160a01b03808216820361013057602083015192818416908185036101305760400151828116809103610130578015610118575f80546001600160a01b03198116831782556040519416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a315610109575060a052608052604051610f40908161014982396080518181816102e00152818161073401528181610bf90152610c78015260a0518181816102ad0152818161053d01528181610b9b0152610d4e0152f35b63d92e233d60e01b8152600490fd5b604051631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe604060808152600480361015610013575f80fd5b5f3560e01c8063205c28781461069a5780632e1a7d4d1461065c5780632f4f21e21461061657806332d323a5146105ad5780635886216f146104b8578063715018a61461041e5780638da5cb5b146103cd57806397a2cb641461021a578063b6b55f25146101dc578063bfe91734146101785763f2fde38b14610094575f80fd5b346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576100cb6106e0565b906100d4610e22565b73ffffffffffffffffffffffffffffffffffffffff8092169283156101455750505f54827fffffffffffffffffffffffff00000000000000000000000000000000000000008216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b905f60249251917f1e4fbdf7000000000000000000000000000000000000000000000000000000008352820152fd5b5f80fd5b50903461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745760243567ffffffffffffffff8111610174576020926101cd6101d59236908301610703565b913533610731565b9051908152f35b5090346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576101d56020923533610c53565b503461017457602091827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457813567ffffffffffffffff808211610174573660238301121561017457818401359081116101745760059360243683871b8501820111610174579490610290610e22565b5f955f9473ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000000000000000000000000000000000000000000016945b86881061030a578a8a8a61030482337f0000000000000000000000000000000000000000000000000000000000000000610e72565b51908152f35b9091929394959698848a831b8401013590848216809203610174575f8281526001808e52908b9020548b517f5886216f000000000000000000000000000000000000000000000000000000008152808a0185905293918e858a818e5afa9283156103c3578f905f94610393575b600196505f5252818c5f205503019901969594939291906102cf565b935085813d83116103bc575b6103a98183610aee565b81010312610174578e6001955193610377565b503d61039f565b8d513d5f823e3d90fd5b8234610174575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745760209073ffffffffffffffffffffffffffffffffffffffff5f54169051908152f35b34610174575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457610454610e22565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b503461017457602091827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457826104f36106e0565b602473ffffffffffffffffffffffffffffffffffffffff9182855196879485937f5886216f00000000000000000000000000000000000000000000000000000000855216908301527f0000000000000000000000000000000000000000000000000000000000000000165afa9182156105a3575f92610574575b5051908152f35b9091508281813d831161059c575b61058c8183610aee565b810103126101745751905f61056d565b503d610582565b50513d5f823e3d90fd5b5090346101745760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576105e66106e0565b916044359267ffffffffffffffff84116101745761060c6101d592602095369101610703565b9160243590610731565b823461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020906101d56106536106e0565b60243590610c53565b5090346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576101d56020923533610b5c565b823461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020906101d56106d76106e0565b60243590610b5c565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361017457565b9181601f840112156101745782359167ffffffffffffffff8311610174576020838186019501011161017457565b917f0000000000000000000000000000000000000000000000000000000000000000604051949081606414610a34578160481461096e578160e0146109315781610100146108f257816060146108175750610160146107b2576004847f68275857000000000000000000000000000000000000000000000000000000008152fd5b610164846101605f949596937f2b67b570000000000000000000000000000000000000000000000000000000008695526004830137826e22d473030f116ddee9f6b43ac78ba35af15b1561080c5761080991610c53565b90565b6040513d5f823e3d90fd5b610164915094602060405f9596979486957f2b67b570000000000000000000000000000000000000000000000000000000008552336004860152602485015260148160508601377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff65ffffffffffff9080601484013560e01c0182166064870152601883013560e01c60848701523060a4870152601c83013560e01c011660c485015261010060e4850152816101048501528280820161012486013701610144830137826e22d473030f116ddee9f6b43ac78ba35af16107fb565b6101049150945f8094959661010082957f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af16107fb565b60e49150945f8094959660e082957fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af16107fb565b6101049150945f809495967f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82957f8fcbaf0c00000000000000000000000000000000000000000000000000000000855233600486015230602486015260206008602883013592803560e01c60448901527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600482013560e01c016064890152600160848901528360ff1c601b0160a48901520160c48701371660e48401525af16107fb565b60e49150945f809495967f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82957fd505accf00000000000000000000000000000000000000000000000000000000855233600486015230602486015260206024604483013592828160448a01377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382013560e01c0160648901528360ff1c601b0160848901520160a48701371660c48401525af16107fb565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2f57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040517f85eda2de000000000000000000000000000000000000000000000000000000008152336004820152602481018390529092916020826044815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1801561080c575f90610c1f575b610c1d925093335f52600160205260405f208281540390557f0000000000000000000000000000000000000000000000000000000000000000610e72565b565b506020823d602011610c4b575b81610c3960209383610aee565b8101031261017457610c1d9151610bdf565b3d9150610c2c565b73ffffffffffffffffffffffffffffffffffffffff91828216908115610df8576064937f00000000000000000000000000000000000000000000000000000000000000006040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201528360448201525f602097889282855af19081610dd6575b5015610dac575f928352600185526040808420805484019055517f3ee5ef1f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff94909416600485015260248401919091528391839160449183917f0000000000000000000000000000000000000000000000000000000000000000165af191821561080c575f92610d8257505090565b90809250813d8311610da5575b610d998183610aee565b81010312610174575190565b503d610d8f565b60046040517ff4059071000000000000000000000000000000000000000000000000000000008152fd5b90503d15610df0575060015f5114601f3d11165b5f610ce0565b3b1515610dea565b60046040517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff5f54163303610e4257565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b9160446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282855af19081610ee8575b5015610ebe57565b60046040517ffb7f5079000000000000000000000000000000000000000000000000000000008152fd5b90503d15610f02575060015f5114601f3d11165b5f610eb6565b3b1515610efc56fea264697066735822122050c2fc8dc11068b5d74ada2d460fc0ef6ed99e8277f609a89ab6190cd71916b864736f6c63430008170033000000000000000000000000111111125421ca6dc452d289314280a0f8842a65000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000011799622f4d98a24514011e8527b969f7488ef47
Deployed Bytecode
0x608060409080825260049081361015610022575b5050503615610020575f80fd5b005b5f3560e01c9081630986bdd514610dd1575080631d9671c314610d3a5780632ce26aeb14610ccc5780633ee5ef1f14610c10578063462ebde2146106265780635886216f146105c5578063715018a61461052957806378e3214f1461041957806385eda2de146103295780638da5cb5b146102d7578063d7ff8a80146101985763f2fde38b146100b25780610013565b346101945760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610194576100e9610e7d565b906100f26111fd565b73ffffffffffffffffffffffffffffffffffffffff809216928315610165575050600154827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b905f60249251917f1e4fbdf7000000000000000000000000000000000000000000000000000000008352820152fd5b5f80fd5b509034610194576101ba906101ac36610f4d565b95509350509693505061106a565b9060c08101359362989680928301908184116102ab5760a0916101dc91610ff3565b91013582810292818404149015171561027f576101fa828286611131565b9382156102535709610211575b6020925051908152f35b9060018101809111610227576020925090610207565b6011837f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6012867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011877f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b503461019457817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457610360610e7d565b73ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000798ef9b5e2dd6cfddb1e6a3e09bb6e4531492db11633036103f157165f525f602052815f209182549160243583039283116103c55750816020935551908152f35b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8284517fa4544199000000000000000000000000000000000000000000000000000000008152fd5b50903461019457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457813573ffffffffffffffffffffffffffffffffffffffff81169081810361019457602435916104766111fd565b8261047d57005b801590811561050b575b50156104fc57508047106104d5575f8080809333611388f16104a761124e565b50156104af57005b517fb12d13eb000000000000000000000000000000000000000000000000000000008152fd5b50517ff4d678b8000000000000000000000000000000000000000000000000000000008152fd5b909150610020925033906113e6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9150145f610487565b34610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945761055f6111fd565b5f73ffffffffffffffffffffffffffffffffffffffff6001547fffffffffffffffffffffffff00000000000000000000000000000000000000008116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b82346101945760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101945760209073ffffffffffffffffffffffffffffffffffffffff610615610e7d565b165f525f8252805f20549051908152f35b5090346101945761063636610ea0565b9398965093965050925073ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000111111125421ca6dc452d289314280a0f8842a65163303610be8576106876113a4565b15610bc057809280917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff95868201828111610b94576106eb907f0200000000000000000000000000000000000000000000000000000000000000918291858761147e565b351614610910575b505050829481978585018581116108e457610733907f0100000000000000000000000000000000000000000000000000000000000000918291888761147e565b3516146107c8575b5050505050810181811161079c5761075490828561147e565b3560fb1c90600c820286019081116101945761076f936114f9565b1561077657005b517f4b576069000000000000000000000000000000000000000000000000000000008152fd5b6011867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b91939750919394508689116101945766038d7ea4c68000833560e01c818102939181159185041417156108b8579161080a61080f9260a08c9695013592610ff3565b611033565b9283610848575b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9250019301905f8080808061073b565b851692835f525f602052875f2054818110610890577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc945f525f60205203875f20555f610816565b8389517fa7fd3792000000000000000000000000000000000000000000000000000000008152fd5b60118a7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b60118c7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b9194509150620186a061095785357fffff0000000000000000000000000000000000000000000000000000000000008082169160028710610b7f575b505060f01c83610ff3565b04908260161161019457600285013560601c9160168601917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea850194839580988860208d013516957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe98401838111610b50576109f8907f0400000000000000000000000000000000000000000000000000000000000000918291868561147e565b351614610ac3575b505050508560808a013516867f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216811480610a95575b15610a685750610a50938280610a58575b505003906114b7565b5f80806106f3565b610a61916114b7565b5f82610a47565b929190610a7f948280610a84575b505003916113e6565b610a50565b610a8e91866113e6565b5f82610a76565b507e8000000000000000000000000000000000000000000000000000000000000060e08b0135161515610a36565b9297509298509350357fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008082169160148610610b38575b505060601c9260141161019457602a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd691019401955f808080610a00565b908092506016886014030160031b1b16165f80610afa565b50505060118f7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b908092508660020360031b1b16165f8061094c565b60118d7f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8888517f8c331638000000000000000000000000000000000000000000000000000000008152fd5b8888517fd25aa106000000000000000000000000000000000000000000000000000000008152fd5b50903461019457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019457610c48610e7d565b73ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000798ef9b5e2dd6cfddb1e6a3e09bb6e4531492db1163303610ca45760209350165f525f8252805f209060243582540180925551908152f35b5050517fa4544199000000000000000000000000000000000000000000000000000000008152fd5b8234610194575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610194576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000798ef9b5e2dd6cfddb1e6a3e09bb6e4531492db1168152f35b50903461019457610d5e91610d4e36610f4d565b955093505095949793505061106a565b90629896809182840293808504841490151715610da55782018092116103c5575092610d9e9160a0610d9660209660c0850135610ff3565b920135611131565b9051908152f35b6011827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b90503461019457610de136610ea0565b5050505050505050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000111111125421ca6dc452d289314280a0f8842a65163303610e2a57005b7fd25aa106000000000000000000000000000000000000000000000000000000008152fd5b9181601f840112156101945782359167ffffffffffffffff8311610194576020838186019501011161019457565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019457565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc81016101e0811261019457610100136101945760049167ffffffffffffffff610104358181116101945783610ef891600401610e4f565b9390939261012435926101443573ffffffffffffffffffffffffffffffffffffffff811681036101945792610164359261018435926101a435926101c43591821161019457610f4991600401610e4f565b9091565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82016101c0811261019457610100136101945760049167ffffffffffffffff90610104358281116101945781610fa791600401610e4f565b9390939261012435926101443573ffffffffffffffffffffffffffffffffffffffff811681036101945792610164359261018435926101a43591821161019457610f4991600401610e4f565b8181029291811591840414171561100657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b811561103d570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b908060031161019457813560e81c8160071161019457600383013560e01c9080158015611129575b156111125750505f915b81600b1161019457600781013560e01c82600e116101945782601111610194577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef6111009301916011810191600b600e83013560e81c92013560e81c8101906112e3565b8181111561110c570390565b50505f90565b620f424091611122914802611033565b049161109c565b508115611092565b9091828202917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848209938380861095039480860395146111ed57848311156111c357829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b60046040517f227bc153000000000000000000000000000000000000000000000000000000008152fd5b5050906111fa9250611033565b90565b73ffffffffffffffffffffffffffffffffffffffff60015416330361121e57565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b3d156112de5767ffffffffffffffff903d8281116112b15760405192601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168401908111848210176112b15760405282523d5f602084013e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b606090565b90919392814211155f146112f8575050505090565b829394919242101561139b57849291925b61131f57506111fa935082039142900302611033565b9060039085821161019457823560e81c906005928784116101945784013560f01c8101948542111561137c575050907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb9193920194019384611309565b9250936111fa9650809550849193500393429003029142030201611033565b50505050505f90565b483a03640277cf2a0048105f146113c2576064604648029102111590565b64183cd7f1004811156113dc576064604148029102111590565b489060011b111590565b9160446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282855af1908161145c575b501561143257565b60046040517ffb7f5079000000000000000000000000000000000000000000000000000000008152fd5b90503d15611476575060015f5114601f3d11165b5f61142a565b3b1515611470565b9082101561148a570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f80809381935af16114c761124e565b50156114cf57565b60046040517f6d963f88000000000000000000000000000000000000000000000000000000008152fd5b9291909283600411610194579069ffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6004843560e01c940195019316925f925b828410611554575050505050505f90565b600a9082821161019457600c918383116101945787013560f01c0195803560b01c86036115875750505050505042101590565b95929394954284116115c65701949360010192917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff49190910190611543565b505050505050505f9056fea26469706673582212205430c44c5fe34972cb4f2595e568f20c54bb9b66c75864ec1431738b3954cd4364736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000111111125421ca6dc452d289314280a0f8842a65000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000011799622f4d98a24514011e8527b969f7488ef47
-----Decoded View---------------
Arg [0] : limitOrderProtocol (address): 0x111111125421cA6dc452d289314280a0f8842A65
Arg [1] : feeToken (address): 0x111111111117dC0aa78b770fA6A738034120C302
Arg [2] : weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : owner (address): 0x11799622F4D98A24514011E8527B969f7488eF47
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000111111125421ca6dc452d289314280a0f8842a65
Arg [1] : 000000000000000000000000111111111117dc0aa78b770fa6a738034120c302
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 00000000000000000000000011799622f4d98a24514011e8527b969f7488ef47
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.