Summary
The setHousePrice function lacks price validation mechanisms, allowing arbitrary price values to be set without any bounds checking or rate limiting.
Proof of Concept
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("RAACHousePrices Price Validation", function () {
let prices;
let owner;
let oracle;
beforeEach(async function () {
[owner, oracle] = await ethers.getSigners();
const RAACHousePrices = await ethers.getContractFactory("RAACHousePrices");
prices = await RAACHousePrices.deploy(owner.address);
await prices.deployed();
await prices.connect(owner).setOracle(oracle.address);
});
it("allows setting extreme prices", async function () {
const extremePrice = ethers.utils.parseEther("999999999999999999");
await prices.connect(oracle).setHousePrice(1, extremePrice);
await prices.connect(oracle).setHousePrice(2, 0);
const [price1] = await prices.getLatestPrice(1);
const [price2] = await prices.getLatestPrice(2);
expect(price1).to.equal(extremePrice);
expect(price2).to.equal(0);
});
it("allows extreme price changes", async function () {
await prices.connect(oracle).setHousePrice(1, ethers.utils.parseEther("100000"));
await prices.connect(oracle).setHousePrice(1, ethers.utils.parseEther("100000000"));
});
});
Impact
Severity: Medium
Malicious or compromised oracle can set arbitrary prices
No protection against flash crashes or price spikes
Zero prices can break calculations in dependent contracts
No rate limiting between updates
Recommended Fix
contract RAACHousePrices is Ownable {
uint256 public constant MAX_PRICE = 1_000_000_000 * 1e18;
uint256 public constant MIN_PRICE = 1000 * 1e18;
uint256 public constant MAX_PRICE_CHANGE_PERCENTAGE = 20;
uint256 public constant MIN_UPDATE_INTERVAL = 1 hours;
mapping(uint256 => uint256) public lastTokenUpdateTime;
function setHousePrice(uint256 _tokenId, uint256 _amount) external onlyOracle {
require(_amount >= MIN_PRICE, "Price too low");
require(_amount <= MAX_PRICE, "Price too high");
require(
block.timestamp >= lastTokenUpdateTime[_tokenId] + MIN_UPDATE_INTERVAL,
"Update too frequent"
);
uint256 oldPrice = tokenToHousePrice[_tokenId];
if (oldPrice > 0) {
uint256 changePercentage = _amount > oldPrice
? ((_amount - oldPrice) * 100) / oldPrice
: ((oldPrice - _amount) * 100) / oldPrice;
require(
changePercentage <= MAX_PRICE_CHANGE_PERCENTAGE,
"Price change too large"
);
}
tokenToHousePrice[_tokenId] = _amount;
lastTokenUpdateTime[_tokenId] = block.timestamp;
lastUpdateTimestamp = block.timestamp;
emit PriceUpdated(_tokenId, _amount);
}
}
Tools Used