पाठ 15 / 25
Oracles, Time and Randomness
Bring off-chain data on chain safely and avoid insecure randomness.
The oracle problem
Smart contracts cannot fetch web data; every node must compute the same result from on-chain data alone. Oracles bring external data, such as asset prices, weather or sports results, on chain. Decentralised oracle networks like Chainlink aggregate data from many independent nodes and publish it in price feeds; consumers should check that answers are fresh (compare updatedAt with the current time against a heartbeat) and positive, and handle feed decimals. Using a single DEX spot price as an oracle is dangerous: attackers can move it within one transaction using flash loans and exploit your contract, a pattern behind many large hacks; use time-weighted averages or robust oracle networks. Randomness is equally tricky: block.timestamp, blockhash and block.prevrandao are visible to, or partly influenced by, validators and other contracts, so they are unsafe for valuable lotteries. Use a verifiable random function (Chainlink VRF) or commit-reveal schemes. Time: block.timestamp is set by validators within protocol limits and suitable for durations of minutes or more, not precise seconds.
Reading a price feed defensively
Check freshness, sign and decimals before using an oracle answer.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
contract PriceConsumer {
AggregatorV3Interface public immutable feed; // e.g. an ETH/USD feed address for your network
uint256 public immutable maxDelay; // e.g. the feed heartbeat plus a margin
error StalePrice(uint256 updatedAt);
error InvalidPrice(int256 answer);
constructor(AggregatorV3Interface feed_, uint256 maxDelay_) {
feed = feed_;
maxDelay = maxDelay_;
}
/// Returns the price scaled to 18 decimals
function price18() public view returns (uint256) {
(, int256 answer, , uint256 updatedAt, ) = feed.latestRoundData();
if (answer <= 0) revert InvalidPrice(answer);
if (block.timestamp - updatedAt > maxDelay) revert StalePrice(updatedAt);
uint8 d = feed.decimals(); // often 8 for USD feeds
return d <= 18 ? uint256(answer) * 10 ** (18 - d) : uint256(answer) / 10 ** (d - 18);
}
function usdValue(uint256 ethAmountWei) external view returns (uint256) {
return (ethAmountWei * price18()) / 1e18; // USD with 18 decimals
}
}Never use spot prices from one pool
If your protocol reads a price directly from a single AMM pool, an attacker can borrow a huge amount, distort the pool, call your contract and repay the loan in one transaction. Use robust oracles and sanity limits.
त्वरित जाँच: Why is block.timestamp unsuitable as a source of randomness for a valuable lottery?
- It is always zero
- It cannot be read in Solidity
- It is predictable and partly influenced by validators, so outcomes can be manipulated
- It changes every second
Answer
It is predictable and partly influenced by validators, so outcomes can be manipulated — On-chain values are public and influenceable; use VRF or commit-reveal.