Somnia 백서

Somnia

원문

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

topics: [ethers.id("GreetingSet(string,string)")], }; // Listen for events provider.on(filter, async (log) => { try { const greeting = await contract.getGreeting(); console.log(`New greeting: "${greeting}"`); } catch (error) { console.error("Error:", error.message); } }); // Keep connection alive setInterval(async () => { try { await provider.getBlockNumber(); } catch (error) { console.error("Connection error"); } }, 30000); // Handle shutdown process.on("SIGINT", () => { provider.destroy(); process.exit(0); }); } // Start listening listen().catch(console.error); ``` </details> ## Prerequisites * Node.js 20+ * Basic understanding of JavaScript * A deployed smart contract on Somnia network ## What are WebSockets? WebSockets are a communication protocol that provides bidirectional communication channels over a single TCP connection. Unlike traditional HTTP requests, WebSockets maintain a persistent connection between the client and server. ### **How WebSockets Work** WebSockets begin with a connection establishment phase where the client initiates a WebSocket handshake through an HTTP upgrade request. Once established, the connection stays open as a persistent channel between client and server. This enables bidirectional communication where both client and server can send messages at any time without waiting for requests. The protocol maintains low latency since there's no need to establish new connections for each message exchange. This design is highly efficient with minimal protocol overhead compared to traditional HTTP polling approaches. **WebSocket Connection Lifecycle** ```bash Client Server | | |----Connection Request-->| |<---Connection Accept----| | | |<===Open Connection====> | | | |----Send Message-------->| |<---Receive Message------| |<---Push Notification----| |----Send Message-------->| | | |<===Open Connection====> | | | |----Close Connection---->| ``` ### WebSocket vs HTTP Polling #### **HTTP Polling Approach** ```javascript // Inefficient: Constantly asking "Any updates?" setInterval(async () => { const response = await fetch('https://api.example.com/events'); const data = await response.json(); if (data.hasNewEvents) { console.log('New event:', data.events); } }, 5000); // Check every 5 seconds ``` The problem with polling is that Polling wastes bandwidth by constantly checking for updates even when none exist, leading to unnecessary network traffic. This approach introduces delays of up to the polling interval (5 seconds in our example), meaning users might wait several seconds to see new events that have already occurred. Additionally, the server must process these unnecessary requests repeatedly, increasing computational load and infrastructure costs. For blockchain applications, this translates to higher costs for RPC providers who often charge based on the number of requests made. #### **WebSocket Approach** ```javascript // Efficient: Server pushes updates immediately const ws = new WebSocket('wss://api.example.com/events'); ws.on('message', (data) => { console.log('New event:', data); // Instant notification }); ``` WebSockets provide real-time updates measured in milliseconds rather than seconds, ensuring users receive notifications instantly when events occur. This eliminates wasted requests since the server only sends data when there are actual updates, significantly reducing bandwidth usage. The reduced request frequency leads to lower server load and better resource utilization. For users, this translates to a superior experience with immediate feedback, while developers benefit from reduced infrastructure costs. ### Blockchain Events and WebSockets Smart contracts emit events when important state changes occur. These events are included in transaction receipts and stored in blockchain logs. The event flow begins when a user calls a Smart Contract function through a Transaction. During execution, the contract updates its Internal State and emits Events containing relevant data about the changes. These Transactions and their associated Events are then included in a new block by Validators. Once the Block is finalized, Nodes across the network broadcast it to their peers. WebSocket connections instantly notify connected clients about these new events, enabling real-time reactions to Blockchain state changes. ### **Example Use Cases for WebSocket Event Listening** | DeFi Applications | <ul><li>Monitor price updates on DEX swaps</li></ul> | | ------------------- | ---------------------------------------------------- | | NFT Marketplaces | <ul><li>Live bidding updates in auctions</li></ul> | | Gaming DApps | <ul><li>Real-time game state updates</li></ul> | | DAOs and Governance | <ul><li>Live voting updates</li></ul> | | Supply Chain | <ul><li>Product status updates</li></ul> | ### Indexed Parameters in Events When you mark an event parameter as `indexed` in Solidity, it becomes part of the event's topics rather than the data section. This enables efficient filtering but changes how you access the data. Non-Indexed String (accessible directly): ```solidity event MessageSent(string message); // Can read 'message' directly from logs ``` Indexed String (hashed for filtering): ```solidity event MessageSent(string indexed message); // 'message' is hashed, cannot read directly ``` **Why Use Indexed Parameters?** `Indexed` parameters enable efficient filtering by allowing nodes to quickly find specific events without scanning through all logs in a block. They provide gas optimization since topics are more gas-efficient for filtering operations compared to parsing event data. Additionally, nodes can index and search these parameters significantly faster, improving overall query performance when applications need to find specific events based on parameter values. **The Trade-off** For strings and bytes, indexing means: * Can filter events by this parameter efficiently * Cannot retrieve the actual value from the event log * Must query contract state to get the current value This is why, in our example, when we receive a `GreetingSet` event with indexed string parameters, we call contract.getGreeting() to retrieve the actual greeting text. > The pattern for listening to blockchain events via WebSocket follows these principles: > > The process begins by establishing a connection to the blockchain node's WebSocket endpoint, ensuring a persistent communication channel. Once connected, you create a filter that defines which events from which contracts to monitor, allowing precise event targeting. Next, you set up listener functions that register callbacks to execute when specific events occur. When events are received, your handler functions process the event data according to your application's needs. Throughout the connection lifetime, you must maintain the connection with periodic activity to prevent timeouts. Finally, when your application terminates, ensure a clean shutdown by properly closing all connections and removing event listeners. ## Code Breakdown ### Connect to Somnia WebSocket ```javascript const wsUrl = 'wss://api.infra.testnet.somnia.network/ws'; //change url for Mainnet const provider = new ethers.WebSocketProvider(wsUrl); await provider._waitUntilReady(); ``` The WebSocket URL for Somnia mainnet is <wss://api.infra.mainnet.somnia.network/ws>. This creates a persistent connection to the network. #### Create Contract Instance ```javascript const contract = new ethers.Contract(contractAddress, abi, provider); ``` You need: * `Contract address`: The deployed address on Somnia. * `ABI`: At minimum, include the events you want to listen for. * `Provider`: The WebSocket connection. ### Define Event Filter ```javascript const filter = { address: contractAddress, topics: [ethers.id("GreetingSet(string,string)")] }; ``` The filter specifies: * Which contract to monitor * Which event signature to listen for ### Set Up Event Listener ```javascript provider.on(filter, async (log) => { // Handle the event const greeting = await contract.getGreeting(); console.log(`New greeting: "${greeting}"`); }); ``` When an event is detected: 1. The callback receives the log data 2. Query the contract for the current state 3. Process/display the data as needed ### Maintain Connection ```javascript setInterval(async () => { await provider.getBlockNumber(); }, 30000); ``` This is because WebSocket connections can timeout. Send periodic requests to keep the connection alive. ### Update the ABI Include only the events and functions you need: ```javascript const abi = [ // Your event definition { "anonymous": false, "inputs": [ // Your event parameters ], "name": "YourEventName", "type": "event" }, // Any read functions you need { "inputs": [], "name": "yourReadFunction", "outputs": [/* outputs */], "stateMutability": "view", "type": "function" } ]; ``` ### Update the Event Filter Change the event signature to match your event: ```javascript const filter = { address: contractAddress, topics: [ethers.id("YourEventName(type1,type2)")] }; ``` ### Handle Event Data Process the event based on your needs: ```javascript provider.on(filter, async (log) => { // For non-indexed parameters, you can parse the log const parsedLog = contract.interface.parseLog(log); // For indexed strings, query the contract state const currentState = await contract.yourReadFunction(); // Process your data console.log('Event detected:', currentState); }); ``` ## Common Patterns ### Multiple Events Listen for multiple events from the same contract: ```javascript // Listen for Event1 provider.on({ address: contractAddress, topics: [ethers.id("Event1(...)")] }, handleEvent1); // Listen for Event2 provider.on({ address: contractAddress, topics: [ethers.id("Event2(...)")] }, handleEvent2); ``` ### Error Recovery Add reconnection logic for production applications: ```javascript async function connectWithRetry() { let retries = 0; while (retries < 5) { try { await listen(); break; } catch (error) { console.log(`Retry ${++retries}/5...`); await new Promise(r => setTimeout(r, 5000)); } } } ``` ### Event History Get recent events on startup: ```javascript // Get last 100 blocks of events const currentBlock = await provider.getBlockNumber(); const events = await contract.queryFilter('YourEventName', currentBlock - 100, currentBlock); events.forEach(event => { console.log('Historical event:', event); }); ``` ## Test Your WebSocket Connection 1. Deploy your Smart Contract to Somnia network. 2. Run the listener in one terminal: ```bash node websocket-listener.js ``` 3. Trigger events from another script or dApp 4. Observe real-time updates in your listener ### Conclusion WebSocket connections provide real-time event monitoring for smart contracts on Somnia. This guide demonstrated: 1. Connecting to Somnia's WebSocket endpoint 2. Listening for specific contract events 3. Handling indexed parameters correctly 4. Maintaining stable connections 5. Adapting the pattern for any smart contract With this foundation, you can build responsive dApps that react instantly to blockchain events without polling. <br> # Oracles Oracles bridge the gap between onchain and offchain worlds. They bring external data like prices, randomness, and real-world events directly into your smart contracts. In this section, you’ll explore: * [How to integrate **DIA price feeds** on Somnia](/developer/building-dapps/oracles/dia-price-feeds) * [How to implement **Protofire Price Feeds**](/developer/building-dapps/oracles/protofire-price-feeds) * [How to implement **Verifiable Randomness (VRF)** via **Protofire + Chainlink**](/developer/building-dapps/oracles/using-verifiable-randomness-vrf) * Use cases for oracles in DeFi, gaming, prediction markets, and more > If your dApp depends on real-world data, randomness, or secure external inputs — this is where to start. # DIA Price Feeds ## **Overview** [DIA](https://docs.diadata.org/) Oracles provide **secure, customizable, and decentralized price feeds** that can be integrated into **smart contracts on the Somnia Testnet**. This guide will walk you through how to access **on-chain price data**, understand the oracle’s functionality, and integrate it into your **Solidity Smart Contracts**. {% embed url="<https://www.youtube.com/watch?v=Fumsv0y4rNU>" %} ## **Oracle Details** ### **Contracts on Somnia** <table><thead><tr><th width="117.1484375">Network</th><th>Contract Address</th></tr></thead><tbody><tr><td>Mainnet</td><td><a href="https://explorer.somnia.network/address/0xbA0E0750A56e995506CA458b2BdD752754CF39C4">0xbA0E0750A56e995506CA458b2BdD752754CF39C4</a></td></tr><tr><td>Testnet</td><td><a href="https://shannon-explorer.somnia.network/address/0x9206296Ea3aEE3E6bdC07F7AaeF14DfCf33d865D">0x9206296Ea3aEE3E6bdC07F7AaeF14DfCf33d865D</a><br></td></tr></tbody></table> ### Gas Wallets The gas wallet is used for pushing data to your contracts. To ensure uninterrupted Oracle operation, please maintain sufficient funds in the gas wallet. You can monitor the wallets below to ensure they remain adequately funded at all times. <table data-header-hidden><thead><tr><th width="167.76171875">Network</th><th>Contract Address</th></tr></thead><tbody><tr><td>Mainnet</td><td><a href="https://explorer.somnia.network/address/0x3073d2E61ecb6E4BF4273Af83d53eDAE099ea04a">0x3073d2E61ecb6E4BF4273Af83d53eDAE099ea04a</a></td></tr><tr><td>Testnet</td><td><a href="https://shannon-explorer.somnia.network/address/0x24384e1c60547b0D5403B21eD9B6FB9457Fb573F">0x24384e1c60547b0d5403b21ed9b6fb9457fb573f</a></td></tr></tbody></table> ### **Oracle Configuration** * **Pricing Methodology:** MAIR * **Deviation Threshold:** 0.5% (Triggers price update if exceeded) * **Refresh Frequency:** Every 120 seconds * **Heartbeat:** Forced price update every 24 hours ## **Supported Asset Feeds** ### **Mainnet** <table data-header-hidden><thead><tr><th width="118.98046875"></th><th width="421.53515625"></th><th></th></tr></thead><tbody><tr><td>Asset Ticker</td><td>Adapter Address</td><td>Asset Markets Overview</td></tr><tr><td>USDT</td><td><a href="https://explorer.somnia.network/address/0x936C4F07fD4d01485849ee0EE2Cdcea2373ba267">0x936C4F07fD4d01485849ee0EE2Cdcea2373ba267</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xdAC17F958D2ee523a2206206994597C13D831ec7/">USDT markets</a></td></tr><tr><td>USDC</td><td><a href="https://explorer.somnia.network/address/0x5D4266f4DD721c1cD8367FEb23E4940d17C83C93">0x5D4266f4DD721c1cD8367FEb23E4940d17C83C93</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/">USDC markets</a></td></tr><tr><td>BTC</td><td><a href="https://explorer.somnia.network/address/0xb12e1d47b0022fA577c455E7df2Ca9943D0152bE">0xb12e1d47b0022fA577c455E7df2Ca9943D0152bE</a></td><td><a href="https://www.diadata.org/app/price/asset/Bitcoin/0x0000000000000000000000000000000000000000/">BTC markets</a></td></tr><tr><td>ARB</td><td><a href="https://explorer.somnia.network/address/0x6a96a0232402c2BC027a12C73f763b604c9F77a6">0x6a96a0232402c2BC027a12C73f763b604c9F77a6</a></td><td><a href="https://www.diadata.org/app/price/asset/Arbitrum/0x912CE59144191C1204E64559FE8253a0e49E6548/">ARB markets</a></td></tr><tr><td>SOL</td><td><a href="https://explorer.somnia.network/address/0xa4a3a8B729939E2a79dCd9079cee7d84b0d96234">0xa4a3a8B729939E2a79dCd9079cee7d84b0d96234</a></td><td><a href="https://www.diadata.org/app/price/asset/Solana/0x0000000000000000000000000000000000000000/">SOL markets</a></td></tr><tr><td>WETH</td><td><a href="https://explorer.somnia.network/address/0x4E5A9Ebc4D48d7dB65bCde4Ab9CBBE89Da2Add52">0x4E5A9Ebc4D48d7dB65bCde4Ab9CBBE89Da2Add52</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/">WETH markets</a></td></tr><tr><td>SOMI</td><td><a href="https://explorer.somnia.network/address/0x1f5f46B0DABEf8806a1f33772522ED683Ba64E27">0x1f5f46B0DABEf8806a1f33772522ED683Ba64E27</a></td><td><a href="https://www.diadata.org/app/price/asset/Somnia/0x0000000000000000000000000000000000000000/">SOMI markets</a></td></tr></tbody></table> ### **Testnet** <table data-header-hidden><thead><tr><th width="134.197265625">Asset Ticker</th><th width="407.466796875">Adapter Address</th><th>Asset Markets Overview</th></tr></thead><tbody><tr><td>USDT</td><td><a href="https://shannon-explorer.somnia.network/address/0x67d2C2a87A17b7267a6DBb1A59575C0E9A1D1c3e">0x67d2C2a87A17b7267a6DBb1A59575C0E9A1D1c3e</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xdAC17F958D2ee523a2206206994597C13D831ec7/">USDT markets</a></td></tr><tr><td>USDC</td><td><a href="https://shannon-explorer.somnia.network/address/0x235266D5ca6f19F134421C49834C108b32C2124e">0x235266D5ca6f19F134421C49834C108b32C2124e</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/">USDC markets</a></td></tr><tr><td>BTC</td><td><a href="https://shannon-explorer.somnia.network/address/0x4803db1ca3A1DA49c3DB991e1c390321c20e1f21">0x4803db1ca3A1DA49c3DB991e1c390321c20e1f21</a></td><td><a href="https://www.diadata.org/app/price/asset/Bitcoin/0x0000000000000000000000000000000000000000/">BTC markets</a></td></tr><tr><td>ARB</td><td><a href="https://shannon-explorer.somnia.network/address/0x74952812B6a9e4f826b2969C6D189c4425CBc19B">0x74952812B6a9e4f826b2969C6D189c4425CBc19B</a></td><td><a href="https://www.diadata.org/app/price/asset/Arbitrum/0x912CE59144191C1204E64559FE8253a0e49E6548/">ARB markets</a></td></tr><tr><td>SOL</td><td><a href="https://shannon-explorer.somnia.network/address/0xD5Ea6C434582F827303423dA21729bEa4F87D519">0xD5Ea6C434582F827303423dA21729bEa4F87D519</a></td><td><a href="https://www.diadata.org/app/price/asset/Solana/0x0000000000000000000000000000000000000000/">SOL markets</a></td></tr><tr><td>WETH</td><td><a href="https://shannon-explorer.somnia.network/address/0x786c7893F8c26b80d42088749562eDb50Ba9601E">0x786c7893F8c26b80d42088749562eDb50Ba9601E</a></td><td><a href="https://www.diadata.org/app/price/asset/Ethereum/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/">WETH markets</a></td></tr><tr><td>SOMI</td><td><a href="https://shannon-explorer.somnia.network/address/0xaEAa92c38939775d3be39fFA832A92611f7D6aDe">0xaEAa92c38939775d3be39fFA832A92611f7D6aDe</a></td><td><a href="https://www.diadata.org/app/price/asset/Somnia/0x0000000000000000000000000000000000000000/">SOMI markets</a></td></tr></tbody></table> ## **How the Oracle Works** DIA oracles continuously fetch and push asset prices **on-chain** using an **oracleUpdater**, which operates within the `DIAOracleV2` contract. The oracle uses **predefined update intervals** and **deviation thresholds** to determine when price updates are necessary. <figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXelAe-nl93fR4uUB8OHaecQRpe5DuDvy7k-1aMyk_8B1DHX2OzmpuZ00anBlexvcuGcg7oilXmYzTBxTDAeGwdytZmbZicu9yKYhz9rgYPh8SCbuEzia98yvw8F77FUVGWdr7vMJg?key=lW9QgbGCuFAGhXIdiB9dWgLT" alt=""><figcaption></figcaption></figure> Each asset price feed has an adapter contract, allowing access through the AggregatorV3Interface. You can use the methods `getRoundData` and `latestRoundData` to fetch pricing information. Learn more [here](https://nexus.diadata.org/how-to-guides/migrate-to-dia). ## **Using the Solidity Library** DIA has a dedicated Solidity library to facilitate the integration of DIA oracles in your own contracts. The library consists of two functions, `getPrice` and `getPriceIfNotOlderThan`. ### Access the library <a href="#access-the-library" id="access-the-library"></a> ``` import { DIAOracleLib } from "./libraries/DIAOracleLib.sol"; ``` ### `getPrice` ``` function getPrice( address oracle, string memory key ) public view returns (uint128 latestPrice, uint128 timestampOflates
프로젝트로 돌아가기