Sui.

Post

Share your knowledge.

NakaMoto. SUI.
Sep 04, 2025
Expert Q&A

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
1
2
Share
Comments
.

Answers

2
Satoshi .
Sep 4 2025, 10:24

Redundant 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

1
Best Answer
Comments
.
290697tz.
Sep 4 2025, 10:04
  1. 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.


  1. 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.


  1. Best Practices

  2. Minimize Trust Surface

Use multiple oracles, or at least make the updater logic replaceable by governance.

  1. Prevent Replay Attacks

Enforce monotonic timestamps or sequence numbers.

Store last seen sequence to reject stale updates.

  1. Keep Shared Objects Small

Don’t log full price history inside the feed.

Use events for historical indexing.

  1. 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.

  1. Gas Efficiency

Avoid storing large vectors of prices in shared objects.

Use single last_price + events to scale.

1
Comments
.

Do you know the answer?

Please log in and share it.