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": {
"RustitrageExecutorOptimizedv4.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/*
* RustitrageExecutorOptimized
* ------------------------------------------------------------
* This contract executes multi‑hop swaps across multiple AMM versions.
* It supports Uniswap V2, Uniswap V3 (including Pancake V3) and
* Uniswap V4 pools via a custom implementation. To minimise gas
* consumption, all relevant swap parameters (zeroForOne flag, pool
* key, etc.) are supplied off‑chain via the SwapParams.extraData
* field. No on‑chain token0/token1 lookups are performed.
*
* V4 integration notes:
*
* - All v4 pools are managed by a single PoolManager contract.
* - The swap must be executed inside an unlock callback; therefore
* this contract implements the IUnlockCallback interface.
* - Off‑chain code must compute the PoolKey (currency0, currency1,
* fee, tickSpacing, hooks) and encode it in extraData. The
* amountSpecified and zeroForOne are provided in the top‑level
* SwapParams.
* - The extraData for a v4 hop should abi‑encode the V4SwapData
* struct defined below. See _swapV4() for details.
*
* - To execute a v4 swap safely, the contract:
* 1. Calls poolManager.sync(tokenIn) before sending tokens in.
* 2. Transfers the input tokens into the PoolManager.
* 3. Calls poolManager.unlock() with encoded data. This
* triggers unlockCallback(), which calls swap(), settles the
* negative delta (input) and withdraws the positive delta
* (output).
* 4. After unlock returns, the amount of output tokens is
* decoded from the return data and compared against
* minAmountOut.
*
* - Only the whitelisted PoolManager may call unlockCallback().
*/
// ----------------- ERC20 + safe wrappers -----------------
interface IERC20 {
function balanceOf(address) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
library SafeERC20 {
function _call(address token, bytes memory data) private returns (bytes memory) {
(bool ok, bytes memory ret) = token.call(data);
require(ok, "TOK_CALL_FAIL");
if (ret.length > 0) require(abi.decode(ret, (bool)), "TOK_FALSE");
return ret;
}
function safeTransfer(IERC20 t, address to, uint256 v) internal {
_call(address(t), abi.encodeWithSelector(t.transfer.selector, to, v));
}
function safeTransferFrom(IERC20 t, address f, address to, uint256 v) internal {
_call(address(t), abi.encodeWithSelector(t.transferFrom.selector, f, to, v));
}
function safeApprove(IERC20 t, address s, uint256 v) internal {
_call(address(t), abi.encodeWithSelector(t.approve.selector, s, v));
}
}
// ----------------- V2 -----------------
interface IUniswapV2Pair {
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}
// ----------------- V3 -----------------
interface IUniswapV3Pool {
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
function token0() external view returns (address);
function token1() external view returns (address);
}
interface IUniswapV3SwapCallback {
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}
interface IPancakeV3SwapCallback {
function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}
// Factory interface (Uniswap/Pancake style)
interface IV3FactoryLike {
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address);
}
// ----------------- V4 -----------------
/**
* @dev Minimal interface for Uniswap v4 PoolManager, used here for swaps.
* Only the functions needed by this contract are included. Currency is
* treated as an address; real implementation treats Currency as a type
* alias of address. swap() returns a BalanceDelta, which packs two
* signed 128‑bit integers representing amount0 and amount1 deltas.
*/
interface IPoolManager {
struct PoolKey {
address currency0;
address currency1;
uint24 fee;
int24 tickSpacing;
address hooks;
}
struct SwapParams {
bool zeroForOne;
int256 amountSpecified;
uint160 sqrtPriceLimitX96;
}
function swap(PoolKey calldata key, SwapParams calldata params, bytes calldata data) external returns (int256 swapDelta);
function take(address currency, address to, uint256 amount) external;
function settle() external payable returns (uint256 paid);
function sync(address currency) external;
function unlock(bytes calldata data) external returns (bytes memory);
}
/**
* @dev Interface for the v4 unlock callback. PoolManager will call
* unlockCallback(data) during unlock(). This contract must inherit
* from this interface and implement unlockCallback() to perform the
* swap and settle/take operations.
*/
interface IUnlockCallback {
function unlockCallback(bytes calldata data) external returns (bytes memory);
}
/**
* @dev Library for unpacking BalanceDelta values returned from v4 swap().
* A BalanceDelta is a single int256 in which the upper 128 bits encode
* amount0 and the lower 128 bits encode amount1. Positive deltas mean
* the PoolManager owes the caller tokens; negative deltas mean the
* caller owes the PoolManager tokens.
*/
library BalanceDeltaLibrary {
function amount0(int256 balanceDelta) internal pure returns (int128 _amount0) {
_amount0 = int128(balanceDelta >> 128);
}
function amount1(int256 balanceDelta) internal pure returns (int128 _amount1) {
// mask lower 128 bits and sign extend
int256 masked = int256(uint256(balanceDelta) & ((1 << 128) - 1));
_amount1 = int128(masked);
}
}
// ----------------- DODO (DVM/DPP-like) -----------------
interface IDODOLike {
function sellBaseToken(uint256 amount, uint256 minReceive, bytes calldata data) external returns (uint256);
function sellQuoteToken(uint256 amount, uint256 minReceive, bytes calldata data) external returns (uint256);
function _BASE_TOKEN_() external view returns (address);
function _QUOTE_TOKEN_() external view returns (address);
}
// ----------------- Ownable (minimal) -----------------
abstract contract Ownable {
address public owner;
event OwnershipTransferred(address indexed prev, address indexed next);
constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); }
modifier onlyOwner() { require(msg.sender == owner, "ONLY_OWNER"); _; }
function transferOwnership(address next) external onlyOwner { require(next != address(0), "ZERO"); emit OwnershipTransferred(owner, next); owner = next; }
}
// ----------------- Executor -----------------
contract RustitrageExecutorOptimized is IUniswapV3SwapCallback, IPancakeV3SwapCallback, IUnlockCallback, Ownable {
using SafeERC20 for IERC20;
// Pool type constants
uint8 internal constant PT_V2 = 1;
uint8 internal constant PT_V3 = 2;
uint8 internal constant PT_DODO = 3;
uint8 internal constant PT_V4 = 4;
// Custom errors
error UNAUTH_FACTORY();
error BAD_V3_POOL();
error NO_DELTA();
error V4_MIN_OUT_NOT_MET();
// Immutable factories for whitelisting v3 pools
address immutable FACTORY_A;
address immutable FACTORY_B;
address immutable FACTORY_C;
// Immutable PoolManager for v4
IPoolManager public immutable POOL_MANAGER;
constructor(address factoryA, address factoryB, address factoryC, IPoolManager poolManager) {
FACTORY_A = factoryA;
FACTORY_B = factoryB;
FACTORY_C = factoryC;
POOL_MANAGER = poolManager;
}
/// @dev pure/view check without storage reads
function _isAllowedFactory(address f) internal view returns (bool) {
if (f == address(0)) return false;
return (f == FACTORY_A) || (f == FACTORY_B) || (f == FACTORY_C);
}
/// @notice parameters describing one hop of a multi‑hop swap
struct SwapParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
address pool;
uint8 poolType;
bool zeroForOne;
bytes extraData;
}
/// @notice extra data for v4 swap: encodes the PoolKey, recipient and price limit
struct V4SwapData {
IPoolManager.PoolKey key;
address recipient;
uint160 sqrtPriceLimitX96;
}
/**
* @notice Execute a sequence of swaps. The caller is expected to ensure
* that the contract holds `hops[0].amountIn` of the first token.
* @param hops An array of SwapParams describing each hop
*/
function execute(SwapParams[] calldata hops) external onlyOwner {
require(hops.length > 0, "NO_HOPS");
uint256 amountIn;
// initial amountIn is specified in hop[0]
amountIn = hops[0].amountIn;
for (uint256 i; i < hops.length; ) {
SwapParams calldata p = hops[i];
if (p.poolType == PT_V2) {
_swapV2(p.pool, p.tokenIn, amountIn, p.minAmountOut, p.zeroForOne);
amountIn = p.minAmountOut;
} else if (p.poolType == PT_V3) {
amountIn = _swapV3(p.pool, p.tokenIn, p.tokenOut, amountIn, p.minAmountOut, p.zeroForOne, p.extraData);
} else if (p.poolType == PT_V4) {
amountIn = _swapV4(p.pool, p.tokenIn, amountIn, p.minAmountOut, p.zeroForOne, p.extraData);
} else {
revert("BAD_POOL_TYPE");
}
unchecked { ++i; }
}
}
// --- V2 swap ---
/**
* @dev Executes a Uniswap V2 style swap. The `zeroForOne` boolean controls
* whether tokenIn is token0 (true) or token1 (false). No external
* calls to token0()/token1() are made, so the caller must set
* `zeroForOne` correctly. If it is incorrect the swap may revert or
* produce the wrong token flow.
*/
function _swapV2(
address pair,
address tokenIn,
uint256 amountIn,
uint256 minOut,
bool zeroForOne
) internal {
// trust zeroForOne: do not call token0/token1
IERC20(tokenIn).safeTransfer(pair, amountIn);
uint amount0Out = zeroForOne ? 0 : minOut;
uint amount1Out = zeroForOne ? minOut : 0;
IUniswapV2Pair(pair).swap(amount0Out, amount1Out, address(this), new bytes(0));
}
// --- V3 swap ---
/**
* @dev Executes a Uniswap V3 style swap. The caller must supply
* zeroForOne to indicate the direction of the swap. The extraData
* parameter may contain:
* - empty: uses default sqrtPrice limits
* - 32 bytes: sqrtPriceLimitX96
* - 64 bytes: fee + factory
* - 96 bytes: sqrtPriceLimitX96 + fee + factory
*/
function _swapV3(
address pool,
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minOut,
bool zeroForOne,
bytes calldata extra
) internal returns (uint256 amountOut) {
(uint160 sqrtLimit, uint24 fee, address factory) = _parseV3Extra(extra, zeroForOne);
bytes memory cb = abi.encode(tokenIn, tokenOut, fee, factory);
(int256 a0, int256 a1) = IUniswapV3Pool(pool).swap(
address(this),
zeroForOne,
int256(amountIn),
sqrtLimit,
cb
);
int256 deltaOut = zeroForOne ? a1 : a0;
require(deltaOut <= 0, "V3_POS_DELTA");
amountOut = uint256(-deltaOut);
require(amountOut >= minOut, "V3_SLIPPAGE");
}
// Extract sqrtPriceLimit, fee and factory from extra data. See header for details.
function _parseV3Extra(bytes calldata extra, bool zeroForOne) internal pure returns (uint160 sqrtLimit, uint24 fee, address factory) {
uint160 defaultLimit = zeroForOne ? (uint160(1)) : (type(uint160).max - 1);
sqrtLimit = defaultLimit;
fee = 0;
factory = address(0);
if (extra.length == 0) {
return (sqrtLimit, fee, factory);
} else if (extra.length == 32) {
uint256 raw256 = abi.decode(extra, (uint256));
uint160 raw = uint160(raw256);
sqrtLimit = _clampSqrtPriceLimit(raw);
} else if (extra.length == 64) {
(fee, factory) = abi.decode(extra, (uint24, address));
} else if (extra.length == 96) {
(sqrtLimit, fee, factory) = abi.decode(extra, (uint160, uint24, address));
sqrtLimit = _clampSqrtPriceLimit(sqrtLimit);
}
}
function _clampSqrtPriceLimit(uint160 x) internal pure returns (uint160) {
if (x <= 1) return 1;
if (x >= type(uint160).max) return type(uint160).max - 1;
return x;
}
/**
* @dev Uniswap V3 callback. Verifies the pool via the provided factory if
* one is supplied in extraData. Does not rely on a storage mapping for
* factory authorisation to save gas.
*/
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override {
if (!(amount0Delta > 0 || amount1Delta > 0)) revert NO_DELTA();
(address tokenIn, address tokenOut, uint24 fee, address factory) = abi.decode(data, (address, address, uint24, address));
if (factory != address(0)) {
if (!_isAllowedFactory(factory)) revert UNAUTH_FACTORY();
address expected = IV3FactoryLike(factory).getPool(tokenIn, tokenOut, fee);
if (expected == address(0) || msg.sender != expected) revert BAD_V3_POOL();
} else {
revert UNAUTH_FACTORY();
}
uint256 amountToPay = uint256(amount0Delta > 0 ? amount0Delta : amount1Delta);
address t0 = IUniswapV3Pool(msg.sender).token0();
address payToken = amount0Delta > 0 ? t0 : IUniswapV3Pool(msg.sender).token1();
IERC20(payToken).safeTransfer(msg.sender, amountToPay);
}
function pancakeV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override {
if (!(amount0Delta > 0 || amount1Delta > 0)) revert NO_DELTA();
(address tokenIn, address tokenOut, uint24 fee, address factory) = abi.decode(data, (address, address, uint24, address));
if (factory != address(0)) {
if (!_isAllowedFactory(factory)) revert UNAUTH_FACTORY();
address expected = IV3FactoryLike(factory).getPool(tokenIn, tokenOut, fee);
if (expected == address(0) || msg.sender != expected) revert BAD_V3_POOL();
} else {
revert UNAUTH_FACTORY();
}
uint256 amountToPay = uint256(amount0Delta > 0 ? amount0Delta : amount1Delta);
address t0 = IUniswapV3Pool(msg.sender).token0();
address payToken = amount0Delta > 0 ? t0 : IUniswapV3Pool(msg.sender).token1();
IERC20(payToken).safeTransfer(msg.sender, amountToPay);
}
// --- V4 swap ---
/**
* @dev Performs a Uniswap v4 swap. The caller must supply a PoolManager
* address in the SwapParams.pool field, and extraData must
* abi‑encode a V4SwapData struct containing the pool key and
* recipient. zeroForOne comes from the top‑level SwapParams.
*/
function _swapV4(
address manager,
address tokenIn,
uint256 amountIn,
uint256 minOut,
bool zeroForOne,
bytes calldata extra
) internal returns (uint256 amountOut) {
require(manager == address(POOL_MANAGER), "V4_BAD_PM");
// decode extra into V4SwapData
V4SwapData memory v4 = abi.decode(extra, (V4SwapData));
// build swap params: negative for exact input
IPoolManager.SwapParams memory params;
params.zeroForOne = zeroForOne;
params.amountSpecified = -int256(amountIn);
if (v4.sqrtPriceLimitX96 == 0) {
// apply extreme limits to avoid hitting virtual range
params.sqrtPriceLimitX96 = zeroForOne ? 1 : (type(uint160).max - 1);
} else {
params.sqrtPriceLimitX96 = v4.sqrtPriceLimitX96;
}
// step 1: sync balance for tokenIn
POOL_MANAGER.sync(tokenIn);
// step 2: transfer input tokens to PoolManager
IERC20(tokenIn).safeTransfer(manager, amountIn);
// step 3: encode data for unlock callback (minOut included for safety)
bytes memory cbData = abi.encode(v4.key, params, v4.recipient, minOut);
// step 4: call unlock on pool manager; returns encoded amountOut
bytes memory result = POOL_MANAGER.unlock(cbData);
amountOut = abi.decode(result, (uint256));
require(amountOut >= minOut, "V4_SLIPPAGE");
}
/**
* @dev V4 unlock callback. Only the whitelisted PoolManager may call this.
* Performs the actual swap, settles negative deltas, withdraws
* positive deltas, and returns the amount out encoded in bytes.
*/
function unlockCallback(bytes calldata data) external override returns (bytes memory) {
require(msg.sender == address(POOL_MANAGER), "V4_ONLY_PM");
(IPoolManager.PoolKey memory key, IPoolManager.SwapParams memory params, address recipient, uint256 minOut) = abi.decode(data, (IPoolManager.PoolKey, IPoolManager.SwapParams, address, uint256));
// perform swap
int256 delta = POOL_MANAGER.swap(key, params, "");
// decode deltas
int128 d0 = BalanceDeltaLibrary.amount0(delta);
int128 d1 = BalanceDeltaLibrary.amount1(delta);
// settle negative input delta
if (d0 < 0) {
POOL_MANAGER.settle();
}
uint256 amountOut;
// withdraw positive output delta
if (d1 > 0) {
amountOut = uint256(int256(d1));
// currency out is key.currency1 if zeroForOne, else key.currency0
address currencyOut = params.zeroForOne ? key.currency1 : key.currency0;
POOL_MANAGER.take(currencyOut, recipient, amountOut);
}
require(amountOut >= minOut, "V4_MIN_OUT");
return abi.encode(amountOut);
}
// --- Utilities ---
function sweep(address token, address to) external onlyOwner {
uint256 bal = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransfer(to, bal);
}
}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 2000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-30 14:07:26
Comments
Log in to comment.
No comments yet.