Source Code
Cross-Chain Transactions
Loading...
Loading
Contract Name:
UniswapV3SwapAdapter
Compiler Version
v0.8.29+commit.ab55807c
Optimization Enabled:
Yes with 149 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {ISwapRouter02, IV3SwapRouter} from "../../src/interfaces/external/ISwapRouter02.sol";
import {ISwapAdapter} from "../interfaces/ISwapAdapter.sol";
/**
* @title Uniswap V3 Swap Adapter
* @author MetaStreet Foundation
*/
contract UniswapV3SwapAdapter is ISwapAdapter, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
/*------------------------------------------------------------------------*/
/* Constants */
/*------------------------------------------------------------------------*/
/**
* @notice Implementation version
*/
string public constant IMPLEMENTATION_VERSION = "1.0";
/**
* @notice Implementation name
*/
string public constant IMPLEMENTATION_NAME = "Uniswap V3 Swap Adapter";
/**
* @notice USD.ai role for access control
*/
bytes32 internal constant USDAI_ROLE = keccak256("USDAI_ROLE");
/**
* @notice Fee for Uniswap V3 swap router (0.01%)
*/
uint24 internal constant UNISWAP_V3_FEE = 100;
/**
* @notice Path address size
*/
uint256 internal constant PATH_ADDR_SIZE = 20;
/**
* @notice Path fee size
*/
uint256 internal constant PATH_FEE_SIZE = 3;
/**
* @notice Path next offset
*/
uint256 internal constant PATH_NEXT_OFFSET = PATH_ADDR_SIZE + PATH_FEE_SIZE;
/*------------------------------------------------------------------------*/
/* Errors */
/*------------------------------------------------------------------------*/
/**
* @notice Invalid token
*/
error InvalidToken();
/**
* @notice Invalid amount
*/
error InvalidAmount();
/**
* @notice Invalid path
*/
error InvalidPath();
/**
* @notice Invalid path format
*/
error InvalidPathFormat();
/*------------------------------------------------------------------------*/
/* Immutable state */
/*------------------------------------------------------------------------*/
/**
* @notice Base token
*/
IERC20 internal immutable _baseToken;
/**
* @notice Swap router
*/
ISwapRouter02 internal immutable _swapRouter;
/*------------------------------------------------------------------------*/
/* State */
/*------------------------------------------------------------------------*/
/**
* @notice Whitelisted tokens
*/
EnumerableSet.AddressSet internal _whitelistedTokens;
/*------------------------------------------------------------------------*/
/* Constructor */
/*------------------------------------------------------------------------*/
/**
* @notice Uniswap V3 Swap Adapter Constructor
* @param baseToken_ Base token
* @param swapRouter_ Swap router
* @param tokens Whitelisted tokens
*/
constructor(address baseToken_, address swapRouter_, address[] memory tokens) {
_baseToken = IERC20(baseToken_);
_swapRouter = ISwapRouter02(swapRouter_);
for (uint256 i; i < tokens.length; i++) {
if (tokens[i] == address(0)) revert InvalidToken();
_whitelistedTokens.add(tokens[i]);
}
/* Grant roles */
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/*------------------------------------------------------------------------*/
/* Modifiers */
/*------------------------------------------------------------------------*/
/**
* @notice Non-zero uint
* @param value Value
*/
modifier nonZeroUint(
uint256 value
) {
if (value == 0) revert InvalidAmount();
_;
}
/**
* @notice Whitelisted token
* @param value Value
*/
modifier whitelistedToken(
address value
) {
if (!_whitelistedTokens.contains(value)) revert InvalidToken();
_;
}
/**
* @notice Valid swap in path
* @param tokenInput Input token
* @param path Path
*/
modifier validSwapInPath(address tokenInput, bytes calldata path) {
if (path.length != 0) {
/* Decode input and output tokens */
(address tokenInput_, address tokenOutput) = _decodeInputAndOutputTokens(path);
/* Validate input and output tokens */
if (tokenInput_ != tokenInput || tokenOutput != address(_baseToken)) {
revert InvalidPath();
}
}
_;
}
/**
* @notice Valid swap out path
* @param tokenOutput Output token
* @param path Path
*/
modifier validSwapOutPath(address tokenOutput, bytes calldata path) {
if (path.length != 0) {
/* Decode input and output tokens */
(address tokenInput, address tokenOutput_) = _decodeInputAndOutputTokens(path);
/* Validate input and output tokens */
if (tokenInput != address(_baseToken) || tokenOutput_ != tokenOutput) {
revert InvalidPath();
}
}
_;
}
/*------------------------------------------------------------------------*/
/* Getters */
/*------------------------------------------------------------------------*/
/**
* @inheritdoc ISwapAdapter
*/
function baseToken() external view returns (address) {
return address(_baseToken);
}
/**
* @notice Whitelisted tokens
* @return Whitelisted tokens
*/
function whitelistedTokens() external view returns (address[] memory) {
return _whitelistedTokens.values();
}
/*------------------------------------------------------------------------*/
/* Helpers */
/*------------------------------------------------------------------------*/
/**
* @notice Decode input and output tokens
* @param path Swap path
* @return tokenInput Input token
* @return tokenOutput Output token
*/
function _decodeInputAndOutputTokens(
bytes calldata path
) internal pure returns (address, address) {
/* Validate path format */
if (
(path.length < PATH_ADDR_SIZE + PATH_FEE_SIZE + PATH_ADDR_SIZE)
|| ((path.length - PATH_ADDR_SIZE) % PATH_NEXT_OFFSET != 0)
) {
revert InvalidPathFormat();
}
/* Get input token */
address tokenInput = address(bytes20(path[:PATH_ADDR_SIZE]));
/* Calculate position of output token */
uint256 numHops = (path.length - PATH_ADDR_SIZE) / PATH_NEXT_OFFSET;
uint256 outputTokenIndex = numHops * PATH_NEXT_OFFSET;
/* Get output token */
address tokenOutput = address(bytes20(path[outputTokenIndex:outputTokenIndex + PATH_ADDR_SIZE]));
return (tokenInput, tokenOutput);
}
/*------------------------------------------------------------------------*/
/* API */
/*------------------------------------------------------------------------*/
/**
* @inheritdoc ISwapAdapter
*/
function swapIn(
address inputToken,
uint256 inputAmount,
uint256 minBaseAmount,
bytes calldata path
)
external
onlyRole(USDAI_ROLE)
nonZeroUint(inputAmount)
whitelistedToken(inputToken)
validSwapInPath(inputToken, path)
returns (uint256)
{
/* Transfer token input from sender to this contract */
IERC20(inputToken).safeTransferFrom(msg.sender, address(this), inputAmount);
/* Approve the router to spend token input */
IERC20(inputToken).forceApprove(address(_swapRouter), inputAmount);
/* Swap token input for base token */
uint256 baseAmount;
if (path.length == 0) {
/* Define swap params */
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
tokenIn: address(inputToken),
tokenOut: address(_baseToken),
fee: UNISWAP_V3_FEE,
recipient: msg.sender,
amountIn: inputAmount,
amountOutMinimum: minBaseAmount,
sqrtPriceLimitX96: 0
});
/* Swap input token for base token */
baseAmount = _swapRouter.exactInputSingle(params);
} else {
/* Define swap params */
IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter.ExactInputParams({
path: path,
recipient: msg.sender,
amountIn: inputAmount,
amountOutMinimum: minBaseAmount
});
/* Swap input token for base token */
baseAmount = _swapRouter.exactInput(params);
}
/* Emit SwappedIn event */
emit SwappedIn(inputToken, inputAmount, baseAmount);
return baseAmount;
}
/**
* @inheritdoc ISwapAdapter
*/
function swapOut(
address outputToken,
uint256 baseAmount,
uint256 minOutputAmount,
bytes calldata path
)
external
onlyRole(USDAI_ROLE)
nonZeroUint(baseAmount)
whitelistedToken(outputToken)
validSwapOutPath(outputToken, path)
returns (uint256)
{
/* Transfer token input from sender to this contract */
_baseToken.safeTransferFrom(msg.sender, address(this), baseAmount);
/* Approve the router to spend base token */
_baseToken.forceApprove(address(_swapRouter), baseAmount);
/* Swap base token for token output */
uint256 outputAmount;
if (path.length == 0) {
/* Define swap params */
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
tokenIn: address(_baseToken),
tokenOut: address(outputToken),
fee: UNISWAP_V3_FEE,
recipient: msg.sender,
amountIn: baseAmount,
amountOutMinimum: minOutputAmount,
sqrtPriceLimitX96: 0
});
/* Swap base token for token output */
outputAmount = _swapRouter.exactInputSingle(params);
} else {
/* Define swap params */
IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter.ExactInputParams({
path: path,
recipient: msg.sender,
amountIn: baseAmount,
amountOutMinimum: minOutputAmount
});
/* Swap base token for token output */
outputAmount = _swapRouter.exactInput(params);
}
/* Emit SwappedOut event */
emit SwappedOut(outputToken, baseAmount, outputAmount);
return outputAmount;
}
/*------------------------------------------------------------------------*/
/* Admin API */
/*------------------------------------------------------------------------*/
/**
* @notice Set whitelisted tokens
* @param whitelistedTokens_ Whitelisted tokens
*/
function setWhitelistedTokens(
address[] memory whitelistedTokens_
) external onlyRole(DEFAULT_ADMIN_ROLE) {
for (uint256 i; i < whitelistedTokens_.length; i++) {
if (whitelistedTokens_[i] == address(0)) revert InvalidToken();
_whitelistedTokens.add(whitelistedTokens_[i]);
}
}
/**
* @notice Remove whitelisted tokens
* @param whitelistedTokens_ Whitelisted tokens
*/
function removeWhitelistedTokens(
address[] memory whitelistedTokens_
) external onlyRole(DEFAULT_ADMIN_ROLE) {
for (uint256 i; i < whitelistedTokens_.length; i++) {
if (whitelistedTokens_[i] == address(0)) revert InvalidToken();
_whitelistedTokens.remove(whitelistedTokens_[i]);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.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 {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* 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 {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @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 rely 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 rely 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}.
* Opposedly, 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 high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import "@uniswap/v3-periphery/contracts/interfaces/ISelfPermit.sol";
import "@uniswap/swap-router-contracts/contracts/interfaces/IV2SwapRouter.sol";
import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol";
import "@uniswap/swap-router-contracts/contracts/interfaces/IMulticallExtended.sol";
import "./IApproveAndCall.sol";
/**
* @title Router token swapping functionality
* @notice From https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/interfaces/ISwapRouter02.sol
*/
interface ISwapRouter02 is IV2SwapRouter, IV3SwapRouter, IApproveAndCall, IMulticallExtended, ISelfPermit {}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Swap Adapter Interface
* @author MetaStreet Foundation
*/
interface ISwapAdapter {
/*------------------------------------------------------------------------*/
/* Events */
/*------------------------------------------------------------------------*/
/**
* @notice Swapped in event
* @param inputToken Input token
* @param inputAmount Input amount
* @param baseOutputAmount Base token output amount
*/
event SwappedIn(address indexed inputToken, uint256 inputAmount, uint256 baseOutputAmount);
/**
* @notice Swapped out event
* @param outputToken Output token
* @param baseInputAmount Base token input amount
* @param outputAmount Output amount
*/
event SwappedOut(address indexed outputToken, uint256 baseInputAmount, uint256 outputAmount);
/*------------------------------------------------------------------------*/
/* Getter */
/*------------------------------------------------------------------------*/
/**
* @notice Base token
* @return Base Token
*/
function baseToken() external view returns (address);
/*------------------------------------------------------------------------*/
/* Permissioned API */
/*------------------------------------------------------------------------*/
/**
* @notice Swap in for base token
* @param inputToken Input token
* @param inputAmount Input amount
* @param minBaseAmount Minimum base token amount
* @param path Swap path
* @return Base amount
*/
function swapIn(
address inputToken,
uint256 inputAmount,
uint256 minBaseAmount,
bytes calldata path
) external returns (uint256);
/**
* @notice Swap out of base token
* @param outputToken Output token
* @param baseAmount Base token amount
* @param minOutputAmount Minimum output amount
* @param path Swap path
* @return Output amount
*/
function swapOut(
address outputToken,
uint256 baseAmount,
uint256 minOutputAmount,
bytes calldata path
) external returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
interface ISelfPermit {
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed.
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V2
interface IV2SwapRouter {
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param amountIn The amount of token to swap
/// @param amountOutMin The minimum amount of output that must be received
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountOut The amount of the received token
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) external payable returns (uint256 amountOut);
/// @notice Swaps as little as possible of one token for an exact amount of another token
/// @param amountOut The amount of token to swap for
/// @param amountInMax The maximum amount of input that the caller will pay
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountIn The amount of token to pay
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to
) external payable returns (uint256 amountIn);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface IV3SwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-periphery/contracts/interfaces/IMulticall.sol';
/// @title MulticallExtended interface
/// @notice Enables calling multiple methods in a single call to the contract with optional validation
interface IMulticallExtended is IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param deadline The time by which this function must be called before failing
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results);
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param previousBlockhash The expected parent blockHash
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes32 previousBlockhash, bytes[] calldata data)
external
payable
returns (bytes[] memory results);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/**
* @title IApproveAndCall
* @notice From https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/interfaces/IApproveAndCall.sol
* @dev Updated the pragma version to >=0.7.5 to match our project
*/
interface IApproveAndCall {
enum ApprovalType {
NOT_REQUIRED,
MAX,
MAX_MINUS_ONE,
ZERO_THEN_MAX,
ZERO_THEN_MAX_MINUS_ONE
}
/// @dev Lens to be called off-chain to determine which (if any) of the relevant approval functions should be called
/// @param token The token to approve
/// @param amount The amount to approve
/// @return The required approval type
function getApprovalType(address token, uint256 amount) external returns (ApprovalType);
/// @notice Approves a token for the maximum possible amount
/// @param token The token to approve
function approveMax(
address token
) external payable;
/// @notice Approves a token for the maximum possible amount minus one
/// @param token The token to approve
function approveMaxMinusOne(
address token
) external payable;
/// @notice Approves a token for zero, then the maximum possible amount
/// @param token The token to approve
function approveZeroThenMax(
address token
) external payable;
/// @notice Approves a token for zero, then the maximum possible amount minus one
/// @param token The token to approve
function approveZeroThenMaxMinusOne(
address token
) external payable;
/// @notice Calls the position manager with arbitrary calldata
/// @param data Calldata to pass along to the position manager
/// @return result The result from the call
function callPositionManager(
bytes memory data
) external payable returns (bytes memory result);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
}
/// @notice Calls the position manager's mint function
/// @param params Calldata to pass along to the position manager
/// @return result The result from the call
function mint(
MintParams calldata params
) external payable returns (bytes memory result);
struct IncreaseLiquidityParams {
address token0;
address token1;
uint256 tokenId;
uint256 amount0Min;
uint256 amount1Min;
}
/// @notice Calls the position manager's increaseLiquidity function
/// @param params Calldata to pass along to the position manager
/// @return result The result from the call
function increaseLiquidity(
IncreaseLiquidityParams calldata params
) external payable returns (bytes memory result);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @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);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/",
"lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/",
"lib/openzeppelin-contracts:openzeppelin/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@uniswap/v3-periphery/=lib/uniswap-v3-periphery/",
"@uniswap/v3-core/=lib/uniswap-v3-core/",
"@uniswap/swap-router-contracts/=lib/uniswap-swap-router-contracts/",
"metastreet-contracts-v2/=lib/metastreet-contracts-v2/contracts/",
"@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero/packages/layerzero-v2/evm/protocol/",
"@layerzerolabs/lz-evm-oapp-v2/=lib/layerzero/packages/layerzero-v2/evm/oapp/",
"@layerzerolabs/test-devtools-evm-foundry/=lib/layerzero-devtools/packages/test-devtools-evm-foundry/",
"@layerzerolabs/lz-evm-messagelib-v2/=lib/layerzero/packages/layerzero-v2/evm/messagelib/",
"@layerzerolabs/lz-evm-v1-0.7/=lib/layerzero-v1/",
"solidity-bytes-utils/=lib/solidity-bytes-utils/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"layerzero-devtools/=lib/layerzero-devtools/packages/toolbox-foundry/src/",
"layerzero-v1/=lib/layerzero-v1/contracts/",
"layerzero/=lib/layerzero/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"uniswap-swap-router-contracts/=lib/uniswap-swap-router-contracts/contracts/",
"uniswap-v3-core/=lib/uniswap-v3-core/",
"uniswap-v3-periphery/=lib/uniswap-v3-periphery/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 149
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"baseToken_","type":"address"},{"internalType":"address","name":"swapRouter_","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidPathFormat","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"inputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseOutputAmount","type":"uint256"}],"name":"SwappedIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseInputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"SwappedOut","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IMPLEMENTATION_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IMPLEMENTATION_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelistedTokens_","type":"address[]"}],"name":"removeWhitelistedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelistedTokens_","type":"address[]"}],"name":"setWhitelistedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseAmount","type":"uint256"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"swapIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"swapOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whitelistedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c0604052346101745761159e803803806100198161018c565b92833981019060608183031261017457610032816101b1565b61003e602083016101b1565b604083015190926001600160401b03821161017457019280601f85011215610174578351936001600160401b038511610178578460051b9060208061008481850161018c565b80988152019282010192831161017457602001905b82821061015c575050506001600160a01b039081166080521660a0525f5b815181101561010e576001600160a01b036100d282846101c5565b5116156100ff576001906100f86001600160a01b036100f183866101c5565b5116610276565b50016100b7565b63c1ab6dc160e01b5f5260045ffd5b610117336101ed565b506040516112af90816102cf82396080518181816101bb01528181610398015281816104010152818161047f0152610650015260a05181818161016301526104ad0152f35b60208091610169846101b1565b815201910190610099565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b6040519190601f01601f191682016001600160401b0381118382101761017857604052565b51906001600160a01b038216820361017457565b80518210156101d95760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b0381165f9081525f51602061157e5f395f51905f52602052604090205460ff16610271576001600160a01b03165f8181525f51602061157e5f395f51905f5260205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f90565b805f52600260205260405f2054155f1461027157600154680100000000000000008110156101785760018101806001558110156101d957819060015f5260205f200155600154905f52600260205260405f205560019056fe60806040526004361015610011575f80fd5b5f3560e01c8062a3fecd146109c157806301ffc9a71461096b578063248a9ca3146109395780632f2ff15d146108fc57806336568abe146108b85780635988f9a5146108585780635e1762a0146107b6578063754b377c1461076d57806391d1485414610725578063a217fddf1461070b578063bcec454f146106ab578063bd8bc59814610430578063c55dae63146103ec578063ccce8e28146101055763d547741f146100bd575f80fd5b34610101576040366003190112610101576100ff6004356100dc610aac565b906100fa6100f5825f525f602052600160405f20015490565b610d95565b610e55565b005b5f80fd5b346101015761011336610b5b565b9361011f929192610d26565b83156103dd576001600160a01b03165f81815260026020526040902054909290156103ce578415908115610357575b905f956020939261016187303389610fa6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692610197888589610ff1565b88146102a0575050604051610219926101af82610a4a565b85825260018060a01b037f000000000000000000000000000000000000000000000000000000000000000016858301526064604083015233606083015286608083015260a08201528660c08201526040519687809481936304e45aaf60e01b835260048301610c79565b03925af1928315610295575f93610254575b505f51602061125a5f395f51905f526040602094935b81519081528486820152a2604051908152f35b92506020833d60201161028d575b8161026f60209383610a66565b81010312610101579151915f51602061125a5f395f51905f5261022b565b3d9150610262565b6040513d5f823e3d90fd5b926102bc6102ea94604051936102b585610a1a565b3691610bef565b82523385830152866040830152606082015260405196878094819363b858183f60e01b835260048301610c35565b03925af1928315610295575f93610316575b505f51602061125a5f395f51905f52604060209493610241565b92506020833d60201161034f575b8161033160209383610a66565b81010312610101579151915f51602061125a5f395f51905f526102fc565b3d9150610324565b9190836103648785610ed5565b6001600160a01b0390911690911480159190610396575b5061038757909161014e565b6320db826760e01b5f5260045ffd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b039081169116141590508761037b565b63c1ab6dc160e01b5f5260045ffd5b63162908e360e11b5f5260045ffd5b34610101575f366003190112610101576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101015761043e36610b5b565b93610447610d26565b83156103dd576001600160a01b03165f81815260026020526040902054909290156103ce578415908115610641575b905f95602093927f0000000000000000000000000000000000000000000000000000000000000000916104ab88303386610fa6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316936104e1898686610ff1565b89146105be57506105439350604051916104fa83610a4a565b60018060a01b0316825285858301526064604083015233606083015286608083015260a08201528660c08201526040519687809481936304e45aaf60e01b835260048301610c79565b03925af1928315610295575f9361057d575b505f51602061123a5f395f51905f5260406020949381519081528486820152a2604051908152f35b92506020833d6020116105b6575b8161059860209383610a66565b81010312610101579151915f51602061123a5f395f51905f52610555565b3d915061058b565b91506102bc6105d494604051936102b585610a1a565b03925af1928315610295575f93610600575b505f51602061123a5f395f51905f52604060209493610241565b92506020833d602011610639575b8161061b60209383610a66565b81010312610101579151915f51602061123a5f395f51905f526105e6565b3d915061060e565b91908361064e8784610ed5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b039081169216919091148015929190610697575b5050610387579091610476565b6001600160a01b031614159050848861068a565b34610101576106b936610ac2565b6106c1610cd7565b5f5b81518110156100ff576001600160a01b036106de8284610bc7565b5116156103ce576001906107046001600160a01b036106fd8386610bc7565b5116611122565b50016106c3565b34610101575f3660031901126101015760206040515f8152f35b346101015760403660031901126101015761073e610aac565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610101575f366003190112610101576107b260405161078e604082610a66565b60038152620312e360ec1b6020820152604051918291602083526020830190610a88565b0390f35b34610101575f366003190112610101576040518060206001549283815201809260015f5260205f20905f5b81811061084257505050816107f7910382610a66565b604051918291602083019060208452518091526040830191905f5b818110610820575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610812565b82548452602090930192600192830192016107e1565b346101015761086636610ac2565b61086e610cd7565b5f5b81518110156100ff576001600160a01b0361088b8284610bc7565b5116156103ce576001906108b16001600160a01b036108aa8386610bc7565b51166110b7565b5001610870565b34610101576040366003190112610101576108d1610aac565b336001600160a01b038216036108ed576100ff90600435610e55565b63334bd91960e11b5f5260045ffd5b34610101576040366003190112610101576100ff60043561091b610aac565b906109346100f5825f525f602052600160405f20015490565b610dcd565b346101015760203660031901126101015760206109636004355f525f602052600160405f20015490565b604051908152f35b346101015760203660031901126101015760043563ffffffff60e01b811680910361010157602090637965db0b60e01b81149081156109b0575b506040519015158152f35b6301ffc9a760e01b149050826109a5565b34610101575f366003190112610101576107b26040516109e2604082610a66565b60178152762ab734b9bbb0b8102b199029bbb0b81020b230b83a32b960491b6020820152604051918291602083526020830190610a88565b6080810190811067ffffffffffffffff821117610a3657604052565b634e487b7160e01b5f52604160045260245ffd5b60e0810190811067ffffffffffffffff821117610a3657604052565b90601f8019910116810190811067ffffffffffffffff821117610a3657604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602435906001600160a01b038216820361010157565b60206003198201126101015760043567ffffffffffffffff811161010157816023820112156101015780600401359167ffffffffffffffff8311610a36578260051b9160405193610b166020850186610a66565b84526024602085019382010191821161010157602401915b818310610b3b5750505090565b82356001600160a01b038116810361010157815260209283019201610b2e565b6080600319820112610101576004356001600160a01b03811681036101015791602435916044359160643567ffffffffffffffff811161010157826023820112156101015780600401359267ffffffffffffffff84116101015760248483010111610101576024019190565b8051821015610bdb5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b92919267ffffffffffffffff8211610a365760405191610c19601f8201601f191660200184610a66565b829481845281830111610101578281602093845f960137010152565b6020815260806060610c52845183602086015260a0850190610a88565b60208501516001600160a01b03166040858101919091528501518285015293015191015290565b81516001600160a01b03908116825260208084015182169083015260408084015162ffffff16908301526060808401518216908301526080808401519083015260a0808401519083015260c092830151169181019190915260e00190565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615610d0f57565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fdc57f31f443db27194675c40fd69f02f80ad026cfa2020f254828e243e4df309602052604090205460ff1615610d5e57565b63e2517d3f60e01b5f52336004527fcd33863658f027235a71d5a353604cad1d9f4c447574c5e997484408e10fc82160245260445ffd5b5f8181526020818152604080832033845290915290205460ff1615610db75750565b63e2517d3f60e01b5f523360045260245260445ffd5b5f818152602081815260408083206001600160a01b038616845290915290205460ff16610e4f575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f818152602081815260408083206001600160a01b038616845290915290205460ff1615610e4f575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b91905f92602b82108015610f8b575b610f7c5781601411610f7857803593601319830190838211610f645780610f5057506017900491601783029280840460171490151715610f3c5760148301808411610f3c571161010157606093841c93910135901c90565b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b81526012600452602490fd5b634e487b7160e01b81526011600452602490fd5b8380fd5b63c3ab029b60e01b8452600484fd5b5060131982019350818411610f3c5760175f94061515610ee4565b6040516323b872dd60e01b60208201526001600160a01b039283166024820152929091166044830152606480830193909352918152610fef91610fea608483610a66565b6111e1565b565b60205f604051938285019063095ea7b360e01b825260018060a01b031695866024870152604486015260448552611029606486610a66565b84519082855af15f513d8261107d575b50501561104557505050565b610fea610fef936040519063095ea7b360e01b602083015260248201525f604482015260448152611077606482610a66565b826111e1565b90915061109a57506001600160a01b0381163b15155b5f80611039565b600114611093565b8054821015610bdb575f5260205f2001905f90565b805f52600260205260405f2054155f1461111d57600154600160401b811015610a36576111066110f082600185940160015560016110a2565b819391549060031b91821b915f19901b19161790565b9055600154905f52600260205260405f2055600190565b505f90565b5f818152600260205260409020548015610e4f575f198101818111610f3c576001545f19810191908211610f3c578181036111a9575b5050506001548015611195575f19016111728160016110a2565b8154905f199060031b1b191690556001555f5260026020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6111cb6111ba6110f09360016110a2565b90549060031b1c92839260016110a2565b90555f52600260205260405f20555f8080611158565b905f602091828151910182855af115610295575f513d61123057506001600160a01b0381163b155b6112105750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561120956fe5827c3e8def2eacec3e946260bf6bc1782b9a823f2b99f600f88e5f80b7cfb3fe166cae232ae4a1823a66ae352fe64900fcf92f4abd454ca894fdf235179b691a264697066735822122077f5cde048d6948fa61eab335461ac07d1355e8fedab563906eae9aaa8dddd9d64736f6c634300081d0033ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b29100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c8062a3fecd146109c157806301ffc9a71461096b578063248a9ca3146109395780632f2ff15d146108fc57806336568abe146108b85780635988f9a5146108585780635e1762a0146107b6578063754b377c1461076d57806391d1485414610725578063a217fddf1461070b578063bcec454f146106ab578063bd8bc59814610430578063c55dae63146103ec578063ccce8e28146101055763d547741f146100bd575f80fd5b34610101576040366003190112610101576100ff6004356100dc610aac565b906100fa6100f5825f525f602052600160405f20015490565b610d95565b610e55565b005b5f80fd5b346101015761011336610b5b565b9361011f929192610d26565b83156103dd576001600160a01b03165f81815260026020526040902054909290156103ce578415908115610357575b905f956020939261016187303389610fa6565b7f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc456001600160a01b031692610197888589610ff1565b88146102a0575050604051610219926101af82610a4a565b85825260018060a01b037f000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b29116858301526064604083015233606083015286608083015260a08201528660c08201526040519687809481936304e45aaf60e01b835260048301610c79565b03925af1928315610295575f93610254575b505f51602061125a5f395f51905f526040602094935b81519081528486820152a2604051908152f35b92506020833d60201161028d575b8161026f60209383610a66565b81010312610101579151915f51602061125a5f395f51905f5261022b565b3d9150610262565b6040513d5f823e3d90fd5b926102bc6102ea94604051936102b585610a1a565b3691610bef565b82523385830152866040830152606082015260405196878094819363b858183f60e01b835260048301610c35565b03925af1928315610295575f93610316575b505f51602061125a5f395f51905f52604060209493610241565b92506020833d60201161034f575b8161033160209383610a66565b81010312610101579151915f51602061125a5f395f51905f526102fc565b3d9150610324565b9190836103648785610ed5565b6001600160a01b0390911690911480159190610396575b5061038757909161014e565b6320db826760e01b5f5260045ffd5b7f000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b2916001600160a01b039081169116141590508761037b565b63c1ab6dc160e01b5f5260045ffd5b63162908e360e11b5f5260045ffd5b34610101575f366003190112610101576040517f000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b2916001600160a01b03168152602090f35b346101015761043e36610b5b565b93610447610d26565b83156103dd576001600160a01b03165f81815260026020526040902054909290156103ce578415908115610641575b905f95602093927f000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b291916104ab88303386610fa6565b7f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc456001600160a01b0316936104e1898686610ff1565b89146105be57506105439350604051916104fa83610a4a565b60018060a01b0316825285858301526064604083015233606083015286608083015260a08201528660c08201526040519687809481936304e45aaf60e01b835260048301610c79565b03925af1928315610295575f9361057d575b505f51602061123a5f395f51905f5260406020949381519081528486820152a2604051908152f35b92506020833d6020116105b6575b8161059860209383610a66565b81010312610101579151915f51602061123a5f395f51905f52610555565b3d915061058b565b91506102bc6105d494604051936102b585610a1a565b03925af1928315610295575f93610600575b505f51602061123a5f395f51905f52604060209493610241565b92506020833d602011610639575b8161061b60209383610a66565b81010312610101579151915f51602061123a5f395f51905f526105e6565b3d915061060e565b91908361064e8784610ed5565b7f000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b2916001600160a01b039081169216919091148015929190610697575b5050610387579091610476565b6001600160a01b031614159050848861068a565b34610101576106b936610ac2565b6106c1610cd7565b5f5b81518110156100ff576001600160a01b036106de8284610bc7565b5116156103ce576001906107046001600160a01b036106fd8386610bc7565b5116611122565b50016106c3565b34610101575f3660031901126101015760206040515f8152f35b346101015760403660031901126101015761073e610aac565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610101575f366003190112610101576107b260405161078e604082610a66565b60038152620312e360ec1b6020820152604051918291602083526020830190610a88565b0390f35b34610101575f366003190112610101576040518060206001549283815201809260015f5260205f20905f5b81811061084257505050816107f7910382610a66565b604051918291602083019060208452518091526040830191905f5b818110610820575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610812565b82548452602090930192600192830192016107e1565b346101015761086636610ac2565b61086e610cd7565b5f5b81518110156100ff576001600160a01b0361088b8284610bc7565b5116156103ce576001906108b16001600160a01b036108aa8386610bc7565b51166110b7565b5001610870565b34610101576040366003190112610101576108d1610aac565b336001600160a01b038216036108ed576100ff90600435610e55565b63334bd91960e11b5f5260045ffd5b34610101576040366003190112610101576100ff60043561091b610aac565b906109346100f5825f525f602052600160405f20015490565b610dcd565b346101015760203660031901126101015760206109636004355f525f602052600160405f20015490565b604051908152f35b346101015760203660031901126101015760043563ffffffff60e01b811680910361010157602090637965db0b60e01b81149081156109b0575b506040519015158152f35b6301ffc9a760e01b149050826109a5565b34610101575f366003190112610101576107b26040516109e2604082610a66565b60178152762ab734b9bbb0b8102b199029bbb0b81020b230b83a32b960491b6020820152604051918291602083526020830190610a88565b6080810190811067ffffffffffffffff821117610a3657604052565b634e487b7160e01b5f52604160045260245ffd5b60e0810190811067ffffffffffffffff821117610a3657604052565b90601f8019910116810190811067ffffffffffffffff821117610a3657604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602435906001600160a01b038216820361010157565b60206003198201126101015760043567ffffffffffffffff811161010157816023820112156101015780600401359167ffffffffffffffff8311610a36578260051b9160405193610b166020850186610a66565b84526024602085019382010191821161010157602401915b818310610b3b5750505090565b82356001600160a01b038116810361010157815260209283019201610b2e565b6080600319820112610101576004356001600160a01b03811681036101015791602435916044359160643567ffffffffffffffff811161010157826023820112156101015780600401359267ffffffffffffffff84116101015760248483010111610101576024019190565b8051821015610bdb5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b92919267ffffffffffffffff8211610a365760405191610c19601f8201601f191660200184610a66565b829481845281830111610101578281602093845f960137010152565b6020815260806060610c52845183602086015260a0850190610a88565b60208501516001600160a01b03166040858101919091528501518285015293015191015290565b81516001600160a01b03908116825260208084015182169083015260408084015162ffffff16908301526060808401518216908301526080808401519083015260a0808401519083015260c092830151169181019190915260e00190565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615610d0f57565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fdc57f31f443db27194675c40fd69f02f80ad026cfa2020f254828e243e4df309602052604090205460ff1615610d5e57565b63e2517d3f60e01b5f52336004527fcd33863658f027235a71d5a353604cad1d9f4c447574c5e997484408e10fc82160245260445ffd5b5f8181526020818152604080832033845290915290205460ff1615610db75750565b63e2517d3f60e01b5f523360045260245260445ffd5b5f818152602081815260408083206001600160a01b038616845290915290205460ff16610e4f575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f818152602081815260408083206001600160a01b038616845290915290205460ff1615610e4f575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b91905f92602b82108015610f8b575b610f7c5781601411610f7857803593601319830190838211610f645780610f5057506017900491601783029280840460171490151715610f3c5760148301808411610f3c571161010157606093841c93910135901c90565b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b81526012600452602490fd5b634e487b7160e01b81526011600452602490fd5b8380fd5b63c3ab029b60e01b8452600484fd5b5060131982019350818411610f3c5760175f94061515610ee4565b6040516323b872dd60e01b60208201526001600160a01b039283166024820152929091166044830152606480830193909352918152610fef91610fea608483610a66565b6111e1565b565b60205f604051938285019063095ea7b360e01b825260018060a01b031695866024870152604486015260448552611029606486610a66565b84519082855af15f513d8261107d575b50501561104557505050565b610fea610fef936040519063095ea7b360e01b602083015260248201525f604482015260448152611077606482610a66565b826111e1565b90915061109a57506001600160a01b0381163b15155b5f80611039565b600114611093565b8054821015610bdb575f5260205f2001905f90565b805f52600260205260405f2054155f1461111d57600154600160401b811015610a36576111066110f082600185940160015560016110a2565b819391549060031b91821b915f19901b19161790565b9055600154905f52600260205260405f2055600190565b505f90565b5f818152600260205260409020548015610e4f575f198101818111610f3c576001545f19810191908211610f3c578181036111a9575b5050506001548015611195575f19016111728160016110a2565b8154905f199060031b1b191690556001555f5260026020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6111cb6111ba6110f09360016110a2565b90549060031b1c92839260016110a2565b90555f52600260205260405f20555f8080611158565b905f602091828151910182855af115610295575f513d61123057506001600160a01b0381163b155b6112105750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561120956fe5827c3e8def2eacec3e946260bf6bc1782b9a823f2b99f600f88e5f80b7cfb3fe166cae232ae4a1823a66ae352fe64900fcf92f4abd454ca894fdf235179b691a264697066735822122077f5cde048d6948fa61eab335461ac07d1355e8fedab563906eae9aaa8dddd9d64736f6c634300081d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b29100000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
-----Decoded View---------------
Arg [0] : baseToken_ (address): 0x437cc33344a0B27A429f795ff6B469C72698B291
Arg [1] : swapRouter_ (address): 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
Arg [2] : tokens (address[]): 0xaf88d065e77c8cC2239327C5EDb3A432268e5831,0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000437cc33344a0b27a429f795ff6b469c72698b291
Arg [1] : 00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [4] : 000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831
Arg [5] : 000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.