0%

defi | 闪电贷

这是一个非常有意思的概念。


参考资料



讲解


其实上面的参考资料已经讲的非常好了,我在这里再讲一下,并且说一下上面那些资料中没说的细节。

本闪电贷主要是针对通过 swap 进行借贷。

关于借贷方式请参考

这里我们以 pancakeswap 作为例子。

首先参考 pair 中的一段代码。

PancakePair.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// this low-level function should be called from a contract which performs important safety checks
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'Pancake: INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake: INSUFFICIENT_LIQUIDITY');

uint balance0;
uint balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'Pancake: INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT');
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(2));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(2));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'Pancake: K');
}

_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}

12 和 第 13

if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens

如果 amount0Outamount1Out 大于 0,就会先把币打到账户上。

注意,这里用的方法是

_safeTransfer

即,假设池子中只有 500 BNB ,你不能借 600 BNB

所以,在借的时候,一定要区分好,你借的币是 token0 还是 token1

接着,第 14

if (data.length > 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);

如果 data 的长度大于 0,就会执行

IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);

这个方法。data 是一个 bytes,无所谓是什么,只需要让它的长度大于 0 就好了。

这里要注意 IPancakeCallee(to) 这个 to 是你的合约地址,所以,如果你要是有 pancakeCall 这个方法,这个时候会调用该方法。

所以说,假设你实现了这个方法,那么,进入到你账户执行的时候,由于你之前已经借出了 token ,所以,可以在 pancakeCall 这个方法中继续使用。

其他的 dex 平台原理差不多都是这样子,但是,方法名可能不一样,比如 biswapbisCall

执行 swap

我们借出币是通过 pair 借出的。

我们拿着借出的币,进行 dexswapToken 方法。

可以参考

OK,这里假设你已经兑换了。并且

  • AB 你借出了 B
  • BC 你兑换了 C
  • CA 你兑换了 A

那么,如何归还呢?

归还

对于 AB 来说,如果你借出 B ,你是归还 A 还是 B 呢?

答案是,两个都可以。

如果你借出 B,归还 B,那么,你只是拿着这个 B 执行了执行了一圈然后还回去而已,你借出 50B 还回 50B 就好了。

如果你借出 B ,归还的是 A,那么,相当于你执行了 swapTokenForToken,用 A 去购买 B。只不过,B 是先打给你,然后你后付的 A 而已。但是,这个时候你就要付手续费了。因为,你本质上就是执行了 swapTokenForToken 的操作,所以要收取手续费。

那么,如何归还,归还给谁?

只需要对 ABpair 转对应数量的 A 即可。

参考

1
2
3
4
uint[] memory backamounts = functionContract(backRouter).getAmountsIn(receiveAmountIn, backPaths);
emit Balance(backamounts[0]);
uint endAmount = functionContract(backToken).balanceOf(address(this));
functionContract(backToken).transfer(pairAddress, backamounts[0]);

其中

  • receiveAmountIn
    • 借出 B 的数量
  • backPaths
    • A 兑换 B 的路径
  • backToken
    • A 的合约地址
  • functionContract
    • 接口合约,放置了一些接口函数

其中 getAmountsIn 的方法,就可以知道你借出多少的 B,需要归还多少的 A

上面的就是归还了。

具体细节可以参考一下 pancakeswap 的公开的方法。

下面说一下整体的手续费的计算,以

  • AB 你借出了 B
  • BC 你兑换了 C
  • CA 你兑换了 A

来说,实际上,上面相当于执行了 3swapTokenForToken 方法。也就是会收 3 次手续费。

盈利

后面就把归还后剩余的 A 发送到相应账户就好了。

我将所有的代码放在了

相对于公开的代码,我写的代码可以适应于大部分的场景。

请我喝杯咖啡吧~