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/ArbitrageBotUniswapV2.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {IERC20} from "@uniswap/v2-core/contracts/interfaces/IERC20.sol";
import {Ownable} from "@openzeppelin-contracts/contracts/access/Ownable.sol";
error ArbitrageBotUniswapV2_NotEnoughProfit();
error ArbitrageBotUniswapV2_InvalidUniswapV2CalleeSender();
error ArbitrageBotUniswapV2_InvalidUniswapV2CalleePair();
/**
@title ArbitrageBotUniswapV2
@notice A contract that performs flash swap arbitrage on Uniswap V2
@dev This contract is used to perform flash swap arbitrage on Uniswap V2
@author Illia Verbanov (illiaverbanov.xyz)
*/
contract ArbitrageBotUniswapV2 is Ownable {
/**
@title FlashSwapParams
@notice Params for the flash swap arbitrage
@param caller The address of the caller of the flash swap
@param pair0 The address of the first pair to get the flash swap from
@param pair1 The address of the second pair to swap to
@param isZeroForOne True if the flash swap is token0 -> token1, false if token1 -> token0
@param amountIn The amount in to repay the flash swap
@param amountOut The amount out to borrow from the flash swap
@param minProfit The minimum profit to make
*/
struct FlashSwapParams {
address caller;
address pair0;
address pair1;
bool isZeroForOne;
uint256 amountIn;
uint256 amountOut;
uint256 minProfit;
}
/**
@notice Event emitted when a profit is made
@param caller The address of the caller of the flash swap
@param token The token that was used for the profit
@param profit The profit made in terms of the input token
*/
event ProfitMade(
address indexed caller,
address indexed token,
uint256 profit
);
constructor() Ownable(msg.sender) {}
/**
@notice Peforme a flash swap arbitrage (core function)
@param pair0 The address of the first pair to get the flash swap from
@param pair1 The address of the second pair to swap to
@param isZeroForOne True if the flash swap is token0 -> token1, false if token1 -> token0
@param amountIn The amount in to repay the flash swap
@param minProfit The minimum profit to make
*/
function flashSwapArbitrage(
address pair0,
address pair1,
bool isZeroForOne,
uint256 amountIn,
uint256 minProfit
) external onlyOwner {
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pair0)
.getReserves();
uint256 amountOut = _getAmountOut(
amountIn,
isZeroForOne ? reserve0 : reserve1,
isZeroForOne ? reserve1 : reserve0
);
bytes memory data = abi.encode(
FlashSwapParams(
msg.sender,
pair0,
pair1,
isZeroForOne,
amountIn,
amountOut,
minProfit
)
);
IUniswapV2Pair(pair0).swap({
amount0Out: isZeroForOne ? 0 : amountOut,
amount1Out: isZeroForOne ? amountOut : 0,
to: address(this),
data: data
});
}
/**
@notice Callback function for the flash swap arbitrage (called by Uniswap V2 Pair)
@param sender The address of the sender of the flash swap
@param amount0 The amount of token0 received from the flash swap
@param amount1 The amount of token1 received from the flash swap
@param data The data of the flash swap (has to have FlashSwapParams struct encoded in current scenario)
*/
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external {
require(
sender == address(this),
ArbitrageBotUniswapV2_InvalidUniswapV2CalleeSender()
);
FlashSwapParams memory params = abi.decode(data, (FlashSwapParams));
require(
msg.sender == params.pair0,
ArbitrageBotUniswapV2_InvalidUniswapV2CalleePair()
);
address token0 = IUniswapV2Pair(params.pair0).token0();
address token1 = IUniswapV2Pair(params.pair0).token1();
address tokenIn = params.isZeroForOne ? token0 : token1;
address tokenOut = params.isZeroForOne ? token1 : token0;
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(params.pair1)
.getReserves();
uint256 amountOut = params.isZeroForOne
? _getAmountOut(params.amountOut, reserve1, reserve0)
: _getAmountOut(params.amountOut, reserve0, reserve1);
IERC20(tokenOut).transfer(params.pair1, params.amountOut);
IUniswapV2Pair(params.pair1).swap({
amount0Out: params.isZeroForOne ? amountOut : 0,
amount1Out: params.isZeroForOne ? 0 : amountOut,
to: address(this),
data: ""
});
IERC20(tokenIn).transfer(params.pair0, params.amountIn);
uint256 profit = amountOut - params.amountIn;
require(
profit >= params.minProfit,
ArbitrageBotUniswapV2_NotEnoughProfit()
);
emit ProfitMade(params.caller, tokenIn, profit);
IERC20(tokenIn).transfer(params.caller, profit);
}
/**
@notice Internal function to get the amount out for a flash swap arbitrage affected by fees (Uniswap V2 formula)
@param amountIn The amount in to get the amount out for
@param reserveIn The reserve of the input token
@param reserveOut The reserve of the output token
@return amountOut The amount out for the flash swap arbitrage
*/
function _getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountOut) {
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = (reserveIn * 1000) + amountInWithFee;
amountOut = numerator / denominator;
}
}
"
},
"lib/v2-core/contracts/interfaces/IUniswapV2Pair.sol": {
"content": "pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
"
},
"lib/v2-core/contracts/interfaces/IERC20.sol": {
"content": "pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
"
},
"lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
}
},
"settings": {
"remappings": [
"@uniswap/v2-periphery/=lib/v2-periphery/",
"@uniswap/v2-core/=lib/v2-core/",
"@openzeppelin-contracts/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"v2-core/=lib/v2-core/contracts/",
"v2-periphery/=lib/v2-periphery/contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": false
}
}}
Submitted on: 2025-10-21 14:08:37
Comments
Log in to comment.
No comments yet.