闪电贷套利合约和JS示例

简介

闪电贷是一种关于 DeFi无抵押贷款的新思路,所有操作都在一笔交易(一个区块)中完成,它允许借款人无需抵押资产即可实现借贷(但需支付额外较少费用)。因为代码保证在一定时间内(以太坊大约是13秒)偿还借款,如果资金没有返还,那么交易会被还原,即撤消之前执行的所有操作,从而确保协议和资金的安全。

套利

在两个不同的平台之间,如果同样token的池子存在差价,那么就可以进行搬砖套利。

而闪电贷可以先借再还,最终实现近乎0成本的套利行为

套利合约

这是在 uniswapv2 和 oneswap 之间套利的合约代码,

pragma solidity 0.6.12;
 
import './libraries/UniswapV2Library.sol';
import './interfaces/IUniswapV2Pair.sol';
import './interfaces/IOneswapPair.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IOneSwapFactory.sol';
import './interfaces/IWETH.sol';
 
struct PairInfo{
    address token0;
    address token1;
    address token0ForOneswap;
    address token1ForOneswap;
    bool token0IsStock;
    bool isOnlySwap;
 
}
contract FlashSwap {
    address public immutable uniswapFactory;  // uniswap 工厂合约地址
    address public immutable oneswapFactory;  // oneswap 工厂合约地址
    address public immutable weth;
 
    event AmountReturn(address,address,uint256);
 
    constructor(address _factory,  address _oneswapFactory, address _weth) public {
        uniswapFactory = _factory;
        oneswapFactory = _oneswapFactory;
        weth = _weth;
    }
 
    receive() external payable { }
 
    function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external{
 
        PairInfo memory pairInfo;
        pairInfo.token0 = IUniswapV2Pair(msg.sender).token0();
        pairInfo.token1 = IUniswapV2Pair(msg.sender).token1();
        pairInfo.token0ForOneswap = (pairInfo.token0 == weth) ? address(0) : pairInfo.token0;
        pairInfo.token1ForOneswap = (pairInfo.token1 == weth) ? address(0) : pairInfo.token1;
 
        address uniswapPair = IUniswapV2Factory(uniswapFactory).getPair(pairInfo.token0,pairInfo.token1);
        {
            // 该调用必须由uniswap pair发起
            assert(msg.sender == uniswapPair);
            require(amount0 == 0 || amount1 == 0,'Either amount0 or amount1 should be zero'); 
        }
 
        address pairAddress;
        {
            // data参数用来帮助合约寻找用户套利的oneswap pair目标
            (pairInfo.token0IsStock, pairInfo.isOnlySwap) = abi.decode(data,(bool,bool));
            address stock = pairInfo.token0IsStock ? pairInfo.token0ForOneswap: pairInfo.token1ForOneswap;
            address money = pairInfo.token0IsStock ? pairInfo.token1ForOneswap: pairInfo.token0ForOneswap;
            pairAddress = IOneSwapFactory(oneswapFactory).tokensToPair(stock,money,pairInfo.isOnlySwap);
            require(pairAddress != address(0), 'OneSwap Pair does not exist!');
        }
 
        IOneSwapPair pair = IOneSwapPair(pairAddress);
        (uint reserve0,uint reserve1,) = IUniswapV2Pair(uniswapPair).getReserves();
 
        if (amount0 > 0) {
            // 如果借出的是token0,则在oneswap pair中添加市价单,将amount0数量的token0卖出,
            // 并计算需要返还给uniswap pair的token1的最小数目
            // 如果通过在oneswap pair出售token0获得的token1的数量大于以上计算的最小数目,
            // 则将向uniswap pair返还该最小数目的token1,并将剩余数量的token1转给用户
            _safeTransferWETHToETH(pairInfo.token0, pairAddress, amount0);
            uint amountReceived = pair.addMarketOrder(pairInfo.token0ForOneswap,address(this),uint112(amount0));
            uint amountRequired = UniswapV2Library.getAmountIn(amount0, reserve1, reserve0);
            require(amountReceived > amountRequired,'No profit to earn');
            _safeTransferETHToWETH(pairInfo.token1,msg.sender,amountRequired);
            _safeTransfer(pairInfo.token1, sender,amountReceived - amountRequired);
            emit AmountReturn(pairInfo.token1, sender, amountReceived - amountRequired);
        } else {
            // 如果借出的是token1,则在oneswap pair中添加市价单,将amount1数量的token1卖出,
            // 并计算需要返还给uniswap pair的token0的最小数目
            // 如果通过在oneswap pair出售token1获得的token0的数量大于以上计算的最小数目,
            // 则将向uniswap pair返还该最小数目的token0,并将剩余数量的token0转给用户
            _safeTransferWETHToETH(pairInfo.token1, pairAddress, amount1);
            uint amountReceived = pair.addMarketOrder(pairInfo.token1ForOneswap,address(this),uint112(amount1));
            uint amountRequired = UniswapV2Library.getAmountIn(amount1, reserve0, reserve1);
            require(amountReceived > amountRequired,'No profit to earn');
            _safeTransferETHToWETH(pairInfo.token0,msg.sender,amountRequired);
            _safeTransfer(pairInfo.token0, sender, amountReceived - amountRequired);
            emit AmountReturn(pairInfo.token0, sender, amountReceived - amountRequired);
 
        }
    }
    function _safeTransferWETHToETH(address token, address to, uint amount) internal {
        if (token == weth){
            IWETH(weth).withdraw(amount);
            _safeTransferETH(to,amount);
        }else{
            require(IERC20(token).transfer(to,amount),'ERC20 transfer failed');
        }
    }
    function _safeTransferETHToWETH(address token, address to, uint amount) internal {
        if (token == weth){
            IWETH(weth).deposit{value:amount}();
            require(IWETH(weth).transfer(to, amount),'WETH transfer failed');
        }else{
            require(IERC20(token).transfer(to,amount),'ERC20 transfer failed');
        }
    }
    function _safeTransfer(address token, address to, uint amount) internal {
        if (token == weth) _safeTransferETH(to, amount);
        else require(IERC20(token).transfer(to,amount),'ERC20 transfer failed');
    }
    function _safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'ETH transfer failed');
    }
}

JS调用

注意:

1 普通交易不能调用 Pair->swap() 函数,因为转入和交换没法一次完成

2 swap() 的两个 amountOut 参数需要通过对两个 token address 进行排序,才能确定

var ethers = require('ethers');
var web3 = require('web3');
var abi = require('./uniswap.abi.json');
 
const WALLET_PRIVATE_KEY = 'xxxxx';
const UNISWAP_PAIR_ADDRESS = 'xxxx';
const ARBITRAGE_CONTRACT_ADDRESS = 'xxxx';
 
async function flashSwap() {
  const provider = ethers.getDefaultProvider('rinkeby', { infura: 'https://rinkeby.infura.io/v3/xxxxxx' });
  const account = new ethers.Wallet(WALLET_PRIVATE_KEY).connect(provider);
 
  const Pair = new ethers.Contract(UNISWAP_PAIR_ADDRESS, abi, account);
  const tx = await Pair.swap(
    10,
    0,
    ARBITRAGE_CONTRACT_ADDRESS,
    web3.eth.abi.encodeParameters(['bool', 'bool'], [true, false]),
    { gasLimit: 8000000, value: 0 },
  );
  console.log('hash:', tx.hash);
 
  const receipt = await tx.wait();
  console.log('block Number:', receipt.blockNumber);
}
 
flashSwap();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值