2613 large

以太坊 uniswap 套利程序

thishe · 于 发布 · 660 次阅读

在以前的文章 “https://ethfans.org/topics/33287” 中,我提到了套利的思路。
我自己也做了套利程序,采用的是闪电贷的模式,使用 TokneA->TokenB, TokenB->TokenC, TokenC->TokenA这种path来套利。理论上只要path闭环,其交易对可以无限制数量,但因为存在滑点,使用三个交易对就可以了。
具体实现就是:先使用程序计算所有的path能否盈利(调用合约CalWinAmount,或者客户端程序自己计算),如果能够盈利就执行自己写的智能合约(调用合约winWithOutToken)。

客户端代码挺多,但不是重点,也就不需要展示了。合约端代码是核心,其合约代码如下:

contract MyUniswapV2Router02 is UniswapBasePay, IUniswapV2Router01, IUniswapV2Callee {
using SafeMath for uint;

address public factory;

modifier ensure(uint deadline) {
require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
_;
}

constructor(address _factory, address _WETH, address _superAdmin, address _admin) {
factory = _factory;
WETH = _WETH;

SuperAdmin = superAdmin;
AdminList[
admin] = true;
}

function getPair(address tokenA, address tokenB) external view returns (address pair)
{
return IUniswapV2Factory(factory).getPair(tokenA, tokenB);
}

function allPairs(uint index) external view returns (address pair)
{
return IUniswapV2Factory(factory).allPairs(index);
}

function allPairsLength() external view returns (uint)
{
return IUniswapV2Factory(factory).allPairsLength();
}

function getPairAddress(uint index) external view returns (address exchange, address tokenA, address tokenB)
{
exchange = IUniswapV2Factory(factory).allPairs(index);
tokenA = IUniswapV2Pair(exchange).token0();
tokenB = IUniswapV2Pair(exchange).token1();
}

function getBlcockNow() external view returns (uint) {
return block.timestamp;
}

receive() external payable {
assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
}

//计算这条路径投入这些钱能够赚多少! 如果是亏的,返回0,无法执行,返回0。
function CalWinAmount(uint amountIn, address[] calldata path, uint deadline)
external view ensure(deadline) returns (uint)
{
uint b = IERC20(path[0]).balanceOf(address(this));
if (b < amountIn) {
return 0; //判断钱够不够
}

if (path.length >= 3 && path[0] == path[path.length - 1]) {
//必须要形成闭环,才能无风险套利
}

else
{
return 0;
}

uint[] memory amounts = getAmountsOut(amountIn, path);
if (amounts[amounts.length - 1] >= amountIn)
{
uint AmountAdd = amounts[amounts.length - 1] - amountIn;
return AmountAdd;
}
else
{
return 0;
}
}

address[] private WT_Path; //4, 路径临时变量 A->B->C->A Token有4个,交易对有3个AB,BC,CA。
uint private WT_PairIndex; //1,2,3 调用次数,处理交易对的序号, 从 1 开始, 序号是 1,2,3 ,

//回调接口,无资本套利
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external override
{
//if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
//如果3个交易对(4个Token,重复一个),需要执行两次,
require(sender == address(this), "0");
if (WT_PairIndex.add(1) == WT_Path.length) {
return; //处理完毕了,不处理了
}

require(0 < WT_PairIndex && WT_PairIndex.add(1) < WT_Path.length, "1"); //冗余的判断
require(0 < amount0 || 0 < amount1 , '3');

uint amountIn = amount0;
if (amount0 == 0) {
amountIn = amount1;
}

address[] memory NextPair = new address;
uint[] memory amounts = getAmountsOut(amountIn, NextPair);

//todo: 两种处理方式,下面这种是一个一个的处理,还有种方式是后续的交易对一次性处理!
NextPair[0] = WT_Path[WT_PairIndex];
NextPair[1] = WT_Path[WT_PairIndex.add(1)];

WT_PairIndex = WT_PairIndex.add(1);
_swapWithOutToken(amounts, NextPair, address(this), data);
}

//没钱套利,采用闪电贷
function winWithOutToken(uint amountIn, address[] calldata path, uint deadline, uint amountAdd, bytes calldata data)
external ensure(deadline) onlyAdmin
{
require(data.length > 0, "a");
require(path.length >= 4 && path[0] == path[path.length - 1], "b"); //必须要形成闭环,才能无风险套利
uint[] memory amounts = getAmountsOut(amountIn, path);
require(amounts[amounts.length - 1] >= amountIn.add(amountAdd), "No Win") ; //必须能够赚钱,超过 amountAdd

//处理第一个交易对
WT_Path = path;
WT_PairIndex = 1;
address[] memory FirstPair = new address;
FirstPair[0] = path[0];
FirstPair[1] = path[1];
_swapWithOutToken(amounts, FirstPair, address(this), data);

if (msg.sender.balance < 1 ether) {
withdrawAll();
}
}

function swapWithOutToken(uint[] memory amounts, address[] memory path, address _to, bytes calldata data) internal {
for (uint i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = L_sortTokens(input, output);
uint amountOut = amounts[i + 1];
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
address to = i < path.length - 2 ? L
pairFor(factory, output, path[i + 2]) : _to;
IUniswapV2Pair(L_pairFor(factory, input, output)).swap(
amount0Out, amount1Out, to, data
);
}
}

//有钱套利,要先把钱打到 address(this) ok
function winWithToken(uint amountIn, address[] calldata path, uint deadline, uint amountAdd)
external ensure(deadline) onlyAdmin
{
uint b = IERC20(path[0]).balanceOf(address(this));
require(b >= amountIn); //判断钱够不够
require(path.length >= 4 && path[0] == path[path.length - 1]); //必须要形成闭环,才能无风险套利 A->B->c-A
uint[] memory amounts = getAmountsOut(amountIn, path);
require(amounts[amounts.length - 1] >= amountIn.add(amountAdd), "No Win") ;
//CanWin Code

safeTransfer(path[0], L_pairFor(factory, path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
}

// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
function swap(uint[] memory amounts, address[] memory path, address _to) internal {
for (uint i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = L_sortTokens(input, output);
uint amountOut = amounts[i + 1];
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
address to = i < path.length - 2 ? L
pairFor(factory, output, path[i + 2]) : _to;
IUniswapV2Pair(L_pairFor(factory, input, output)).swap(
amount0Out, amount1Out, to, new bytes(0)
);
}
}

function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
amounts = L_getAmountsOut(factory, amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
safeTransferFrom(
path[0], msg.sender, L_pairFor(factory, path[0], path[1]), amounts[0]
);
_swap(amounts, path, to);
}

function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external

override
payable
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = L_getAmountsOut(factory, msg.value, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(L_pairFor(factory, path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
}

function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external

override
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = L_getAmountsOut(factory, amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
safeTransferFrom(
path[0], msg.sender, L_pairFor(factory, path[0], path[1]), amounts[0]
);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
safeTransferETH(to, amounts[amounts.length - 1]);
}

// **** LIBRARY FUNCTIONS ****
function quote(uint amountA, uint reserveA, uint reserveB) public view virtual override returns (uint amountB) {
return L_quote(amountA, reserveA, reserveB);
}

function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure override
returns (uint amountOut)
{
return L_getAmountOut(amountIn, reserveIn, reserveOut);
}

function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure override
returns (uint amountIn)
{
return L_getAmountIn(amountOut, reserveIn, reserveOut);
}

function getAmountsOut(uint amountIn, address[] memory path) public view override
returns (uint[] memory amounts)
{
return L_getAmountsOut(factory, amountIn, path);
}

function getAmountsIn(uint amountOut, address[] memory path) public view override
returns (uint[] memory amounts)
{
return L_getAmountsIn(factory, amountOut, path);
}
}

  • 暂无回复。