---
marp: true
title: 02 Plugin oracle feeds on XDC
---

# 02 Plugin (PLI) oracle feeds on XDC

---

## What a consumer sees
- Plugin publishes price feeds on XDC mainnet as **proxy contracts**
- Read API is AggregatorV3-style:
```solidity
interface IPluginFeed {
    function decimals() external view returns (uint8);
    function description() external view returns (string memory);
    function latestRoundData() external view returns (
        uint80 roundId, int256 answer, uint256 startedAt,
        uint256 updatedAt, uint80 answeredInRound);
}
```

---

## Gotchas observed on mainnet feeds
- **Decimals differ per feed:** most live feeds use 16, some older ones 18 (Plugin's contracts allow others, such as 25) — always read `decimals()`
- Heartbeats differ: crypto pairs ~1800 s, FX / equities / metals 86400 s (market hours)
- Some proxies revert `"No access"` (whitelisted) or revert empty (broken / access-controlled)
- Example: XDC/USDT `0x896CC6fE8AD04dF9f481332606c445768e286076` (16 dp)

---

## The five checks every consumer needs
1. `answer > 0`
2. `updatedAt != 0`
3. `updatedAt <= block.timestamp`
4. `block.timestamp - updatedAt <= maxStaleness` (per feed!)
5. `answeredInRound >= roundId`

Optional: min/max bounds, max deviation vs. last accepted price

---

## Scaling to 18 decimals
```solidity
uint256 p = latestPrice();
if (feedDecimals < 18) return p * 10 ** (18 - feedDecimals);
return p / 10 ** (feedDecimals - 18);
```

---

## Testing feeds
- Unit tests: a **mock feed** you control (`set(answer, updatedAt)`, `setRounds`, `setNoAccess`)
- Integration: **read-only fork** of mainnet
  `XDC_FORK_RPC=https://rpc.xinfin.network forge test --match-path 'test/fork/*'`
- `vm.warp` past the heartbeat to prove your staleness check works

→ Lab 2
