Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/SwapRouter.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.27;
import "./IVenue.sol";
import "./Utils.sol";
import "./Fees.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@solmate/tokens/ERC20.sol";
import "@solmate/auth/Owned.sol";
/// @title SwapRouter
/// @notice Orchestrates token swaps across multiple venues with fee handling and gas tracking
contract SwapRouter is Owned, Fees {
/// @notice Parameters for executing a swap operation
/// @dev Used to pass swap configuration to execute function
struct SwapParams {
/// @notice Array of venue contracts to route the swap through
IVenue[] venues;
/// @notice 2D array of token pairs representing the swap path for each venue
Pair[][] paths;
/// @notice Minimum acceptable output amount (slippage protection)
uint256 minOutput;
/// @notice Recipient address for the output tokens
address to;
/// @notice Referral address for fee distribution
address referral;
}
/// @notice Result data from a swap execution
struct Result {
/// @notice Amount of output tokens received
uint256 amountOut;
/// @notice Gas consumed during the swap
uint256 gas;
}
/// @notice Emitted when a swap is successfully executed
/// @param tokenIn Address of the input token
/// @param tokenOut Address of the output token
/// @param amountIn Amount of input tokens swapped
/// @param amountOut Amount of output tokens received
/// @param from Address that initiated the swap
/// @param to Address that received the output tokens
event Swap(
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 amountOut,
address indexed from,
address to
);
/// @notice Initializes the SwapRouter with an owner
/// @param _owner Address to be set as the contract owner
constructor(address _owner) Owned(_owner) {}
/// @notice Executes a single token swap operation across one or more venues
/// @dev Handles fee deduction on either input or output tokens and tracks gas consumption
/// @param params Swap parameters including venues, paths, minimum output, recipient, and referral
/// @return result Struct containing output amount and gas consumed
function execute(SwapParams memory params) public payable returns (Result memory result) {
// Unpack parameters for easier access
IVenue[] memory venues = params.venues;
Pair[][] memory paths = params.paths;
address to = params.to;
uint256 minOutput = params.minOutput;
address referral = params.referral;
// Record initial gas for gas consumption calculation
uint256 gas0 = gasleft();
// Extract input and output tokens from the swap path
address tokenIn = paths[0][0].tokenIn;
address tokenOut = paths[paths.length - 1][paths[paths.length - 1].length - 1].tokenOut;
uint256 amountIn = paths[0][0].amountIn;
// Determine fee location: check if input token has priority fee status
bool inFee = hasFeePriority(tokenIn);
bool outFee = inFee ? false : true;
// Route tokens: if input has fee, receiver is this contract; otherwise send directly to first venue
address inReceiver = inFee ? address(this) : address(venues[0]);
// Route output: if output has fee, receiver is this contract; otherwise send directly to recipient
address outReceiver = outFee ? address(this) : to;
// Transfer input tokens from sender to appropriate receiver
UtilsLib.transferFrom(tokenIn, msg.sender, inReceiver, amountIn);
// Handle input token fee if applicable
if (inFee) {
// Deduct router and referral fees from input amount
paths[0][0].amountIn = _takeFee(tokenIn, amountIn, referral);
// Withdraw net amount to the first venue
UtilsLib.withdraw(tokenIn, address(venues[0]), paths[0][0].amountIn);
}
// Execute the swap through venues and get output amount
result.amountOut = IVenue(payable(venues[0])).executeBatch(venues, paths, outReceiver, minOutput);
// Handle output token fee if applicable
if (outFee) {
// Deduct router and referral fees from output amount
result.amountOut = _takeFee(tokenOut, result.amountOut, referral);
// Withdraw net amount to the recipient
UtilsLib.withdraw(tokenOut, to, result.amountOut);
}
// Emit swap event with transaction details
emit Swap(tokenIn, tokenOut, amountIn, result.amountOut, msg.sender, to);
// Calculate and store gas consumption
result.gas = gas0 - gasleft();
}
/// @notice Executes multiple swap operations in a single transaction
/// @param batch Array of swap parameters, each representing an independent swap
/// @return returnData Array of Results, one for each swap in the batch
function executeBatch(SwapParams[] memory batch) public payable returns (Result[] memory returnData) {
uint256 length = batch.length;
returnData = new Result[](length);
// Execute each swap sequentially and collect results
for (uint256 i = 0; i < length; i++) {
returnData[i] = execute(batch[i]);
}
}
/// @notice Sets priority fee status for specified tokens
/// @dev Only callable by contract owner. Priority tokens pay fees on input amount instead of output
/// @param tokens Array of token addresses to update
/// @param enable True to add to priority list, false to remove
function setPriorityTokens(address[] calldata tokens, bool enable) external onlyOwner {
_setPriorityTokens(tokens, enable);
}
/// @notice Updates the router fee percentage
/// @dev Only callable by contract owner. Fee is applied to swaps (either input or output)
/// @param fee New router fee in basis points (e.g., 100 = 1%)
function setRouterFee(uint32 fee) external onlyOwner {
_setRouterFee(fee);
}
/// @notice Updates the referral fee percentage
/// @dev Only callable by contract owner. Fee is applied to swaps when referral address is provided
/// @param fee New referral fee in basis points (e.g., 100 = 1%)
function setReferralFee(uint32 fee) external onlyOwner {
_setReferralFee(fee);
}
/// @notice Withdraws accumulated fees for a single token
/// @dev Can be called by owner to withdraw fees held by contract, or by any address to withdraw their own fees
/// @param token Address of the token to withdraw fees from
/// @param amount Amount of fees to withdraw
/// @param to Recipient address for the withdrawn fees
function withdrawFee(address token, uint256 amount, address to) external {
// If caller is owner, withdraw from contract; otherwise withdraw from caller's balance
address from = msg.sender == owner ? address(this) : msg.sender;
_withdrawFee(token, from, to, amount);
}
/// @notice Withdraws accumulated fees for multiple tokens
/// @dev Can be called by owner to withdraw fees held by contract, or by any address to withdraw their own fees
/// @param tokens Array of token addresses to withdraw fees from
/// @param amounts Array of amounts to withdraw for each token (must match tokens array length)
/// @param to Recipient address for all withdrawn fees
function withdrawFees(address[] calldata tokens, uint256[] calldata amounts, address to) external {
require(tokens.length > 0, "EMPTY_LIST");
require(tokens.length == amounts.length, "BAD_LENGTH");
// If caller is owner, withdraw from contract; otherwise withdraw from caller's balance
address from = msg.sender == owner ? address(this) : msg.sender;
// Withdraw fees for each token
for (uint256 i = 0; i < tokens.length; i++) {
_withdrawFee(tokens[i], from, to, amounts[i]);
}
}
/// @notice Allows the contract to receive native currency (ETH)
/// @dev Required for receiving ETH payments and for operations that may send ETH to this contract
receive() external payable {}
}
"
},
"src/IVenue.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.27;
import "./Utils.sol";
import "./VenueFactory.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@solmate/auth/Owned.sol";
struct Pair {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 amountOut;
uint256 gas;
bytes data;
}
interface IFlashLoanRecipient {
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external;
}
abstract contract IVenue is IFlashLoanRecipient, Owned {
VenueFactory public factory;
constructor(address payable _factory) Owned(VenueFactory(_factory).owner()) {
factory = VenueFactory(_factory);
}
modifier onlyFactory() virtual {
require(msg.sender == address(factory), "UNAUTHORIZED");
_;
}
function execute(Pair[] memory chain, address to) external virtual returns (uint256);
function executeBatch(IVenue[] memory venues, Pair[][] memory paths, address receiver, uint256 minOutput)
public
returns (uint256 outputAmount)
{
require(venues.length > 0, "EMPTY_LIST");
require(paths.length == venues.length, "BAD_LENGTH");
uint256 amount = paths[0][0].amountIn;
address tokenIn = paths[0][0].tokenIn;
address tokenOut = paths[paths.length - 1][paths[paths.length - 1].length - 1].tokenOut;
uint256 outBalanceBefore = UtilsLib.balanceOf(tokenOut, address(this));
bool isCycle = false;
if (tokenIn == tokenOut) {
outBalanceBefore -= amount;
isCycle = true;
}
address to;
for (uint256 i = 0; i < venues.length; i++) {
IVenue venue = venues[i];
require(factory.isActive(address(venue)), "NOT_ACTIVE");
if (i < venues.length - 1) {
to = address(venues[i + 1]);
} else {
to = address(this);
}
paths[i][0].amountIn = amount;
amount = venue.execute(paths[i], to);
}
uint256 outBalanceAfter = UtilsLib.balanceOf(tokenOut, address(this));
if (msg.sender == UtilsLib.BALANCER_VAULT) {
// msg.sender is a Balancer vault, repay loan
uint256 inBalanceAfter = outBalanceAfter;
if (!isCycle) {
inBalanceAfter = UtilsLib.balanceOf(tokenIn, address(this));
}
require(inBalanceAfter > paths[0][0].amountIn, "BAD_FLASH_LOAN");
UtilsLib.withdraw(paths[0][0].tokenIn, msg.sender, paths[0][0].amountIn);
if (isCycle) {
outBalanceAfter = UtilsLib.balanceOf(tokenOut, address(this));
}
}
outputAmount = outBalanceAfter - outBalanceBefore;
require(outputAmount >= minOutput, "LOW_OUTPUT");
UtilsLib.withdraw(address(tokenOut), receiver, outputAmount);
}
function receiveFlashLoan(IERC20[] memory, uint256[] memory, uint256[] memory, bytes memory userData)
external
override
{
IVenue[] memory venues;
Pair[][] memory path;
address receiver;
uint256 minOutput;
(venues, path, receiver, minOutput) = abi.decode(userData, (IVenue[], Pair[][], address, uint256));
executeBatch(venues, path, receiver, minOutput);
}
function setFactory(address payable _factory) external onlyOwner {
factory = VenueFactory(_factory);
}
function withdraw(address token, address to, uint256 amount) public onlyOwner {
UtilsLib.withdraw(token, to, amount);
}
// Function to receive Ether. msg.data must be empty
receive() external payable {}
}
"
},
"src/Utils.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.27;
import "@solmate/utils/SafeTransferLib.sol";
import "@solmate/tokens/ERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
interface WToken is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
library UtilsLib {
address public constant NATIVE_COIN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address public constant WRAPPED_TOKEN_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
address public constant UNISWAPV2_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
uint256 public constant APPROVE_MUL = 2;
WToken public constant WTOKEN = WToken(WRAPPED_TOKEN_ADDRESS);
function isNativeCoin(address asset) internal pure returns (bool) {
return asset == NATIVE_COIN_ADDRESS;
}
function isWrappedToken(address asset) internal pure returns (bool) {
return asset == WRAPPED_TOKEN_ADDRESS;
}
function wrapEth(uint256 amount) internal {
WTOKEN.deposit{value: amount}();
}
function unwrapEth(uint256 amount) internal {
WTOKEN.withdraw(amount);
}
function wrapAndTransfer(address to, uint256 amount) internal {
wrapEth(amount);
withdraw(WRAPPED_TOKEN_ADDRESS, to, amount);
}
function unwrapAndTransfer(address to, uint256 amount) internal {
unwrapEth(amount);
withdrawEth(to, amount);
}
function withdrawEth(address to, uint256 amount) internal {
withdraw(NATIVE_COIN_ADDRESS, to, amount);
}
function withdrawOrUnwrapEth(address to, uint256 amount) internal {
uint256 eth_balance = address(this).balance;
if (eth_balance < amount) {
unwrapEth(amount - eth_balance);
}
withdrawEth(to, amount);
}
function withdraw(address asset, address to, uint256 amount) internal {
if (address(this) == to) return;
if (isNativeCoin(asset)) {
SafeTransferLib.safeTransferETH(to, amount);
} else {
SafeTransferLib.safeTransfer(ERC20(asset), to, amount);
}
}
function transferFrom(address asset, address from, address to, uint256 amount) internal {
if (address(this) == from && address(this) == to) return;
if (isNativeCoin(asset)) {
require(msg.value >= amount, "TRANSFER_FROM_ETH_LOW");
if (to != address(this)) {
SafeTransferLib.safeTransferETH(to, amount);
}
} else {
SafeTransferLib.safeTransferFrom(ERC20(asset), from, to, amount);
}
}
function balanceOf(address asset, address owner) internal view returns (uint256) {
if (isNativeCoin(asset)) {
return owner.balance;
} else {
return ERC20(asset).balanceOf(owner);
}
}
function approve(address token, address to, uint256 amount) internal {
if (ERC20(token).allowance(address(this), to) < amount) {
SafeTransferLib.safeApprove(ERC20(token), to, 0);
SafeTransferLib.safeApprove(ERC20(token), to, amount);
}
}
function approveReserve(address token, address to, uint256 amount) internal {
approve(token, to, amount * APPROVE_MUL);
}
}
"
},
"src/Fees.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.27;
import "./Utils.sol";
abstract contract Fees {
event FeeChanged(uint32 owner, uint32 referral);
event FeeTaken(address indexed token, address indexed referral, uint256 referralFee, uint256 routerFee);
event FeeWithdrawal(address indexed token, address indexed referral, address to, uint256 amount);
mapping(address => bool) private priorityTokens;
// referral => token => balance
mapping(address => mapping(address => uint256)) private balances;
uint32 public constant FEE_DENOMINATOR = 1000000;
uint32 public ROUTER_FEE = 500;
uint32 public REFERRAL_FEE = 500;
function _setPriorityTokens(address[] calldata tokens, bool enable) internal {
for (uint256 i = 0; i < tokens.length; i++) {
priorityTokens[tokens[i]] = enable;
}
}
function _setRouterFee(uint32 fee) internal {
require(fee + REFERRAL_FEE < FEE_DENOMINATOR, "MAX_FEE");
ROUTER_FEE = fee;
emit FeeChanged(fee, REFERRAL_FEE);
}
function _setReferralFee(uint32 fee) internal {
require(fee + ROUTER_FEE < FEE_DENOMINATOR, "MAX_FEE");
REFERRAL_FEE = fee;
emit FeeChanged(ROUTER_FEE, fee);
}
function _takeFee(address token, uint256 amount, address referral) internal returns (uint256 output) {
uint256 routerFee = amount * ROUTER_FEE / FEE_DENOMINATOR;
uint256 referralFee = 0;
balances[address(this)][token] += routerFee;
uint256 overallFee = routerFee;
if (referral != address(0x0)) {
referralFee = amount * REFERRAL_FEE / FEE_DENOMINATOR;
balances[referral][token] += referralFee;
overallFee += referralFee;
}
emit FeeTaken(token, referral, referralFee, routerFee);
output = amount - overallFee;
}
function _withdrawFee(address token, address from, address to, uint256 amount) internal {
require(amount > 0);
uint256 balance = balances[from][token];
require(balance >= amount);
balances[from][token] -= amount;
UtilsLib.withdraw(token, to, amount);
emit FeeWithdrawal(token, from, to, amount);
}
function hasFeePriority(address token) public view returns (bool) {
return priorityTokens[token];
}
function feeBalance(address owner, address token) public view returns (uint256) {
return balances[owner][token];
}
function feeBalance(address owner, address[] calldata tokens) public view returns (uint256[] memory fees) {
fees = new uint256[](tokens.length);
for (uint256 i = 0; i < tokens.length; i++) {
fees[i] = balances[owner][tokens[i]];
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
"
},
"lib/solmate/src/tokens/ERC20.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
"
},
"lib/solmate/src/auth/Owned.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
"
},
"src/VenueFactory.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.27;
import "./Utils.sol";
import "./IVenue.sol";
import "@solmate/auth/Owned.sol";
import "@solmate/utils/CREATE3.sol";
contract VenueFactory is Owned {
event VenueCreated(address indexed venue);
mapping(address => uint256) private venues;
constructor(address _owner) Owned(_owner) {}
function getDeployed(bytes32 salt) external view returns (address) {
return CREATE3.getDeployed(salt);
}
function create(bytes memory creationCode, bytes32 salt)
external
payable
onlyOwner
returns (address payable venue)
{
venue = payable(CREATE3.getDeployed(salt));
require(!isDeployed(venue), "COLLISION");
venues[venue] = toState(true, true);
venue = payable(CREATE3.deploy(salt, creationCode, msg.value));
emit VenueCreated(venue);
}
function toState(bool deployed, bool active) internal pure returns (uint256) {
return uint256(deployed ? 1 : 0) | (uint256(active ? 1 : 0) << 1);
}
function fromState(uint256 state) internal pure returns (bool deployed, bool active) {
deployed = bool(state & 1 == 1 ? true : false);
active = bool(state & 2 == 2 ? true : false);
}
function isActive(address venue) public view returns (bool) {
(, bool active) = fromState(venues[venue]);
return active;
}
function isDeployed(address venue) public view returns (bool) {
(bool deployed,) = fromState(venues[venue]);
return deployed;
}
function deactivate(address venue) external onlyOwner {
venues[venue] = toState(true, false);
}
function activate(address venue) external onlyOwner {
venues[venue] = toState(true, true);
}
function withdraw(address token, address to, uint256 amount) public onlyOwner {
UtilsLib.withdraw(token, to, amount);
}
receive() external payable {}
}
"
},
"lib/solmate/src/utils/SafeTransferLib.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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);
}
"
},
"lib/solmate/src/utils/CREATE3.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {Bytes32AddressLib} from "./Bytes32AddressLib.sol";
/// @notice Deploy to deterministic addresses without an initcode factor.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol)
library CREATE3 {
using Bytes32AddressLib for bytes32;
//--------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//--------------------------------------------------------------------------------//
// 0x36 | 0x36 | CALLDATASIZE | size //
// 0x3d | 0x3d | RETURNDATASIZE | 0 size //
// 0x3d | 0x3d | RETURNDATASIZE | 0 0 size //
// 0x37 | 0x37 | CALLDATACOPY | //
// 0x36 | 0x36 | CALLDATASIZE | size //
// 0x3d | 0x3d | RETURNDATASIZE | 0 size //
// 0x34 | 0x34 | CALLVALUE | value 0 size //
// 0xf0 | 0xf0 | CREATE | newContract //
//--------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//--------------------------------------------------------------------------------//
// 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode //
// 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode //
// 0x52 | 0x52 | MSTORE | //
// 0x60 | 0x6008 | PUSH1 08 | 8 //
// 0x60 | 0x6018 | PUSH1 18 | 24 8 //
// 0xf3 | 0xf3 | RETURN | //
//--------------------------------------------------------------------------------//
bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3";
bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);
function deploy(
bytes32 salt,
bytes memory creationCode,
uint256 value
) internal returns (address deployed) {
bytes memory proxyChildBytecode = PROXY_BYTECODE;
address proxy;
/// @solidity memory-safe-assembly
assembly {
// Deploy a new contract with our pre-made bytecode via CREATE2.
// We start 32 bytes into the code to avoid copying the byte length.
proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt)
}
require(proxy != address(0), "DEPLOYMENT_FAILED");
deployed = getDeployed(salt);
(bool success, ) = proxy.call{value: value}(creationCode);
require(success && deployed.code.length != 0, "INITIALIZATION_FAILED");
}
function getDeployed(bytes32 salt) internal view returns (address) {
return getDeployed(salt, address(this));
}
function getDeployed(bytes32 salt, address creator) internal pure returns (address) {
address proxy = keccak256(
abi.encodePacked(
// Prefix:
bytes1(0xFF),
// Creator:
creator,
// Salt:
salt,
// Bytecode hash:
PROXY_BYTECODE_HASH
)
).fromLast20Bytes();
return
keccak256(
abi.encodePacked(
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01)
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
hex"d6_94",
proxy,
hex"01" // Nonce of the proxy contract (1)
)
).fromLast20Bytes();
}
}
"
},
"lib/solmate/src/utils/Bytes32AddressLib.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {
return address(uint160(uint256(bytesValue)));
}
function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {
return bytes32(bytes20(addressValue));
}
}
"
}
},
"settings": {
"remappings": [
"@uniswap/v3-core/=lib/v3-core/",
"@uniswap/v3-periphery/=lib/v3-periphery/",
"@uniswap/v2-core/=lib/v2-core/",
"@uniswap/v2-periphery/=lib/v2-periphery/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"@forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"v3-core/=lib/v3-core/",
"v3-periphery/=lib/v3-periphery/contracts/",
"solmate/=lib/solmate/src/",
"@solmate/=lib/solmate/src/",
"@balancer-labs/=lib/balancer-v2-monorepo/pkg/",
"v2-periphery/=lib/v2-periphery/contracts/",
"v2-core/=lib/v2-core/contracts/",
"balancer-v2-monorepo/=lib/balancer-v2-monorepo/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}
}}
Submitted on: 2025-11-02 12:42:27
Comments
Log in to comment.
No comments yet.