Summary
The function RToken::transfer() takes lending's normalized income into account when updating balances. However, the implementation is incorrect that can cause the amount transferred less than expected
Vulnerability Details
The function transfer() indeed scales the transferred amount by the normalized income twice, such that it divides twice the normalized income.
This can cause the actual value sent less than expected.
function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
@> uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
return super.transfer(recipient, scaledAmount);
}
function _update(address from, address to, uint256 amount) internal override {
@> uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
super._update(from, to, scaledAmount);
}
PoC
Add the test to file test/unit/core/pools/StabilityPool/StabilityPool.test.js
it.only("transfer() updates balance incorrectly", async function() {
const tokenId = 1;
const price = ethers.parseEther("100");
await raacHousePrices.setOracle(owner.address);
await raacHousePrices.setHousePrice(tokenId, price);
await crvusd.mint(user1.address, price)
await crvusd.connect(user1).approve(raacNFT.target, price)
await raacNFT.connect(user1).mint(tokenId, price);
await raacNFT.connect(user1).approve(lendingPool.target, tokenId);
await lendingPool.connect(user1).depositNFT(tokenId);
const borrowAmount = ethers.parseEther("50");
await lendingPool.connect(user1).borrow(borrowAmount);
await ethers.provider.send("evm_increaseTime", [86400]);
await ethers.provider.send("evm_mine");
await lendingPool.connect(user1).updateState();
let balanceBefore = await rToken.balanceOf(user1.address);
const amount = ethers.parseEther("10")
await rToken.connect(user2).transfer(user1.address, amount);
let balanceAfter = await rToken.balanceOf(user1.address);
let diff = balanceAfter - balanceBefore
console.log(`actual diff\t ${diff}\nexpected diff\t ${amount}`)
})
Run the test and it shows
StabilityPool
Core Functionality
Deposits
actual diff 9999988284548955336
expected diff 10000000000000000000
✔ transfer() updates balance incorrectly
It means that the actual value transferred is lower than the expected amount
Impact
Tools Used
Manual
Recommendations
function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
- uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
- return super.transfer(recipient, scaledAmount);
+ return super.transfer(recipient, amount);
}