构建自定义预言机合约
学习如何在 Solidity 中设计和实现自己的预言机模式,包括请求—响应流程、可信更新者以及在链上验证数据新鲜度。
构建自定义预言机合约 是 CoddyKit 上的免费 Blockchain Smart Contracts with Solidity 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Blockchain Smart Contracts with Solidity 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Blockchain Smart Contracts with Solidity 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Beyond Third-Party Oracles
Chainlink and similar networks cover common feeds, but sometimes you need data nobody else provides, like a proprietary API result. In those cases you build a custom oracle: an off-chain updater plus an on-chain contract that stores and serves the data.
The Two-Party Design
A custom oracle has two sides:
- Off-chain updater: a server that reads the external source and sends transactions.
- On-chain contract: stores the latest value and exposes a read function for other contracts.
A Minimal Storage Oracle
The simplest oracle just lets a trusted address write a value that anyone can read.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleOracle {
address public updater;
uint256 public value;
uint256 public lastUpdated;
constructor() {
updater = msg.sender;
}
function setValue(uint256 newValue) external {
require(msg.sender == updater, 'not updater');
value = newValue;
lastUpdated = block.timestamp;
}
}Guarding the Updater
The single point of trust is the updater address. Protect it with access control and allow it to be rotated safely if a key is compromised.
function transferUpdater(address newUpdater) external {
require(msg.sender == updater, 'not updater');
require(newUpdater != address(0), 'zero address');
updater = newUpdater;
}Checking Data Freshness
Stale data is dangerous. Consumers should reject values older than a threshold using the stored lastUpdated timestamp.
uint256 public constant MAX_AGE = 1 hours;
function getFreshValue() external view returns (uint256) {
require(block.timestamp - lastUpdated <= MAX_AGE, 'stale data');
return value;
}Request-Response Pattern
For on-demand data, a consumer emits a request event. The off-chain updater watches for it and replies with the answer in a callback.
event DataRequested(uint256 indexed requestId, string query);
uint256 private nextId;
function requestData(string calldata query) external returns (uint256) {
uint256 id = nextId++;
emit DataRequested(id, query);
return id;
}Fulfilling a Request
The updater calls back with the result for a given request id. Store it keyed by id so the consumer can read it later.
mapping(uint256 => uint256) public answers;
mapping(uint256 => bool) public fulfilled;
function fulfill(uint256 requestId, uint256 result) external {
require(msg.sender == updater, 'not updater');
answers[requestId] = result;
fulfilled[requestId] = true;
}Reducing Single-Source Risk
One updater is one point of failure. To harden the oracle you can require multiple updaters to agree (an aggregation or median of submissions) before a value is accepted.
mapping(address => bool) public isUpdater;
mapping(address => uint256) public submitted;
address[] public updaters;
function submit(uint256 v) external {
require(isUpdater[msg.sender], 'not updater');
submitted[msg.sender] = v;
}Computing a Median
A median resists a single malicious updater better than an average. Collect submissions, sort them off-chain or with a small on-chain helper, and take the middle value.
function median(uint256[] memory data) internal pure returns (uint256) {
// assume data already sorted
uint256 n = data.length;
if (n % 2 == 1) return data[n / 2];
return (data[n / 2 - 1] + data[n / 2]) / 2;
}Trust Tradeoffs
Custom oracles trade decentralization for flexibility. Document clearly who runs the updater, what data is sourced, and how consumers should react to staleness. For high-value DeFi, prefer established decentralized oracle networks.
Consumer Integration
A consumer contract simply imports the oracle interface and reads from it, applying its own freshness checks.
interface IOracle {
function getFreshValue() external view returns (uint256);
}
contract Consumer {
IOracle public oracle;
constructor(address o) { oracle = IOracle(o); }
function price() external view returns (uint256) {
return oracle.getFreshValue();
}
}Quick Check
Test your grasp of custom oracle design.
Recap
You built a custom oracle with an off-chain updater and an on-chain store. Key ideas:
- Trusted updater with rotatable access control
- Freshness checks via
lastUpdated - Request-response flow with events and callbacks
- Multiple updaters and medians to reduce single-source risk
Use custom oracles for niche data, but prefer decentralized networks for high-stakes feeds.
常见问题解答
「构建自定义预言机合约」课时是免费的吗?
是的 — 「构建自定义预言机合约」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Blockchain Smart Contracts with Solidity 课程的其余内容,请升级到 CoddyKit PRO。 Blockchain Smart Contracts with Solidity 课程共包含 4 节课。
「构建自定义预言机合约」这节课中我会学到什么?
学习如何在 Solidity 中设计和实现自己的预言机模式,包括请求—响应流程、可信更新者以及在链上验证数据新鲜度。 你通过在浏览器中直接运行的动手代码来练习 Blockchain Smart Contracts with Solidity,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Blockchain Smart Contracts with Solidity 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Blockchain Smart Contracts with Solidity 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「构建自定义预言机合约」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Blockchain Smart Contracts with Solidity 课中编写并运行代码吗?
能。每节 Blockchain Smart Contracts with Solidity 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 预言机问题解析
- 集成 Chainlink 预言机
- 处理链下数据获取
- 构建自定义预言机合约