Skip to content

Instantly share code, notes, and snippets.

@shenqihui
Last active February 27, 2024 13:15
Show Gist options
  • Save shenqihui/d5a2ae5d449133e83027f47d6ad8682f to your computer and use it in GitHub Desktop.
Save shenqihui/d5a2ae5d449133e83027f47d6ad8682f to your computer and use it in GitHub Desktop.
闪电 swap 套利代码

套利的说明

提前提示:

合约只是套利的10%,剩下90%工作才是大头。

这个代码并非最佳的代码,你可以在网上随便一搜就搜出很多类似套利 swap 的代码。

如何进行套利

1,发掘链上全部的交易对。
2,监控每个交易对之间是否存在利润。
3,如果存在利润,那就发对应的套利交易出去,具体看能不能成功,旧得看有没有抢到了

如何发掘链上全部的交易对

大概思路说下,具体实现和爬会比较花时间。
第一步:发掘出链上所有的 swap 的router。大概就是检测链上的交易里面,data前面是那几个 swapExactTokensForTokens 类似的那几个函数的前缀。
第二步:对应的router,读取出factory ,
第三步:根据factory,爬出对应的所有pair_create的交易对。

监控每个交易对之间是否存在利润

注意,这里的话,没必要做区块外的套利了,因为没多少利润,如果够机器那就做吧。

这里理解的话,需要理解什么是三文治交易。
监控正在交易中的,如果data是router的那些 swapExactTokensForTokens 开头的data,
再进一步解析判断里面的token,然后匹配全链的对应交易对是否存在利润。

更深入的做法,判断跨链多一个token的利润差,有的话可以做。但是我没做,因为还没想通怎么计算最佳利润。

如果存在利润,那就跟这个交易的gas price 一样的价格发出套利交易。

(但是我告诉你个严重的问题:同样思路的,目前成熟的链上已经有对应的竞争者很多了,我之所以开源因为我觉得这个套利方式走到头了)

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.10;
interface V5IICherryPair {
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}
interface IERC20 {
function balanceOf(address owner) external view returns (uint);
function transfer(address to, uint value) external returns (bool);
}
library SSV5SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SSV5-SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SSV5-SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SSV5-SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SSV5-SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SSV5-SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
contract SSV5 {
using SSV5SafeMath for uint;
// 输出数量
uint gx;
uint gy;
// // todo 生产删除
// uint public gmintenTime;
constructor() public {}
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
// require(amountIn > 0, 'getOut: INSUFFICIENT_INPUT_AMOUNT');
// require(reserveIn > 0 && reserveOut > 0, 'getOut: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
// require(amountOut > 0, 'getIn: INSUFFICIENT_OUTPUT_AMOUNT');
// require(reserveIn > 0 && reserveOut > 0, 'getIn: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
amountIn = (numerator / denominator).add(1);
}
function sortTokens(address tokenA, address tokenB) internal pure returns (address tokens0, address tokens1) {
// require(tokenA != tokenB, 'CherryLibrary: IDENTICAL_ADDRESSES');
(tokens0, tokens1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
// require(tokens0 != address(0), 'CherryLibrary: ZERO_ADDRESS');
}
function getRestY(uint a, uint b, uint c, uint d) internal pure returns (uint y) {
// const y = (997 * 997 * c * b - 1000 * 1000 * a * d) / (997 * 1000 * a + 997 * 997 * c);
uint dividedBy = uint(997).mul(1000).mul(a).add(uint(997).mul(997).mul(c));
uint subFrom = uint(997).mul(997).mul(c).mul(b);
uint subBy = uint(1000).mul(1000).mul(a).mul(d);
if (subFrom > subBy) {
y = subFrom.sub(subBy).div(dividedBy);
// 这个时候取一半作为利润点吧。
y = y.div(2);
}
else {
y = 0;
}
// uint rest = 997yc / (1000d + 997y) - 1000ay / 997(b - y) + 1;
// z = uint(997).mul(y).mul(c).div(uint(1000).mul(d).add(uint(997).mul(y)));
// x = uint(1000).mul(a).mul(y).div(uint(997).mul(uint(b).sub(y)));
}
// function uint2str(uint256 _i) internal pure returns (string memory str)
// {
// if (_i == 0) {
// return "0";
// }
// uint256 j = _i;
// uint256 length;
// while (j != 0) {
// length++;
// j /= 10;
// }
// bytes memory bstr = new bytes(length);
// uint256 k = length;
// j = _i;
// while (j != 0) {
// bstr[--k] = bytes1(uint8(48 + j % 10));
// j /= 10;
// }
// str = string(bstr);
// }
function swapCallback(address sender, uint amount0, uint amount1, bytes calldata data)
internal {
// address pairFrom = 0xB742e08FF92178B9c45e342b7D4235E126300777; // gas saving
uint _y = gy; // gas saving
bool reverse = false;
bytes1 data0 = data[0];
if (data0 == bytes1('1')) {
reverse = true;
}
else if (data0 == bytes1('0')) {
reverse = false;
}
// require(data.length == 0, uint2str(data.length));
// require(data.length != 0, uint2str(data.length));
bytes calldata bytesPairFrom = data[1:21];
address pairFrom = bytesToAddress(bytesPairFrom);
bytes calldata bytesInput = data[21:41];
address input = bytesToAddress(bytesInput);
// 回调第1步,将闪电借的币种给第1池子。
{
bool transfered = IERC20(input).transfer(pairFrom, uint(gx).add(1));
require(transfered, 'swapCallback 1');
// // bytes4(keccak256(bytes('transfer(address,uint256)')));
// // address(input).call(abi.encodeWithSelector(0xa9059cbb, pairFrom, x));
// (bool success, bytes memory data1) = address(input).call(abi.encodeWithSelector(0xa9059cbb, pairFrom, x));
// require(success && (data1.length == 0 || abi.decode(data1, (bool))), 'swapCallback 1');
}
// 回调第2步,从第1个池子拿钱还给2
{
// (uint amount0OutB, uint amount1OutB) = (token0 == input) ? (uint(0), uint(_y)) : (uint(_y), uint(0));
(uint amount0OutB, uint amount1OutB) = reverse ? (uint(_y), uint(0)) : (uint(0), uint(_y));
// 回调第三部,从另一个池子拿钱
V5IICherryPair(pairFrom).swap(amount0OutB, amount1OutB, msg.sender, new bytes(0));
}
}
function lottoswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function ogeeswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function flyCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function xfswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function joinswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function hubDaoCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function QkswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function YouSwapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function complusV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function swapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function DepthswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function hdexV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function LavaSwapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function butterCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function makiswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function MdisCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function elkCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function PHOswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function hswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function kisSwapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function myswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function PippiCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function KswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function islandCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function jwapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function goswapCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function UnoxCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
function cherryCall(address sender, uint amount0, uint amount1, bytes calldata data)
external
{
swapCallback(sender, amount0, amount1, data);
}
// Copies 'len' bytes from 'self' into a new 'bytes memory', starting at index '0'.
// Returns the newly created 'bytes memory'
// The returned bytes will be of length 'len'.
function toBytes(bytes32 self, uint8 len) internal pure returns (bytes memory bts) {
require(len <= 32);
bts = new bytes(len);
// Even though the bytes will allocate a full word, we don't want
// any potential garbage bytes in there.
uint data = uint(self) & ~uint(0) << (32 - len)*8;
assembly {
mstore(add(bts, /*BYTES_HEADER_SIZE*/32), data)
}
}
function addressToBytes(address self) internal pure returns (bytes memory bts) {
bts = toBytes(bytes32(uint(self) << 96), 20);
}
function bytesToAddress(bytes memory _bytes) internal pure returns (address) {
address tempAddress;
assembly {
tempAddress := div(mload(add(add(_bytes, 0x20), 0)), 0x1000000000000000000000000)
}
return tempAddress;
}
function concat(bytes memory a, bytes memory b)
internal pure returns (bytes memory) {
return abi.encodePacked(a, b);
}
// function uintToBytes0(uint _num) public returns (bytes memory _ret) {
// assembly {
// _ret := mload(0x10)
// mstore(_ret, 0x20)
// mstore(add(_ret, 0x20), _num)
// }
// }
function buildSwapBytes(bool reverse, address pairFrom, address input, uint x, uint y) internal returns (bytes memory) {
bytes memory bytesReverse = new bytes(1);
bytesReverse[0] = reverse ? bytes1('1') : bytes1('0');
bytes memory bytesPairFrom = addressToBytes(pairFrom);
bytes memory bytesInput = addressToBytes(input);
bytes memory allBytes = concat(concat(bytesReverse, bytesPairFrom), bytesInput);
return allBytes;
}
function checkAndMint(bool reverse, uint minRest, address input, address pairFrom, address pairTo) internal returns (bool) {
uint112 a;
uint112 b;
uint112 c;
uint112 d;
if (reverse) {
(b, a,) = V5IICherryPair(pairFrom).getReserves();
(d, c,) = V5IICherryPair(pairTo).getReserves();
}
else {
(a, b,) = V5IICherryPair(pairFrom).getReserves();
(c, d,) = V5IICherryPair(pairTo).getReserves();
}
uint y = getRestY(uint(a), uint(b), uint(c), uint(d));
if (y <= 0) {
return false;
}
uint x = getAmountIn(y, a, b);
uint z = getAmountOut(y, d, c);
if (z > x) {
if (z.sub(x) > minRest) {
bytes memory databytes = buildSwapBytes(reverse, pairFrom, input, x, y);
(uint amount0Out, uint amount1Out) = reverse ? (uint(0), uint(z)) : (uint(z), uint(0)) ;
// bytes memory databytes = new bytes(1);
// databytes[0] = reverse ? bytes1('1') : bytes1('0');
// // bytes allBytes = databytes = bytesConcat(databytes, )
// todo: 将信息存储到回调函数,避免读取内存消耗gas。但是这个后期再弄看看。
// bytes memory data = bytes32(uint256(uint160(addr)) << 96);
// 第一步,从第二个池子里面闪电借出来。
// require(amount0Out > 0 || amount1Out > 0, 'need amount0Out or amount1Out > 0');
gx = x;
gy = y;
// 这里应该加锁的。还得判断是否成功,不然reverse,但是谁会乱搞?
V5IICherryPair(pairTo).swap(amount0Out, amount1Out, address(this), databytes);
// pairFrom = 0xB742e08FF92178B9c45e342b7D4235E126300777;
return true;
}
}
return false;
}
function mint(bool reverse, uint minRest, address input, address[] calldata pairs) external {
uint allLength = pairs.length;
// uint mintenTime = 0;
for (uint i = 0;i < allLength - 1; i++) {
address pairFrom = pairs[i];
address pairTo = pairs[i + 1];
bool minted = checkAndMint(reverse, minRest, input, pairFrom, pairTo);
// // 看看反过来有没有差价
if (!minted) {
minted = checkAndMint(reverse, minRest, input, pairTo, pairFrom);
}
// 已经参与过了,调出循环
if (minted) {
i = allLength;
}
// 如果已经有差价了,那就重新进行计算。拿到最大的利润
// if (minted) {
// i = 0;
// minted = false;
// mintenTime += 1;
// }
// // todo 生产需要去掉
// uint balance = IERC20(input).balanceOf(address(this));
// if (balance > 0) {
// address owner = 0xB742e08FF92178B9c45e342b7D4235E126300777;
// IERC20(input).transfer(owner, balance);
// }
}
// gmintenTime = mintenTime;
}
// 不要判断,直接进行钱的输出,但是只输出到 god
function transferAll(address token) external {
address owner = 0xB742e08FF92178B9c45e342b7D4235E126300777;
IERC20(token).transfer(owner, IERC20(token).balanceOf(address(this)));
}
// 不要判断,直接进行钱的输出,但是只输出到 god
function transfer(address token, uint256 amount) external {
address owner = 0xB742e08FF92178B9c45e342b7D4235E126300777;
IERC20(token).transfer(owner, amount);
// // bytes4(keccak256(bytes('transfer(address,uint256)')));
// token.call(abi.encodeWithSelector(0xa9059cbb, owner, amount));
// (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, owner, amount));
// require(success && (data.length == 0 || abi.decode(data, (bool))), 'send1: TRANSFER_FAILED');
}
// 不要判断,直接进行钱的输出,但是只输出到 god
function send() external {
address payable owner = 0xB742e08FF92178B9c45e342b7D4235E126300777;
owner.transfer(address(this).balance);
}
receive() external payable {
address payable owner = 0xB742e08FF92178B9c45e342b7D4235E126300777;
owner.transfer(address(this).balance);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment