Menjembatani kontrak dengan dunia nyata: konsep oracle, price feeds Chainlink, memilih data feed yang benar, serta praktik mengintegrasikan oracle ke dalam kontrak

Setelah di episode 10 kalian membangun NFT, pada episode ini kita menjawab satu masalah yang paling sering diremehkan pemula: bagaimana kontrak tahu harga ETH saat ini? Blockchain, dengan segala kekuatannya, adalah mesin yang buta — ia hanya tahu apa yang ada di dalam dirinya. Harga pasar, cuaca, hasil olahraga, atau saldo bank ada di dunia luar.
Di sinilah oracle berperan: jembatan yang membawa data dunia nyata ke on-chain. Episode ini fokus pada Chainlink — jaringan oracle terbesar — dan cara mengintegrasikannya dengan aman.
Kontrak tidak bisa memanggil API eksternal langsung — eksekusi harus deterministik agar semua node mencapai hasil yang sama. Maka data eksternal harus diantarkan ke on-chain oleh oracle, atau dipicu oleh pihak luar.
Ada dua jenis pola oracle yang wajib kalian bedakan:
| Pola | Cara Kerja | Contoh |
|---|---|---|
| Push | Oracle mengupdate data on-chain secara berkala | Price feeds Chainlink |
| Pull (Request/Response) | Kontrak meminta data, oracle menjawab | Verifikasi event olahraga, random number |
Price feed adalah pola push yang paling banyak dipakai: jaringan node Chainlink mengirimkan harga agregat ke kontrak aggregator yang ter-update berkala. Kontrak kalian cukup membaca harga terbaru.
Alamat aggregator berbeda-beda per chain dan per pair. Integrasi dasarnya:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IPriceFeed {
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
contract PriceConsumer {
IPriceFeed public immutable priceFeed;
// Feed ETH/USD di Sepolia
constructor(address _priceFeed) {
priceFeed = IPriceFeed(_priceFeed);
}
function getLatestPrice() public view returns (uint256) {
(, int256 answer, , uint256 updatedAt, ) =
priceFeed.latestRoundData();
require(block.timestamp - updatedAt < 3600, "stale price");
// Harga dikembalikan dengan 8 desimal
return uint256(answer);
}
}Perhatikan: kita selalu memeriksa stale price (updatedAt terlalu lama = data usang). Data feed yang basi adalah sumber bug halus yang sering ditemukan audit.
Warning
Jangan pernah membaca harga dari satu sumber tanpa validasi. Di production, pastikan: (1) harga tidak stale, (2) feed sesuai dengan chain tempat kontrak berjalan, dan (3) pahami unit harga (biasanya 8 desimal, tapi cek dokumentasi pair). Kesalahan kecil di sini bisa menguras dana protokol.
Bukan semua pasangan harga tersedia di semua chain. Sebelum menulis kode, cek Chainlink data feeds registry untuk pair dan chain yang kalian butuhkan. Aturan praktis:
Contoh kombinasi dua feed:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IPriceFeed {
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
contract CrossPrice {
// TOKEN/ETH dan ETH/USD → harga token dalam USD
function getTokenUsd(
address _tokenEthFeed,
address _ethUsdFeed
) external view returns (uint256) {
(, int256 tokenEth, , uint256 updatedAt, ) =
IPriceFeed(_tokenEthFeed).latestRoundData();
require(block.timestamp - updatedAt < 3600, "stale token/eth");
(, int256 ethUsd, , uint256 updatedAt2, ) =
IPriceFeed(_ethUsdFeed).latestRoundData();
require(block.timestamp - updatedAt2 < 3600, "stale eth/usd");
// tokenEth (8 des) * ethUsd (8 des) → 16 desimal
return (uint256(tokenEth) * uint256(ethUsd)) / 1e8;
}
}Selain harga, oracle juga menyediakan random number (untuk NFT dan game) dan verifikasi event (untuk prediksi). Chainlink VRF (Verifiable Random Function) adalah standar untuk randomness yang bisa dibuktikan — penting karena block.timestamp atau blockhash adalah sumber randomness yang bisa dimanipulasi (kita bahas di episode 18).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2Plus.sol";
contract VRFRandom is VRFConsumerBaseV2Plus {
uint256 public requestId;
uint256 public randomNumber;
// Konstruktor menerima SUBSCRIPTION_ID dan keyHash
constructor(
address _vrfCoordinator,
uint256 _subId,
bytes32 _keyHash
)
VRFConsumerBaseV2Plus(_vrfCoordinator)
{}
function requestRandom(uint256 _subId, bytes32 _keyHash)
external
returns (uint256)
{
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: _keyHash,
subId: _subId,
requestConfirmations: 3,
callbackGasLimit: 100_000,
numWords: 1
})
);
return requestId;
}
function fulfillRandomWords(
uint256 _requestId,
uint256[] calldata _randomWords
) internal override {
randomNumber = _randomWords[0];
}
}VRF memakai pola callback: kontrak meminta, oracle menghitung randomness on-chain yang bisa diverifikasi, lalu memanggil fungsi callback. Pola request/response seperti ini adalah standar untuk semua data yang tidak bisa di-push berkala.
Tip
Untuk menguji integrasi oracle tanpa biaya, gunakan local node (anvil/hardhat node) dengan mock feed. forge test dengan vm.mockCall juga bisa mensimulasikan respons feed untuk menguji logika kontrak kalian secara menyeluruh.
Inti yang harus dibawa pulang:
Di episode 12 selanjutnya kita memperluas cakrawala — multi-chain development: Solana, BNB Chain, Base, dan Polygon, strategi multi-chain 2026, dan praktik men-deploy lintas chain. Mari lanjut ke episode 12!