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": {
"src/ZeusRouter.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0 <0.9.0;
import {IWETH} from "./interfaces/IWETH.sol";
import {IUniswapV3Factory, IV4PoolManager, V4SwapParams, V4PoolKey} from "./interfaces/Uniswap.sol";
import {Swap} from "./lib/Swap.sol";
import {IPermit2} from "./interfaces/IPermit2.sol";
import {Commands} from "./lib/Commands.sol";
import {Inputs} from "./lib/Inputs.sol";
import {SafeTransferLib} from "./lib/SafeTransferLib.sol";
/*
ZZZZZZZZZZZZZZZZZZZ
Z:::::::::::::::::Z
Z:::::::::::::::::Z
Z:::ZZZZZZZZ:::::Z
ZZZZZ Z:::::Z eeeeeeeeeeee uuuuuu uuuuuu ssssssssss
Z:::::Z ee::::::::::::ee u::::u u::::u ss::::::::::s
Z:::::Z e::::::eeeee:::::eeu::::u u::::u ss:::::::::::::s
Z:::::Z e::::::e e:::::eu::::u u::::u s::::::ssss:::::s
Z:::::Z e:::::::eeeee::::::eu::::u u::::u s:::::s ssssss
Z:::::Z e:::::::::::::::::e u::::u u::::u s::::::s
Z:::::Z e::::::eeeeeeeeeee u::::u u::::u s::::::s
ZZZ:::::Z ZZZZZe:::::::e u:::::uuuu:::::u ssssss s:::::s
Z::::::ZZZZZZZZ:::Ze::::::::e u:::::::::::::::uus:::::ssss::::::s
Z:::::::::::::::::Z e::::::::eeeeeeee u:::::::::::::::us::::::::::::::s
Z:::::::::::::::::Z ee:::::::::::::e uu::::::::uu:::u s:::::::::::ss
ZZZZZZZZZZZZZZZZZZZ eeeeeeeeeeeeee uuuuuuuu uuuu sssssssssss
Swap router contract made for Zeus
Github: https://github.com/greekfetacheese/zeus-router-v1
Main repo: https://github.com/greekfetacheese/zeus
*/
library BalanceDeltaLibrary {
function amount0(int256 balanceDelta) internal pure returns (int128 _amount0) {
assembly ("memory-safe") {
_amount0 := sar(128, balanceDelta)
}
}
function amount1(int256 balanceDelta) internal pure returns (int128 _amount1) {
assembly ("memory-safe") {
_amount1 := signextend(15, balanceDelta)
}
}
}
contract ZeusRouter {
using BalanceDeltaLibrary for int256;
address internal constant ETH = address(0);
address public immutable WETH;
address public immutable PERMIT2;
address public immutable V4_POOL_MANAGER;
address public immutable UNISWAP_V3_FACTORY;
address public immutable PANCAKE_SWAP_V3_FACTORY;
struct DeployParams {
address weth;
address permit2;
address v4PoolManager;
address uniswapV3Factory;
address pancakeSwapV3Factory;
}
constructor(DeployParams memory params) {
WETH = params.weth;
PERMIT2 = params.permit2;
V4_POOL_MANAGER = params.v4PoolManager;
UNISWAP_V3_FACTORY = params.uniswapV3Factory;
PANCAKE_SWAP_V3_FACTORY = params.pancakeSwapV3Factory;
}
function zSwap(Inputs.ZParams calldata params) public payable {
require(params.deadline >= block.timestamp, "Deadline: Expired");
uint256 currencyOutBalanceBefore;
if (params.currencyOut == ETH) {
currencyOutBalanceBefore = msg.sender.balance;
} else {
currencyOutBalanceBefore = SafeTransferLib.balanceOf(params.currencyOut, msg.sender);
}
execute(params);
uint256 balanceAfter;
if (params.currencyOut == ETH) {
balanceAfter = msg.sender.balance;
} else {
balanceAfter = SafeTransferLib.balanceOf(params.currencyOut, msg.sender);
}
require(balanceAfter > currencyOutBalanceBefore, "Bad Swap: No amount received");
uint256 realAmountOut = balanceAfter - currencyOutBalanceBefore;
require(realAmountOut >= params.amountMin, "SlippageCheck: Insufficient output");
}
/// @notice Executes a series of commands with their corresponding inputs.
function execute(Inputs.ZParams calldata swapParams) internal {
for (uint256 i = 0; i < swapParams.commands.length; i++) {
bytes1 command = swapParams.commands[i];
bytes memory input = swapParams.inputs[i];
bool isValid = command >= Commands.PERMIT2_PERMIT && command <= Commands.SWEEP;
require(isValid, "Invalid command");
if (command == Commands.PERMIT2_PERMIT) {
Inputs.Permit2Permit memory params = abi.decode(input, (Inputs.Permit2Permit));
IPermit2(PERMIT2).permit(msg.sender, params.permitSingle, params.signature);
}
if (command == Commands.V2_SWAP || command == Commands.V3_SWAP) {
_swapV2V3(input);
}
if (command == Commands.V4_SWAP) {
_swapV4(input);
}
if (command == Commands.WRAP_ETH) {
wrapETH(input);
}
if (command == Commands.WRAP_ALL_ETH) {
wrapAllETH(input);
}
if (command == Commands.UNWRAP_WETH) {
unwrapWETH(input);
}
if (command == Commands.SWEEP) {
sweep(input);
}
}
}
/// @notice Wraps a specified amount of contract's ETH into WETH and sends it to the recipient
function wrapETH(bytes memory input) internal {
Inputs.WrapETH memory params = abi.decode(input, (Inputs.WrapETH));
IWETH(WETH).deposit{value: params.amount}();
if (params.recipient != address(this)) {
SafeTransferLib.safeTransfer(WETH, params.recipient, params.amount);
}
}
/// @notice Wraps all contract's ETH into WETH and sends it to the recipient
function wrapAllETH(bytes memory input) internal {
Inputs.WrapAllETH memory params = abi.decode(input, (Inputs.WrapAllETH));
IWETH(WETH).deposit{value: address(this).balance}();
uint256 wethBalance = SafeTransferLib.balanceOf(WETH, address(this));
if (params.recipient != address(this)) {
SafeTransferLib.safeTransfer(WETH, params.recipient, wethBalance);
}
}
/// @notice Unwraps all WETH from the contract and sends all the remaining ETH to the recipient
function unwrapWETH(bytes memory input) internal {
Inputs.UnwrapWETH memory params = abi.decode(input, (Inputs.UnwrapWETH));
uint256 wethBalance = SafeTransferLib.balanceOf(WETH, address(this));
IWETH(WETH).withdraw(wethBalance);
uint256 ethBalance = address(this).balance;
if (params.recipient != address(this)) {
SafeTransferLib.forceSafeTransferETH(params.recipient, ethBalance);
}
}
/// @notice Sweeps all of the contract's ERC20 or ETH and sends it to the recipient
function sweep(bytes memory input) internal {
Inputs.Sweep memory params = abi.decode(input, (Inputs.Sweep));
uint256 balance;
if (params.currency == ETH) {
balance = address(this).balance;
SafeTransferLib.forceSafeTransferETH(params.recipient, balance);
} else {
balance = SafeTransferLib.balanceOf(params.currency, address(this));
SafeTransferLib.safeTransfer(params.currency, params.recipient, balance);
}
}
function _swapV2V3(bytes memory input) internal {
Inputs.V2V3SwapParams memory params = abi.decode(input, (Inputs.V2V3SwapParams));
if (params.poolVariant == 0) {
if (params.permit2) {
IPermit2(PERMIT2).transferFrom(msg.sender, params.pool, uint160(params.amountIn), params.tokenIn);
} else {
SafeTransferLib.safeTransfer(params.tokenIn, params.pool, params.amountIn);
}
Swap.OnUniswapV2(params);
} else if (params.poolVariant == 1) {
Swap.OnUniswapV3(params, msg.sender);
} else {
revert("Invalid pool variant");
}
}
function _swapV4(bytes memory input) internal {
Inputs.V4SwapParams memory params = abi.decode(input, (Inputs.V4SwapParams));
Swap.OnUniswapV4(params, msg.sender, V4_POOL_MANAGER);
}
/// @notice Callback for Uniswap V3 swaps.
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
(address tokenIn, address tokenOut, uint256 amountIn, address payer, uint24 fee, bool permit2) = abi.decode(
data,
(address, address, uint256, address, uint24, bool)
);
bool zeroForOne = tokenIn < tokenOut;
address pool = IUniswapV3Factory(UNISWAP_V3_FACTORY).getPool(tokenIn, tokenOut, fee);
require(msg.sender == pool && pool != address(0), "UniswapV3SwapCallback: Msg.sender is not a pool");
uint256 amountToPay = zeroForOne ? uint256(amount0Delta) : uint256(amount1Delta);
require(amountToPay == amountIn, "UniswapV3SwapCallback: amountToPay != amountIn");
if (permit2) {
IPermit2(PERMIT2).transferFrom(payer, pool, uint160(amountToPay), tokenIn);
} else {
SafeTransferLib.safeTransfer(tokenIn, pool, amountToPay);
}
}
/// @notice Callback for Pancake V3 swaps.
function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
(address tokenIn, address tokenOut, uint256 amountIn, address payer, uint24 fee, bool permit2) = abi.decode(
data,
(address, address, uint256, address, uint24, bool)
);
bool zeroForOne = tokenIn < tokenOut;
// Verify caller is the correct pool
address pool = IUniswapV3Factory(PANCAKE_SWAP_V3_FACTORY).getPool(tokenIn, tokenOut, fee);
require(msg.sender == pool && pool != address(0), "PancakeV3SwapCallback: Msg.sender is not a pool");
uint256 amountToPay = zeroForOne ? uint256(amount0Delta) : uint256(amount1Delta);
require(amountToPay == amountIn, "PancakeV3SwapCallback: amountToPay != amountIn");
if (permit2) {
IPermit2(PERMIT2).transferFrom(payer, pool, uint160(amountToPay), tokenIn);
} else {
SafeTransferLib.safeTransfer(tokenIn, pool, amountToPay);
}
}
/// @notice Callback for Uniswap V4 swaps.
function unlockCallback(bytes calldata callbackData) public payable returns (bytes memory result) {
require(msg.sender == V4_POOL_MANAGER, "UniswapV4SwapCallback: Msg.sender is not PoolManager");
Swap.V4CallBackData memory data = abi.decode(callbackData, (Swap.V4CallBackData));
(uint256 amountOut, uint256 amountToPay) = _swap(data);
IV4PoolManager poolManager = IV4PoolManager(V4_POOL_MANAGER);
// Settle input
if (data.params.currencyIn == ETH) {
poolManager.settle{value: amountToPay}();
} else {
if (data.params.permit2) {
IPermit2(PERMIT2).transferFrom(
data.payer,
V4_POOL_MANAGER,
uint160(amountToPay),
data.params.currencyIn
);
} else {
SafeTransferLib.safeTransfer(data.params.currencyIn, V4_POOL_MANAGER, amountToPay);
}
poolManager.settle();
}
poolManager.take(data.params.currencyOut, data.params.recipient, amountOut);
return "";
}
function _swap(Swap.V4CallBackData memory data) internal returns (uint256 amountOut, uint256 amountToPay) {
(address currency0, address currency1) = sortCurrencies(data.params.currencyIn, data.params.currencyOut);
V4PoolKey memory poolKey = V4PoolKey(
currency0,
currency1,
data.params.fee,
data.params.tickSpacing,
data.params.hooks
);
uint160 sqrtPriceLimitX96 = data.params.zeroForOne ? Swap.MIN_SQRT_RATIO : Swap.MAX_SQRT_RATIO;
V4SwapParams memory swapParams = V4SwapParams(
data.params.zeroForOne,
-int256(data.params.amountIn),
sqrtPriceLimitX96
);
IV4PoolManager poolManager = IV4PoolManager(V4_POOL_MANAGER);
int256 delta = poolManager.swap(poolKey, swapParams, data.params.hookData);
poolManager.sync(data.params.currencyIn);
int128 inputDelta = data.params.zeroForOne ? delta.amount0() : delta.amount1();
require(inputDelta < 0, "V4: Positive input delta");
amountToPay = uint256(uint128(-inputDelta));
require(amountToPay == data.params.amountIn, "V4: amountToPay != amountIn");
int128 outputDelta = data.params.zeroForOne ? delta.amount1() : delta.amount0();
require(outputDelta > 0, "V4: Negative output delta");
amountOut = uint256(uint128(outputDelta));
return (amountOut, amountToPay);
}
function sortCurrencies(address currencyA, address currencyB) internal pure returns (address, address) {
if (currencyA < currencyB) {
return (currencyA, currencyB);
} else {
return (currencyB, currencyA);
}
}
receive() external payable {}
}
"
},
"src/interfaces/IWETH.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
interface IWETH {
function deposit() external payable;
function withdraw(uint256 amount) external;
}"
},
"src/interfaces/Uniswap.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
interface IUniswapV2Pair {
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}
interface IUniswapV3Factory {
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
}
interface IUniswapV3Pool {
function swap(
address recipient,
bool zeroForOne,
int amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int amount0, int amount1);
function fee() external view returns (uint24);
function factory() external view returns (address);
}
struct V4PoolKey {
address currency0;
address currency1;
uint24 fee;
int24 tickSpacing;
address hooks;
}
struct V4SwapParams {
bool zeroForOne;
int256 amountSpecified;
uint160 sqrtPriceLimitX96;
}
interface IV4PoolManager {
function unlock(bytes calldata data) external returns (bytes memory);
function swap(
V4PoolKey memory key,
V4SwapParams memory params,
bytes calldata hookData
) external returns (int256 swapDelta);
function sync(address currency) external;
function settle() external payable returns (uint256 paid);
function take(address currency, address to, uint256 amount) external;
}
"
},
"src/lib/Swap.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0 <0.9.0;
import {Inputs} from "./Inputs.sol";
import {IUniswapV3Pool, IUniswapV2Pair, IV4PoolManager} from "../interfaces/Uniswap.sol";
library Swap {
uint160 internal constant MIN_SQRT_RATIO = 4295128749;
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970341;
struct V4CallBackData {
address payer;
Inputs.V4SwapParams params;
}
function OnUniswapV2(Inputs.V2V3SwapParams memory params) internal {
uint reserveIn;
uint reserveOut;
{
(uint reserve0, uint reserve1, ) = IUniswapV2Pair(params.pool).getReserves();
// sort reserves
if (params.tokenIn < params.tokenOut) {
reserveIn = reserve0;
reserveOut = reserve1;
} else {
reserveIn = reserve1;
reserveOut = reserve0;
}
}
uint256 amountOut = getAmountOut(params.amountIn, reserveIn, reserveOut, params.fee);
(uint amount0Out, uint amount1Out) = params.tokenIn < params.tokenOut
? (uint(0), amountOut)
: (amountOut, uint(0));
IUniswapV2Pair(params.pool).swap(amount0Out, amount1Out, params.recipient, new bytes(0));
}
function OnUniswapV3(Inputs.V2V3SwapParams memory params, address payer) internal {
bool zeroForOne = params.tokenIn < params.tokenOut;
uint160 sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO : MAX_SQRT_RATIO;
IUniswapV3Pool(params.pool).swap(
params.recipient,
zeroForOne,
int256(params.amountIn),
sqrtPriceLimitX96,
abi.encode(params.tokenIn, params.tokenOut, params.amountIn, payer, params.fee, params.permit2)
);
}
function OnUniswapV4(Inputs.V4SwapParams memory params, address payer, address poolManager) internal {
V4CallBackData memory data = V4CallBackData(payer, params);
IV4PoolManager(poolManager).unlock(abi.encode(data));
}
function getAmountOut(
uint amountIn,
uint reserveIn,
uint reserveOut,
uint poolfee
) internal pure returns (uint amountOut) {
require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
uint fee = (10000 - poolfee / 100) / 10;
uint amountInWithFee = amountIn * fee;
uint numerator = amountInWithFee * reserveOut;
uint denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
}
"
},
"src/interfaces/IPermit2.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
interface IPermit2 is IEIP712 {
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
}
"
},
"src/lib/Commands.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
library Commands {
bytes1 constant PERMIT2_PERMIT = 0x00;
bytes1 constant V2_SWAP = 0x01;
bytes1 constant V3_SWAP = 0x02;
bytes1 constant V4_SWAP = 0x03;
bytes1 constant WRAP_ETH = 0x04;
bytes1 constant WRAP_ALL_ETH = 0x05;
bytes1 constant UNWRAP_WETH = 0x06;
bytes1 constant SWEEP = 0x07;
}
"
},
"src/lib/Inputs.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {IPermit2} from "../interfaces/IPermit2.sol";
library Inputs {
struct ZParams {
bytes commands;
bytes[] inputs;
address currencyOut;
uint256 amountMin;
uint256 deadline;
}
struct Permit2Permit {
IPermit2.PermitSingle permitSingle;
bytes signature;
}
/// @notice Parameters for a V2/V3 swap
/// @param amountIn The amount of tokenIn to swap
/// @param tokenIn The input token
/// @param tokenOut The output token
/// @param pool The pool to swap on
/// @param poolVariant 0 for V2, 1 for V3
/// @param recipient The recipient of the tokenOut
/// @param fee The pool fee in hundredths of bips (eg. 3000 for 0.3%)
/// @param permit2 Whether the funds should come from permit or are already in the router
struct V2V3SwapParams {
uint256 amountIn;
address tokenIn;
address tokenOut;
address pool;
uint poolVariant;
address recipient;
uint24 fee;
bool permit2;
}
/// @notice Parameters for a V4 swap
/// @param currencyIn The input currency (address(0) for ETH)
/// @param currencyOut The output currency (address(0) for ETH)
/// @param amountIn The amount of currencyIn to swap
/// @param fee The pool fee in hundredths of bips (eg. 3000 for 0.3%)
/// @param tickSpacing The tick spacing of the pool
/// @param zeroForOne Whether the swap is from currencyIn to currencyOut
/// @param hooks The hooks to use for the swap
/// @param hookData The data to pass to the hooks
/// @param recipient The recipient of the currencyOut
/// @param permit2 Whether the funds should come from permit or are already in the router
struct V4SwapParams {
address currencyIn;
address currencyOut;
uint256 amountIn;
uint24 fee;
int24 tickSpacing;
bool zeroForOne;
address hooks;
bytes hookData;
address recipient;
bool permit2;
}
/// @notice WrapETH
/// @param recipient The recipient of the wrapped ETH
/// @param amount The amount of ETH to wrap
struct WrapETH {
address recipient;
uint256 amount;
}
/// @notice WrapAllETH
/// @param recipient The recipient of the wrapped ETH
struct WrapAllETH {
address recipient;
}
/// @notice UnwrapWETH
/// @param recipient The recipient of the unwrapped ETH
struct UnwrapWETH {
address recipient;
}
/// @notice Sweep
/// @param currency The currency to sweep, Use address(0) for ETH
/// @param recipient The recipient of the tokens
struct Sweep {
address currency;
address recipient;
}
}
"
},
"src/lib/SafeTransferLib.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) {
revert(codesize(), codesize())
} // For gas estimation.
}
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount := mul(
// The arguments of `mul` are evaluated from right to left.
mload(0x20),
and(
// The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
"
}
},
"settings": {
"remappings": [
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 500
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}
}}
Submitted on: 2025-11-03 12:37:10
Comments
Log in to comment.
No comments yet.