代币交换swap源码分析,同时结合部署和使用进行分析 [ 3 ]
部署和使用博客
https://blog.csdn.net/weixin_43458715/article/details/141711985
swapExactTokensForTokens函数分析
1、 通过getAmountsOut函数获取swap所需要的token数量
从源码可以得出获取输出金额公式:
a
m
o
u
n
t
O
u
t
=
a
m
o
u
n
t
I
n
×
997
×
r
e
s
e
r
v
e
O
u
t
r
e
s
e
r
v
e
I
n
×
1000
+
a
m
o
u
n
t
I
n
×
997
{ amountOut=\frac{amountIn\times997 \times reserveOut }{reserveIn \times1000+amountIn\times997}}
amountOut=reserveIn×1000+amountIn×997amountIn×997×reserveOut
公式来源:
a
m
o
u
n
t
I
n
×
0.997
(
F
e
e
)
×
a
m
o
u
n
t
O
u
t
=
r
e
s
e
r
v
e
I
n
×
r
e
s
e
r
v
e
O
u
t
=
k
amountIn \times 0.997(Fee) \times amountOut=reserveIn \times reserveOut =k
amountIn×0.997(Fee)×amountOut=reserveIn×reserveOut=k
a
m
o
u
n
t
O
u
t
=
90
∗
1
0
18
×
997
×
100
∗
1
0
18
180
∗
1
0
18
×
1000
+
997
×
100
∗
1
0
18
=
8973
∗
1
0
19
2797
amountOut = \frac{90*10^{18} \times 997 \times 100*10^{18} }{180*10^{18} \times 1000+997 \times 100*10^{18}}= \frac{8973*10^{19}}{2797}
amountOut=180∗1018×1000+997×100∗101890∗1018×997×100∗1018=27978973∗1019
a
m
o
u
n
t
O
u
t
=
32080800858062209510
amountOut = 32080800858062209510
amountOut=32080800858062209510
2、将amountIn数量的token转给pair合约,可以理解为用户付款
3、 _swap()函数
3.1 根据输入代币和输出代币的关系,确定具体的输出数量(amount0Out和amount1Out)
a m o u n t 0 O u t = 32080800858062209510 , a m o u n t 1 O u t = 0 amount0Out=32080800858062209510,amount1Out=0 amount0Out=32080800858062209510,amount1Out=0
3.2 swap函数,执行交换操作
将amountOut数量的token从pair转出给调用者,可以理解为用户支付所获的的,其中有0、1两个是为了区分,是token0换token1,还是token1换token0
1. 获取swap完成后的token0和token1的余额
r
e
s
e
r
v
e
0
=
90
∗
1
0
18
,
r
e
s
e
r
v
e
1
=
180
∗
1
0
18
reserve0=90*10^{18},reserve1=180*10^{18}
reserve0=90∗1018,reserve1=180∗1018
a
m
o
u
n
t
0
O
u
t
=
32080800858062209510
,
a
m
o
u
n
t
1
O
u
t
=
0
amount0Out=32080800858062209510,amount1Out=0
amount0Out=32080800858062209510,amount1Out=0
b
a
l
a
n
c
e
0
=
57919199141937790490
,
b
a
l
a
n
c
e
1
=
280
∗
1
0
18
balance0= 57919199141937790490,balance1=280*10^{18}
balance0=57919199141937790490,balance1=280∗1018
2. 计算输入代币的数量
b
a
l
a
n
c
e
0
!
>
r
e
s
e
r
v
e
0
−
a
m
o
u
n
t
0
O
u
t
=
>
a
m
o
u
n
t
0
I
n
=
0
balance0 !> reserve0-amount0Out => amount0In=0
balance0!>reserve0−amount0Out=>amount0In=0
b
a
l
a
n
c
e
1
>
r
e
s
e
r
v
e
1
a
m
o
u
n
t
1
O
u
t
=
>
a
m
o
u
n
t
1
I
n
=
100
∗
1
0
18
balance1 > reserve1amount1Out => amount1In=100*10^{18}
balance1>reserve1amount1Out=>amount1In=100∗1018
3. 计算调整后的余额,代码通过从代币的交易额中扣除0.3%的交易费用来调整,这样做是为了确保费用在池中的表现是一致的。
b
a
l
a
n
c
e
0
A
d
j
u
s
t
e
d
=
b
a
l
a
n
c
e
0
∗
1000
−
a
m
o
u
n
t
0
I
n
∗
3
=
57919199141937790490000
balance0Adjusted = balance0*1000 - amount0In*3=57919199141937790490000
balance0Adjusted=balance0∗1000−amount0In∗3=57919199141937790490000
b
a
l
a
n
c
e
1
A
d
j
u
s
t
e
d
=
b
a
l
a
n
c
e
1
∗
1000
−
a
m
o
u
n
t
1
I
n
∗
3
=
279700
∗
1
0
18
balance1Adjusted = balance1*1000 - amount1In*3=279700*10^{18}
balance1Adjusted=balance1∗1000−amount1In∗3=279700∗1018
不用amountOut,选择amountIn的原因:
假设用户用tokenA类型换tokenB类型
在pair合约中,tokenB所减少的金额已经是扣除手续费后的金额,而tokenA类型增加的金额没有处理手续费,这样手续费就留在了流动池里,因此而奖励了流动性提供者。(可以参考本文最后的TIPS)
4. 保持K值不变: 使用require语句确保交易后的调整余额乘积不小于交易前的储备乘积
K
1
=
b
a
l
a
n
c
e
0
A
d
j
u
s
t
e
d
∗
b
a
l
a
n
c
e
1
A
d
j
u
s
t
e
d
=
16200000000000000000053
∗
1
0
24
K_1=balance0Adjusted*balance1Adjusted=16200000000000000000053*10^{24}
K1=balance0Adjusted∗balance1Adjusted=16200000000000000000053∗1024
K
=
r
e
s
e
r
v
e
0
∗
r
e
s
e
r
v
e
1
=
16200000000000000
∗
1
0
24
K=reserve0*reserve1=16200000000000000*10^{24}
K=reserve0∗reserve1=16200000000000000∗1024
K
1
>
K
K_1>K
K1>K
分析终端输出
swap前owner的
kokoCoin:
1820
∗
1
0
18
1820*10^{18}
1820∗1018
acCoin:
2910
∗
1
0
18
2910*10^{18}
2910∗1018
swap后owner的
kokoCoin:
1820
∗
1
0
18
−
100
∗
1
0
18
(
a
m
o
u
n
t
I
n
)
=
1720
∗
1
0
18
1820*10^{18}-100*10^{18}(amountIn)=1720*10^{18}
1820∗1018−100∗1018(amountIn)=1720∗1018
acCoin:
2910
∗
1
0
18
+
32080800858062209510
(
a
m
o
u
n
t
O
u
t
)
=
2942080800858062209510
2910*10^{18}+32080800858062209510(amountOut)=2942080800858062209510
2910∗1018+32080800858062209510(amountOut)=2942080800858062209510
tip:需要注意一下pair的地址有进行排序,因此pair存储量的前者为acToken,后者为kokoToken
TIPS:
当一个交易发生时,交易中涉及的代币的数量将根据 0.3% 的费率从交易者手中扣除。
这部分手续费直接加到流动性池中对应的代币储备里。这意味着流动性池的大小会随每笔交易逐渐增加。
流动性提供者在加入流动性池时会获得代表其份额的流动性代币(例如 LP 代币)。
随着手续费的累积,流动性池中的资金增加,但流动性代币的总供应量保持不变。
结果是每个流动性代币代表的底层资产(即流动性池中的代币)增多,增加了流动性代币的价值。