Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

RToken transfer() sends less value than expected

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 {
// Scale amount by normalized income for all operations (mint, burn, transfer)
@> 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() {
// @audit PoC transfer() updates balance incorrectly
// borrow from lending pool
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

  • Incorrect balances updates for RToken holders when sending tokens

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);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::transfer and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::transfer and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!