Post
Share your knowledge.
Best way to integrate oracles with Move smart contracts
If I need to use off-chain data (like price feeds), what’s the safest and most efficient way to verify it inside a Sui Move smart contract?
- Sui
- Architecture
- Transaction Processing
Answers
2Redundant uploads: Content is mirrored across aggregators to ensure availability even if one fails.
Content addressing: Uses cryptographic hashes to verify integrity and retrieve data reliably from any node.
Best practices for optimizing storage cost and durability in multi-aggregator setups:
. Tiered storage strategy: Use cheaper, long-term storage (e.g., Filecoin) for archival and faster, short-term storage (e.g., IPFS) for access.
. Content deduplication: Avoid storing
- Core Principle: Chain Cannot Pull, Only Verify
Sui Move smart contracts cannot query off-chain APIs (like price feeds).
Instead, oracles (trusted relayers or networks) submit signed transactions containing the data.
Your Move module’s job is to verify integrity, not fetch.
- Common Design Patterns
a. Signed Data with Verifier Keys
Oracle signs a price update off-chain with its private key.
Move module stores the oracle’s public key (or multiple keys for multi-sig).
When oracle submits a price, the module checks the signature and timestamp.
If valid, the price is stored in a shared PriceFeed object or consumed immediately.
module oracle::price_feed { use sui::object::{Self, UID}; use sui::tx_context::TxContext; use sui::transfer; use sui::event; use std::vector;
/// Shared feed object
struct PriceFeed has key {
id: UID,
last_price: u64,
last_timestamp: u64,
authority: address, // oracle signer
}
/// Initialize feed with trusted oracle
public entry fun init(authority: address, ctx: &mut TxContext) {
let feed = PriceFeed {
id: object::new(ctx),
last_price: 0,
last_timestamp: 0,
authority,
};
transfer::share_object(feed);
}
/// Oracle pushes price
public entry fun update(feed: &mut PriceFeed, price: u64, timestamp: u64, signer: address) {
// Require signer is trusted oracle
assert!(signer == feed.authority, 1);
// Require monotonic timestamp
assert!(timestamp > feed.last_timestamp, 2);
feed.last_price = price;
feed.last_timestamp = timestamp;
event::emit((signer, price, timestamp));
}
}
b. Multi-Oracles with Aggregation
Instead of trusting one oracle, require a threshold of signatures.
E.g., store prices from 3 sources, use median or weighted average.
Prevents single-point manipulation.
c. Event-Driven Feeds
Oracles push updates as events (append-only).
On-chain contracts don’t store much state (just verify).
Off-chain apps reconstruct the price feed by consuming events, keeping shared object mutations minimal.
d. Capability Tokens for Update Rights
Issue an OracleCap object to each authorized updater.
Only holders of these caps can update the feed.
This avoids storing large lists of addresses inside the shared feed.
-
Best Practices
-
Minimize Trust Surface
Use multiple oracles, or at least make the updater logic replaceable by governance.
- Prevent Replay Attacks
Enforce monotonic timestamps or sequence numbers.
Store last seen sequence to reject stale updates.
- Keep Shared Objects Small
Don’t log full price history inside the feed.
Use events for historical indexing.
- Think in Lifetimes
Off-chain consumers (traders, dApps) should check freshness (timestamp) before using.
On-chain consumers should fail gracefully if no recent update exists.
- Gas Efficiency
Avoid storing large vectors of prices in shared objects.
Use single last_price + events to scale.
Do you know the answer?
Please log in and share it.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- How to Maximize Profit Holding SUI: Sui Staking vs Liquid Staking616
- Why does BCS require exact field order for deserialization when Move structs have named fields?65
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution55
- Sui Move Error - Unable to process transaction No valid gas coins found for the transaction419
- Sui Transaction Failing: Objects Reserved for Another Transaction410