Fetch live stablecoin prices from the IFÁ Labs oracle on Sui — using Move contracts, the Sui TypeScript SDK, and the Sui CLI.
This page covers how to read price feeds from the IFÁ Labs oracle on Sui Testnet. The integration model is fundamentally different from EVM — instead of calling a contract address, you pass a shared object by reference in a transaction or inspect call.If you are building on Base Mainnet or another EVM chain, see Read Latest Price for the Solidity integration guide.
Always pass the Feed Object ID when reading prices — not the Package ID. The Package ID is only used when importing ifa_oracle as a dependency in your own Move package.
module my_protocol::price_reader;use ifa_oracle::bytes32;use ifa_oracle::price_feed::{Self, IfaPriceFeed};/// Read the USDT/USD price from IFÁ Labs/// feed: pass the IfaPriceFeed shared object by referencepublic fun get_usdt_price(feed: &IfaPriceFeed): u256 { let asset_id = bytes32::new( x"6ca0cef6107263f3b09a51448617b659278cff744f0e702c24a2f88c91e65a0d" ); let (price_feed, exists) = price_feed::get_asset_info(feed, asset_id); assert!(exists, 0); // asset must exist — no false flag on Sui // Sui uses positive decimals // decimal = 18 means divide by 10^18 // price_feed::price_decimal(price_feed) returns 18 // price_feed::price(price_feed) returns e.g. 1_000_124_000_000_000_000 // human-readable: 1_000_124_000_000_000_000 / 10^18 = 1.000124 price_feed::price(price_feed)}
module my_protocol::price_reader;use ifa_oracle::bytes32;use ifa_oracle::price_feed::{Self, IfaPriceFeed};use sui::clock::Clock;const E_PRICE_STALE: u64 = 1;const MAX_AGE_MS: u64 = 3_600_000; // 1 hour in millisecondspublic fun get_fresh_usdt_price(feed: &IfaPriceFeed, clock: &Clock): u256 { let asset_id = bytes32::new( x"6ca0cef6107263f3b09a51448617b659278cff744f0e702c24a2f88c91e65a0d" ); let (price_feed, exists) = price_feed::get_asset_info(feed, asset_id); assert!(exists, 0); // Use the built-in is_fresh helper // current_time comes from the Sui Clock object let current_time = sui::clock::timestamp_ms(clock); assert!( price_feed::is_fresh(price_feed, current_time, MAX_AGE_MS), E_PRICE_STALE ); price_feed::price(price_feed)}
Sui timestamps are in milliseconds. Set MAX_AGE_MS accordingly — 1 hour is 3_600_000, not 3_600. This is different from EVM where block.timestamp is in seconds.
module my_protocol::price_reader;use ifa_oracle::bytes32;use ifa_oracle::price_feed::{Self, IfaPriceFeed, DerivedPair};// Direction: 0 = Forward (asset0 / asset1), 1 = Backward (asset1 / asset0)const FORWARD: u8 = 0;/// Get CNGN priced in USDT termspublic fun get_cngn_usdt_price(feed: &IfaPriceFeed): DerivedPair { let cngn_id = bytes32::new( x"83a18c73cf75a028a24b79cbedb3b8d8ba363b748a3210ddbcaa95eec3b87b3a" ); let usdt_id = bytes32::new( x"6ca0cef6107263f3b09a51448617b659278cff744f0e702c24a2f88c91e65a0d" ); // Returns DerivedPair with decimal = 30 always // derived_price is scaled by 10^30 price_feed::get_pair_by_id(feed, cngn_id, usdt_id, FORWARD)}
Derived pair functions abort if either underlying asset does not exist in the feed. Unlike EVM which returns exists = false, Sui will revert your entire transaction. Always verify both assets exist with get_asset_info before calling pair functions in production.
The asset ID is passed as a JSON array of decimal byte values — not as a hex string. The array above is the decimal representation of the USDT/USD asset ID bytes.