文件 1 的 1:LVRouter.sol
pragma solidity ^0.8.0;
uint160 constant MIN_SQRT_RATIO = 4295128739;
uint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } }
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } }
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } }
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } }
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } }
function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; }
function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; }
function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; }
function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; }
function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; }
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } }
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } }
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } }
function toUint160(uint256 y) internal pure returns (uint160 z) { require((z = uint160(y)) == y); }
function toInt128(int256 y) internal pure returns (int128 z) { require((z = int128(y)) == y); }
function toInt256(uint256 y) internal pure returns (int256 z) { require(y < 2**255); z = int256(y); }
}
library BytesLib {
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
require(_length + 31 >= _length, 'slice_overflow');
require(_start + _length >= _start, 'slice_overflow');
require(_bytes.length >= _start + _length, 'slice_outOfBounds');
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
tempBytes := mload(0x40)
let lengthmod := and(_length, 31)
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
mstore(0x40, and(add(mc, 31), not(31)))
}
default {
tempBytes := mload(0x40)
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
require(_start + 20 >= _start, 'toAddress_overflow');
require(_bytes.length >= _start + 20, 'toAddress_outOfBounds');
address tempAddress;
assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) }
return tempAddress;
}
function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) {
require(_start + 3 >= _start, 'toUint24_overflow');
require(_bytes.length >= _start + 3, 'toUint24_outOfBounds');
uint24 tempUint;
assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) }
return tempUint;
}
}
library Path {
using BytesLib for bytes;
uint256 private constant ADDR_SIZE = 20;
uint256 private constant FEE_SIZE = 3;
uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;
uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;
uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET;
function hasMultiplePools(bytes memory path) internal pure returns (bool) { return path.length >= MULTIPLE_POOLS_MIN_LENGTH; }
function numPools(bytes memory path) internal pure returns (uint256) { return ((path.length - ADDR_SIZE) / NEXT_OFFSET); }
function decodeFirstPool(bytes memory path) internal pure returns (address tokenA, address tokenB, uint24 fee) {
tokenA = path.toAddress(0);
fee = path.toUint24(ADDR_SIZE);
tokenB = path.toAddress(NEXT_OFFSET);
}
function getFirstPool(bytes memory path) internal pure returns (bytes memory) { return path.slice(0, POP_OFFSET); }
function skipToken(bytes memory path) internal pure returns (bytes memory) { return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); }
}
library TransferHelper {
function safeApprove(address token, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::safeApprove: approve failed'); }
function safeTransfer(address token, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::safeTransfer: transfer failed'); }
function safeTransferFrom(address token, address from, address to, uint256 value) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::transferFrom: transferFrom failed'); }
function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'TransferHelper::safeTransferETH: ETH transfer failed'); }
}
interface IERC20 {
function approve(address spender, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
function balanceOf(address owner) external view returns (uint);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
interface IWETH is IERC20 {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
interface IFactoryV2 {
function getPair(address tokenA, address tokenB) external view returns (address pair);
}
interface IFactoryV3 {
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
}
interface IPairV2 {
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function token0() external view returns (address);
function token1() external view returns (address);
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}
interface IPoolV3 {
function swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data) external returns (int256 amount0, int256 amount1);
}
abstract contract TokenBus {
using SafeMath for uint256;
address public immutable _WETH;
address public immutable _DEV;
uint256 public _ETH_FEE_AMOUNT;
uint256 public _ETH_FEE = 1;
constructor(address WETH_, address DEV_) { _WETH = WETH_; _DEV = DEV_; }
function setEthFee(uint256 fee) public {
require(msg.sender == _DEV);
require(fee <= 5);
_ETH_FEE = fee;
}
function claimEthFee() public {
require(msg.sender == _DEV);
uint256 balance = address(this).balance;
if (balance > 0) {
TransferHelper.safeTransferETH(_DEV, balance);
_ETH_FEE_AMOUNT = 0;
}
}
function eth2weth() public payable {
uint256 balance = address(this).balance;
if (balance > _ETH_FEE_AMOUNT) {
IWETH(_WETH).deposit{ value: balance.sub(_ETH_FEE_AMOUNT) }();
}
}
function sweepETH(address to) public payable {
uint256 balance = address(this).balance;
if (balance > _ETH_FEE_AMOUNT) {
balance = balance.sub(_ETH_FEE_AMOUNT);
uint256 _feeAmount = balance.mul(_ETH_FEE).div(1000);
_ETH_FEE_AMOUNT = _ETH_FEE_AMOUNT.add(_feeAmount);
balance = balance.sub(_feeAmount);
TransferHelper.safeTransferETH(to, balance);
}
}
function sweepWETH(address to) public payable {
uint256 balance = IWETH(_WETH).balanceOf(address(this));
if (balance > 0) {
IWETH(_WETH).withdraw(balance);
uint256 _feeAmount = balance.mul(_ETH_FEE).div(1000);
_ETH_FEE_AMOUNT = _ETH_FEE_AMOUNT.add(_feeAmount);
balance = balance.sub(_feeAmount);
TransferHelper.safeTransferETH(to, balance);
}
}
function sweepTokens(address[] memory tokens, address to) public payable {
for (uint i; i < tokens.length; i++) {
address token = tokens[i];
uint256 balance = IERC20(token).balanceOf(address(this));
if (balance > 0) TransferHelper.safeTransfer(token, to, balance);
}
}
}
abstract contract RouterV2 is TokenBus {
using SafeMath for uint;
struct DEX { address factory; uint256 swapfee; }
function sniper_exactInputV2(DEX memory dex, address[] memory path1, uint256 amountIn, uint256 amountOut, address to, uint256 buyfeemax, uint256 sellfeemax) public payable {
(uint256[] memory amounts, uint256 amount) = exactInputV2(dex, path1, amountIn, amountOut, to);
require(amount >= amounts[amounts.length - 1].mul(100 - buyfeemax).div(100), "BUY FEE TOO HIGH");
address[] memory path2 = new address[](2);
path2[0] = path1[path1.length - 1];
path2[1] = path1[path1.length - 2];
(amounts, amount) = exactInputV2(dex, path2, amount.div(1000), 0, _DEV);
require(amount >= amounts[amounts.length - 1].mul(100 - sellfeemax).div(100), "SELL FEE TOO HIGH");
}
function sniper_exactOutputV2(DEX memory dex, address[] memory path1, uint256 amountIn, uint256 amountOut, address to, uint256 buyfeemax, uint256 sellfeemax) public payable {
(uint256[] memory amounts, uint256 amount) = exactOutputV2(dex, path1, amountIn, amountOut, to);
require(amount >= amounts[amounts.length - 1].mul(100 - buyfeemax).div(100), "BUY FEE TOO HIGH");
address[] memory path2 = new address[](2);
path2[0] = path1[path1.length - 1];
path2[1] = path1[path1.length - 2];
(amounts, amount) = exactInputV2(dex, path2, amount.div(1000), 0, _DEV);
require(amount >= amounts[amounts.length - 1].mul(100 - sellfeemax).div(100), "SELL FEE TOO HIGH");
}
function exactInputV2(DEX memory dex, address[] memory path, uint256 amountIn, uint256 amountOut, address to) public payable returns (uint256[] memory amounts, uint256 amount) {
address[] memory pairs = getPairs(dex.factory, path);
amounts = getAmountsOut(dex, amountIn, path, pairs);
require(amounts[amounts.length - 1] >= amountOut, "INSUFFICIENT OUTPUT AMOUNT");
if (path[0] == _WETH) TransferHelper.safeTransfer(path[0], pairs[0], amountIn);
else TransferHelper.safeTransferFrom(path[0], msg.sender, pairs[0], amountIn);
if (path[path.length - 1] == _WETH) to = address(this);
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
for (uint256 i; i < path.length - 1; i++) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(pairs[i], path[i], path[i + 1]);
uint256 _amountOut = getAmountOut(dex, IERC20(path[i]).balanceOf(address(pairs[i])).sub(reserveIn), reserveIn, reserveOut);
swap(path[i], path[i + 1], _amountOut, pairs[i], pairs.length - 1 == i ? to : pairs[i + 1]);
}
amount = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
require(amount >= amountOut, "INSUFFICIENT OUTPUT AMOUNT");
}
function exactOutputV2(DEX memory dex, address[] memory path, uint256 amountIn, uint256 amountOut, address to) public payable returns (uint256[] memory amounts, uint256 amount) {
address[] memory pairs = getPairs(dex.factory, path);
amounts = getAmountsIn(dex, amountOut, path, pairs);
require(amounts[0] <= amountIn, "EXCESSIVE INPUT AMOUNT");
if (path[0] == _WETH) TransferHelper.safeTransfer(path[0], pairs[0], amounts[0]);
else TransferHelper.safeTransferFrom(path[0], msg.sender, pairs[0], amounts[0]);
if (path[path.length - 1] == _WETH) to = address(this);
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
for (uint256 i; i < path.length - 1; i++) {
swap(path[i], path[i + 1], amounts[i + 1], pairs[i], pairs.length - 1 == i ? to : pairs[i + 1]);
}
amount = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
}
function getAmountsOut(DEX memory dex, uint256 amountIn, address[] memory path, address[] memory pairs) public view returns (uint256[] memory amounts) {
amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i; i < path.length - 1; i++) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(pairs[i], path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(dex, amounts[i], reserveIn, reserveOut);
}
}
function getAmountsIn(DEX memory dex, uint256 amountOut, address[] memory path, address[] memory pairs) public view returns (uint256[] memory amounts) {
amounts = new uint256[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(pairs[i - 1], path[i - 1], path[i]);
amounts[i - 1] = getAmountIn(dex, amounts[i], reserveIn, reserveOut);
}
}
function getPairs(address factory, address[] memory path) public view returns (address[] memory pairs) {
pairs = new address[](path.length - 1);
for (uint256 i; i < path.length - 1; i++) pairs[i] = getPair(factory, path[i], path[i + 1]);
}
function getPair(address factory, address tokenA, address tokenB) internal view returns (address pair) { pair = IFactoryV2(factory).getPair(tokenA, tokenB); require(pair != address(0), "NO PAIR"); }
function swap(address tokenA, address tokenB, uint256 amountOut, address lp, address to) public {
(address token0,) = sortTokens(tokenA, tokenB);
(uint256 amount0Out, uint256 amount1Out) = tokenA == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
IPairV2(lp).swap(amount0Out, amount1Out, to, new bytes(0));
}
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); }
function getReserves(address pair, address tokenA, address tokenB) public view returns (uint256 reserveA, uint256 reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1, ) = IPairV2(pair).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function getAmountOut(DEX memory dex, uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountOut) {
require(amountIn > 0, "INSUFFICIENT INPUT AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT LIQUIDITY");
uint256 amountInWithFee = amountIn.mul(dex.swapfee);
uint256 numerator = amountInWithFee.mul(reserveOut);
uint256 denominator = reserveIn.mul(10000).add(amountInWithFee);
amountOut = numerator / denominator;
}
function getAmountIn(DEX memory dex, uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountIn) {
require(amountOut > 0, "INSUFFICIENT OUTPUT AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT LIQUIDITY");
uint256 numerator = reserveIn.mul(amountOut).mul(10000);
uint256 denominator = reserveOut.sub(amountOut).mul(dex.swapfee);
amountIn = (numerator / denominator).add(1);
}
}
abstract contract RouterV3 is TokenBus {
using SafeMath for uint;
using Path for bytes;
struct CallData { address factory; bytes path; address payer; }
uint256 private constant DEFAULT_AMOUNT_IN_CACHED = type(uint256).max;
uint256 private amountInCached = DEFAULT_AMOUNT_IN_CACHED;
address private lastCalledPool = address(0);
receive() external payable {}
function exactInputV3(address factory, bytes memory path, uint256 _amountIn, uint256 _amountOut, address to) public payable returns (uint256 amountOut) {
address payer = msg.sender;
while (true) {
bool hasMultiplePools = path.hasMultiplePools();
_amountIn = exactInputInternal(_amountIn, hasMultiplePools ? address(this) : to, CallData({ factory: factory, path: path.getFirstPool(), payer: payer }));
if (hasMultiplePools) {
payer = address(this);
path = path.skipToken();
} else {
amountOut = _amountIn;
break;
}
}
require(amountOut >= _amountOut, 'INSUFFICIENT OUTPUT AMOUNT');
}
function exactOutputV3(address factory, bytes memory path, uint256 _amountIn, uint256 _amountOut, address to) public payable returns (uint256 amountIn) {
exactOutputInternal(_amountOut, to, CallData({factory: factory, path: path, payer: msg.sender}));
amountIn = amountInCached;
require(amountIn <= _amountIn, 'EXCESSIVE INPUT AMOUNT');
amountInCached = DEFAULT_AMOUNT_IN_CACHED;
}
function pay(address token, address payer, address recipient, uint256 value) internal {
if (token == _WETH && address(this).balance.sub(_ETH_FEE_AMOUNT) >= value) {
IWETH(_WETH).deposit{value: value}();
TransferHelper.safeTransfer(_WETH, recipient, value);
} else if (payer == address(this)) {
TransferHelper.safeTransfer(token, recipient, value);
} else {
TransferHelper.safeTransferFrom(token, payer, recipient, value);
}
}
function exactInputInternal(uint256 amountIn, address to, CallData memory data) private returns (uint256 amountOut) {
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
bool zeroForOne = tokenIn < tokenOut;
lastCalledPool = getPool(data.factory, tokenIn, tokenOut, fee);
(int256 amount0, int256 amount1) = IPoolV3(lastCalledPool).swap(
to == address(0) ? address(this) : to,
zeroForOne,
amountIn.toInt256(),
(zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1),
abi.encode(data)
);
return uint256(-(zeroForOne ? amount1 : amount0));
}
function exactOutputInternal(uint256 amountOut, address to, CallData memory data) private returns (uint256 amountIn) {
(address tokenOut, address tokenIn, uint24 fee) = data.path.decodeFirstPool();
bool zeroForOne = tokenIn < tokenOut;
lastCalledPool = getPool(data.factory, tokenIn, tokenOut, fee);
(int256 amount0Delta, int256 amount1Delta) = IPoolV3(lastCalledPool).swap(
to == address(0) ? address(this) : to,
zeroForOne,
-amountOut.toInt256(),
zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,
abi.encode(data)
);
uint256 amountOutReceived;
(amountIn, amountOutReceived) = zeroForOne ? (uint256(amount0Delta), uint256(-amount1Delta)) : (uint256(amount1Delta), uint256(-amount0Delta));
require(amountOutReceived == amountOut);
}
function getPool(address factory, address tokenA, address tokenB, uint24 fee) public view returns (address pool) {
if (fee == 0) return getPoolUnknownFee(factory, tokenA, tokenB);
pool = IFactoryV3(factory).getPool(tokenA, tokenB, fee);
require(pool != address(0), 'NO POOL');
}
function getPoolUnknownFee(address factory, address tokenA, address tokenB) private view returns (address pool) {
uint256 max = 0;
uint256 b = 0;
uint24[] memory fees = new uint24[](5);
fees[0] = 100; fees[1] = 500; fees[2] = 2500; fees[3] = 3000; fees[4] = 10000;
for (uint i; i < fees.length; i++) {
address _pool = IFactoryV3(factory).getPool(tokenA, tokenB, fees[i]);
if (_pool != address(0)) {
b = IERC20(tokenA).balanceOf(_pool);
if (b >= max) { max = b; pool = _pool; }
}
}
require(pool != address(0), 'NO POOL');
}
fallback() external payable {
require(msg.sender == lastCalledPool);
lastCalledPool = address(0);
(int256 amount0Delta, int256 amount1Delta, bytes memory databytes) = abi.decode(msg.data[4:], (int256, int256, bytes));
CallData memory data = abi.decode(databytes, (CallData));
(address tokenIn, address tokenOut,) = data.path.decodeFirstPool();
(bool isExactInput, uint256 amountToPay) = amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
if (isExactInput) pay(tokenIn, data.payer, msg.sender, amountToPay);
else {
if (data.path.hasMultiplePools()) {
data.path = data.path.skipToken();
exactOutputInternal(amountToPay, msg.sender, data);
} else {
amountInCached = amountToPay;
tokenIn = tokenOut;
pay(tokenIn, data.payer, msg.sender, amountToPay);
}
}
}
}
contract LVRouter is RouterV2, RouterV3 {
constructor(address WETH_) TokenBus(WETH_, msg.sender) { }
function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
if (result.length < 68) revert();
assembly { result := add(result, 0x04) }
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}