Description:
Decentralized Finance (DeFi) protocol contract providing Swap, Factory functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/swap/UniswapV3DexModule.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.30;\r
\r
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";\r
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";\r
\r
/**\r
* @title UniswapV3DexModule\r
* @author Andrei Averin — CTO dsf.finance\r
* @notice Uniswap V3 module for UniversalRouter\r
* @dev - Supports 1-hop and 2-hop routes (via hub tokens).\r
* - Uses a QuoterV2-compatible contract for pricing (prefers StaticQuoterV2).\r
* - Executes swaps via Uniswap V3 ISwapRouter's exactInput.\r
*/\r
\r
/* ──────────── External interfaces ──────────── */\r
\r
interface IUniswapV3Factory {\r
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);\r
}\r
\r
interface ISwapRouterV3 {\r
/// @notice Parameters for exactInput swap\r
struct ExactInputParams {\r
bytes path;\r
address recipient;\r
uint256 deadline;\r
uint256 amountIn;\r
uint256 amountOutMinimum;\r
}\r
\r
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);\r
}\r
\r
/**\r
* @title IQuoterV2\r
* @notice Quoter interface aligned with a view-return implementation (e.g. StaticQuoterV2),\r
* while still compatible with Uniswap's revert-data returning QuoterV2 via try/catch.\r
*/\r
interface IQuoterV2 {\r
/// @notice Params for quoting a single v3 hop\r
struct QuoteExactInputSingleParams {\r
address tokenIn;\r
address tokenOut;\r
uint24 fee;\r
uint256 amountIn;\r
uint160 sqrtPriceLimitX96; // 0 = no limit\r
}\r
\r
/**\r
* @notice Quote an exact-input multi-hop path.\r
* @dev Implementations may return via normal return or revert(data).\r
* @param path V3-encoded path\r
* @param amountIn Input amount\r
* @return amountOut Quoted output amount\r
* @return sqrtPriceX96AfterList sqrt(P) after each hop\r
* @return initializedTicksCrossedList ticks crossed per hop\r
* @return gasEstimate Auxiliary value (0 for static quoters)\r
*/\r
function quoteExactInput(bytes memory path, uint256 amountIn)\r
external\r
view\r
returns (\r
uint256 amountOut,\r
uint160[] memory sqrtPriceX96AfterList,\r
uint32[] memory initializedTicksCrossedList,\r
uint256 gasEstimate\r
);\r
\r
/**\r
* @notice Quote a single-hop exact-input swap.\r
* @param params QuoteExactInputSingleParams\r
* @return amountOut Quoted output\r
* @return sqrtPriceX96After sqrt(P) after swap\r
* @return initializedTicksCrossed Ticks crossed\r
* @return gasEstimate Auxiliary value (0 for static quoters)\r
*/\r
function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\r
external\r
view\r
returns (\r
uint256 amountOut,\r
uint160 sqrtPriceX96After,\r
uint32 initializedTicksCrossed,\r
uint256 gasEstimate\r
);\r
}\r
\r
/* ──────────── Aggregator interfaces ───────────── */\r
\r
/**\r
* @title IDexModule\r
* @notice Generic DEX module interface used by an aggregator/UR.\r
*/\r
interface IDexModule {\r
/**\r
* @notice Compute the best 1-hop and 2-hop routes.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return best1HopRoute Serialized 1-hop route\r
* @return amountOut1Hop Quoted 1-hop output\r
* @return best2HopRoute Serialized 2-hop route\r
* @return amountOut2Hop Quoted 2-hop output\r
*/\r
function getBestRoute(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn\r
) external view returns (\r
DexRoute memory best1HopRoute,\r
uint256 amountOut1Hop,\r
DexRoute memory best2HopRoute,\r
uint256 amountOut2Hop\r
);\r
\r
/**\r
* @notice Execute a previously returned route with a slippage check based on a percentage.\r
* @param route Serialized route\r
* @param to Recipient of the final tokens\r
* @param percent Slippage tolerance (e.g., 9900 for 1% slippage, where 10000 is 0% slippage)\r
* @return amountOut Actual output received\r
*/\r
function swapRoute(\r
DexRoute calldata route,\r
address to,\r
uint256 percent\r
) external returns (\r
uint256 amountOut\r
);\r
\r
/**\r
* @notice Simulate a route (1–2 hops) encoded as {DexRoute}.\r
* @param route Serialized route\r
* @param percent Dummy parameter (or for future use)\r
* @return amountOut Quoted total output amount\r
*/\r
function simulateRoute(\r
DexRoute calldata route,\r
uint256 percent\r
) external view returns (uint256 amountOut);\r
}\r
\r
/// @notice Serialized route container; each hop is ABI-encoded\r
struct DexRoute {\r
/// @dev Each element = abi.encode(tokenIn, tokenOut, pool, fee, amountIn)\r
bytes[] data;\r
}\r
\r
/// @notice Quote for a hop or a complete route\r
struct Quote {\r
address tokenIn;\r
address tokenOut;\r
address pool; // v3 pool\r
uint24 fee; // v3 fee tier\r
uint256 amountIn;\r
uint256 amountOut;\r
}\r
\r
/// @notice Decoded hop for UI/debugging\r
struct RouteStep {\r
address tokenIn;\r
address tokenOut;\r
address pool;\r
uint24 fee;\r
uint256 amountIn;\r
}\r
\r
/**\r
* @notice Full route option with serialized route, hop quotes and final amountOut\r
* @dev Used for debugging/inspection (e.g., in getAllRoutes).\r
*/\r
struct RouteOption {\r
DexRoute route;\r
Quote[] quotes; // 1 or 2 elements\r
uint256 amountOut; // final out\r
}\r
\r
/* ──────────── Constants & small utils ─────────── */\r
\r
/**\r
* @title V3Path\r
* @notice Helpers for V3 path encoding\r
*/\r
library V3Path {\r
/**\r
* @notice ABI-pack a fee tier (uint24) to 3-byte representation used in v3 paths.\r
* @param v Fee tier\r
* @return b 3-byte packed fee\r
*/\r
function _u24(uint24 v) internal pure returns (bytes memory b) {\r
b = abi.encodePacked(bytes1(uint8(v >> 16)), bytes1(uint8(v >> 8)), bytes1(uint8(v)));\r
}\r
}\r
\r
/* ────────────────── Dex module ────────────────── */\r
\r
/**\r
* @title UniswapV3DexModule\r
* @notice Specific implementation of IDexModule for Uniswap V3.\r
* @dev Static contract, no administrative functions and no owner.\r
* Supports 1-hop and 2-hop routes (via WETH/USDC),\r
* takes quotes from a QuoterV2-compatible contract,\r
* executes swaps via Uniswap V3 Router.\r
*/\r
contract UniswapV3DexModule is IDexModule {\r
using SafeERC20 for IERC20;\r
using V3Path for uint24;\r
\r
/// @notice Uniswap V3 factory & router (mainnet)\r
IUniswapV3Factory public constant FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);\r
ISwapRouterV3 public constant ROUTER = ISwapRouterV3( 0xE592427A0AEce92De3Edee1F18E0157C05861564);\r
\r
/// @notice Quoter contract (prefer StaticQuoterV2)\r
IQuoterV2 public immutable QUOTER;\r
\r
/// @notice Common hub tokens used for 2-hop discovery\r
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;\r
\r
/// @notice Fee tiers probed everywhere\r
uint24[4] private ALL_FEES = [100, 500, 3000, 10000];\r
\r
/**\r
* @notice Create the module with a given QuoterV2-compatible address.\r
* @param quoter Address of the quoter (e.g., StaticQuoterV2 or Uniswap's QuoterV2)\r
*/\r
constructor(address quoter) {\r
require(quoter != address(0), "V3: quoter=0");\r
QUOTER = IQuoterV2(quoter);\r
}\r
\r
/* ───────────────── External VIEW ──────────────── */\r
\r
/**\r
* @inheritdoc IDexModule\r
*/\r
function getBestRoute(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn\r
) external view override returns (\r
DexRoute memory best1HopRoute,\r
uint256 amountOut1Hop,\r
DexRoute memory best2HopRoute,\r
uint256 amountOut2Hop\r
) {\r
require(tokenIn != address(0) && tokenOut != address(0), "UniswapV3: zero addr");\r
require(tokenIn != tokenOut, "UniswapV3: same token");\r
require(amountIn > 0, "UniswapV3: zero amountIn");\r
\r
// 1-hop\r
(bool ok1, Quote[] memory best1, uint256 out1) = _bestOneHop(tokenIn, tokenOut, amountIn);\r
\r
// 2-hop via hubs\r
(bool ok2, Quote[] memory best2, uint256 out2) = _bestTwoHop(tokenIn, tokenOut, amountIn);\r
\r
if (ok1) {\r
best1HopRoute = DexRoute({ data: _encodeRouteFromQuotes(best1) });\r
amountOut1Hop = out1;\r
} else {\r
// Return empty route and zero amount if no 1-hop route found\r
best1HopRoute = DexRoute({ data: new bytes[](0) });\r
amountOut1Hop = 0;\r
}\r
\r
if (ok2) {\r
best2HopRoute = DexRoute({ data: _encodeRouteFromQuotes(best2) });\r
amountOut2Hop = out2;\r
} else {\r
// Return empty route and zero amount if no 2-hop route found\r
best2HopRoute = DexRoute({ data: new bytes[](0) });\r
amountOut2Hop = 0;\r
}\r
\r
require(amountOut1Hop > 0 || amountOut2Hop > 0, "UniswapV3: no route");\r
}\r
\r
/**\r
* @notice Enumerate all viable 1-hop and 2-hop routes for debugging/inspection.\r
* @dev Not intended for production routing; this is a convenience view for UIs/tests.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return routes Array of routes, each represented only by its Quote[] list\r
*/\r
function getAllRoutes(address tokenIn, address tokenOut, uint256 amountIn)\r
external\r
view\r
returns (Quote[][] memory routes)\r
{\r
require(tokenIn != address(0) && tokenOut != address(0), "UniswapV3: zero addr");\r
require(tokenIn != tokenOut, "UniswapV3: same token");\r
require(amountIn > 0, "UniswapV3: zero amountIn");\r
\r
RouteOption[] memory oneHop = _collectOneHopOptions(tokenIn, tokenOut, amountIn);\r
RouteOption[] memory twoHop = _collectTwoHopOptions(tokenIn, tokenOut, amountIn);\r
\r
uint256 total = oneHop.length + twoHop.length;\r
routes = new Quote[][](total);\r
\r
uint256 k;\r
for (uint256 i; i < oneHop.length; ++i) {\r
routes[k++] = _copyQuotes(oneHop[i].quotes);\r
}\r
for (uint256 j; j < twoHop.length; ++j) {\r
routes[k++] = _copyQuotes(twoHop[j].quotes);\r
}\r
}\r
\r
/**\r
* @inheritdoc IDexModule\r
*/\r
function simulateRoute(\r
DexRoute calldata route,\r
uint256 percent\r
) external view override returns (uint256 amountOut) {\r
require(route.data.length == 1 || route.data.length == 2, "UniswapV3: bad route");\r
require(percent <= 100, "UniswapV3: percent > 100");\r
\r
// Read the original amountIn from the route (the full quoted amount)\r
(, , , , uint256 originalAmountIn) =\r
abi.decode(route.data[0], (address, address, address, uint24, uint256));\r
require(originalAmountIn > 0, "UniswapV3: zero original amountIn");\r
\r
// Calculate the actual amount to simulate (amountIn * percent / 100)\r
uint256 actualAmountIn = (originalAmountIn * percent) / 100;\r
require(actualAmountIn > 0, "UniswapV3: zero actual amountIn");\r
\r
// Use the calculated amount for quoting\r
bytes memory path = _buildPath(route);\r
(amountOut,, ,) = _safeQuote(path, actualAmountIn);\r
}\r
\r
/**\r
* @notice Simulate a route (1–2 hops) encoded as {DexRoute}\r
* @param route Serialized route (each hop ABI-encoded as (tokenIn, tokenOut, pool, fee, amountIn))\r
* @return amountOut Quoted total output amount\r
*/\r
function quoteExactInput(DexRoute calldata route)\r
external\r
view\r
returns (uint256 amountOut)\r
{\r
require(route.data.length == 1 || route.data.length == 2, "UniswapV3: bad route");\r
(,,,, uint256 amountIn) =\r
abi.decode(route.data[0], (address, address, address, uint24, uint256));\r
bytes memory path = _buildPath(route);\r
(amountOut,, ,) = _safeQuote(path, amountIn);\r
}\r
\r
/**\r
* @notice Decode a serialized route into human-readable steps (for UI/debugging)\r
* @param route Serialized route\r
* @return steps Array of RouteStep with resolved fields\r
*/\r
function decodeRoute(DexRoute calldata route)\r
external\r
pure\r
returns (RouteStep[] memory steps)\r
{\r
uint256 len = route.data.length;\r
steps = new RouteStep[](len);\r
for (uint256 n; n < len; ++n) {\r
(address tokenIn, address tokenOut, address pool, uint24 fee, uint256 amountIn) =\r
abi.decode(route.data[n], (address, address, address, uint24, uint256));\r
\r
steps[n] = RouteStep({\r
tokenIn: tokenIn,\r
tokenOut: tokenOut,\r
pool: pool,\r
fee: fee,\r
amountIn: amountIn\r
});\r
}\r
}\r
\r
/* ──────────── External STATE-CHANGING ─────────── */\r
\r
/**\r
* @inheritdoc IDexModule\r
*/\r
function swapRoute(\r
DexRoute calldata route,\r
address to,\r
uint256 percent // Percentage (0-100) of amountIn from the route to be swapped\r
) external override returns (uint256 amountOut) {\r
require(to != address(0), "UniswapV3: bad recipient");\r
require(route.data.length == 1 || route.data.length == 2, "UniswapV3: only 1-2 hops");\r
require(percent <= 100, "UniswapV3: percent > 100");\r
\r
// Read the first hop to know amountIn/tokenIn\r
(address tokenIn,, , , uint256 originalAmountIn) =\r
abi.decode(route.data[0], (address, address, address, uint24, uint256));\r
require(originalAmountIn > 0, "UniswapV3: zero amountIn");\r
\r
uint256 amountIn = (originalAmountIn * percent) / 100;\r
require(amountIn > 0, "UniswapV3: zero actual amountIn");\r
\r
uint256 balBefore = IERC20(tokenIn).balanceOf(address(this));\r
_pullToken(tokenIn, amountIn);\r
uint256 received = IERC20(tokenIn).balanceOf(address(this)) - balBefore;\r
require(received == amountIn, "UniswapV3: FOT not supported");\r
_smartApprove(tokenIn, address(ROUTER), amountIn);\r
\r
bytes memory path = _buildPath(route);\r
\r
amountOut = ROUTER.exactInput(\r
ISwapRouterV3.ExactInputParams({\r
path: path,\r
recipient: to,\r
deadline: block.timestamp,\r
amountIn: amountIn,\r
amountOutMinimum: 0\r
})\r
);\r
}\r
\r
/* ──────────────── Internal VIEW ─────────────── */\r
\r
/**\r
* @notice Collect all viable 1-hop options across ALL_FEES.\r
* @dev Returns only options that both exist (pool!=0) and quote to >0.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return opts Dense array of valid RouteOption instances\r
*/\r
function _collectOneHopOptions(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn\r
) internal view returns (RouteOption[] memory opts) {\r
RouteOption[] memory tempOpts = new RouteOption[](ALL_FEES.length);\r
uint256 found = 0;\r
\r
(address a, address b) = _sortTokens(tokenIn, tokenOut);\r
\r
for (uint256 i = 0; i < ALL_FEES.length; i++) {\r
uint24 fee = ALL_FEES[i];\r
address pool = FACTORY.getPool(a, b, fee);\r
if (pool == address(0)) {\r
continue;\r
}\r
\r
(uint256 out,, ,) = _safeQuoteSingle(IQuoterV2.QuoteExactInputSingleParams({\r
tokenIn: tokenIn,\r
tokenOut: tokenOut,\r
fee: fee,\r
amountIn: amountIn,\r
sqrtPriceLimitX96: 0\r
}));\r
if (out == 0) {\r
continue;\r
}\r
\r
// Correctly build and assign the DexRoute for the single hop\r
bytes[] memory routeData = new bytes[](1);\r
routeData[0] = abi.encode(tokenIn, tokenOut, pool, fee, amountIn);\r
\r
// Correctly build and assign the Quote array\r
Quote[] memory qs = new Quote[](1);\r
qs[0] = Quote({\r
tokenIn: tokenIn,\r
tokenOut: tokenOut,\r
pool: pool,\r
fee: fee,\r
amountIn: amountIn,\r
amountOut: out\r
});\r
\r
// Assign the complete RouteOption struct\r
tempOpts[found] = RouteOption({\r
route: DexRoute({ data: routeData }),\r
quotes: qs,\r
amountOut: out\r
});\r
unchecked { ++found; }\r
}\r
\r
// Trim the temporary array to the final size\r
opts = new RouteOption[](found);\r
for (uint256 k = 0; k < found; k++) {\r
opts[k] = tempOpts[k];\r
}\r
}\r
\r
/**\r
* @notice Collect all viable 2-hop options across HUB_TOKENS × ALL_FEES × ALL_FEES.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return opts Dense array of valid RouteOption instances\r
*/\r
function _collectTwoHopOptions(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn\r
) internal view returns (RouteOption[] memory opts) {\r
RouteOption[] memory tmp = new RouteOption[](\r
2 * ALL_FEES.length * ALL_FEES.length\r
);\r
uint256 found;\r
\r
for (uint256 h = 0; h < 2; ++h) {\r
address hub = _hubAt(h);\r
if (hub == tokenIn || hub == tokenOut) continue;\r
\r
RouteOption[] memory part = _collectTwoHopForHub(tokenIn, tokenOut, amountIn, hub);\r
for (uint256 p = 0; p < part.length; ++p) {\r
tmp[found] = part[p];\r
unchecked { ++found; }\r
}\r
}\r
\r
opts = new RouteOption[](found);\r
for (uint256 k = 0; k < found; ++k) {\r
opts[k] = tmp[k];\r
}\r
}\r
\r
/**\r
* @notice Collect 2-hop options for a specific hub across ALL_FEES × ALL_FEES.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @param hub Hub token address\r
* @return opts Dense array of valid RouteOption instances for this hub\r
*/\r
function _collectTwoHopForHub(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn,\r
address hub\r
) internal view returns (RouteOption[] memory opts) {\r
RouteOption[] memory tmp = new RouteOption[](ALL_FEES.length * ALL_FEES.length);\r
uint256 found;\r
\r
for (uint256 i = 0; i < ALL_FEES.length; ++i) {\r
for (uint256 j = 0; j < ALL_FEES.length; ++j) {\r
RouteOption memory opt = _buildTwoHopOption(\r
tokenIn, tokenOut, hub, ALL_FEES[i], ALL_FEES[j], amountIn\r
);\r
if (opt.amountOut == 0) continue;\r
\r
tmp[found] = opt;\r
unchecked { ++found; }\r
}\r
}\r
\r
opts = new RouteOption[](found);\r
for (uint256 k = 0; k < found; ++k) {\r
opts[k] = tmp[k];\r
}\r
}\r
\r
/**\r
* @notice Try to build a valid 2-hop option (tokenIn → hub → tokenOut) for specific fee pair.\r
* @dev Returns zeroed struct if either pool is missing or any quote is zero.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param hub Hub token\r
* @param fee0 Fee tier for tokenIn→hub\r
* @param fee1 Fee tier for hub→tokenOut\r
* @param amountIn Input amount\r
* @return opt Fully-populated RouteOption or zeroed if invalid\r
*/\r
function _buildTwoHopOption(\r
address tokenIn,\r
address tokenOut,\r
address hub,\r
uint24 fee0,\r
uint24 fee1,\r
uint256 amountIn\r
) internal view returns (RouteOption memory opt) {\r
address pool0 = FACTORY.getPool(\r
tokenIn < hub ? tokenIn : hub,\r
tokenIn < hub ? hub : tokenIn,\r
fee0\r
);\r
if (pool0 == address(0)) {\r
return opt;\r
}\r
\r
address pool1 = FACTORY.getPool(\r
hub < tokenOut ? hub : tokenOut,\r
hub < tokenOut ? tokenOut: hub,\r
fee1\r
);\r
if (pool1 == address(0)) {\r
return opt;\r
}\r
\r
(uint256 hop0Out,, ,) = _safeQuoteSingle(IQuoterV2.QuoteExactInputSingleParams({\r
tokenIn: tokenIn,\r
tokenOut: hub,\r
fee: fee0,\r
amountIn: amountIn,\r
sqrtPriceLimitX96: 0\r
}));\r
if (hop0Out == 0) {\r
return opt;\r
}\r
\r
(uint256 finalOut,, ,) = _safeQuote(\r
abi.encodePacked(tokenIn, fee0._u24(), hub, fee1._u24(), tokenOut),\r
amountIn\r
);\r
if (finalOut == 0) {\r
return opt;\r
}\r
\r
Quote[] memory qs = new Quote[](2);\r
\r
qs[0] = Quote({\r
tokenIn: tokenIn,\r
tokenOut: hub,\r
pool: pool0,\r
fee: fee0,\r
amountIn: amountIn,\r
amountOut: hop0Out\r
});\r
\r
qs[1] = Quote({\r
tokenIn: hub,\r
tokenOut: tokenOut,\r
pool: pool1,\r
fee: fee1,\r
amountIn: hop0Out,\r
amountOut: finalOut\r
});\r
\r
bytes[] memory routeData = new bytes[](2);\r
routeData[0] = abi.encode(tokenIn, hub, pool0, fee0, amountIn);\r
routeData[1] = abi.encode(hub, tokenOut, pool1, fee1, hop0Out);\r
\r
opt = RouteOption({\r
route: DexRoute({ data: routeData }),\r
quotes: qs,\r
amountOut: finalOut\r
});\r
}\r
\r
/* ──────────────── Internal HELPERS ─────────────── */\r
\r
/**\r
* @notice Build a v3 path from a serialized route (1–2 hop)\r
* @dev Path = tokenIn | fee0 | tokenMid? | fee1? | tokenOut\r
* @param route DexRoute (bytes representation of hops)\r
* @return path Constructed v3 path ready for router/quoter\r
*/\r
function _buildPath(DexRoute calldata route) internal pure returns (bytes memory path) {\r
uint256 hops = route.data.length;\r
require(hops >= 1 && hops <= 2, "UniswapV3: bad hops");\r
\r
// read first token\r
(address tokenIn,, , ,) = abi.decode(route.data[0], (address, address, address, uint24, uint256));\r
path = abi.encodePacked(tokenIn);\r
\r
for (uint256 i; i < hops; ++i) {\r
(, address tokenOut,, uint24 fee,) =\r
abi.decode(route.data[i], (address, address, address, uint24, uint256));\r
path = bytes.concat(path, fee._u24(), abi.encodePacked(tokenOut));\r
}\r
}\r
\r
/**\r
* @notice Transfer the specified amount of tokens from the caller to this contract.\r
* @dev Uses SafeERC20.safeTransferFrom.\r
* @param token ERC20 address\r
* @param amount Amount to transfer\r
*/\r
function _pullToken(address token, uint256 amount) internal {\r
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\r
}\r
\r
/**\r
* @notice Safely set allowance for a spender, handling non-standard ERC20s.\r
* @param token ERC20 token address\r
* @param spender Allowance spender\r
* @param amount Minimum required allowance amount\r
*/\r
function _smartApprove (address token, address spender, uint256 amount) internal {\r
if (IERC20(token).allowance(address(this), spender) < amount) {\r
IERC20(token).forceApprove(spender, type(uint256).max);\r
}\r
}\r
\r
/**\r
* @notice Sort two token addresses ascending\r
* @param x First token\r
* @param y Second token\r
* @return a Lower address (token0)\r
* @return b Higher address (token1)\r
*/\r
function _sortTokens(address x, address y) internal pure returns (address a, address b) {\r
require(x != address(0) && y != address(0), "UniswapV3: zero addr");\r
require(x != y, "UniswapV3: same token");\r
(a, b) = x < y ? (x, y) : (y, x);\r
}\r
\r
/* ───────────────── Quote helpers ───────────────── */\r
\r
/**\r
* @notice Try to quote a multi-hop path via a view-return quoter first; fallback to Uniswap's revert-data format\r
* @param path V3-encoded path\r
* @param amountIn Input amount\r
* @return amountOut Quoted output\r
* @return s Sqrt(P) after each hop (may be empty if v1-only)\r
* @return t Initialized ticks crossed per hop (may be empty if v1-only)\r
* @return gasEstimate Compatibility value (0 for StaticQuoterV2)\r
*/\r
function _safeQuote(bytes memory path, uint256 amountIn)\r
internal\r
view\r
returns (uint256 amountOut, uint160[] memory s, uint32[] memory t, uint256 gasEstimate)\r
{\r
// Fast path: StaticQuoterV2 returns normally\r
try QUOTER.quoteExactInput(path, amountIn) returns (\r
uint256 a,\r
uint160[] memory s2,\r
uint32[] memory t2,\r
uint256 g\r
) {\r
return (a, s2, t2, g);\r
} catch (bytes memory ret) {\r
if (ret.length > 4) {\r
bytes memory data = _stripSelector(ret);\r
uint256 a2 = _decodeAmountOutLoose(data);\r
if (a2 > 0) {\r
return (a2, new uint160[](0), new uint32[](0), 0);\r
}\r
}\r
// Corrected default return for dynamic arrays\r
return (0, new uint160[](0), new uint32[](0), 0);\r
}\r
}\r
\r
/**\r
* @notice Quote a single-hop swap with a QuoterV2-compatible contract.\r
* @dev Tries normal view-return first; falls back to decoding revert(data).\r
* @param params IQuoterV2.QuoteExactInputSingleParams\r
* @return amountOut Quoted output\r
* @return spAfter Sqrt(P) after swap (0 if v1-only)\r
* @return ticks ticks crossed (0 if v1-only)\r
* @return gasEstimate Compatibility value (0 for StaticQuoterV2)\r
*/\r
function _safeQuoteSingle(IQuoterV2.QuoteExactInputSingleParams memory params)\r
internal\r
view\r
returns (uint256 amountOut, uint160 spAfter, uint32 ticks, uint256 gasEstimate)\r
{\r
// Fast path: StaticQuoterV2\r
try QUOTER.quoteExactInputSingle(params) returns (uint256 a, uint160 s, uint32 c, uint256 g) {\r
return (a, s, c, g);\r
} catch (bytes memory ret) {\r
if (ret.length > 4) {\r
bytes memory data = _stripSelector(ret);\r
uint256 a2 = _decodeAmountOutLoose(data);\r
if (a2 > 0) {\r
return (a2, 0, 0, 0);\r
}\r
}\r
return (0, 0, 0, 0);\r
}\r
}\r
\r
/* ────────── Revert-data decoding helpers ───────── */\r
\r
/**\r
* @notice Strip the first 4 bytes (function selector) from revert data\r
* @param revertData Raw revert data\r
* @return out Revert payload without selector\r
*/\r
function _stripSelector(bytes memory revertData) private pure returns (bytes memory out) {\r
if (revertData.length <= 4) return "";\r
out = new bytes(revertData.length - 4);\r
for (uint256 i = 0; i < out.length; ++i) {\r
out[i] = revertData[i + 4];\r
}\r
}\r
\r
/**\r
* @notice Leniently extracts the first 32 bytes (amountOut) from revert payload.\r
* @dev Works for both full-tuple and amount-only QuoterV2 encodings.\r
*/\r
function _decodeAmountOutLoose(bytes memory data) internal pure returns (uint256 out) {\r
if (data.length < 32) return 0;\r
assembly { out := mload(add(data, 32)) } // first 32 bytes = amountOut\r
}\r
\r
/**\r
* @notice Shallow-copy a memory array of Quote.\r
* @param src Source array\r
* @return dst New array with the same elements\r
*/\r
function _copyQuotes(Quote[] memory src) private pure returns (Quote[] memory dst) {\r
uint256 n = src.length;\r
dst = new Quote[](n);\r
for (uint256 i; i < n; ++i) {\r
dst[i] = src[i];\r
}\r
}\r
\r
/**\r
* @notice Turn Quote[] into the serialized bytes[] format used by swapRoute.\r
* @dev Each hop encodes (tokenIn, tokenOut, pool, fee, amountIn).\r
* @param qs Quotes (len 1 or 2)\r
* @return data bytes[] suitable for DexRoute\r
*/\r
function _encodeRouteFromQuotes(Quote[] memory qs) internal pure returns (bytes[] memory data) {\r
uint256 hops = qs.length;\r
require(hops == 1 || hops == 2, "UniswapV3: only 1-2 hops");\r
\r
data = new bytes[](hops);\r
\r
// hop0: amountIn = original input amount\r
data[0] = abi.encode(\r
qs[0].tokenIn,\r
qs[0].tokenOut,\r
qs[0].pool,\r
qs[0].fee,\r
qs[0].amountIn\r
);\r
\r
if (hops == 2) {\r
require(qs[0].tokenOut == qs[1].tokenIn, "UniswapV3: hops mismatch");\r
\r
uint256 hop1In = qs[1].amountIn;\r
if (hop1In == 0) {\r
hop1In = qs[0].amountOut;\r
require(hop1In > 0, "UniswapV3: hop1 amountIn=0");\r
}\r
\r
data[1] = abi.encode(\r
qs[1].tokenIn,\r
qs[1].tokenOut,\r
qs[1].pool,\r
qs[1].fee,\r
hop1In\r
);\r
}\r
}\r
\r
/**\r
* @notice Find the best single-hop route across ALL_FEES\r
* @dev uses quoteExactInputSingle to avoid path-encoding overhead\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return ok True if a valid 1-hop route was found\r
* @return best Quotes array (length 1) for the best route\r
* @return bestOut Final quoted output\r
*/\r
function _bestOneHop(\r
address tokenIn,\r
address tokenOut,\r
uint256 amountIn\r
) internal view returns (bool ok, Quote[] memory best, uint256 bestOut) {\r
(address a, address b) = _sortTokens(tokenIn, tokenOut);\r
\r
for (uint256 i; i < ALL_FEES.length; ) {\r
uint24 fee = ALL_FEES[i];\r
\r
address pool = FACTORY.getPool(a, b, fee);\r
if (pool != address(0)) {\r
(uint256 out,, ,) = _safeQuoteSingle(IQuoterV2.QuoteExactInputSingleParams({\r
tokenIn: tokenIn,\r
tokenOut: tokenOut,\r
fee: fee,\r
amountIn: amountIn,\r
sqrtPriceLimitX96: 0\r
}));\r
\r
if (out > bestOut) {\r
ok = true;\r
bestOut = out;\r
best = new Quote[](1);\r
best[0] = Quote({\r
tokenIn: tokenIn,\r
tokenOut: tokenOut,\r
pool: pool,\r
fee: fee,\r
amountIn: amountIn,\r
amountOut: out\r
});\r
}\r
}\r
unchecked { ++i; }\r
}\r
}\r
\r
/**\r
* @notice Find the best two-hop route via configured hubs.\r
* @param tokenIn Input token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return ok True if a valid 2-hop route was found\r
* @return best Quotes array (length 2) for the best route\r
* @return bestOut Final quoted output\r
*/\r
function _bestTwoHop(address tokenIn, address tokenOut, uint256 amountIn)\r
internal\r
view\r
returns (bool ok, Quote[] memory best, uint256 bestOut)\r
{\r
address bestHub;\r
uint24 bestFee0;\r
uint24 bestFee1;\r
\r
for (uint256 h; h < 2; ) {\r
address hub = _hubAt(h);\r
if (hub != tokenIn && hub != tokenOut) {\r
(uint24 f0, uint24 f1, uint256 outCand) = _bestTwoHopForHub(tokenIn, hub, tokenOut, amountIn);\r
if (outCand > bestOut) {\r
ok = true;\r
bestOut = outCand;\r
bestHub = hub;\r
bestFee0 = f0;\r
bestFee1 = f1;\r
}\r
}\r
unchecked { ++h; }\r
}\r
\r
if (ok) {\r
address pool0 = _getPool(tokenIn, bestHub, bestFee0);\r
address pool1 = _getPool(bestHub, tokenOut, bestFee1);\r
\r
(uint256 hop0Out,, ,) = _safeQuoteSingle(IQuoterV2.QuoteExactInputSingleParams({\r
tokenIn: tokenIn,\r
tokenOut: bestHub,\r
fee: bestFee0,\r
amountIn: amountIn,\r
sqrtPriceLimitX96: 0\r
}));\r
require(hop0Out > 0, "UniswapV3: hop0 quote=0");\r
\r
Quote[] memory q = new Quote[](2);\r
q[0] = Quote({\r
tokenIn: tokenIn,\r
tokenOut: bestHub,\r
pool: pool0,\r
fee: bestFee0,\r
amountIn: amountIn,\r
amountOut: hop0Out\r
});\r
q[1] = Quote({\r
tokenIn: bestHub,\r
tokenOut: tokenOut,\r
pool: pool1,\r
fee: bestFee1,\r
amountIn: hop0Out,\r
amountOut: bestOut\r
});\r
\r
best = q;\r
}\r
}\r
\r
/**\r
* @notice Small hub selector to keep the loop tight and avoid reading HUB_TOKENS in memory.\r
* @param i Index in {0,1}\r
* @return The hub address (0→WETH, 1→USDC)\r
*/\r
function _hubAt(uint256 i) internal pure returns (address) {\r
if (i == 0) return WETH;\r
return USDC;\r
}\r
\r
/**\r
* @notice For a given hub, find the best fee pair (fee0, fee1) and its output.\r
* @dev Single full-path quote per (fee0, fee1) pair; pools existence checked upfront.\r
* @param tokenIn Input token\r
* @param hub Hub token\r
* @param tokenOut Output token\r
* @param amountIn Input amount\r
* @return bestFee0 Best fee for tokenIn→hub\r
* @return bestFee1 Best fee for hub→tokenOut\r
* @return bestOut Best final output for this hub\r
*/\r
function _bestTwoHopForHub(\r
address tokenIn,\r
address hub,\r
address tokenOut,\r
uint256 amountIn\r
) internal view returns (uint24 bestFee0, uint24 bestFee1, uint256 bestOut) {\r
for (uint256 i; i < ALL_FEES.length; ) {\r
uint24 fee0 = ALL_FEES[i];\r
address pool0 = _getPool(tokenIn, hub, fee0);\r
if (pool0 != address(0)) {\r
for (uint256 j; j < ALL_FEES.length; ) {\r
uint24 fee1 = ALL_FEES[j];\r
address pool1 = _getPool(hub, tokenOut, fee1);\r
if (pool1 != address(0)) {\r
bytes memory path = abi.encodePacked(tokenIn, fee0._u24(), hub, fee1._u24(), tokenOut);\r
(uint256 outTmp,, ,) = _safeQuote(path, amountIn);\r
if (outTmp > bestOut) { bestOut = outTmp; bestFee0 = fee0; bestFee1 = fee1; }\r
}\r
unchecked { ++j; }\r
}\r
}\r
unchecked { ++i; }\r
}\r
}\r
\r
/**\r
* @notice Thin wrapper that sorts tokens and calls FACTORY.getPool.\r
* @param x Token A\r
* @param y Token B\r
* @param fee Fee tier\r
* @return Pool address or zero if missing\r
*/\r
function _getPool(address x, address y, uint24 fee) internal view returns (address) {\r
(address a, address b) = _sortTokens(x, y);\r
return FACTORY.getPool(a, b, fee);\r
}\r
}\r
"
},
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(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);
}
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @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);
}
"
},
"@openzeppelin/contracts/interfaces/IERC1363.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
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);
}
"
},
"@openzeppelin/contracts/interfaces/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";
"
},
"@openzeppelin/contracts/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @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);
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-08 12:18:06
Comments
Log in to comment.
No comments yet.