SODA合约漏洞分析
soda是一个DEFI借贷的合约,其中的逻辑是这样的:用户超额抵押WETH
然后贷出SOETH
,抵押的WETH
可以挖矿,SOETH
则可以通过uniswap交易或者参与别的活动。
当用户抵押`WETH...
[soda][soda-git]是一个DEFI借贷的合约,其中的逻辑是这样的:用户超额抵押 `WETH`然后贷出 `SOETH`,抵押的 `WETH`可以挖矿,`SOETH`则可以通过uniswap交易或者参与别的活动。 当用户抵押 `WETH`后每日会产生一定的贷款利息,当贷出的 `SOETH`与总利息超过一个阈值时,别人就可以进行清算。清算就是别人把贷和利息还上,然后获得抵押人的抵押物,即 `WETH`。 比如,小明抵押了 `100 WETH`,贷出 `70 SOETH`,每日利息是0.05%,则每日需要付 `70 * 0.05% = 0.035 SOETH`. 假设平仓位是抵押额的0.9,则平仓位为 `100 * 0.9 = 90 SOETH`。 那么当n天后,`70 + n * 0.035 >= 90`时(计算可得 `n >= 572`),别人就可以清算,支付 `90 SOETH`以及5%抵押量的手续费,就可以拿走 `100 WETH`。最终需要支付 `95 SOETH`,获得 `100 WETH`。 我们整理一下思路: 对于借贷: 1. 用户抵押 `WETH`后,可以贷出70%的 `SOETH` 2. 用户的日息为贷出额度的0.05% 3. 用户的平仓位为90%的抵押量 4. 当贷出额度+总利息超过平仓位时,不再产生利息,并且可以被别人清算。 对于清算: 1. 需要还被清算者的贷款额度,即70%的 `SOETH` 2. 需要还清被清算者的利息 3. 需要出抵押值5%的手续费 4. 因此清算发起人,需要支付95%抵押量的 `SOETH`,并获取100%的抵押物 `WETH`,利润率为5% 而soda的问题就在于,清算时,平仓仓位由抵押量的90%,变成了贷出量的90%。也就是说贷出即爆仓。 还是以小明为例: 小明抵押了 `100 WETH`,贷出 `70 SOETH`,理论上小明的平仓位为 `100 * 0.9 = 90 SOETH`,但是由于合约的BUG,清算时,平仓价位变成了 `70 * 0.9 = 63 SOETH`。也就是说,下单后,别人就可以对他的贷进行平仓清算。而清算发起者需要支出的费用由三部分组成:70%的贷、利息、5%的手续费。而在短时间内,利息几乎为0,那么清算者一共只需要出 `75 SOETH`,而获取小明的 `100 WETH`。利润率为25%。 问题代码: ```js function collectDebt(uint256 _loanId) external override { ... // should be 'loanInfo[_loanId].lockedAmount' uint256 maximumLoan = loanInfo[_loanId].amount.mul(loanInfo[_loanId].maximumLTV).div(LTV_BASE); // You can collect only if the user defaults. require(loanTotal >= maximumLoan, "collectDebt: >="); ... } ``` 截止目前为止,依然有4310枚 `WETH`锁在合约内,分别是: - LoanId 16: `480 weth` - LoanId 92: `1100 weth` - LoanId 93: `270 weth` - LoanId 119: `1230 weth` 这些仓位暂时是安全的,因为 `SOETH`的挖出的总量才2156(被销毁了一部分?),流通在uniswap市场上的 `SOETH`只有500多枚。假如要清算 `270 WETH`,则至少要购入 `202 SOETH`,按照uniswap的计价规则,这样会导致 `SOETH`的价格剧烈攀升,而利用bug清算的利润率才25%左右。 目前soda已经更新了合约,但是合约生效需要48小时,所以还是存在一定的风险。 [soda-git]: https://github.com/soda-finance/soda-contracts
soda是一个DEFI借贷的合约,其中的逻辑是这样的:用户超额抵押 WETH
然后贷出 SOETH
,抵押的 WETH
可以挖矿,SOETH
则可以通过uniswap交易或者参与别的活动。
当用户抵押 WETH
后每日会产生一定的贷款利息,当贷出的 SOETH
与总利息超过一个阈值时,别人就可以进行清算。清算就是别人把贷和利息还上,然后获得抵押人的抵押物,即 WETH
。
比如,小明抵押了 100 WETH
,贷出 70 SOETH
,每日利息是0.05%,则每日需要付 70 * 0.05% = 0.035 SOETH
. 假设平仓位是抵押额的0.9,则平仓位为 100 * 0.9 = 90 SOETH
。 那么当n天后,70 + n * 0.035 >= 90
时(计算可得 n >= 572
),别人就可以清算,支付 90 SOETH
以及5%抵押量的手续费,就可以拿走 100 WETH
。最终需要支付 95 SOETH
,获得 100 WETH
。
我们整理一下思路: 对于借贷:
- 用户抵押
WETH
后,可以贷出70%的SOETH
- 用户的日息为贷出额度的0.05%
- 用户的平仓位为90%的抵押量
- 当贷出额度+总利息超过平仓位时,不再产生利息,并且可以被别人清算。
对于清算:
- 需要还被清算者的贷款额度,即70%的
SOETH
- 需要还清被清算者的利息
- 需要出抵押值5%的手续费
- 因此清算发起人,需要支付95%抵押量的
SOETH
,并获取100%的抵押物WETH
,利润率为5%
而soda的问题就在于,清算时,平仓仓位由抵押量的90%,变成了贷出量的90%。也就是说贷出即爆仓。
还是以小明为例: 小明抵押了 100 WETH
,贷出 70 SOETH
,理论上小明的平仓位为 100 * 0.9 = 90 SOETH
,但是由于合约的BUG,清算时,平仓价位变成了 70 * 0.9 = 63 SOETH
。也就是说,下单后,别人就可以对他的贷进行平仓清算。而清算发起者需要支出的费用由三部分组成:70%的贷、利息、5%的手续费。而在短时间内,利息几乎为0,那么清算者一共只需要出 75 SOETH
,而获取小明的 100 WETH
。利润率为25%。
问题代码:
function collectDebt(uint256 _loanId) external override {
...
// should be 'loanInfo[_loanId].lockedAmount'
uint256 maximumLoan = loanInfo[_loanId].amount.mul(loanInfo[_loanId].maximumLTV).div(LTV_BASE);
// You can collect only if the user defaults.
require(loanTotal >= maximumLoan, "collectDebt: >=");
...
}
截止目前为止,依然有4310枚 WETH
锁在合约内,分别是:
- LoanId 16:
480 weth
- LoanId 92:
1100 weth
- LoanId 93:
270 weth
- LoanId 119:
1230 weth
这些仓位暂时是安全的,因为 SOETH
的挖出的总量才2156(被销毁了一部分?),流通在uniswap市场上的 SOETH
只有500多枚。假如要清算 270 WETH
,则至少要购入 202 SOETH
,按照uniswap的计价规则,这样会导致 SOETH
的价格剧烈攀升,而利用bug清算的利润率才25%左右。
目前soda已经更新了合约,但是合约生效需要48小时,所以还是存在一定的风险。
区块链技术网。
- 发表于 2020-09-22 00:11
- 阅读 ( 779 )
- 学分 ( 16 )
- 分类:DApp
评论