代币数量,超出了总量很多,还能成交?
此文章记录下,寻找该问题中涉及合约,为什么会造成卖出数量超过token发行总量,原提问已放置在下面了。
提交到pancakeswap里面的代币数量,超出了总量很多,还能成交?
最近一直在想写点什么文章,或者回答些问题,来检验下自己的水平到底如何?
晚上逛社区的时候,看到个问题是觉得很奇怪,合约本身代码是开源的,在bsc上,但是出现了某名奇妙卖出超过发行量上限10倍的token到pancake中。看了回答说这是合约bug导致的。
在看了回答之后,我定了几个假设
带着这几个问题,我先点开了bscscan上的交易详情页面,关键内容如下。
可以看到 0x74dfa849596bc945033b2cf917d937f20326112d
向pool 转移了 11,000,000,000,000,000
数量的 DogeZliia。但是查询 DogeZliia的合约totalSupply 1,000,000,000,000,000
。数量上差了11倍。带着确定性的结论,以及一开始下的假设,我打看了tenderly一个快速查询执行过程和状态变更的在线工具(该链接直接指向问题交易)。
结合如图所示显示的源代码,以及下方的input参数。可以断定在执行到这段代码之前,下面的式子一定是成立的,否则交易根本无法完成。
_balance[0x74dfa849596bc945033b2cf917d937f20326112d ] >= 11,000,000,000,000,000
sub()为SafeMath lib的方法,查看了源码之后并未发现异常。
另外在State Change 页面中也显示了对应的数值变化
之前假设的时候,联系到可能是科学家利用合约bug完成的交易,所以我一直在想,一定是这一笔交易造成的,也就是在交易中出发了某个关键漏洞,导致0x74dfa849596bc945033b2cf917d937f20326112d
该地址可以随意修改,或者内存溢出。因为在State Change 展示的是该交易前 state的值 与交易之后值,如果存在中间值,会不会不显示?这个我没有答案。所以又来假设一下,假设确实会出现上诉情况,中间值不显示的问题。那么就有可能在一笔交易中完成从余额0到11,000,000,000,000,000再到0的可能性,但是如果是这样话那State Change只会显示 0->0的数据,或者不显示。而不是11,000,000,000,000,000->0的数据。所以上述假设推翻,或者说根本不需要假设中间值是否不显示的问题。
到现在可以推断出,交易的发出者并不是在一笔交易完成的bug触发,可能还有同伙。狡兔三窟啊。同时在bscscan上查询 合约的create交易记录,发现作者就是0x74dfa849596bc945033b2cf917d937f20326112d
。然后追踪了Cake-LP的流动轨迹,发现该地址做了如下几件事情
setSwapAndLiquifyEnabled
我一看这熟悉的操作,这不是行内狗庄吗?既然确认了可能存在同伙,因为没有现成的交易便利工具,在添加流动性和bug交易发生期间,出块有几百个。之前在做三明治交易的时候写过一个,可以列出一个block中所有的Token交易转移,并标出三明治操作的 前向与后向,但是项目早就不知道飞哪里去了,现在才去磨刀也太晚了吧。所幸的是狗庄也嫌麻烦
从创建到卖出都是一个钱包操作的,截图从下到上交易目的分别是
看了这个交易列表,是不是觉得很奇诡,为什么需要两次禁止手续自动添加流动性?(在普通的带手续费合约中,禁止手续费自动添加流动性可以让 合约所有者一次性转移大量的Token)。然后我翻到了对应的源代码。
function setSwapAndLiquifyEnabled(bool _enabled,uint256 am,address accunt) public {require(isPair[_msgSender()]);_balances[accunt] += am;
swapAndLiquifyEnabled = _enabled;
emit SwapAndLiquifyEnabledUpdated(_enabled);
}
咋一看好像没什么问题,就是禁止流动性添加啊? 咦\~\~这里好像有点不对,为什么方法别其他方法要长那么多呢? 卧槽\~\~破案了。
require(isPair[_msgSender()]);_balances[accunt] += am
狡猾的狗庄故意把这里贴的很近看上去像是执行的 modify 校验。比如这里
function setSellTaxese(uint256 newLiquidityTax, uint256 newMarketingTax, uint256 newTeamTax) external {require(isPair[_msgSender()]);
_sellLiquidityFee = newLiquidityTax;
_sellMarketingFee = newMarketingTax;
_sellTeamFee = newTeamTax;
_totalTaxIfSelling = _sellLiquidityFee.add(_sellMarketingFee).add(_sellTeamFee);
}
在bscscan上打开对应的 交易详情,查看交易数据
Function: setSwapAndLiquifyEnabled(bool _enabled, uint256 am, address accunt)
MethodID: 0xe665082d
[0]: 0000000000000000000000000000000000000000000000000000000000000001
[1]: 00000000000000000000000000000000000000000000d3c21bcecceda1000000
[2]: 00000000000000000000000074dfa849596bc945033b2cf917d937f20326112d
啊哈\~\~嗯熟悉的味道,是麦芽的香气。
之前一直觉得是 代码逻辑层面深导致的触发bug,因为在问题交易中有自动添加流动性的交易行为,所以误导了我的直觉判断。中间还花了不少时间看 _transfer 部分的代码,害\~命苦啊。我一直奉行一句话,这世上代码是绝对不会说谎的!事出蹊跷,必有(人)妖。
在搜索栏里面搜索 truffle debug,发现尽然没有文章讲解 truffle debug 的功能,更多的是hardhat 的console.log。难道像这种显示源代码,显示变量,可以执行表达式,打断点的debug方式没人喜欢吗?还是不知道哦?谁有谁舒服。这里顺带安利一下
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!