// SPDX-License-Identifier: MIT
// ─────────────────────────────────────────────────────────────────────────────
// Compiler: 0.8.24+commit.e11b9ed9 (Remix: "0.8.24")
// Language: Solidity
// EVM version: cancun
// Optimizer: Enabled, 200 runs
// License: MIT
// ─────────────────────────────────────────────────────────────────────────────
pragma solidity 0.8.24;
// lib/openzeppelin-contracts/contracts/utils/Context.sol
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
/**
* @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;
}
}
// lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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);
}
// lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}
// lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
// lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
// lib/openzeppelin-contracts/contracts/access/Ownable.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.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);
}
}
// lib/openzeppelin-contracts/contracts/utils/Pausable.sol
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol
// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol)
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
* by the {ReentrancyGuardTransient} variant in v6.0.
*
* @custom:stateless
*/
abstract contract ReentrancyGuard {
using StorageSlot for bytes32;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev A `view` only version of {nonReentrant}. Use to block view functions
* from being called, preventing reading from inconsistent contract state.
*
* CAUTION: This is a "view" modifier and does not change the reentrancy
* status. Use it only on view functions. For payable or non-payable functions,
* use the standard {nonReentrant} modifier instead.
*/
modifier nonReentrantView() {
_nonReentrantBeforeView();
_;
}
function _nonReentrantBeforeView() private view {
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
_nonReentrantBeforeView();
// Any calls to nonReentrant after this point will fail
_reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
}
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
return REENTRANCY_GUARD_STORAGE;
}
}
// lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
// lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/utils/SafeERC20.sol)
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_safeTransfer(token, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
if (!_safeTransferFrom(token, from, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _safeTransfer(token, to, value, false);
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _safeTransferFrom(token, from, to, value, false);
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
if (!_safeApprove(token, spender, value, false)) {
if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Oppositely, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the
* return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.transfer.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(to, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
/**
* @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return
* value: the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param from The sender of the tokens
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value,
bool bubble
) private returns (bool success) {
bytes4 selector = IERC20.transferFrom.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(from, shr(96, not(0))))
mstore(0x24, and(to, shr(96, not(0))))
mstore(0x44, value)
success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
mstore(0x60, 0)
}
}
/**
* @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:
* the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param spender The spender of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.approve.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(spender, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
}
// src/DustSwapSweepRouter.sol (Robinhood Chain adaptation — contract name only; weth/IWETH9 kept)
/// @notice Permit2 one-shot SignatureTransfer interface (exact, per-sweep, no standing allowance).
interface ISignatureTransfer {
struct TokenPermissions {
address token;
uint256 amount;
}
struct PermitBatchTransferFrom {
TokenPermissions[] permitted;
uint256 nonce;
uint256 deadline;
}
struct SignatureTransferDetails {
address to;
uint256 requestedAmount;
}
function permitWitnessTransferFrom(
PermitBatchTransferFrom calldata permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
}
/// @notice Permit2 AllowanceTransfer interface, used only for the Uniswap Universal Router / V4
/// special case where the router pulls tokens through Permit2 (spender == Permit2).
interface IAllowanceTransfer {
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
}
/// @notice Minimal WETH interface for unwrapping to native ETH on native-output sweeps.
interface IWETH9 {
function withdraw(uint256 amount) external;
}
/// @title DustSwapSweepRouterRobinhood (DustSweep V3 — Robinhood Chain)
/// @author DustSwap
/// @notice Best-effort, multi-DEX dust sweep router for Robinhood Chain (chainId 4663). Sweeps
/// many small ERC-20 balances into a single output token (ERC-20 or native ETH) using
/// allowlisted swap targets, with hard, per-sweep, exact-amount approvals only.
/// @dev Robinhood Chain deployment of the V3 contract — same logic and external ABI as the
/// Base/Ethereum/BSC builds; the app targets it via DUST_SWEEP_ROUTER_V3_ADDRESS_4663.
/// Native gas token IS ETH here, so weth/IWETH9 naming is kept (no WBNB-style rename).
///
/// SECURITY MODEL — APPROVALS (non-negotiable):
/// `route.amountIn` is the single source of truth. The user permits/approves EXACTLY
/// `route.amountIn` per token, the router pulls EXACTLY that, and approves the DEX EXACTLY
/// that amount, resetting to 0 immediately after. No infinite/standing allowance path
/// exists. Fee-on-transfer tokens (where the router would hold < `amountIn`) are skipped
/// and refunded rather than over-approved or failing the whole batch.
contract DustSwapSweepRouterRobinhood is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
// ──────────────────────────── Types ────────────────────────────
/// @notice How user inputs are pulled into the router for this sweep.
/// @dev Permit2Signature = one-shot Permit2 SignatureTransfer with a witness (plain EOAs and
/// EIP-1271 smart accounts). Allowance = ERC-20 transferFrom, used by EIP-7702/EIP-5792
/// wallets that batch an exact-amount approve + sweep atomically.
enum SweepMode {
Permit2Signature,
Allowance
}
/// @notice One swap leg. Same field shape as V2 so existing route builders stay compatible.
/// @dev `value` MUST be 0 (ERC-20 inputs only; the entrypoint is non-payable).
struct SweepRoute {
address tokenIn;
uint256 amountIn;
address target;
address spender;
uint256 value;
bytes data;
}
/// @notice Output / settlement parameters for the whole sweep.
/// @dev `outputToken` may be a normal ERC-20 or NATIVE_TOKEN_SENTINEL for native ETH.
/// `feeBpsOverride` is the per-sweep fee in bps; use FEE_OVERRIDE_SENTINEL to fall back
/// to the contract default `feeBps`. Any explicit override must be <= MAX_FEE_BPS.
struct SweepParams {
address outputToken;
address recipient;
uint256 minAmountOut;
uint256 deadline;
uint16 feeBpsOverride;
}
/// @dev Internal settlement scratch space, threaded as one stack slot to avoid stack-too-deep.
struct SweepState {
address actualOutput;
uint256 initialOutputBalance;
uint16 effectiveFeeBps;
uint256 routeCount;
}
// ──────────────────────────── Constants ────────────────────────
uint256 public constant BPS_DENOMINATOR = 10_000;
uint256 public constant MAX_BATCH_SIZE = 50;
uint16 public constant MAX_FEE_BPS = 300; // 3% hard cap, can never be exceeded in any mode
uint16 public constant FEE_OVERRIDE_SENTINEL = type(uint16).max; // "use default feeBps"
address public constant NATIVE_TOKEN_SENTINEL = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
bytes32 public constant SWEEP_ROUTE_TYPEHASH = keccak256(
"SweepRoute(address tokenIn,uint256 amountIn,address target,address spender,uint256 value,bytes32 dataHash)"
);
bytes32 public constant DUST_SWEEP_WITNESS_TYPEHASH = keccak256(
"DustSweepWitness(bytes32 routeHash,address outputToken,address recipient,uint256 minAmountOut,uint256 deadline,uint16 feeBps)"
);
string public constant PERMIT2_WITNESS_TYPE_STRING =
"DustSweepWitness witness)DustSweepWitness(bytes32 routeHash,address outputToken,address recipient,uint256 minAmountOut,uint256 deadline,uint16 feeBps)TokenPermissions(address token,uint256 amount)";
// ──────────────────────────── Immutables ───────────────────────
ISignatureTransfer public immutable permit2;
IWETH9 public immutable weth;
// ──────────────────────────── State ────────────────────────────
uint16 public feeBps;
address public feeCollector;
mapping(address => bool) public allowedTargets;
mapping(address => bool) public allowedSpenders;
// ──────────────────────────── Events ───────────────────────────
event DustSwept(
address indexed user,
address indexed recipient,
address indexed outputToken,
uint256 routeCount,
uint256 grossAmountOut,
uint256 feeAmount,
uint256 netAmountOut
);
event RouteExecuted(address indexed tokenIn, address indexed target, address indexed spender, uint256 amountIn);
event RouteSkipped(address indexed tokenIn, uint256 amountIn, bytes reason);
event InputRefunded(address indexed token, address indexed to, uint256 amount);
event RefundFailed(address indexed token, address indexed to, uint256 amount);
event ProtocolFeePaid(address indexed token, address indexed payer, address indexed collector, uint256 amount);
event FeeBpsUpdated(uint16 oldFeeBps, uint16 newFeeBps);
event FeeCollectorUpdated(address oldCollector, address newCollector);
event AllowedTargetUpdated(address indexed target, bool allowed);
event AllowedSpenderUpdated(address indexed spender, bool allowed);
event ERC20Rescued(address indexed token, address indexed to, uint256 amount);
event NativeRescued(address indexed to, uint256 amount);
// ──────────────────────────── Errors ───────────────────────────
error ZeroAddress();
error ZeroAmount();
error EmptyRoutes();
error BatchTooLarge();
error DeadlineExpired();
error FeeTooHigh();
error FeeCollectorRequired();
error PermitLengthMismatch();
error PermitTokenMismatch(uint256 index);
error PermitAmountMismatch(uint256 index);
error PermitDeadlineMismatch();
error TargetNotAllowed(address target);
error SpenderNotAllowed(address spender);
error MalformedRouteData(uint256 index);
error NonZeroValue(uint256 index);
error NativeInputUnsupported();
error InsufficientOutput();
error SwapCallFailed(address target);
error AmountTooLargeForPermit2Allowance();
error Permit2AllowanceExpirationTooLarge();
error NativeTransferFailed();
error InsufficientHeldBalance();
error NotSelf();
error UnauthorizedNativeSender();
/// @param permit2_ Canonical Permit2 on Robinhood Chain (same address on every chain,
/// verified live 2026-07-26): 0x000000000022D473030F116dDEE9F6B43aC78BA3
/// @param weth_ WETH on Robinhood Chain: 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73
/// @param owner_ Contract owner (config + pause + rescue).
/// @param feeCollector_ Address that receives protocol fees.
/// @param feeBps_ Default fee in bps (<= MAX_FEE_BPS).
constructor(
address permit2_,
address weth_,
address owner_,
address feeCollector_,
uint16 feeBps_
) Ownable(owner_) {
if (permit2_ == address(0) || weth_ == address(0) || owner_ == address(0)) revert ZeroAddress();
if (feeBps_ > MAX_FEE_BPS) revert FeeTooHigh();
if (feeBps_ > 0 && feeCollector_ == address(0)) revert FeeCollectorRequired();
permit2 = ISignatureTransfer(permit2_);
weth = IWETH9(weth_);
feeCollector = feeCollector_;
feeBps = feeBps_;
}
/// @dev Accept ETH only from the WETH contract (native-output unwraps). Rejecting other
/// senders is hygiene only; any force-sent ETH (e.g. selfdestruct) is still recoverable
/// via rescueNative, and no logic ever depends on address(this).balance.
receive() external payable {
if (msg.sender != address(weth)) revert UnauthorizedNativeSender();
}
// ──────────────────────────── Entry point ──────────────────────
/// @notice Sweep a batch of dust tokens into a single output token (ERC-20 or native ETH).
/// @dev Single entrypoint. `mode` selects how inputs are pulled. In Allowance mode the
/// `permit`/`signature` args are ignored and may be empty. Caller-agnostic: only
/// `msg.sender` is used, so plain EOAs, EIP-7702 smart accounts, and contract callers
/// all work (Permit2 validates EIP-1271 signatures for smart-account signers).
/// @param mode Permit2Signature or Allowance.
/// @param routes Swap legs; each `tokenIn`/`amountIn` is the single source of truth.
/// @param params Output token, recipient, slippage floor, deadline, per-sweep fee override.
/// @param permit Permit2 batch permit (Permit2Signature mode only).
/// @param signature Permit2 signature over the witness (Permit2Signature mode only).
/// @return grossAmountOut Realized output before fee.
/// @return feeAmount Protocol fee taken from gross output.
/// @return netAmountOut Output delivered to `recipient`.
function sweep(
SweepMode mode,
SweepRoute[] calldata routes,
SweepParams calldata params,
ISignatureTransfer.PermitBatchTransferFrom calldata permit,
bytes calldata signature
) external nonReentrant whenNotPaused returns (uint256, uint256, uint256) {
SweepState memory st;
st.effectiveFeeBps = _resolveFeeBps(params.feeBpsOverride);
_validateParams(routes, params);
st.actualOutput =
params.outputToken == NATIVE_TOKEN_SENTINEL ? address(weth) : params.outputToken;
_validateRoutes(routes, st.actualOutput);
st.initialOutputBalance = IERC20(st.actualOutput).balanceOf(address(this));
st.routeCount = routes.length;
_pullAndExecute(mode, routes, params, st.effectiveFeeBps, st.actualOutput, permit, signature);
return _settleOutput(st, params);
}
/// @dev Snapshot inputs, pull them in (exact amounts), run best-effort swaps, refund leftovers.
/// Kept in its own frame so the snapshot arrays do not crowd the entrypoint's stack.
/// `actualOutput` is threaded through so passthrough legs (tokenIn == output) skip the
/// swap and are NOT refunded (the output token is settled by _settleOutput, not here).
function _pullAndExecute(
SweepMode mode,
SweepRoute[] calldata routes,
SweepParams calldata params,
uint16 effectiveFeeBps,
address actualOutput,
ISignatureTransfer.PermitBatchTransferFrom calldata permit,
bytes calldata signature
) internal {
(address[] memory inputTokens, uint256[] memory inputInitial) = _snapshotInputs(routes);
if (mode == SweepMode.Permit2Signature) {
_pullInputsWithPermit2(routes, params, effectiveFeeBps, permit, signature);
} else {
_pullInputsWithAllowance(routes);
}
_executeRoutesBestEffort(routes, params.deadline, actualOutput);
_refundLeftoverInputs(inputTokens, inputInitial, actualOutput);
}
/// @notice Execute one route. External + self-only so the parent can wrap it in try/catch.
/// @dev Reverting here (failed swap, or fee-on-transfer leaving < amountIn held) rolls back
/// every approval made in this frame, so a skipped route leaves NO dangling allowance.
/// On success, approvals are explicitly reset to 0 before returning.
function executeRoute(SweepRoute calldata route, uint256 deadline) external {
if (msg.sender != address(this)) revert NotSelf();
IERC20 tokenIn = IERC20(route.tokenIn);
// Fee-on-transfer / under-delivery guard: never approve more than we actually hold.
if (tokenIn.balanceOf(address(this)) < route.amountIn) revert InsufficientHeldBalance();
// Router -> DEX approval == EXACTLY the amount about to be swapped.
tokenIn.forceApprove(route.spender, 0);
tokenIn.forceApprove(route.spender, route.amountIn);
// Uniswap Universal Router / V4 special case: the router pulls via Permit2.
if (route.spender == address(permit2)) {
if (route.amountIn > type(uint160).max) revert AmountTooLargeForPermit2Allowance();
if (deadline > type(uint48).max) revert Permit2AllowanceExpirationTooLarge();
IAllowanceTransfer(address(permit2)).approve(
route.tokenIn, route.target, uint160(route.amountIn), uint48(deadline)
);
}
(bool success, bytes memory returnData) = route.target.call(route.data);
// Reset both approval rails to 0 regardless of swap outcome.
if (route.spender == address(permit2)) {
IAllowanceTransfer(address(permit2)).approve(route.tokenIn, route.target, 0, uint48(block.timestamp));
}
tokenIn.forceApprove(route.spender, 0);
if (!success) {
if (returnData.length > 0) {
assembly {
revert(add(returnData, 0x20), mload(returnData))
}
}
revert SwapCallFailed(route.target);
}
}
// ──────────────────────────── Internal: validation ─────────────
function _resolveFeeBps(uint16 feeBpsOverride) internal view returns (uint16 effectiveFeeBps) {
effectiveFeeBps = feeBpsOverride == FEE_OVERRIDE_SENTINEL ? feeBps : feeBpsOverride;
if (effectiveFeeBps > MAX_FEE_BPS) revert FeeTooHigh();
}
function _validateParams(SweepRoute[] calldata routes, SweepParams calldata params) internal view {
if (block.timestamp > params.deadline) revert DeadlineExpired();
if (routes.length == 0) revert EmptyRoutes();
if (routes.length > MAX_BATCH_SIZE) revert BatchTooLarge();
if (params.outputToken == address(0) || params.recipient == address(0)) revert ZeroAddress();
}
function _validateRoutes(SweepRoute[] calldata routes, address actualOutput) internal view {
for (uint256 i; i < routes.length;) {
SweepRoute calldata route = routes[i];
if (route.tokenIn == address(0)) revert ZeroAddress();
if (route.tokenIn == NATIVE_TOKEN_SENTINEL) revert NativeInputUnsupported();
if (route.amountIn == 0) revert ZeroAmount();
if (route.value != 0) revert NonZeroValue(i);
// PASSTHROUGH LEG: the input already IS the settlement token (e.g. WETH swept into
// native ETH, since actualOutput == WETH; or the output ERC-20 itself included as dust).
// Nothing is swapped — the pulled tokens settle directly as output — so target/spender/
// data are unused and are NOT allowlist-checked. This is safe: only the user's own
// tokens move, straight from `msg.sender` into the output pool and back to `recipient`
// (minus fee); no external `target.call` is ever made for these legs.
if (route.tokenIn == actualOutput) {
unchecked {
++i;
}
continue;
}
if (route.target == address(0) || route.spender == address(0)) revert ZeroAddress();
if (route.data.length < 4) revert MalformedRouteData(i);
if (!allowedTargets[route.target]) revert TargetNotAllowed(route.target);
if (!allowedSpenders[route.spender]) revert SpenderNotAllowed(route.spender);
unchecked {
++i;
}
}
}
function _validatePermit(
SweepRoute[] calldata routes,
SweepParams calldata params,
ISignatureTransfer.PermitBatchTransferFrom calldata permit
) internal pure {
if (permit.deadline != params.deadline) revert PermitDeadlineMismatch();
if (permit.permitted.length != routes.length) revert PermitLengthMismatch();
for (uint256 i; i < routes.length;) {
if (permit.permitted[i].token != routes[i].tokenIn) revert PermitTokenMismatch(i);
if (permit.permitted[i].amount != routes[i].amountIn) revert PermitAmountMismatch(i);
unchecked {
++i;
}
}
}
// ──────────────────────────── Internal: input pulls ────────────
function _pullInputsWithPermit2(
SweepRoute[] calldata routes,
SweepParams calldata params,
uint16 effectiveFeeBps,
ISignatureTransfer.PermitBatchTransferFrom calldata permit,
bytes calldata signature
) internal {
_validatePermit(routes, params, permit);
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails =
new ISignatureTransfer.SignatureTransferDetails[](routes.length);
for (uint256 i; i < routes.length;) {
transferDetails[i] = ISignatureTransfer.SignatureTransferDetails({
to: address(this),
requestedAmount: routes[i].amountIn // EXACTLY route.amountIn
});
unchecked {
++i;
}
}
bytes32 witness = hashSweepWitness(
hashRoutes(routes),
params.outputToken,
params.recipient,
params.minAmountOut,
params.deadline,
effectiveFeeBps
);
permit2.permitWitnessTransferFrom(
permit, transferDetails, msg.sender, witness, PERMIT2_WITNESS_TYPE_STRING, signature
);
}
function _pullInputsWithAllowance(SweepRoute[] calldata routes) internal {
for (uint256 i; i < routes.length;) {
// Pull EXACTLY route.amountIn; the wallet's approval must match this exactly.
IERC20(routes[i].tokenIn).safeTransferFrom(msg.sender, address(this), routes[i].amountIn);
unchecked {
++i;
}
}
}
// ──────────────────────────── Internal: execution ──────────────
function _executeRoutesBestEffort(SweepRoute[] calldata routes, uint256 deadline, address actualOutput)
internal
{
for (uint256 i; i < routes.length;) {
SweepRoute calldata route = routes[i];
if (route.tokenIn == actualOutput) {
// Passthrough: the pulled tokens are already in the output denomination (e.g. WETH
// for a native-ETH sweep). No swap, no external call — they simply stay in the
// router and settle as output in _settleOutput (which unwraps WETH -> ETH on native
// output). Emit with target/spender == tokenIn so the leg is still observable.
emit RouteExecuted(route.tokenIn, route.tokenIn, route.tokenIn, route.amountIn);
} else {
try this.executeRoute(route, deadline) {
emit RouteExecuted(route.tokenIn, route.target, route.spender, route.amountIn);
} catch (bytes memory reason) {
// Best-effort: skip the failed leg. Its tokenIn stays in the router and is
// returned to the user by _refundLeftoverInputs — never stranded, never reverted.
emit RouteSkipped(route.tokenIn, route.amountIn, reason);
}
}
unchecked {
++i;
}
}
}
// ──────────────────────────── Internal: refunds & settle ───────
/// @dev Snapshot unique input-token balances BEFORE pulling, so leftover refunds only return
/// what this sweep brought in (and any pre-existing stuck balance is left for rescue()).
function _snapshotInputs(SweepRoute[] calldata routes)
internal
view
returns (address[] memory tokens, uint256[] memory initialBalances)
{
tokens = new address[](routes.length);
initialBalances = new uint256[](routes.length);
uint256 count;
for (uint256 i; i < routes.length;) {
address token = routes[i].tokenIn;
bool seen;
for (uint256 j; j < count;) {
if (tokens[j] == token) {
seen = true;
break;
}
unchecked {
++j;
}
}
if (!seen) {
tokens[count] = token;
initialBalances[count] = IERC20(token).balanceOf(address(this));
unchecked {
++count;
}
}
unchecked {
++i;
}
}
// Trim to unique count.
assembly {
mstore(tokens, count)
mstore(initialBalances, count)
}
}
/// @dev Return any input tokens not consumed by swaps (failed legs, fee-on-transfer skips,
/// router refunds) back to the user. Best-effort so one weird token can't brick the batch.
/// The output token is NEVER refunded here: its whole balance is settled by _settleOutput
/// (gross - fee -> recipient). Skipping it is what lets a passthrough leg (tokenIn ==
/// output) be delivered as output instead of being handed back to the user.
function _refundLeftoverInputs(
address[] memory tokens,
uint256[] memory initialBalances,
address actualOutput
) internal {
for (uint256 i; i < tokens.length;) {
if (tokens[i] == actualOutput) {
unchecked {
++i;
}
continue;
}
uint256 current = IERC20(tokens[i]).balanceOf(address(this));
if (current > initialBalances[i]) {
uint256 refund = current - initialBalances[i];
(bool ok, bytes memory ret) = tokens[i].call(
abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, refund)
);
// A compliant transfer returns nothing or 32 bytes == true. Never abi.decode a
// short (1-31 byte) return: that itself reverts and would brick the whole sweep,
// defeating best-effort. Treat anything non-compliant as a failed refund instead.
bool refunded = ok && (ret.length == 0 || (ret.length >= 32 && abi.decode(ret, (bool))));
if (refunded) {
emit InputRefunded(tokens[i], msg.sender, refund);
} else {
// Leave it for rescue() rather than reverting the whole sweep.
emit RefundFailed(tokens[i], msg.sender, refund);
}
}
unchecked {
++i;
}
}
}
function _settleOutput(SweepState memory st, SweepParams calldata params)
internal
returns (uint256 grossAmountOut, uint256 feeAmount, uint256 netAmountOut)
{
grossAmountOut = IERC20(st.actualOutput).balanceOf(address(this)) - st.initialOutputBalance;
if (grossAmountOut < params.minAmountOut) revert InsufficientOutput();
feeAmount = (grossAmountOut * st.effectiveFeeBps) / BPS_DENOMINATOR;
netAmountOut = grossAmountOut - feeAmount;
if (feeAmount > 0) {
address collector = feeCollector;
if (collector == address(0)) revert FeeCollectorRequired();
IERC20(st.actualOutput).safeTransfer(collector, feeAmount);
emit ProtocolFeePaid(params.outputToken, msg.sender, collector, feeAmount);
}
if (params.outputToken == NATIVE_TOKEN_SENTINEL) {
weth.withdraw(netAmountOut);
(bool success,) = params.recipient.call{value: netAmountOut}("");
if (!success) revert NativeTransferFailed();
} else {
IERC20(st.actualOutput).safeTransfer(params.recipient, netAmountOut);
}
emit DustSwept(
msg.sender,
params.recipient,
params.outputToken,
st.routeCount,
grossAmountOut,
feeAmount,
netAmountOut
);
}
// ──────────────────────────── Witness hashing ──────────────────
/// @notice Hash the route array for witness binding (order-sensitive).
function hashRoutes(SweepRoute[] calldata routes) public pure returns (bytes32 routeHash) {
bytes32[] memory routeHashes = new bytes32[](routes.length);
for (uint256 i; i < routes.length;) {
SweepRoute calldata route = routes[i];
routeHashes[i] = keccak256(
abi.encode(
SWEEP_ROUTE_TYPEHASH,
route.tokenIn,
route.amountIn,
route.target,
route.spender,
route.value,
keccak256(route.data)
)
);
unchecked {
++i;
}
}
routeHash = keccak256(abi.encodePacked(routeHashes));
}
/// @notice Hash the Permit2 witness the user signs. Binds routes, output, recipient,
/// slippage floor, deadline AND the effective fee the user will pay.
function hashSweepWitness(
bytes32 routeHash,
address outputToken,
address recipient,
uint256 minAmountOut,
uint256 deadline,
uint16 feeBps_
) public pure returns (bytes32) {
return keccak256(
abi.encode(
DUST_SWEEP_WITNESS_TYPEHASH, routeHash, outputToken, recipient, minAmountOut, deadline, feeBps_
)
);
}
// ──────────────────────────── Admin ────────────────────────────
function setFeeBps(uint16 newFeeBps) external onlyOwner {
if (newFeeBps > MAX_FEE_BPS) revert FeeTooHigh();
if (newFeeBps > 0 && feeCollector == address(0)) revert FeeCollectorRequired();
uint16 oldFeeBps = feeBps;
feeBps = newFeeBps;
emit FeeBpsUpdated(oldFeeBps, newFeeBps);
}
function setFeeCollector(address newCollector) external onlyOwner {
if (newCollector == address(0) && feeBps > 0) revert FeeCollectorRequired();
address oldCollector = feeCollector;
feeCollector = newCollector;
emit FeeCollectorUpdated(oldCollector, newCollector);
}
function setAllowedTarget(address target, bool allowed) external onlyOwner {
if (target == address(0)) revert ZeroAddress();
allowedTargets[target] = allowed;
emit AllowedTargetUpdated(target, allowed);
}
function setAllowedSpender(address spender, bool allowed) external onlyOwner {
if (spender == address(0)) revert ZeroAddress();
allowedSpenders[spender] = allowed;
emit AllowedSpenderUpdated(spender, allowed);
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function rescueERC20(address token, address to, uint256 amount) external onlyOwner nonReentrant {
if (token == address(0) || to == address(0)) revert ZeroAddress();
if (amount == 0) revert ZeroAmount();
IERC20(token).safeTransfer(to, amount);
emit ERC20Rescued(token, to, amount);
}
function rescueNative(address payable to, uint256 amount) external onlyOwner nonReentrant {
if (to == address(0)) revert ZeroAddress();
if (amount == 0) revert ZeroAmount();
(bool success,) = to.call{value: amount}("");
if (!success) revert NativeTransferFailed();
emit NativeRescued(to, amount);
}
}[
{
"type": "constructor",
"inputs": [
{
"name": "permit2_",
"type": "address",
"internalType": "address"
},
{
"name": "weth_",
"type": "address",
"internalType": "address"
},
{
"name": "owner_",
"type": "address",
"internalType": "address"
},
{
"name": "feeCollector_",
"type": "address",
"internalType": "address"
},
{
"name": "feeBps_",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "nonpayable"
},
{
"name": "AmountTooLargeForPermit2Allowance",
"type": "error",
"inputs": []
},
{
"name": "BatchTooLarge",
"type": "error",
"inputs": []
},
{
"name": "DeadlineExpired",
"type": "error",
"inputs": []
},
{
"name": "EmptyRoutes",
"type": "error",
"inputs": []
},
{
"name": "EnforcedPause",
"type": "error",
"inputs": []
},
{
"name": "ExpectedPause",
"type": "error",
"inputs": []
},
{
"name": "FeeCollectorRequired",
"type": "error",
"inputs": []
},
{
"name": "FeeTooHigh",
"type": "error",
"inputs": []
},
{
"name": "InsufficientHeldBalance",
"type": "error",
"inputs": []
},
{
"name": "InsufficientOutput",
"type": "error",
"inputs": []
},
{
"name": "MalformedRouteData",
"type": "error",
"inputs": [
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "NativeInputUnsupported",
"type": "error",
"inputs": []
},
{
"name": "NativeTransferFailed",
"type": "error",
"inputs": []
},
{
"name": "NonZeroValue",
"type": "error",
"inputs": [
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "NotSelf",
"type": "error",
"inputs": []
},
{
"name": "OwnableInvalidOwner",
"type": "error",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "OwnableUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "Permit2AllowanceExpirationTooLarge",
"type": "error",
"inputs": []
},
{
"name": "PermitAmountMismatch",
"type": "error",
"inputs": [
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "PermitDeadlineMismatch",
"type": "error",
"inputs": []
},
{
"name": "PermitLengthMismatch",
"type": "error",
"inputs": []
},
{
"name": "PermitTokenMismatch",
"type": "error",
"inputs": [
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "ReentrancyGuardReentrantCall",
"type": "error",
"inputs": []
},
{
"name": "SafeERC20FailedOperation",
"type": "error",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "SpenderNotAllowed",
"type": "error",
"inputs": [
{
"name": "spender",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "SwapCallFailed",
"type": "error",
"inputs": [
{
"name": "target",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "TargetNotAllowed",
"type": "error",
"inputs": [
{
"name": "target",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "UnauthorizedNativeSender",
"type": "error",
"inputs": []
},
{
"name": "ZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "ZeroAmount",
"type": "error",
"inputs": []
},
{
"name": "AllowedSpenderUpdated",
"type": "event",
"inputs": [
{
"name": "spender",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "allowed",
"type": "bool",
"indexed": false,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "AllowedTargetUpdated",
"type": "event",
"inputs": [
{
"name": "target",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "allowed",
"type": "bool",
"indexed": false,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "DustSwept",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "recipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "outputToken",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "routeCount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "grossAmountOut",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feeAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "netAmountOut",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "ERC20Rescued",
"type": "event",
"inputs": [
{
"name": "token",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "FeeBpsUpdated",
"type": "event",
"inputs": [
{
"name": "oldFeeBps",
"type": "uint16",
"indexed": false,
"internalType": "uint16"
},
{
"name": "newFeeBps",
"type": "uint16",
"indexed": false,
"internalType": "uint16"
}
],
"anonymous": false
},
{
"name": "FeeCollectorUpdated",
"type": "event",
"inputs": [
{
"name": "oldCollector",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "newCollector",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "InputRefunded",
"type": "event",
"inputs": [
{
"name": "token",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "NativeRescued",
"type": "event",
"inputs": [
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "Paused",
"type": "event",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "ProtocolFeePaid",
"type": "event",
"inputs": [
{
"name": "token",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "payer",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "collector",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RefundFailed",
"type": "event",
"inputs": [
{
"name": "token",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RouteExecuted",
"type": "event",
"inputs": [
{
"name": "tokenIn",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "target",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RouteSkipped",
"type": "event",
"inputs": [
{
"name": "tokenIn",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "reason",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"name": "Unpaused",
"type": "event",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "BPS_DENOMINATOR",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "DUST_SWEEP_WITNESS_TYPEHASH",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "FEE_OVERRIDE_SENTINEL",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"name": "MAX_BATCH_SIZE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "MAX_FEE_BPS",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"name": "NATIVE_TOKEN_SENTINEL",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "PERMIT2_WITNESS_TYPE_STRING",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "string",
"internalType": "string"
}
],
"stateMutability": "view"
},
{
"name": "SWEEP_ROUTE_TYPEHASH",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "allowedSpenders",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "allowedTargets",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "executeRoute",
"type": "function",
"inputs": [
{
"name": "route",
"type": "tuple",
"components": [
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "target",
"type": "address",
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct DustSwapSweepRouterRobinhood.SweepRoute"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "feeBps",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"name": "feeCollector",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "hashRoutes",
"type": "function",
"inputs": [
{
"name": "routes",
"type": "tuple[]",
"components": [
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "target",
"type": "address",
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct DustSwapSweepRouterRobinhood.SweepRoute[]"
}
],
"outputs": [
{
"name": "routeHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"name": "hashSweepWitness",
"type": "function",
"inputs": [
{
"name": "routeHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "outputToken",
"type": "address",
"internalType": "address"
},
{
"name": "recipient",
"type": "address",
"internalType": "address"
},
{
"name": "minAmountOut",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "feeBps_",
"type": "uint16",
"internalType": "uint16"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "pause",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "paused",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "permit2",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract ISignatureTransfer"
}
],
"stateMutability": "view"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "rescueERC20",
"type": "function",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
},
{
"name": "to",
"type": "address",
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "rescueNative",
"type": "function",
"inputs": [
{
"name": "to",
"type": "address",
"internalType": "address payable"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setAllowedSpender",
"type": "function",
"inputs": [
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "allowed",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setAllowedTarget",
"type": "function",
"inputs": [
{
"name": "target",
"type": "address",
"internalType": "address"
},
{
"name": "allowed",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setFeeBps",
"type": "function",
"inputs": [
{
"name": "newFeeBps",
"type": "uint16",
"internalType": "uint16"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setFeeCollector",
"type": "function",
"inputs": [
{
"name": "newCollector",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "sweep",
"type": "function",
"inputs": [
{
"name": "mode",
"type": "uint8",
"internalType": "enum DustSwapSweepRouterRobinhood.SweepMode"
},
{
"name": "routes",
"type": "tuple[]",
"components": [
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "target",
"type": "address",
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct DustSwapSweepRouterRobinhood.SweepRoute[]"
},
{
"name": "params",
"type": "tuple",
"components": [
{
"name": "outputToken",
"type": "address",
"internalType": "address"
},
{
"name": "recipient",
"type": "address",
"internalType": "address"
},
{
"name": "minAmountOut",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "feeBpsOverride",
"type": "uint16",
"internalType": "uint16"
}
],
"internalType": "struct DustSwapSweepRouterRobinhood.SweepParams"
},
{
"name": "permit",
"type": "tuple",
"components": [
{
"name": "permitted",
"type": "tuple[]",
"components": [
{
"name": "token",
"type": "address",
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct ISignatureTransfer.TokenPermissions[]"
},
{
"name": "nonce",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct ISignatureTransfer.PermitBatchTransferFrom"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "unpause",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "weth",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IWETH9"
}
],
"stateMutability": "view"
},
{
"type": "receive",
"stateMutability": "payable"
}
]0x60c060405234801562000010575f80fd5b50604051620034fe380380620034fe833981016040819052620000339162000200565b826001600160a01b0381166200006257604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200006d8162000195565b5060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00556001600160a01b0385161580620000b057506001600160a01b038416155b80620000c357506001600160a01b038316155b15620000e25760405163d92e233d60e01b815260040160405180910390fd5b61012c61ffff821611156200010a5760405163cd4e616760e01b815260040160405180910390fd5b5f8161ffff161180156200012557506001600160a01b038216155b15620001445760405163b2f6ce2360e01b815260040160405180910390fd5b6001600160a01b0394851660805292841660a052600180546001600160a01b03191691909416179092555f805461ffff60a81b1916600160a81b61ffff909316929092029190911790555062000275565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114620001fb575f80fd5b919050565b5f805f805f60a0868803121562000215575f80fd5b6200022086620001e4565b94506200023060208701620001e4565b93506200024060408701620001e4565b92506200025060608701620001e4565b9150608086015161ffff8116811462000267575f80fd5b809150509295509295909350565b60805160a05161322f620002cf5f395f81816101d80152818161037f0152818161125c0152611b4101525f818161024c01528181610de401528181610e7f01528181610fbd0152818161100501526120dd015261322f5ff3fe6080604052600436106101c8575f3560e01c8063a42dce80116100f2578063d55be8c611610092578063e1a4521811610062578063e1a45218146105c0578063e1ce0ae0146105d5578063e8f410c3146105f4578063f2fde38b1461062e575f80fd5b8063d55be8c61461052b578063d58e213714610540578063d606bdf81461055f578063d8528af014610592575f80fd5b8063c415b95c116100cd578063c415b95c146104b8578063c6182fbf146104d7578063ca1dd22e146104f8578063cfdbf25414610517575f80fd5b8063a42dce801461044c578063b2118a8d1461046b578063b8fe8d5f1461048a575f80fd5b80633f4ba83a11610168578063715018a611610138578063715018a6146103e95780638456cb59146103fd5780638da5cb5b146104115780639ede86481461042d575f80fd5b80633f4ba83a1461035a5780633fc8cef31461036e5780635c975abb146103a157806361f63190146103ca575f80fd5b80631520206c116101a35780631520206c146102aa5780631e18ede3146102d25780631ed6d2871461031357806324a9d8531461033a575f80fd5b8063023b1fc91461021c57806312261ee71461023b5780631291f79d1461028b575f80fd5b3661021857336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102165760405163f8a49ccb60e01b815260040160405180910390fd5b005b5f80fd5b348015610227575f80fd5b50610216610236366004612948565b61064d565b348015610246575f80fd5b5061026e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b348015610296575f80fd5b506102166102a536600461297c565b61071b565b3480156102b5575f80fd5b506102bf61ffff81565b60405161ffff9091168152602001610282565b3480156102dd575f80fd5b506103057f6572ccd6f51654624c69e01ae3d41b1603750f3ec68f7c106bc616fc7b66325b81565b604051908152602001610282565b34801561031e575f80fd5b5061026e73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610345575f80fd5b505f546102bf90600160a81b900461ffff1681565b348015610365575f80fd5b50610216610840565b348015610379575f80fd5b5061026e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103ac575f80fd5b505f54600160a01b900460ff165b6040519015158152602001610282565b3480156103d5575f80fd5b506103056103e43660046129a6565b610852565b3480156103f4575f80fd5b506102166108d9565b348015610408575f80fd5b506102166108ea565b34801561041c575f80fd5b505f546001600160a01b031661026e565b348015610438575f80fd5b50610305610447366004612a4c565b6108fa565b348015610457575f80fd5b50610216610466366004612a8a565b610a9e565b348015610476575f80fd5b50610216610485366004612aa5565b610b40565b348015610495575f80fd5b506103ba6104a4366004612a8a565b60026020525f908152604090205460ff1681565b3480156104c3575f80fd5b5060015461026e906001600160a01b031681565b3480156104e2575f80fd5b506104eb610c27565b6040516102829190612b30565b348015610503575f80fd5b50610216610512366004612b4f565b610c44565b348015610522575f80fd5b50610305603281565b348015610536575f80fd5b506102bf61012c81565b34801561054b575f80fd5b5061021661055a366004612b86565b610cd2565b34801561056a575f80fd5b506103057f028224fb20e87629342dae7f283cffce3324a34b8001ea97b70f631e2b7d744d81565b34801561059d575f80fd5b506103ba6105ac366004612a8a565b60036020525f908152604090205460ff1681565b3480156105cb575f80fd5b5061030561271081565b3480156105e0575f80fd5b506102166105ef366004612b4f565b611130565b3480156105ff575f80fd5b5061061361060e366004612c10565b6111b6565b60408051938452602084019290925290820152606001610282565b348015610639575f80fd5b50610216610648366004612a8a565b61135a565b610655611397565b61012c61ffff8216111561067c5760405163cd4e616760e01b815260040160405180910390fd5b5f8161ffff1611801561069857506001546001600160a01b0316155b156106b65760405163b2f6ce2360e01b815260040160405180910390fd5b5f805461ffff838116600160a81b81810261ffff60a81b1985161790945560408051949093049091168084526020840191909152917f8d10f5697a370f640ed5d474159aba3cc86e9bc260a5e9d2db875ad992cb1a1f91015b60405180910390a15050565b610723611397565b61072b6113c3565b6001600160a01b0382166107525760405163d92e233d60e01b815260040160405180910390fd5b805f0361077257604051631f2a200560e01b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146107bb576040519150601f19603f3d011682016040523d82523d5f602084013e6107c0565b606091505b50509050806107e257604051633d2cec6f60e21b815260040160405180910390fd5b826001600160a01b03167fe3eb98b7fe2a0c1d490b92af73eeae611e9b00ab3c3f70b20bd7bb43f67a0f438360405161081d91815260200190565b60405180910390a25061083c60015f805160206131da83398151915255565b5050565b610848611397565b6108506113de565b565b604080517f028224fb20e87629342dae7f283cffce3324a34b8001ea97b70f631e2b7d744d60208201529081018790526001600160a01b0380871660608301528516608082015260a0810184905260c0810183905261ffff821660e08201525f90610100016040516020818303038152906040528051906020012090509695505050505050565b6108e1611397565b6108505f611432565b6108f2611397565b610850611481565b5f80826001600160401b0381111561091457610914612cd2565b60405190808252806020026020018201604052801561093d578160200160208202803683370190505b5090505f5b83811015610a6d573685858381811061095d5761095d612ce6565b905060200281019061096f9190612cfa565b90507f6572ccd6f51654624c69e01ae3d41b1603750f3ec68f7c106bc616fc7b66325b61099f6020830183612a8a565b60208301356109b46060850160408601612a8a565b6109c46080860160608701612a8a565b60808601356109d660a0880188612d18565b6040516109e4929190612d5a565b6040805191829003822060208301989098526001600160a01b03968716908201526060810194909452918416608084015290921660a082015260c081019190915260e08101919091526101000160405160208183030381529060405280519060200120838381518110610a5957610a59612ce6565b602090810291909101015250600101610942565b5080604051602001610a7f9190612d69565b6040516020818303038152906040528051906020012091505092915050565b610aa6611397565b6001600160a01b038116158015610ac857505f54600160a81b900461ffff1615155b15610ae65760405163b2f6ce2360e01b815260040160405180910390fd5b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f38910161070f565b610b48611397565b610b506113c3565b6001600160a01b0383161580610b6d57506001600160a01b038216155b15610b8b5760405163d92e233d60e01b815260040160405180910390fd5b805f03610bab57604051631f2a200560e01b815260040160405180910390fd5b610bbf6001600160a01b03841683836114c3565b816001600160a01b0316836001600160a01b03167f8bbfbb5d7fcacf6fc74005cdede0635561638507f576c95f7f294c22141be2e583604051610c0491815260200190565b60405180910390a3610c2260015f805160206131da83398151915255565b505050565b60405180610100016040528060c4815260200161311660c4913981565b610c4c611397565b6001600160a01b038216610c735760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260026020908152604091829020805460ff191685151590811790915591519182527fa0a8236a39c9e79c2c39814563a83e040679f95d9a2ca65c8adefbf1ec32ca8891015b60405180910390a25050565b333014610cf2576040516314e1dbf760e11b815260040160405180910390fd5b5f610d006020840184612a8a565b6040516370a0823160e01b81523060048201529091506020840135906001600160a01b038316906370a0823190602401602060405180830381865afa158015610d4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6f9190612d9e565b1015610d8e57604051633811de6160e21b815260040160405180910390fd5b610db2610da16080850160608601612a8a565b6001600160a01b038316905f6114f8565b610dda610dc56080850160608601612a8a565b6001600160a01b0383169060208601356114f8565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016610e146080850160608601612a8a565b6001600160a01b031603610f39576001600160a01b0360208401351115610e4e57604051634c87966b60e01b815260040160405180910390fd5b65ffffffffffff821115610e755760405163b5468d6b60e01b815260040160405180910390fd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166387517c45610eb16020860186612a8a565b610ec16060870160408801612a8a565b60405160e084901b6001600160e01b03191681526001600160a01b03928316600482015290821660248201526020870135909116604482015265ffffffffffff851660648201526084015f604051808303815f87803b158015610f22575f80fd5b505af1158015610f34573d5f803e3d5ffd5b505050505b5f80610f4b6060860160408701612a8a565b6001600160a01b0316610f6160a0870187612d18565b604051610f6f929190612d5a565b5f604051808303815f865af19150503d805f8114610fa8576040519150601f19603f3d011682016040523d82523d5f602084013e610fad565b606091505b5090925090506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016610fed6080870160608801612a8a565b6001600160a01b0316036110b7576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166387517c456110376020880188612a8a565b6110476060890160408a01612a8a565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201525f604482015265ffffffffffff421660648201526084015f604051808303815f87803b1580156110a0575f80fd5b505af11580156110b2573d5f803e3d5ffd5b505050505b6110db6110ca6080870160608801612a8a565b6001600160a01b038516905f6114f8565b81611129578051156110ef57805160208201fd5b6110ff6060860160408701612a8a565b60405163e96e549f60e01b81526001600160a01b0390911660048201526024015b60405180910390fd5b5050505050565b611138611397565b6001600160a01b03821661115f5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260036020908152604091829020805460ff191685151590811790915591519182527f590d4c011dbe7f18ffb4784ab975d08a8ae3449000df3d4ff9f30fa111e6c60a9101610cc6565b5f805f6111c16113c3565b6111c961154a565b604080516080810182525f8082526020820181905291810182905260608101919091526112046111ff60a08a0160808b01612948565b611574565b61ffff1660408201526112188a8a8a6115c2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61123a60208a018a612a8a565b6001600160a01b03161461125a576112556020890189612a8a565b61127c565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316808252611295908b908b90611682565b80516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156112da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112fe9190612d9e565b60208201526060810189905260408101518151611326918d918d918d918d91908d8d8d611916565b6113308189611985565b9350935093505061134d60015f805160206131da83398151915255565b9750975097945050505050565b611362611397565b6001600160a01b03811661138b57604051631e4fbdf760e01b81525f6004820152602401611120565b61139481611432565b50565b5f546001600160a01b031633146108505760405163118cdaa760e01b8152336004820152602401611120565b6113cb611cd4565b60025f805160206131da83398151915255565b6113e6611d03565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61148961154a565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114153390565b6114d08383836001611d2c565b610c2257604051635274afe760e01b81526001600160a01b0384166004820152602401611120565b6115048383835f611d8e565b610c225761151583835f6001611d8e565b61153d57604051635274afe760e01b81526001600160a01b0384166004820152602401611120565b6114d08383836001611d8e565b5f54600160a01b900460ff16156108505760405163d93c066560e01b815260040160405180910390fd5b5f61ffff828116146115865781611594565b5f54600160a81b900461ffff165b905061012c61ffff821611156115bd5760405163cd4e616760e01b815260040160405180910390fd5b919050565b80606001354211156115e757604051631ab7da6b60e01b815260040160405180910390fd5b5f8290036116085760405163b6d45c2360e01b815260040160405180910390fd5b603282111561162a576040516305beb17160e11b815260040160405180910390fd5b5f6116386020830183612a8a565b6001600160a01b0316148061166457505f6116596040830160208401612a8a565b6001600160a01b0316145b15610c225760405163d92e233d60e01b815260040160405180910390fd5b5f5b82811015611910573684848381811061169f5761169f612ce6565b90506020028101906116b19190612cfa565b90505f6116c16020830183612a8a565b6001600160a01b0316036116e85760405163d92e233d60e01b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61170a6020830183612a8a565b6001600160a01b03160361173157604051633407ecb560e11b815260040160405180910390fd5b80602001355f0361175557604051631f2a200560e01b815260040160405180910390fd5b60808101351561177b57604051631d00736d60e31b815260048101839052602401611120565b6001600160a01b0383166117926020830183612a8a565b6001600160a01b0316036117a95750600101611684565b5f6117ba6060830160408401612a8a565b6001600160a01b031614806117e657505f6117db6080830160608401612a8a565b6001600160a01b0316145b156118045760405163d92e233d60e01b815260040160405180910390fd5b600461181360a0830183612d18565b905010156118375760405163a085984960e01b815260048101839052602401611120565b60025f61184a6060840160408501612a8a565b6001600160a01b0316815260208101919091526040015f205460ff1661189f5761187a6060820160408301612a8a565b60405163e356c1d360e01b81526001600160a01b039091166004820152602401611120565b60035f6118b26080840160608501612a8a565b6001600160a01b0316815260208101919091526040015f205460ff16611907576118e26080820160608301612a8a565b60405163046f5c8360e41b81526001600160a01b039091166004820152602401611120565b50600101611684565b50505050565b5f806119228a8a611dd8565b90925090505f8b600181111561193a5761193a612db5565b036119535761194e8a8a8a8a898989611fbd565b61195d565b61195d8a8a612184565b61196d8a8a8a6060013589612206565b611978828288612448565b5050505050505050505050565b602082015182516040516370a0823160e01b81523060048201525f92839283926001600160a01b03909116906370a0823190602401602060405180830381865afa1580156119d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119f99190612d9e565b611a039190612ddd565b92508360400135831015611a2a5760405163bb2875c360e01b815260040160405180910390fd5b612710856040015161ffff1684611a419190612df6565b611a4b9190612e0d565b9150611a578284612ddd565b90508115611afb576001546001600160a01b031680611a895760405163b2f6ce2360e01b815260040160405180910390fd5b8551611a9f906001600160a01b031682856114c3565b6001600160a01b03811633611ab76020880188612a8a565b6001600160a01b03167fa0c24741ddac735f5a6c1e1ef4f075407189d185a06db3329d022e99e9784a6086604051611af191815260200190565b60405180910390a4505b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee611b1d6020860186612a8a565b6001600160a01b031603611c2757604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024015f604051808303815f87803b158015611b8a575f80fd5b505af1158015611b9c573d5f803e3d5ffd5b505f9250611bb39150506040860160208701612a8a565b6001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611bfa576040519150601f19603f3d011682016040523d82523d5f602084013e611bff565b606091505b5050905080611c2157604051633d2cec6f60e21b815260040160405180910390fd5b50611c4c565b611c4c611c3a6040860160208701612a8a565b86516001600160a01b031690836114c3565b611c596020850185612a8a565b6001600160a01b0316611c726040860160208701612a8a565b606080880151604080519182526020820188905281018690529081018490526001600160a01b03919091169033907f7b772d4395daa77305e23b590cd333e6a98f4d1f2ba1db9a45a7fe90763e05089060800160405180910390a49250925092565b5f805160206131da8339815191525460020361085057604051633ee5aeb560e01b815260040160405180910390fd5b5f54600160a01b900460ff1661085057604051638dfc202b60e01b815260040160405180910390fd5b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611d82578383151615611d76573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b60405163095ea7b360e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611d82578383151615611d76573d5f823e3d81fd5b606080826001600160401b03811115611df357611df3612cd2565b604051908082528060200260200182016040528015611e1c578160200160208202803683370190505b509150826001600160401b03811115611e3757611e37612cd2565b604051908082528060200260200182016040528015611e60578160200160208202803683370190505b5090505f805b84811015611fae575f868683818110611e8157611e81612ce6565b9050602002810190611e939190612cfa565b611ea1906020810190612a8a565b90505f805b84811015611ef157826001600160a01b0316878281518110611eca57611eca612ce6565b60200260200101516001600160a01b031603611ee95760019150611ef1565b600101611ea6565b5080611fa45781868581518110611f0a57611f0a612ce6565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b8152306004820152908316906370a0823190602401602060405180830381865afa158015611f5b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7f9190612d9e565b858581518110611f9157611f91612ce6565b6020026020010181815250508360010193505b5050600101611e66565b50808352808252509250929050565b611fc98787878661272d565b5f866001600160401b03811115611fe257611fe2612cd2565b60405190808252806020026020018201604052801561202657816020015b604080518082019091525f80825260208201528152602001906001900390816120005790505b5090505f5b8781101561209d576040518060400160405280306001600160a01b031681526020018a8a8481811061205f5761205f612ce6565b90506020028101906120719190612cfa565b6020013581525082828151811061208a5761208a612ce6565b602090810291909101015260010161202b565b505f6120d96120ac8a8a6108fa565b6120b960208a018a612a8a565b6120c960408b0160208c01612a8a565b8a604001358b606001358b610852565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fe8ec1a78684338560405180610100016040528060c4815260200161311660c491398a8a6040518863ffffffff1660e01b815260040161214c9796959493929190612ea2565b5f604051808303815f87803b158015612163575f80fd5b505af1158015612175573d5f803e3d5ffd5b50505050505050505050505050565b5f5b81811015610c22576121fe33308585858181106121a5576121a5612ce6565b90506020028101906121b79190612cfa565b602001358686868181106121cd576121cd612ce6565b90506020028101906121df9190612cfa565b6121ed906020810190612a8a565b6001600160a01b0316929190612894565b600101612186565b5f5b83811015611129573685858381811061222357612223612ce6565b90506020028101906122359190612cfa565b90506001600160a01b03831661224e6020830183612a8a565b6001600160a01b0316036122e0576122696020820182612a8a565b6001600160a01b031661227f6020830183612a8a565b6001600160a01b03166122956020840184612a8a565b6001600160a01b03167f2d681b89f676ee730151ed8b542885aa75739e8e95e649b7aa21492ce48b7a2784602001356040516122d391815260200190565b60405180910390a461243f565b60405163d58e213760e01b8152309063d58e2137906123059084908890600401612fb9565b5f604051808303815f87803b15801561231c575f80fd5b505af192505050801561232d575060015b6123b9573d80801561235a576040519150601f19603f3d011682016040523d82523d5f602084013e61235f565b606091505b5061236d6020830183612a8a565b6001600160a01b03167f0f49126b7dcef8b87bb652c6d76d4525b6c8de7fd4dbdfb8f57121a3afa55f878360200135836040516123ab929190613084565b60405180910390a25061243f565b6123c96080820160608301612a8a565b6001600160a01b03166123e26060830160408401612a8a565b6001600160a01b03166123f86020840184612a8a565b6001600160a01b03167f2d681b89f676ee730151ed8b542885aa75739e8e95e649b7aa21492ce48b7a27846020013560405161243691815260200190565b60405180910390a45b50600101612208565b5f5b835181101561191057816001600160a01b031684828151811061246f5761246f612ce6565b60200260200101516001600160a01b03160361248d5760010161244a565b5f8482815181106124a0576124a0612ce6565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156124ee573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125129190612d9e565b905083828151811061252657612526612ce6565b6020026020010151811115612724575f84838151811061254857612548612ce6565b60200260200101518261255b9190612ddd565b90505f8087858151811061257157612571612ce6565b602090810291909101810151604080513360248201526044808201889052825180830390910181526064909101825292830180516001600160e01b031663a9059cbb60e01b179052516001600160a01b03909116916125cf916130a4565b5f604051808303815f865af19150503d805f8114612608576040519150601f19603f3d011682016040523d82523d5f602084013e61260d565b606091505b50915091505f8280156126455750815115806126455750602082511015801561264557508180602001905181019061264591906130b5565b905080156126b857336001600160a01b031689878151811061266957612669612ce6565b60200260200101516001600160a01b03167f8e1d9efaedb6cfd516f7be35435a5fd6a2576c1355d1ea5c6c42808cf4d6fafa866040516126ab91815260200190565b60405180910390a361271f565b336001600160a01b03168987815181106126d4576126d4612ce6565b60200260200101516001600160a01b03167f4514c4b6c254c1d3d4043b76e9ea439703b5dca394e1576b409f8484065e7dac8660405161271691815260200190565b60405180910390a35b505050505b5060010161244a565b816060013581604001351461275557604051631320e7a560e01b815260040160405180910390fd5b8261276082806130d0565b905014612780576040516307182afb60e31b815260040160405180910390fd5b5f5b838110156111295784848281811061279c5761279c612ce6565b90506020028101906127ae9190612cfa565b6127bc906020810190612a8a565b6001600160a01b03166127cf83806130d0565b838181106127df576127df612ce6565b6127f59260206040909202019081019150612a8a565b6001600160a01b03161461281f5760405163774cd1cb60e11b815260048101829052602401611120565b84848281811061283157612831612ce6565b90506020028101906128439190612cfa565b6020013561285183806130d0565b8381811061286157612861612ce6565b905060400201602001351461288c57604051630780043560e41b815260048101829052602401611120565b600101612782565b6128a28484848460016128ca565b61191057604051635274afe760e01b81526001600160a01b0385166004820152602401611120565b6040516323b872dd60e01b5f8181526001600160a01b038781166004528616602452604485905291602083606481808c5af1925060015f5114831661292657838315161561291a573d5f823e3d81fd5b5f883b113d1516831692505b604052505f60605295945050505050565b803561ffff811681146115bd575f80fd5b5f60208284031215612958575f80fd5b61296182612937565b9392505050565b6001600160a01b0381168114611394575f80fd5b5f806040838503121561298d575f80fd5b823561299881612968565b946020939093013593505050565b5f805f805f8060c087890312156129bb575f80fd5b8635955060208701356129cd81612968565b945060408701356129dd81612968565b935060608701359250608087013591506129f960a08801612937565b90509295509295509295565b5f8083601f840112612a15575f80fd5b5081356001600160401b03811115612a2b575f80fd5b6020830191508360208260051b8501011115612a45575f80fd5b9250929050565b5f8060208385031215612a5d575f80fd5b82356001600160401b03811115612a72575f80fd5b612a7e85828601612a05565b90969095509350505050565b5f60208284031215612a9a575f80fd5b813561296181612968565b5f805f60608486031215612ab7575f80fd5b8335612ac281612968565b92506020840135612ad281612968565b929592945050506040919091013590565b5f5b83811015612afd578181015183820152602001612ae5565b50505f910152565b5f8151808452612b1c816020860160208601612ae3565b601f01601f19169290920160200192915050565b602081525f6129616020830184612b05565b8015158114611394575f80fd5b5f8060408385031215612b60575f80fd5b8235612b6b81612968565b91506020830135612b7b81612b42565b809150509250929050565b5f8060408385031215612b97575f80fd5b82356001600160401b03811115612bac575f80fd5b830160c08186031215612998575f80fd5b5f60608284031215612bcd575f80fd5b50919050565b5f8083601f840112612be3575f80fd5b5081356001600160401b03811115612bf9575f80fd5b602083019150836020828501011115612a45575f80fd5b5f805f805f805f878903610120811215612c28575f80fd5b883560028110612c36575f80fd5b975060208901356001600160401b0380821115612c51575f80fd5b612c5d8c838d01612a05565b909950975087915060a0603f1984011215612c76575f80fd5b60408b01965060e08b0135925080831115612c8f575f80fd5b612c9b8c848d01612bbd565b95506101008b0135925080831115612cb1575f80fd5b5050612cbf8a828b01612bd3565b989b979a50959850939692959293505050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f823560be19833603018112612d0e575f80fd5b9190910192915050565b5f808335601e19843603018112612d2d575f80fd5b8301803591506001600160401b03821115612d46575f80fd5b602001915036819003821315612a45575f80fd5b818382375f9101908152919050565b81515f9082906020808601845b83811015612d9257815185529382019390820190600101612d76565b50929695505050505050565b5f60208284031215612dae575f80fd5b5051919050565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115612df057612df0612dc9565b92915050565b8082028115828204841417612df057612df0612dc9565b5f82612e2757634e487b7160e01b5f52601260045260245ffd5b500490565b5f815180845260208085019450602084015f5b83811015612e6f57815180516001600160a01b031688528301518388015260409096019590820190600101612e3f565b509495945050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60c081525f61012082018935601e198b3603018112612ebf575f80fd5b8a0160208181019135906001600160401b03821115612edc575f80fd5b8160061b3603831315612eed575f80fd5b606060c08701529281905290915f919061014086015b82841015612f3c578435612f1681612968565b6001600160a01b0316815284820135828201526040948501946001949094019301612f03565b60208e013560e088015260408e01356101008801528681036020880152612f63818e612e2c565b945050505050612f7e60408401896001600160a01b03169052565b8660608401528281036080840152612f968187612b05565b905082810360a0840152612fab818587612e7a565b9a9950505050505050505050565b604081525f8335612fc981612968565b6001600160a01b039081166040848101919091526020860135606085015285013590612ff482612968565b908116608084015260608501359061300b82612968565b1660a083810191909152608085013560c084015284013536859003601e19018112613034575f80fd5b84016020810190356001600160401b0381111561304f575f80fd5b80360382131561305d575f80fd5b60c060e085015261307361010085018284612e7a565b925050508260208301529392505050565b828152604060208201525f61309c6040830184612b05565b949350505050565b5f8251612d0e818460208701612ae3565b5f602082840312156130c5575f80fd5b815161296181612b42565b5f808335601e198436030181126130e5575f80fd5b8301803591506001600160401b038211156130fe575f80fd5b6020019150600681901b3603821315612a45575f80fdfe4475737453776565705769746e657373207769746e657373294475737453776565705769746e657373286279746573333220726f757465486173682c61646472657373206f7574707574546f6b656e2c6164647265737320726563697069656e742c75696e74323536206d696e416d6f756e744f75742c75696e7432353620646561646c696e652c75696e7431362066656542707329546f6b656e5065726d697373696f6e73286164647265737320746f6b656e2c75696e7432353620616d6f756e74299b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212200c339ad6bc973946535359e4d22ed889b32cd3b02a385b1fe35b0683b83dc6f364736f6c63430008180033000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad73000000000000000000000000b4cc7bfdd62b6faa6c24ff01c597b085dfadf470000000000000000000000000e09495e955794708d4bc28045c46891caf64373300000000000000000000000000000000000000000000000000000000000000c8
0x6080604052600436106101c8575f3560e01c8063a42dce80116100f2578063d55be8c611610092578063e1a4521811610062578063e1a45218146105c0578063e1ce0ae0146105d5578063e8f410c3146105f4578063f2fde38b1461062e575f80fd5b8063d55be8c61461052b578063d58e213714610540578063d606bdf81461055f578063d8528af014610592575f80fd5b8063c415b95c116100cd578063c415b95c146104b8578063c6182fbf146104d7578063ca1dd22e146104f8578063cfdbf25414610517575f80fd5b8063a42dce801461044c578063b2118a8d1461046b578063b8fe8d5f1461048a575f80fd5b80633f4ba83a11610168578063715018a611610138578063715018a6146103e95780638456cb59146103fd5780638da5cb5b146104115780639ede86481461042d575f80fd5b80633f4ba83a1461035a5780633fc8cef31461036e5780635c975abb146103a157806361f63190146103ca575f80fd5b80631520206c116101a35780631520206c146102aa5780631e18ede3146102d25780631ed6d2871461031357806324a9d8531461033a575f80fd5b8063023b1fc91461021c57806312261ee71461023b5780631291f79d1461028b575f80fd5b3661021857336001600160a01b037f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad7316146102165760405163f8a49ccb60e01b815260040160405180910390fd5b005b5f80fd5b348015610227575f80fd5b50610216610236366004612948565b61064d565b348015610246575f80fd5b5061026e7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b6040516001600160a01b0390911681526020015b60405180910390f35b348015610296575f80fd5b506102166102a536600461297c565b61071b565b3480156102b5575f80fd5b506102bf61ffff81565b60405161ffff9091168152602001610282565b3480156102dd575f80fd5b506103057f6572ccd6f51654624c69e01ae3d41b1603750f3ec68f7c106bc616fc7b66325b81565b604051908152602001610282565b34801561031e575f80fd5b5061026e73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610345575f80fd5b505f546102bf90600160a81b900461ffff1681565b348015610365575f80fd5b50610216610840565b348015610379575f80fd5b5061026e7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad7381565b3480156103ac575f80fd5b505f54600160a01b900460ff165b6040519015158152602001610282565b3480156103d5575f80fd5b506103056103e43660046129a6565b610852565b3480156103f4575f80fd5b506102166108d9565b348015610408575f80fd5b506102166108ea565b34801561041c575f80fd5b505f546001600160a01b031661026e565b348015610438575f80fd5b50610305610447366004612a4c565b6108fa565b348015610457575f80fd5b50610216610466366004612a8a565b610a9e565b348015610476575f80fd5b50610216610485366004612aa5565b610b40565b348015610495575f80fd5b506103ba6104a4366004612a8a565b60026020525f908152604090205460ff1681565b3480156104c3575f80fd5b5060015461026e906001600160a01b031681565b3480156104e2575f80fd5b506104eb610c27565b6040516102829190612b30565b348015610503575f80fd5b50610216610512366004612b4f565b610c44565b348015610522575f80fd5b50610305603281565b348015610536575f80fd5b506102bf61012c81565b34801561054b575f80fd5b5061021661055a366004612b86565b610cd2565b34801561056a575f80fd5b506103057f028224fb20e87629342dae7f283cffce3324a34b8001ea97b70f631e2b7d744d81565b34801561059d575f80fd5b506103ba6105ac366004612a8a565b60036020525f908152604090205460ff1681565b3480156105cb575f80fd5b5061030561271081565b3480156105e0575f80fd5b506102166105ef366004612b4f565b611130565b3480156105ff575f80fd5b5061061361060e366004612c10565b6111b6565b60408051938452602084019290925290820152606001610282565b348015610639575f80fd5b50610216610648366004612a8a565b61135a565b610655611397565b61012c61ffff8216111561067c5760405163cd4e616760e01b815260040160405180910390fd5b5f8161ffff1611801561069857506001546001600160a01b0316155b156106b65760405163b2f6ce2360e01b815260040160405180910390fd5b5f805461ffff838116600160a81b81810261ffff60a81b1985161790945560408051949093049091168084526020840191909152917f8d10f5697a370f640ed5d474159aba3cc86e9bc260a5e9d2db875ad992cb1a1f91015b60405180910390a15050565b610723611397565b61072b6113c3565b6001600160a01b0382166107525760405163d92e233d60e01b815260040160405180910390fd5b805f0361077257604051631f2a200560e01b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146107bb576040519150601f19603f3d011682016040523d82523d5f602084013e6107c0565b606091505b50509050806107e257604051633d2cec6f60e21b815260040160405180910390fd5b826001600160a01b03167fe3eb98b7fe2a0c1d490b92af73eeae611e9b00ab3c3f70b20bd7bb43f67a0f438360405161081d91815260200190565b60405180910390a25061083c60015f805160206131da83398151915255565b5050565b610848611397565b6108506113de565b565b604080517f028224fb20e87629342dae7f283cffce3324a34b8001ea97b70f631e2b7d744d60208201529081018790526001600160a01b0380871660608301528516608082015260a0810184905260c0810183905261ffff821660e08201525f90610100016040516020818303038152906040528051906020012090509695505050505050565b6108e1611397565b6108505f611432565b6108f2611397565b610850611481565b5f80826001600160401b0381111561091457610914612cd2565b60405190808252806020026020018201604052801561093d578160200160208202803683370190505b5090505f5b83811015610a6d573685858381811061095d5761095d612ce6565b905060200281019061096f9190612cfa565b90507f6572ccd6f51654624c69e01ae3d41b1603750f3ec68f7c106bc616fc7b66325b61099f6020830183612a8a565b60208301356109b46060850160408601612a8a565b6109c46080860160608701612a8a565b60808601356109d660a0880188612d18565b6040516109e4929190612d5a565b6040805191829003822060208301989098526001600160a01b03968716908201526060810194909452918416608084015290921660a082015260c081019190915260e08101919091526101000160405160208183030381529060405280519060200120838381518110610a5957610a59612ce6565b602090810291909101015250600101610942565b5080604051602001610a7f9190612d69565b6040516020818303038152906040528051906020012091505092915050565b610aa6611397565b6001600160a01b038116158015610ac857505f54600160a81b900461ffff1615155b15610ae65760405163b2f6ce2360e01b815260040160405180910390fd5b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f5d16ad41baeb009cd23eb8f6c7cde5c2e0cd5acf4a33926ab488875c37c37f38910161070f565b610b48611397565b610b506113c3565b6001600160a01b0383161580610b6d57506001600160a01b038216155b15610b8b5760405163d92e233d60e01b815260040160405180910390fd5b805f03610bab57604051631f2a200560e01b815260040160405180910390fd5b610bbf6001600160a01b03841683836114c3565b816001600160a01b0316836001600160a01b03167f8bbfbb5d7fcacf6fc74005cdede0635561638507f576c95f7f294c22141be2e583604051610c0491815260200190565b60405180910390a3610c2260015f805160206131da83398151915255565b505050565b60405180610100016040528060c4815260200161311660c4913981565b610c4c611397565b6001600160a01b038216610c735760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260026020908152604091829020805460ff191685151590811790915591519182527fa0a8236a39c9e79c2c39814563a83e040679f95d9a2ca65c8adefbf1ec32ca8891015b60405180910390a25050565b333014610cf2576040516314e1dbf760e11b815260040160405180910390fd5b5f610d006020840184612a8a565b6040516370a0823160e01b81523060048201529091506020840135906001600160a01b038316906370a0823190602401602060405180830381865afa158015610d4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6f9190612d9e565b1015610d8e57604051633811de6160e21b815260040160405180910390fd5b610db2610da16080850160608601612a8a565b6001600160a01b038316905f6114f8565b610dda610dc56080850160608601612a8a565b6001600160a01b0383169060208601356114f8565b6001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba316610e146080850160608601612a8a565b6001600160a01b031603610f39576001600160a01b0360208401351115610e4e57604051634c87966b60e01b815260040160405180910390fd5b65ffffffffffff821115610e755760405163b5468d6b60e01b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3166387517c45610eb16020860186612a8a565b610ec16060870160408801612a8a565b60405160e084901b6001600160e01b03191681526001600160a01b03928316600482015290821660248201526020870135909116604482015265ffffffffffff851660648201526084015f604051808303815f87803b158015610f22575f80fd5b505af1158015610f34573d5f803e3d5ffd5b505050505b5f80610f4b6060860160408701612a8a565b6001600160a01b0316610f6160a0870187612d18565b604051610f6f929190612d5a565b5f604051808303815f865af19150503d805f8114610fa8576040519150601f19603f3d011682016040523d82523d5f602084013e610fad565b606091505b5090925090506001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba316610fed6080870160608801612a8a565b6001600160a01b0316036110b7576001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3166387517c456110376020880188612a8a565b6110476060890160408a01612a8a565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201525f604482015265ffffffffffff421660648201526084015f604051808303815f87803b1580156110a0575f80fd5b505af11580156110b2573d5f803e3d5ffd5b505050505b6110db6110ca6080870160608801612a8a565b6001600160a01b038516905f6114f8565b81611129578051156110ef57805160208201fd5b6110ff6060860160408701612a8a565b60405163e96e549f60e01b81526001600160a01b0390911660048201526024015b60405180910390fd5b5050505050565b611138611397565b6001600160a01b03821661115f5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260036020908152604091829020805460ff191685151590811790915591519182527f590d4c011dbe7f18ffb4784ab975d08a8ae3449000df3d4ff9f30fa111e6c60a9101610cc6565b5f805f6111c16113c3565b6111c961154a565b604080516080810182525f8082526020820181905291810182905260608101919091526112046111ff60a08a0160808b01612948565b611574565b61ffff1660408201526112188a8a8a6115c2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61123a60208a018a612a8a565b6001600160a01b03161461125a576112556020890189612a8a565b61127c565b7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad735b6001600160a01b0316808252611295908b908b90611682565b80516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156112da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112fe9190612d9e565b60208201526060810189905260408101518151611326918d918d918d918d91908d8d8d611916565b6113308189611985565b9350935093505061134d60015f805160206131da83398151915255565b9750975097945050505050565b611362611397565b6001600160a01b03811661138b57604051631e4fbdf760e01b81525f6004820152602401611120565b61139481611432565b50565b5f546001600160a01b031633146108505760405163118cdaa760e01b8152336004820152602401611120565b6113cb611cd4565b60025f805160206131da83398151915255565b6113e6611d03565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61148961154a565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114153390565b6114d08383836001611d2c565b610c2257604051635274afe760e01b81526001600160a01b0384166004820152602401611120565b6115048383835f611d8e565b610c225761151583835f6001611d8e565b61153d57604051635274afe760e01b81526001600160a01b0384166004820152602401611120565b6114d08383836001611d8e565b5f54600160a01b900460ff16156108505760405163d93c066560e01b815260040160405180910390fd5b5f61ffff828116146115865781611594565b5f54600160a81b900461ffff165b905061012c61ffff821611156115bd5760405163cd4e616760e01b815260040160405180910390fd5b919050565b80606001354211156115e757604051631ab7da6b60e01b815260040160405180910390fd5b5f8290036116085760405163b6d45c2360e01b815260040160405180910390fd5b603282111561162a576040516305beb17160e11b815260040160405180910390fd5b5f6116386020830183612a8a565b6001600160a01b0316148061166457505f6116596040830160208401612a8a565b6001600160a01b0316145b15610c225760405163d92e233d60e01b815260040160405180910390fd5b5f5b82811015611910573684848381811061169f5761169f612ce6565b90506020028101906116b19190612cfa565b90505f6116c16020830183612a8a565b6001600160a01b0316036116e85760405163d92e233d60e01b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61170a6020830183612a8a565b6001600160a01b03160361173157604051633407ecb560e11b815260040160405180910390fd5b80602001355f0361175557604051631f2a200560e01b815260040160405180910390fd5b60808101351561177b57604051631d00736d60e31b815260048101839052602401611120565b6001600160a01b0383166117926020830183612a8a565b6001600160a01b0316036117a95750600101611684565b5f6117ba6060830160408401612a8a565b6001600160a01b031614806117e657505f6117db6080830160608401612a8a565b6001600160a01b0316145b156118045760405163d92e233d60e01b815260040160405180910390fd5b600461181360a0830183612d18565b905010156118375760405163a085984960e01b815260048101839052602401611120565b60025f61184a6060840160408501612a8a565b6001600160a01b0316815260208101919091526040015f205460ff1661189f5761187a6060820160408301612a8a565b60405163e356c1d360e01b81526001600160a01b039091166004820152602401611120565b60035f6118b26080840160608501612a8a565b6001600160a01b0316815260208101919091526040015f205460ff16611907576118e26080820160608301612a8a565b60405163046f5c8360e41b81526001600160a01b039091166004820152602401611120565b50600101611684565b50505050565b5f806119228a8a611dd8565b90925090505f8b600181111561193a5761193a612db5565b036119535761194e8a8a8a8a898989611fbd565b61195d565b61195d8a8a612184565b61196d8a8a8a6060013589612206565b611978828288612448565b5050505050505050505050565b602082015182516040516370a0823160e01b81523060048201525f92839283926001600160a01b03909116906370a0823190602401602060405180830381865afa1580156119d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119f99190612d9e565b611a039190612ddd565b92508360400135831015611a2a5760405163bb2875c360e01b815260040160405180910390fd5b612710856040015161ffff1684611a419190612df6565b611a4b9190612e0d565b9150611a578284612ddd565b90508115611afb576001546001600160a01b031680611a895760405163b2f6ce2360e01b815260040160405180910390fd5b8551611a9f906001600160a01b031682856114c3565b6001600160a01b03811633611ab76020880188612a8a565b6001600160a01b03167fa0c24741ddac735f5a6c1e1ef4f075407189d185a06db3329d022e99e9784a6086604051611af191815260200190565b60405180910390a4505b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee611b1d6020860186612a8a565b6001600160a01b031603611c2757604051632e1a7d4d60e01b8152600481018290527f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b031690632e1a7d4d906024015f604051808303815f87803b158015611b8a575f80fd5b505af1158015611b9c573d5f803e3d5ffd5b505f9250611bb39150506040860160208701612a8a565b6001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611bfa576040519150601f19603f3d011682016040523d82523d5f602084013e611bff565b606091505b5050905080611c2157604051633d2cec6f60e21b815260040160405180910390fd5b50611c4c565b611c4c611c3a6040860160208701612a8a565b86516001600160a01b031690836114c3565b611c596020850185612a8a565b6001600160a01b0316611c726040860160208701612a8a565b606080880151604080519182526020820188905281018690529081018490526001600160a01b03919091169033907f7b772d4395daa77305e23b590cd333e6a98f4d1f2ba1db9a45a7fe90763e05089060800160405180910390a49250925092565b5f805160206131da8339815191525460020361085057604051633ee5aeb560e01b815260040160405180910390fd5b5f54600160a01b900460ff1661085057604051638dfc202b60e01b815260040160405180910390fd5b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611d82578383151615611d76573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b60405163095ea7b360e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611d82578383151615611d76573d5f823e3d81fd5b606080826001600160401b03811115611df357611df3612cd2565b604051908082528060200260200182016040528015611e1c578160200160208202803683370190505b509150826001600160401b03811115611e3757611e37612cd2565b604051908082528060200260200182016040528015611e60578160200160208202803683370190505b5090505f805b84811015611fae575f868683818110611e8157611e81612ce6565b9050602002810190611e939190612cfa565b611ea1906020810190612a8a565b90505f805b84811015611ef157826001600160a01b0316878281518110611eca57611eca612ce6565b60200260200101516001600160a01b031603611ee95760019150611ef1565b600101611ea6565b5080611fa45781868581518110611f0a57611f0a612ce6565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b8152306004820152908316906370a0823190602401602060405180830381865afa158015611f5b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7f9190612d9e565b858581518110611f9157611f91612ce6565b6020026020010181815250508360010193505b5050600101611e66565b50808352808252509250929050565b611fc98787878661272d565b5f866001600160401b03811115611fe257611fe2612cd2565b60405190808252806020026020018201604052801561202657816020015b604080518082019091525f80825260208201528152602001906001900390816120005790505b5090505f5b8781101561209d576040518060400160405280306001600160a01b031681526020018a8a8481811061205f5761205f612ce6565b90506020028101906120719190612cfa565b6020013581525082828151811061208a5761208a612ce6565b602090810291909101015260010161202b565b505f6120d96120ac8a8a6108fa565b6120b960208a018a612a8a565b6120c960408b0160208c01612a8a565b8a604001358b606001358b610852565b90507f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba36001600160a01b031663fe8ec1a78684338560405180610100016040528060c4815260200161311660c491398a8a6040518863ffffffff1660e01b815260040161214c9796959493929190612ea2565b5f604051808303815f87803b158015612163575f80fd5b505af1158015612175573d5f803e3d5ffd5b50505050505050505050505050565b5f5b81811015610c22576121fe33308585858181106121a5576121a5612ce6565b90506020028101906121b79190612cfa565b602001358686868181106121cd576121cd612ce6565b90506020028101906121df9190612cfa565b6121ed906020810190612a8a565b6001600160a01b0316929190612894565b600101612186565b5f5b83811015611129573685858381811061222357612223612ce6565b90506020028101906122359190612cfa565b90506001600160a01b03831661224e6020830183612a8a565b6001600160a01b0316036122e0576122696020820182612a8a565b6001600160a01b031661227f6020830183612a8a565b6001600160a01b03166122956020840184612a8a565b6001600160a01b03167f2d681b89f676ee730151ed8b542885aa75739e8e95e649b7aa21492ce48b7a2784602001356040516122d391815260200190565b60405180910390a461243f565b60405163d58e213760e01b8152309063d58e2137906123059084908890600401612fb9565b5f604051808303815f87803b15801561231c575f80fd5b505af192505050801561232d575060015b6123b9573d80801561235a576040519150601f19603f3d011682016040523d82523d5f602084013e61235f565b606091505b5061236d6020830183612a8a565b6001600160a01b03167f0f49126b7dcef8b87bb652c6d76d4525b6c8de7fd4dbdfb8f57121a3afa55f878360200135836040516123ab929190613084565b60405180910390a25061243f565b6123c96080820160608301612a8a565b6001600160a01b03166123e26060830160408401612a8a565b6001600160a01b03166123f86020840184612a8a565b6001600160a01b03167f2d681b89f676ee730151ed8b542885aa75739e8e95e649b7aa21492ce48b7a27846020013560405161243691815260200190565b60405180910390a45b50600101612208565b5f5b835181101561191057816001600160a01b031684828151811061246f5761246f612ce6565b60200260200101516001600160a01b03160361248d5760010161244a565b5f8482815181106124a0576124a0612ce6565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156124ee573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125129190612d9e565b905083828151811061252657612526612ce6565b6020026020010151811115612724575f84838151811061254857612548612ce6565b60200260200101518261255b9190612ddd565b90505f8087858151811061257157612571612ce6565b602090810291909101810151604080513360248201526044808201889052825180830390910181526064909101825292830180516001600160e01b031663a9059cbb60e01b179052516001600160a01b03909116916125cf916130a4565b5f604051808303815f865af19150503d805f8114612608576040519150601f19603f3d011682016040523d82523d5f602084013e61260d565b606091505b50915091505f8280156126455750815115806126455750602082511015801561264557508180602001905181019061264591906130b5565b905080156126b857336001600160a01b031689878151811061266957612669612ce6565b60200260200101516001600160a01b03167f8e1d9efaedb6cfd516f7be35435a5fd6a2576c1355d1ea5c6c42808cf4d6fafa866040516126ab91815260200190565b60405180910390a361271f565b336001600160a01b03168987815181106126d4576126d4612ce6565b60200260200101516001600160a01b03167f4514c4b6c254c1d3d4043b76e9ea439703b5dca394e1576b409f8484065e7dac8660405161271691815260200190565b60405180910390a35b505050505b5060010161244a565b816060013581604001351461275557604051631320e7a560e01b815260040160405180910390fd5b8261276082806130d0565b905014612780576040516307182afb60e31b815260040160405180910390fd5b5f5b838110156111295784848281811061279c5761279c612ce6565b90506020028101906127ae9190612cfa565b6127bc906020810190612a8a565b6001600160a01b03166127cf83806130d0565b838181106127df576127df612ce6565b6127f59260206040909202019081019150612a8a565b6001600160a01b03161461281f5760405163774cd1cb60e11b815260048101829052602401611120565b84848281811061283157612831612ce6565b90506020028101906128439190612cfa565b6020013561285183806130d0565b8381811061286157612861612ce6565b905060400201602001351461288c57604051630780043560e41b815260048101829052602401611120565b600101612782565b6128a28484848460016128ca565b61191057604051635274afe760e01b81526001600160a01b0385166004820152602401611120565b6040516323b872dd60e01b5f8181526001600160a01b038781166004528616602452604485905291602083606481808c5af1925060015f5114831661292657838315161561291a573d5f823e3d81fd5b5f883b113d1516831692505b604052505f60605295945050505050565b803561ffff811681146115bd575f80fd5b5f60208284031215612958575f80fd5b61296182612937565b9392505050565b6001600160a01b0381168114611394575f80fd5b5f806040838503121561298d575f80fd5b823561299881612968565b946020939093013593505050565b5f805f805f8060c087890312156129bb575f80fd5b8635955060208701356129cd81612968565b945060408701356129dd81612968565b935060608701359250608087013591506129f960a08801612937565b90509295509295509295565b5f8083601f840112612a15575f80fd5b5081356001600160401b03811115612a2b575f80fd5b6020830191508360208260051b8501011115612a45575f80fd5b9250929050565b5f8060208385031215612a5d575f80fd5b82356001600160401b03811115612a72575f80fd5b612a7e85828601612a05565b90969095509350505050565b5f60208284031215612a9a575f80fd5b813561296181612968565b5f805f60608486031215612ab7575f80fd5b8335612ac281612968565b92506020840135612ad281612968565b929592945050506040919091013590565b5f5b83811015612afd578181015183820152602001612ae5565b50505f910152565b5f8151808452612b1c816020860160208601612ae3565b601f01601f19169290920160200192915050565b602081525f6129616020830184612b05565b8015158114611394575f80fd5b5f8060408385031215612b60575f80fd5b8235612b6b81612968565b91506020830135612b7b81612b42565b809150509250929050565b5f8060408385031215612b97575f80fd5b82356001600160401b03811115612bac575f80fd5b830160c08186031215612998575f80fd5b5f60608284031215612bcd575f80fd5b50919050565b5f8083601f840112612be3575f80fd5b5081356001600160401b03811115612bf9575f80fd5b602083019150836020828501011115612a45575f80fd5b5f805f805f805f878903610120811215612c28575f80fd5b883560028110612c36575f80fd5b975060208901356001600160401b0380821115612c51575f80fd5b612c5d8c838d01612a05565b909950975087915060a0603f1984011215612c76575f80fd5b60408b01965060e08b0135925080831115612c8f575f80fd5b612c9b8c848d01612bbd565b95506101008b0135925080831115612cb1575f80fd5b5050612cbf8a828b01612bd3565b989b979a50959850939692959293505050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f823560be19833603018112612d0e575f80fd5b9190910192915050565b5f808335601e19843603018112612d2d575f80fd5b8301803591506001600160401b03821115612d46575f80fd5b602001915036819003821315612a45575f80fd5b818382375f9101908152919050565b81515f9082906020808601845b83811015612d9257815185529382019390820190600101612d76565b50929695505050505050565b5f60208284031215612dae575f80fd5b5051919050565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115612df057612df0612dc9565b92915050565b8082028115828204841417612df057612df0612dc9565b5f82612e2757634e487b7160e01b5f52601260045260245ffd5b500490565b5f815180845260208085019450602084015f5b83811015612e6f57815180516001600160a01b031688528301518388015260409096019590820190600101612e3f565b509495945050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60c081525f61012082018935601e198b3603018112612ebf575f80fd5b8a0160208181019135906001600160401b03821115612edc575f80fd5b8160061b3603831315612eed575f80fd5b606060c08701529281905290915f919061014086015b82841015612f3c578435612f1681612968565b6001600160a01b0316815284820135828201526040948501946001949094019301612f03565b60208e013560e088015260408e01356101008801528681036020880152612f63818e612e2c565b945050505050612f7e60408401896001600160a01b03169052565b8660608401528281036080840152612f968187612b05565b905082810360a0840152612fab818587612e7a565b9a9950505050505050505050565b604081525f8335612fc981612968565b6001600160a01b039081166040848101919091526020860135606085015285013590612ff482612968565b908116608084015260608501359061300b82612968565b1660a083810191909152608085013560c084015284013536859003601e19018112613034575f80fd5b84016020810190356001600160401b0381111561304f575f80fd5b80360382131561305d575f80fd5b60c060e085015261307361010085018284612e7a565b925050508260208301529392505050565b828152604060208201525f61309c6040830184612b05565b949350505050565b5f8251612d0e818460208701612ae3565b5f602082840312156130c5575f80fd5b815161296181612b42565b5f808335601e198436030181126130e5575f80fd5b8301803591506001600160401b038211156130fe575f80fd5b6020019150600681901b3603821315612a45575f80fdfe4475737453776565705769746e657373207769746e657373294475737453776565705769746e657373286279746573333220726f757465486173682c61646472657373206f7574707574546f6b656e2c6164647265737320726563697069656e742c75696e74323536206d696e416d6f756e744f75742c75696e7432353620646561646c696e652c75696e7431362066656542707329546f6b656e5065726d697373696f6e73286164647265737320746f6b656e2c75696e7432353620616d6f756e74299b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212200c339ad6bc973946535359e4d22ed889b32cd3b02a385b1fe35b0683b83dc6f364736f6c63430008180033
| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| no token holdings | ||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| 0x9101eb…fca0c3 | 22 days agoSun, 26 Jul 2026 17:05:14 UTC | 0xa0a823…ca88 | [0] 0x000000000000…959e5cb2 data: 0x000000000000000000…00000001 |
| 0x126163…92fd6f | 22 days agoSun, 26 Jul 2026 17:04:58 UTC | 0xa0a823…ca88 | [0] 0x000000000000…eb649eba data: 0x000000000000000000…00000001 |
| 0xe3324d…280f69 | 22 days agoSun, 26 Jul 2026 17:04:42 UTC | 0xa0a823…ca88 | [0] 0x000000000000…b66337b5 data: 0x000000000000000000…00000001 |
| 0x81de6b…d06f78 | 22 days agoSun, 26 Jul 2026 17:04:26 UTC | 0xa0a823…ca88 | [0] 0x000000000000…72414af3 data: 0x000000000000000000…00000001 |
| 0x0fbc1b…bdd6f0 | 22 days agoSun, 26 Jul 2026 17:04:11 UTC | 0xa0a823…ca88 | [0] 0x000000000000…72c22734 data: 0x000000000000000000…00000001 |
| 0xe4b336…0eaa6a | 22 days agoSun, 26 Jul 2026 17:03:56 UTC | 0xa0a823…ca88 | [0] 0x000000000000…a3304e64 data: 0x000000000000000000…00000001 |
| 0x3795ba…e0844c | 22 days agoSun, 26 Jul 2026 17:03:32 UTC | 0x590d4c…c60a | [0] 0x000000000000…a3304e64 data: 0x000000000000000000…00000001 |
| 0x63d798…d4e918 | 22 days agoSun, 26 Jul 2026 17:02:08 UTC | 0x590d4c…c60a | [0] 0x000000000000…72c22734 data: 0x000000000000000000…00000001 |
| 0xbbc990…4b7d63 | 22 days agoSun, 26 Jul 2026 17:01:50 UTC | 0x590d4c…c60a | [0] 0x000000000000…72414af3 data: 0x000000000000000000…00000001 |
| 0x50fb0d…46ff2e | 22 days agoSun, 26 Jul 2026 17:01:01 UTC | 0x590d4c…c60a | [0] 0x000000000000…b66337b5 data: 0x000000000000000000…00000001 |
| 0xa64145…229978 | 22 days agoSun, 26 Jul 2026 16:59:26 UTC | 0x590d4c…c60a | [0] 0x000000000000…b66337b5 data: 0x000000000000000000…00000001 |
| 0x3af64c…37713d | 22 days agoSun, 26 Jul 2026 16:58:16 UTC | 0x590d4c…c60a | [0] 0x000000000000…eb649eba data: 0x000000000000000000…00000001 |
| 0x672adb…0e0440 | 22 days agoSun, 26 Jul 2026 16:58:03 UTC | 0x590d4c…c60a | [0] 0x000000000000…eb649eba data: 0x000000000000000000…00000001 |
| 0x9daa74…c040c9 | 22 days agoSun, 26 Jul 2026 16:57:41 UTC | 0x590d4c…c60a | [0] 0x000000000000…959e5cb2 data: 0x000000000000000000…00000001 |
| 0xf80297…3e62ff | 22 days agoSun, 26 Jul 2026 16:37:58 UTC | 0x8be007…57e0 | [0] 0x000000000000…00000000 [1] 0x000000000000…dfadf470 |
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| no token transfers for this address yet | |||||||||
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| 0x9101eb…fca0c3 | setAllowedTarget | 20,036,381 | 22 days agoSun, 26 Jul 2026 17:05:14 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000242 | |
| 0x126163…92fd6f | setAllowedTarget | 20,036,229 | 22 days agoSun, 26 Jul 2026 17:04:58 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000247 | |
| 0xe3324d…280f69 | setAllowedTarget | 20,036,065 | 22 days agoSun, 26 Jul 2026 17:04:42 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000247 | |
| 0x81de6b…d06f78 | setAllowedTarget | 20,035,905 | 22 days agoSun, 26 Jul 2026 17:04:26 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000247 | |
| 0x0fbc1b…bdd6f0 | setAllowedTarget | 20,035,755 | 22 days agoSun, 26 Jul 2026 17:04:11 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000242 | |
| 0xe4b336…0eaa6a | setAllowedTarget | 20,035,611 | 22 days agoSun, 26 Jul 2026 17:03:56 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000244 | |
| 0x3795ba…e0844c | setAllowedSpender | 20,035,371 | 22 days agoSun, 26 Jul 2026 17:03:32 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000246 | |
| 0x63d798…d4e918 | setAllowedSpender | 20,034,535 | 22 days agoSun, 26 Jul 2026 17:02:08 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000243 | |
| 0xbbc990…4b7d63 | setAllowedSpender | 20,034,354 | 22 days agoSun, 26 Jul 2026 17:01:50 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000242 | |
| 0x50fb0d…46ff2e | setAllowedSpender | 20,033,847 | 22 days agoSun, 26 Jul 2026 17:01:01 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000144 | |
| 0xa64145…229978 | setAllowedSpender | 20,032,903 | 22 days agoSun, 26 Jul 2026 16:59:26 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000244 | |
| 0x3af64c…37713d | setAllowedSpender | 20,032,197 | 22 days agoSun, 26 Jul 2026 16:58:16 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000143 | |
| 0x672adb…0e0440 | setAllowedSpender | 20,032,076 | 22 days agoSun, 26 Jul 2026 16:58:03 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000244 | |
| 0x9daa74…c040c9 | setAllowedSpender | 20,031,850 | 22 days agoSun, 26 Jul 2026 16:57:41 UTC | 0xb4cc…f470 | IN | DustSwapSweepRouterRobinhood | $0.000 ETH | 0.00000242 |
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| no internal transactions found for this address yet (traced blocks + on-demand) | ||||||||