Core Contracts

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

Missing Token-Specific Update Timestamps

Summary

The contract's documentation indicates token-specific timestamp tracking, but implementation uses a single global timestamp, leading to inaccurate price update history.

Vulnerability Details

The contract comments suggest individual token timestamps:

/**
* Updates timestamp for each token individually
*/
function setHousePrice(uint256 _tokenId, uint256 _amount) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
lastUpdateTimestamp = block.timestamp; // Only updates global timestamp
emit PriceUpdated(_tokenId, _amount);
}

Impact

Severity: Low

  • Incorrect timestamp tracking for individual tokens

  • Inability to implement proper staleness checks

  • Misleading documentation vs. implementation

Proof of Concept

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("RAACHousePrices Timestamp Issue", function () {
let prices, owner, oracle;
beforeEach(async () => {
[owner, oracle] = await ethers.getSigners();
const RAACHousePrices = await ethers.getContractFactory("RAACHousePrices");
prices = await RAACHousePrices.deploy(owner.address);
await prices.connect(owner).setOracle(oracle.address);
});
it("demonstrates incorrect timestamp tracking", async () => {
// Update token 1
await prices.connect(oracle).setHousePrice(1, ethers.utils.parseEther("100"));
const firstUpdateTime = (await ethers.provider.getBlock('latest')).timestamp;
// Wait 1 hour
await ethers.provider.send("evm_increaseTime", [3600]);
await ethers.provider.send("evm_mine");
// Update token 2
await prices.connect(oracle).setHousePrice(2, ethers.utils.parseEther("200"));
const secondUpdateTime = (await ethers.provider.getBlock('latest')).timestamp;
// Check timestamps
const [, token1Time] = await prices.getLatestPrice(1);
const [, token2Time] = await prices.getLatestPrice(2);
// Both tokens show the latest update time instead of their individual times
expect(token1Time).to.equal(secondUpdateTime);
expect(token2Time).to.equal(secondUpdateTime);
expect(token1Time).to.not.equal(firstUpdateTime); // Should be equal if tracked individually
});
});

Recommended Fix

contract RAACHousePrices is Ownable {
// Add token-specific timestamp mapping
mapping(uint256 => uint256) public tokenUpdateTimestamps;
event PriceUpdated(uint256 indexed tokenId, uint256 newPrice, uint256 timestamp);
function setHousePrice(uint256 _tokenId, uint256 _amount) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
tokenUpdateTimestamps[_tokenId] = block.timestamp;
emit PriceUpdated(_tokenId, _amount, block.timestamp);
}
function getLatestPrice(uint256 _tokenId) external view returns (uint256, uint256) {
return (tokenToHousePrice[_tokenId], tokenUpdateTimestamps[_tokenId]);
}
function isPriceStale(uint256 _tokenId, uint256 _maxAge) external view returns (bool) {
return block.timestamp - tokenUpdateTimestamps[_tokenId] > _maxAge;
}
}

Tools Used

  • Manual code review

  • Hardhat testing framework

Updates

Lead Judging Commences

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

RAACHousePrices uses a single global lastUpdateTimestamp for all NFTs instead of per-token tracking, causing misleading price freshness data

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

RAACHousePrices uses a single global lastUpdateTimestamp for all NFTs instead of per-token tracking, causing misleading price freshness data

Support

FAQs

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

Give us feedback!