Somnia 백서

Somnia

원문

bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 54쪽 중 31쪽

tPrice); ``` **Returns the price of a specified asset along with the update timestamp**. ### **`getPriceIfNotOlderThan`** ``` function getPriceIfNotOlderThan( address oracle, string memory key, uint128 maxTimePassed ) public view returns (uint128 price, bool inTime) { ``` **Checks if the Oracle price is older than `maxTimePassed`** ## Using DIAOracleV2 Interface The following contract provides an integration example of retrieving prices and verifying price age. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; interface IDIAOracleV2 { function getValue(string memory) external view returns (uint128, uint128); } contract DIAOracleSample { address diaOracle; constructor(address _oracle) { diaOracle = _oracle; } function getPrice(string memory key) external view returns ( uint128 latestPrice, uint128 timestampOflatestPrice ) { (latestPrice, timestampOflatestPrice) = IDIAOracleV2(diaOracle).getValue(key); } } ``` [**Full Example on DIA Docs**](https://www.diadata.org/docs/nexus/how-to-guides/fetch-price-data/solidity) ## Adapter contracts To consume price data from DIA Oracle, you can use the adapter Smart Contract located at the [adapter address](https://docs.google.com/document/d/1tqK1Va0dOrgZcOjqwLg2fh7hDOK6pQWDfF3ZrljDN-w/edit?tab=t.0#heading=h.iyqc22917lsc) for each asset. This will allow you to access the same methods on the `AggregatorV3Interface` such as `getRoundData` & `latestRoundData`. You can learn more [here](https://www.diadata.org/docs/nexus/how-to-guides/migrate-to-dia). ## **Glossary** | Term | Definition | | --------------------- | --------------------------------------------------------------------- | | **Deviation** | Percentage threshold that triggers a price update when exceeded. | | **Refresh Frequency** | Time interval for checking and updating prices if conditions are met. | | **Trade Window** | Time interval used to aggregate trades for price calculation. | | **Heartbeat** | Forced price update at a fixed interval. | ## **Support** If you need further assistance integrating DIA Oracles, reach out through DIA’s[ official documentation](https://docs.diadata.org/) and ask your questions in the #dev-support channel on [Discord](https://discord.com/invite/somnia). Developers can build secure, real-time, and on-chain financial applications with reliable pricing data by integrating DIA Oracles on Somnia. # Protofire Price Feeds ## Somnia Mainnet Price Feeds | **Asset Pair** | **OCR Aggregator** | **Proxy (read‑only)** | | -------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | USDC / USD | [0x4b74EcA574Ce996448b485100e4FFf84866911dF](https://explorer.somnia.network/address/0x4b74EcA574Ce996448b485100e4FFf84866911dF) | [0x843B6812E9Aa67b3773675d2836646BCbd216642](https://explorer.somnia.network/address/0x843B6812E9Aa67b3773675d2836646BCbd216642) | | ETH / USD | [0xa3060dd6Bb56EdfB2E0d78c88ef63A974a392D36](https://explorer.somnia.network/address/0xa3060dd6Bb56EdfB2E0d78c88ef63A974a392D36) | [0xeC25a820A6F194118ef8274216a7F225Da019526](https://explorer.somnia.network/address/0xeC25a820A6F194118ef8274216a7F225Da019526) | | BTC / USD | [0x3cBdF7F02956c8e946192Bff64bb2Dd470dd589C](https://explorer.somnia.network/address/0x3cBdF7F02956c8e946192Bff64bb2Dd470dd589C) | [0xa57d637618252669fD859B1F4C7bE6F52Bef67ed](https://explorer.somnia.network/address/0xa57d637618252669fD859B1F4C7bE6F52Bef67ed) | ## Somnia Testnet Price Feeds <table data-header-hidden><thead><tr><th></th><th width="456.400390625"></th></tr></thead><tbody><tr><td><strong>Token Pair</strong></td><td><strong>Contract Address</strong></td></tr><tr><td>USDC/USD</td><td><a href="http://shannon-explorer.somnia.network/address/0xa2515C9480e62B510065917136B08F3f7ad743B4">0xa2515C9480e62B510065917136B08F3f7ad743B4</a></td></tr><tr><td>ETH/USD</td><td><a href="http://shannon-explorer.somnia.network/address/0xd9132c1d762D432672493F640a63B758891B449e">0xd9132c1d762D432672493F640a63B758891B449e</a></td></tr><tr><td>BTC/USD</td><td><a href="http://shannon-explorer.somnia.network/address/0x8CeE6c58b8CbD8afdEaF14e6fCA0876765e161fE">0x8CeE6c58b8CbD8afdEaF14e6fCA0876765e161fE</a></td></tr></tbody></table> Use any of these when deploying the PriceConsumer Smart Contract for your dApp. {% embed url="<https://www.youtube.com/watch?v=sH3gxoT4rTM>" %} ## What Are Oracles and Why Do They Matter This service is powered by Protofire, an infrastructure provider that integrates decentralized oracle networks for Somnia. Learn more at[ protofire.io](https://protofire.io/services/solution/oracle-integration). Oracles are critical infrastructure in the blockchain ecosystem that enable Smart Contracts to interact with real-world data. Blockchains are deterministic by design and cannot fetch off-chain information directly. This creates a need for oracles, which are trusted data feeds that can push external data, like market prices, sports scores, or weather conditions, into Smart Contracts. Chainlink is the most widely adopted decentralized oracle network. It allows developers to access reliable data feeds that are resistant to manipulation and downtime. In this tutorial, we focus on Protofire Chainlink Price Feeds, which provide real-time market prices for assets like ETH, BTC, and USDC on Somnia Smart Contracts that rely on accurate pricing (e.g., lending, trading, insurance) benefit immensely from using decentralized oracles like Protofire. Oracles unlock use cases that were previously impossible due to blockchain isolation. In this guide, we will build a live crypto price tracker that displays BTC/USD, ETH/USD, and USDC/USD using Protofire Chainlink Price Feeds deployed on the Somnia Testnet. ## Prerequisites 1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript. 2. Basic knowledge of React & Next.js. ### Solidity Contract (Chainlink Oracle Consumer) ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumer { AggregatorV3Interface internal priceFeed; constructor(address _priceFeed) { priceFeed = AggregatorV3Interface(_priceFeed); } /** * Returns the latest price */ function getLatestPrice() public view returns (int256) { ( /* uint80 roundID */, int256 price, /* uint startedAt */, /* uint timeStamp */, /* uint80 answeredInRound */ ) = priceFeed.latestRoundData(); return price; } /** * Returns price decimals */ function getDecimals() public view returns (uint8) { return priceFeed.decimals(); } } ``` ### Code Breakdown ```solidity import `@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol ``` Pulls in the AggregatorV3Interface from the Chainlink library. This interface allows interaction with Chainlink oracle contracts for Price Feeds. ```solidity constructor(address _priceFeed) { } ``` The contract's constructor runs once when the contract is deployed. It takes the address of the Protofire Chainlink Price Feed contract and stores it. ```solidity priceFeed = AggregatorV3Interface(_priceFeed); ``` Instantiates the interface with the provided address, enabling function calls to the external oracle. ```solidity function getLatestPrice() public view returns (int256) { ( /* uint80 roundID */, int256 price, /* uint startedAt */, /* uint timeStamp */, /* uint80 answeredInRound */ ) = priceFeed.latestRoundData(); return price; } ``` `getLatestPrice()` is a public function which is callable from outside the contract. It does not modify blockchain state and returns the latest price from the Chainlink feed. It calls the Chainlink function **`latestRoundData()`** which returns a 5-value tuple: ```solidity (uint80 roundId, int256 price, uint startedAt, uint timeStamp, uint80 answeredInRound) ``` This function extracts only the price and ignores the rest using commas. It returns the latest price as an int256. ```solidity function getDecimals() public view returns (uint8) { return priceFeed.decimals(); } ``` `getDecimals()` is a helper function to return the number of decimals used by the price feed. Ensures consumers of the contract know how to scale the price properly. ## How Price Oracle latestRoundData() works It's helpful to understand how the Price Feed data is structured. The function `latestRoundData()` from the Chainlink Aggregator interface returns the following tuple: ```solidity (uint80 roundId, int256 answer, uint startedAt, uint timeStamp, uint80 answeredInRound) ``` * `roundId`: The current round number for the feed * `price`: The actual price value (e.g. ETH/USD = 1900.42 × 10^8) * `startedAt`: Timestamp when this round started * `timeStamp`: When the answer was last updated * `answeredInRound`: The round in which the answer was submitted<br> Most price consumer contracts use only `price`, but for more robust designs, `timeStamp` can be checked to ensure the price is recent. Additionally, Protofire Chainlink Price Feeds often return prices with 8 decimals. This means the raw price value needs to be normalized by dividing it by 10 \*\* decimals. This is essential because Solidity doesn't support floating-point math. Prices are represented using fixed-point math. For example, if ETH/USD is $1,940.82 and the feed uses 8 decimals, the returned price would be 194082000000. You must divide by 10 \*\* 8 to display $1940.82 in your UI. Always use the `getDecimals()` method provided by the Aggregator interface to dynamically adjust for different feeds that may use 6, 8, or 18 decimals depending on the asset. ## Deploy with Hardhat Update ignition/modules/deploy.js: ```typescript import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; const PriceConsumerModule = buildModule("PriceConsumerModule", (m) => { // Replace this with the correct feed address for your chosen pair const feedAddress = m.getParameter( "feedAddress", "0xd9132c1d762D432672493F640a63B758891B449e" // Example: ETH/USD on Somnia ); const priceConsumer = m.contract("PriceConsumer", [feedAddress]); return { priceConsumer }; }); export default PriceConsumerModule; ``` Deploy using: ```bash npx hardhat ignition deploy ./ignition/modules/Lock.js --network somnia ``` ## Building the UI Now that we’ve deployed the contract and confirmed it fetches live price data from Somnia’s Protofire Oracles, let’s bring it to life with a clean and responsive UI. We’ll use React and Viem.js to build a real-time dashboard that displays crypto prices with auto-refresh and token selection. Start by creating a new Next.js app. ```bash npx create-next-app@latest somnia-protofire-example cd somnia-protofire-example ``` Then, install required dependencies. ```bash npm install viem ``` Add imports to the `index.js` file ```typescript import { useEffect, useState } from 'react'; import { createPublicClient, http, parseAbi, formatUnits } from 'viem'; import { somniaTestnet } from 'viem/chains'; ``` * useEffect, useState: React hooks for lifecycle and state management. * createPublicClient: Creates a read-only client to interact with the blockchain. * http: Defines the transport layer for the client (uses Somnia RPC). * parseAbi: Parses the contract's ABI. * formatUnits: Converts big numbers (like token prices) to a human readable format.s. #### Create a Viem Client for Somnia ```typescript const client = createPublicClient({ chain: somniaTestnet, transport: http(), }); ``` Creates a blockchain client configured for Somnia Testnet using its RPC URL and allows reading smart contract data without needing a wallet or signer. #### Declare a variable for the deployed Smart Contracts for your Price Feed Addresses ```typescript const FEEDS = { //Testnet Price Feeds ETH: '0x604CF5063eC760A78d1C089AA55dFf29B90937f9', BTC: '0x3dF17dbaa3BA861D03772b501ADB343B4326C676', USDC: '0xA4a08Eb26f85A53d40E3f908B406b2a69B1A2441', }; ``` This will map token pairs to their corresponding Chainlink oracle contract addresses deployed on Somnia.<br> Parse the ABI ```typescript const abi = parseAbi([ 'function getLatestPrice() view returns (int256)', 'function getDecimals() view returns (uint8)', ]); ``` #### Set up the State ```typescript export default function PriceWidget() { const [price, setPrice] = useState(''); const [selectedToken, setSelectedToken] = useState<'ETH' | 'BTC' | 'USDC'>('ETH'); ``` The `price` state will store the formatted token price and `selectedToken` will track which token is selected from the dropdown (default: ETH). #### Fetch the Latest Price ```typescript const fetchPrice = async () => { const contractAddress = FEEDS[selectedToken]; const [rawPrice, decimals] = await Promise.all([ client.readContract({ address: contractAddress, abi, functionName: 'getLatestPrice' }), client.readContract({ address: contractAddress, abi, functionName: 'getDecimals' }), ]); const normalized = formatUnits(rawPrice, decimals); setPrice(parseFloat(normalized).toFixed(2)); }; ``` Reads the price and its decimal precision from the Chainlink oracle contract. The function declaration uses `Promise.all()` to optimize performance by fetching both at once and formats the price to 2 decimal places for display. #### Fetch Price on Load & Every 10 Seconds ```typescript useEffect(() => { fetchPrice(); const interval = setInterval(fetchPrice, 10000); return () => clearInterval(interval); }, [selectedToken]); ``` The `useEffect` hook runs `fetchPrice()`, once on component mount and every time `selectedToken` changes. The hook also refreshes price data every 10 seconds Live Price Display in the `return` statement. ```typescript return ( ... <p>${price}</p> ... ) ``` ## Complete Code <details> <summary>index.js</summary> ```typescript import { useEffect, useState } from 'react'; import { createPublicClient, http, parseAbi, formatUnits } from 'viem'; import { somniaTestnet } from 'viem/chains'; const client = createPublicClient({ chain: somniaTestnet, transport: http(), }); const FEEDS = { ETH: '0x604CF5063eC760A78d1C089AA55dFf29B90937f9', BTC: '0x3dF17dbaa3BA861D03772b501ADB343B4326C676', USDC: '0xA4a08Eb26f85A53d40E3f908B406b2a69B1A2441', }; const abi = parseAbi([ 'function getLatestPrice() view returns (int256)', 'function getDecimals() view returns (uint8)', ]); export default function PriceWidget() { const [price, setPrice] = useState(''); const [selectedToken, setSelectedToken] = useState<'ETH' | 'BTC' | 'USDC'>( 'ETH' ); const fetchPrice = async () => { const contractAddress = FEEDS[selectedToken]; const [rawPrice, decimals] = await Promise.all([ client.readContract({ address: contractAddress, abi, functionName: 'getLatestPrice', }), client.readContract({ address: contractAddress, abi, functionName: 'getDecimals', }), ]); const normalized = formatUnits(rawPrice, decimals); setPrice(parseFloat(normalized).toFixed(2)); }; useEffect(() => { fetchPrice(); const interval = setInterval(fetchPrice, 10000); return () => clearInterval(interval); }, [selectedToken]); return ( <div className='min-h-screen flex items-center justify-center bg-gray-50'> <div className='text-center p-6 border border-gray-200 rounded-lg shadow-lg bg-white max-w-sm w-full'> <h3 className='text-2xl font-bold mb-4 text-gray-800'> {selectedToken}/USD on Somnia </h3> <select value={selectedToken} onChange={(e) => setSelectedToken(e.target.value as 'ETH' | 'BTC' | 'USDC') } className='mb-6 px-4 py-2 border border-gray-300 rounded-md w-full text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500' > <option value='ETH'>ETH/USD</option> <option value='BTC'>BTC/USD</option> <option value='USDC'>USDC/USD</option> </select> <p className='text-4xl font-semibold text-blue-600'>${price}</p> </div> </div> ); } ``` </details> ## Conclusion The Protofire Oracle integration on Somnia provides developers with reliable, on-chain price feeds for key assets like ETH, BTC, and USDC. Using verified oracles and standardized data formats enables accurate, real-time pricing essential for building GaemFi, DeFi, trading, and financial applications. <br> # Using Verifiable Randomness (VRF) **Somnia VRF** is a native VRF coordinator built on Somnia's Reactivity primitive and mixed with drand via Somnia Agents. Consumers pay only their tx gas; the coordinator pays for entropy delivery out of its own balance. No off-chain fulfiller runs behind it. Two entropy modes share one coordinator: * **Fast**: entropy from `blockhash(revealBlock - 1)`. Cheap and low-latency, but the block producer for the reveal block can influence it. Testnet-grade only. * **Verifiable**: the reveal block's hash is mixed with a drand round pulled through the Somnia Agents JSON API Request agent. Publicly verifiable seed; compromising either drand or the reveal-block producer alone does not compromise the result. The only mode available on mainnet. The [live demo](https://vrf-showcase.vercel.app) connects your wallet, flips a coin, and shows the whole cycle end-to-end. ## Mainnet contracts (chain 5031) | Contract | Address | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `SomniaVRFCoordinator` | [`0x50AAEE5ECE41627F2bc4b64Ad29a4659a5C018cb`](https://explorer.somnia.network/address/0x50AAEE5ECE41627F2bc4b64Ad29a4659a5C018cb) | | `CoinFlip` (example consumer) | [`0x0c80964dB885091194F547eaDf95249b74Ba651D`](https://explorer.somnia.network/address/0x0c80964dB885091194F547eaDf95249b74Ba651D) | | Somnia Agents platform | [`0x5E5205CF39E766118C01636bED000A54D93163E6`](https://explorer.somnia.network/address/0x5E5205CF39E766118C01636bED000A54D93163E6) | The mainnet coordinator is deployed with `FAST_MODE_ENABLED = false`; blockhash-only requests revert at request time. ## Testnet contracts (chain 50312) | Contract | Address | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `SomniaVRFCoordinator` | [`0x0834459256bbb8d2efee23dc6c3f1722266182dd`](https://shannon-explorer.somnia.network/address/0x0834459256bbb8d2efee23dc6c3f1722266182dd) | | `CoinFlip` (example consumer) | [`0x9c220e99bf41df220ce54147a0a10a025a151e63`](https://shannon-explorer.somnia.network/address/0x9c220e99bf41df220ce54147a0a10a025a151e63) | | Somnia Agents platform | [`0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776`](https://shannon-explorer.somnia.network/address/0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776) | Both modes are enabled on the testnet coordinator. ## Cost and latency Every request is paid out of the coordinator's balance, not the consumer's. Consumers only pay the tx gas for `requestRandomWords`. | Mode | Cost per request | End-to-end latency | | ---------- | ---------------- | ----------------------------------- | | Fast | \~0.00067 native | \~4 seconds (16-block commit delay) | | Verifiable | \~0.10 native | \~10 seconds (adds drand fetch) | ## Consumer contract A consumer only needs two things: the coordinator address and the two selectors it will use (`requestRandomWords` outbound, `rawFulfillRandomWords` inbound). No package install, no external repo dependency. The example below is a full, drop-in consumer that just needs the coordinator address at construction: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; interface ISomniaVRFCoordinator { struct RandomWordsRequest { uint32 callbackGasLimit; uint16 commitDelayBlocks; uint32 numWords; bool useVerifiableEntropy; } function requestRandomWords(RandomWordsReq
프로젝트로 돌아가기